mirror of
https://github.com/github/codeql-action.git
synced 2026-10-29 09:07:12 +00:00
Front-load caching and rear-load saving to disk
This implementation saves CPU and I/O by not trying to write to the file on every set-cache.
This commit is contained in:
13
lib/entry-points.js
generated
13
lib/entry-points.js
generated
@@ -149934,9 +149934,12 @@ function readCommandCacheFile() {
|
||||
}
|
||||
return {};
|
||||
}
|
||||
function writeCommandCacheFile(data) {
|
||||
function writeCommandCacheFile() {
|
||||
try {
|
||||
fs6.writeFileSync(getCommandCacheFilePath(), JSON.stringify(data));
|
||||
fs6.writeFileSync(
|
||||
getCommandCacheFilePath(),
|
||||
JSON.stringify(Object.fromEntries(inMemoryCache))
|
||||
);
|
||||
} catch (e) {
|
||||
logger.warning(`Failed to write command cache file: ${e}`);
|
||||
}
|
||||
@@ -149944,9 +149947,6 @@ function writeCommandCacheFile(data) {
|
||||
function cacheCommandOutput(key, cmd, output) {
|
||||
const entry = { cmd, output };
|
||||
inMemoryCache.set(key, entry);
|
||||
const data = readCommandCacheFile();
|
||||
data[key] = entry;
|
||||
writeCommandCacheFile(data);
|
||||
}
|
||||
function getCachedCommandOutput(key, cmd, validate) {
|
||||
const memoized = inMemoryCache.get(key);
|
||||
@@ -159013,6 +159013,8 @@ async function run3(startedAt) {
|
||||
toolsVersion = initCodeQLResult.toolsVersion;
|
||||
toolsSource = initCodeQLResult.toolsSource;
|
||||
zstdAvailability = initCodeQLResult.zstdAvailability;
|
||||
await codeql.getVersion();
|
||||
await codeql.resolveLanguages();
|
||||
await checkWorkflow(logger2, codeql);
|
||||
if (
|
||||
// Only enable the experimental features env variable for Rust analysis if the user has explicitly
|
||||
@@ -159322,6 +159324,7 @@ exec ${goBinaryPath} "$@"`
|
||||
await saveConfig(config, logger2);
|
||||
core20.setOutput("codeql-path", config.codeQLCmd);
|
||||
core20.setOutput("codeql-version", (await codeql.getVersion()).version);
|
||||
writeCommandCacheFile();
|
||||
} catch (unwrappedError) {
|
||||
const error3 = wrapError(unwrappedError);
|
||||
core20.setFailed(error3.message);
|
||||
|
||||
@@ -170,32 +170,19 @@ test.serial(
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"cacheCommandOutput persists the output to both the memo and the file",
|
||||
async (t) => {
|
||||
await withCacheDir((cacheFilePath) => {
|
||||
cacheCommandOutput(CommandCacheKey.Version, "/path/to/codeql", {
|
||||
hello: "world",
|
||||
});
|
||||
|
||||
// Tier 2: the temporary file contains the entry.
|
||||
const onDisk = JSON.parse(
|
||||
fs.readFileSync(cacheFilePath, "utf8"),
|
||||
) as Record<string, unknown>;
|
||||
t.deepEqual(onDisk[CommandCacheKey.Version], {
|
||||
cmd: "/path/to/codeql",
|
||||
output: { hello: "world" },
|
||||
});
|
||||
|
||||
// Tier 1: the value is served from the memo even after the file is gone.
|
||||
fs.rmSync(cacheFilePath);
|
||||
t.deepEqual(
|
||||
getCachedCommandOutput(CommandCacheKey.Version, "/path/to/codeql"),
|
||||
{ hello: "world" },
|
||||
);
|
||||
test.serial("cacheCommandOutput persists the output to the memo", async (t) => {
|
||||
await withCacheDir(() => {
|
||||
cacheCommandOutput(CommandCacheKey.Version, "/path/to/codeql", {
|
||||
hello: "world",
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Tier 1: the value is immediately available from the memo.
|
||||
t.deepEqual(
|
||||
getCachedCommandOutput(CommandCacheKey.Version, "/path/to/codeql"),
|
||||
{ hello: "world" },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test.serial(
|
||||
"getCachedCommandOutput prefers the in-memory memo over the file",
|
||||
@@ -207,7 +194,10 @@ test.serial(
|
||||
|
||||
// Overwrite the file with a different value; the memo (tier 1) should win.
|
||||
writeCacheFile(cacheFilePath, {
|
||||
[CommandCacheKey.Version]: { cmd: "/path/to/codeql", output: { value: 2 } },
|
||||
[CommandCacheKey.Version]: {
|
||||
cmd: "/path/to/codeql",
|
||||
output: { value: 2 },
|
||||
},
|
||||
});
|
||||
t.deepEqual(
|
||||
getCachedCommandOutput(CommandCacheKey.Version, "/path/to/codeql"),
|
||||
|
||||
16
src/cache.ts
16
src/cache.ts
@@ -65,20 +65,22 @@ function readCommandCacheFile(): Record<string, CommandCacheEntry> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the cache to the temporary file. Best-effort: a failure to write
|
||||
* Persists the in-memory cache to the temporary file. Best-effort: a failure to write
|
||||
* just means a later step re-runs the CLI.
|
||||
*/
|
||||
function writeCommandCacheFile(data: Record<string, CommandCacheEntry>): void {
|
||||
export function writeCommandCacheFile(): void {
|
||||
try {
|
||||
fs.writeFileSync(getCommandCacheFilePath(), JSON.stringify(data));
|
||||
fs.writeFileSync(
|
||||
getCommandCacheFilePath(),
|
||||
JSON.stringify(Object.fromEntries(inMemoryCache)),
|
||||
);
|
||||
} catch (e) {
|
||||
logger.warning(`Failed to write command cache file: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the output of a command under `key`, writing it to both the in-memory
|
||||
* memo (tier 1) and the temporary file (tier 2).
|
||||
* Stores the output of a CLI command under `key` in a module-global object.
|
||||
*/
|
||||
export function cacheCommandOutput(
|
||||
key: CommandCacheKey,
|
||||
@@ -87,10 +89,6 @@ export function cacheCommandOutput(
|
||||
): void {
|
||||
const entry: CommandCacheEntry = { cmd, output };
|
||||
inMemoryCache.set(key, entry);
|
||||
|
||||
const data = readCommandCacheFile();
|
||||
data[key] = entry;
|
||||
writeCommandCacheFile(data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
} from "./actions-util";
|
||||
import { AnalysisKind, getAnalysisKinds } from "./analyses";
|
||||
import { getGitHubVersion, GitHubApiCombinedDetails } from "./api-client";
|
||||
import { writeCommandCacheFile } from "./cache";
|
||||
import {
|
||||
getDependencyCachingEnabled,
|
||||
getTotalCacheSize,
|
||||
@@ -323,6 +324,11 @@ async function run(startedAt: Date) {
|
||||
toolsSource = initCodeQLResult.toolsSource;
|
||||
zstdAvailability = initCodeQLResult.zstdAvailability;
|
||||
|
||||
// Populate the in-memory command cache with CLI version and languages.
|
||||
// These results will be persisted to disk at the end of the init action.
|
||||
await codeql.getVersion();
|
||||
await codeql.resolveLanguages();
|
||||
|
||||
// Check the workflow for problems. If there are any problems, they are reported
|
||||
// to the workflow log. No exceptions are thrown.
|
||||
await checkWorkflow(logger, codeql);
|
||||
@@ -766,6 +772,9 @@ async function run(startedAt: Date) {
|
||||
|
||||
core.setOutput("codeql-path", config.codeQLCmd);
|
||||
core.setOutput("codeql-version", (await codeql.getVersion()).version);
|
||||
|
||||
// Persist the command cache to disk at the end of a successful init.
|
||||
writeCommandCacheFile();
|
||||
} catch (unwrappedError) {
|
||||
const error = wrapError(unwrappedError);
|
||||
core.setFailed(error.message);
|
||||
|
||||
Reference in New Issue
Block a user