Skip to content

Commit

Permalink
Chunked Cache Upload APIs (#128)
Browse files Browse the repository at this point in the history
* Initial pass at chunked upload apis

* Fix cacheEntry type

* Linting

* Fix download cache entry tests

* Linting tests

* Pull in fixes from testing branch

* Fix typo in ReserveCacheResponse

* Add test convering reserve cache failure

* Add retries to upload chunk

* PR feedback

* Format default chunk size

* Remove responses array
  • Loading branch information
Josh Gross authored and GitHub committed Jan 6, 2020
1 parent a631fad commit b45d91c
Show file tree
Hide file tree
Showing 8 changed files with 321 additions and 58 deletions.
15 changes: 12 additions & 3 deletions __tests__/restore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,10 @@ test("restore with cache found", async () => {
expect(getCacheMock).toHaveBeenCalledWith([key]);
expect(setCacheStateMock).toHaveBeenCalledWith(cacheEntry);
expect(createTempDirectoryMock).toHaveBeenCalledTimes(1);
expect(downloadCacheMock).toHaveBeenCalledWith(cacheEntry, archivePath);
expect(downloadCacheMock).toHaveBeenCalledWith(
cacheEntry.archiveLocation,
archivePath
);
expect(getArchiveFileSizeMock).toHaveBeenCalledWith(archivePath);

expect(extractTarMock).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -312,7 +315,10 @@ test("restore with a pull request event and cache found", async () => {
expect(getCacheMock).toHaveBeenCalledWith([key]);
expect(setCacheStateMock).toHaveBeenCalledWith(cacheEntry);
expect(createTempDirectoryMock).toHaveBeenCalledTimes(1);
expect(downloadCacheMock).toHaveBeenCalledWith(cacheEntry, archivePath);
expect(downloadCacheMock).toHaveBeenCalledWith(
cacheEntry.archiveLocation,
archivePath
);
expect(getArchiveFileSizeMock).toHaveBeenCalledWith(archivePath);
expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~60 MB (62915000 B)`);

Expand Down Expand Up @@ -377,7 +383,10 @@ test("restore with cache found for restore key", async () => {
expect(getCacheMock).toHaveBeenCalledWith([key, restoreKey]);
expect(setCacheStateMock).toHaveBeenCalledWith(cacheEntry);
expect(createTempDirectoryMock).toHaveBeenCalledTimes(1);
expect(downloadCacheMock).toHaveBeenCalledWith(cacheEntry, archivePath);
expect(downloadCacheMock).toHaveBeenCalledWith(
cacheEntry.archiveLocation,
archivePath
);
expect(getArchiveFileSizeMock).toHaveBeenCalledWith(archivePath);
expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~0 MB (142 B)`);

Expand Down
80 changes: 76 additions & 4 deletions __tests__/save.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ test("save with large cache outputs warning", async () => {

const createTarMock = jest.spyOn(tar, "createTar");

const cacheSize = 1024 * 1024 * 1024; //~1GB, over the 400MB limit
const cacheSize = 4 * 1024 * 1024 * 1024; //~4GB, over the 2GB limit
jest.spyOn(actionUtils, "getArchiveFileSize").mockImplementationOnce(() => {
return cacheSize;
});
Expand All @@ -208,12 +208,63 @@ test("save with large cache outputs warning", async () => {

expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(logWarningMock).toHaveBeenCalledWith(
"Cache size of ~1024 MB (1073741824 B) is over the 400MB limit, not saving cache."
"Cache size of ~4096 MB (4294967296 B) is over the 2GB limit, not saving cache."
);

expect(failedMock).toHaveBeenCalledTimes(0);
});

test("save with reserve cache failure outputs warning", async () => {
const infoMock = jest.spyOn(core, "info");
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");

const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const cacheEntry: ArtifactCacheEntry = {
cacheKey: "Linux-node-",
scope: "refs/heads/master",
creationTime: "2019-11-13T19:18:02+00:00",
archiveLocation: "www.actionscache.test/download"
};

jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return JSON.stringify(cacheEntry);
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});

const inputPath = "node_modules";
testUtils.setInput(Inputs.Path, inputPath);

const reserveCacheMock = jest
.spyOn(cacheHttpClient, "reserveCache")
.mockImplementationOnce(() => {
return Promise.resolve(-1);
});

const createTarMock = jest.spyOn(tar, "createTar");

const saveCacheMock = jest.spyOn(cacheHttpClient, "saveCache");

await run();

expect(reserveCacheMock).toHaveBeenCalledTimes(1);
expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey);

expect(infoMock).toHaveBeenCalledWith(
`Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.`
);

expect(createTarMock).toHaveBeenCalledTimes(0);
expect(saveCacheMock).toHaveBeenCalledTimes(0);
expect(logWarningMock).toHaveBeenCalledTimes(0);
expect(failedMock).toHaveBeenCalledTimes(0);
});

test("save with server error outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
Expand All @@ -240,6 +291,13 @@ test("save with server error outputs warning", async () => {
const cachePath = path.resolve(inputPath);
testUtils.setInput(Inputs.Path, inputPath);

const cacheId = 4;
const reserveCacheMock = jest
.spyOn(cacheHttpClient, "reserveCache")
.mockImplementationOnce(() => {
return Promise.resolve(cacheId);
});

const createTarMock = jest.spyOn(tar, "createTar");

const saveCacheMock = jest
Expand All @@ -250,13 +308,16 @@ test("save with server error outputs warning", async () => {

await run();

expect(reserveCacheMock).toHaveBeenCalledTimes(1);
expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey);

const archivePath = path.join("/foo/bar", "cache.tgz");

expect(createTarMock).toHaveBeenCalledTimes(1);
expect(createTarMock).toHaveBeenCalledWith(archivePath, cachePath);

expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith(primaryKey, archivePath);
expect(saveCacheMock).toHaveBeenCalledWith(cacheId, archivePath);

expect(logWarningMock).toHaveBeenCalledTimes(1);
expect(logWarningMock).toHaveBeenCalledWith("HTTP Error Occurred");
Expand Down Expand Up @@ -289,18 +350,29 @@ test("save with valid inputs uploads a cache", async () => {
const cachePath = path.resolve(inputPath);
testUtils.setInput(Inputs.Path, inputPath);

const cacheId = 4;
const reserveCacheMock = jest
.spyOn(cacheHttpClient, "reserveCache")
.mockImplementationOnce(() => {
return Promise.resolve(cacheId);
});

const createTarMock = jest.spyOn(tar, "createTar");

const saveCacheMock = jest.spyOn(cacheHttpClient, "saveCache");

await run();

expect(reserveCacheMock).toHaveBeenCalledTimes(1);
expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey);

const archivePath = path.join("/foo/bar", "cache.tgz");

expect(createTarMock).toHaveBeenCalledTimes(1);
expect(createTarMock).toHaveBeenCalledWith(archivePath, cachePath);

expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith(primaryKey, archivePath);
expect(saveCacheMock).toHaveBeenCalledWith(cacheId, archivePath);

expect(failedMock).toHaveBeenCalledTimes(0);
});
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cache",
"version": "1.0.3",
"version": "1.1.0",
"private": true,
"description": "Cache dependencies and build outputs",
"main": "dist/restore/index.js",
Expand Down
Loading

0 comments on commit b45d91c

Please sign in to comment.