Add telemetry for TRAP caching

This commit is contained in:
Edoardo Pirovano 2022-08-15 14:44:43 +01:00
parent ca10a6d552
commit 4139682b64
No known key found for this signature in database
GPG key ID: 047556B5D93FFE28
63 changed files with 1195 additions and 126 deletions

View file

@ -30,6 +30,7 @@ test("emptyPaths", async (t) => {
queriesInputCombines: false,
},
trapCaches: {},
trapCacheDownloadTime: 0,
};
analysisPaths.includeAndExcludeAnalysisPaths(config);
t.is(process.env["LGTM_INDEX_INCLUDE"], undefined);
@ -60,6 +61,7 @@ test("nonEmptyPaths", async (t) => {
queriesInputCombines: false,
},
trapCaches: {},
trapCacheDownloadTime: 0,
};
analysisPaths.includeAndExcludeAnalysisPaths(config);
t.is(process.env["LGTM_INDEX_INCLUDE"], "path1\npath2");
@ -93,6 +95,7 @@ test("exclude temp dir", async (t) => {
queriesInputCombines: false,
},
trapCaches: {},
trapCacheDownloadTime: 0,
};
analysisPaths.includeAndExcludeAnalysisPaths(config);
t.is(process.env["LGTM_INDEX_INCLUDE"], undefined);

View file

@ -15,7 +15,7 @@ import { uploadDatabases } from "./database-upload";
import { GitHubFeatureFlags } from "./feature-flags";
import { getActionsLogger } from "./logging";
import { parseRepositoryNwo } from "./repository";
import { uploadTrapCaches } from "./trap-caching";
import { getTotalCacheSize, uploadTrapCaches } from "./trap-caching";
import * as upload_lib from "./upload-lib";
import { UploadResult } from "./upload-lib";
import * as util from "./util";
@ -29,13 +29,18 @@ interface AnalysisStatusReport
interface FinishStatusReport
extends actionsUtil.StatusReportBase,
AnalysisStatusReport {}
AnalysisStatusReport {
trap_cache_upload_size_bytes: number;
trap_cache_upload_duration_ms: number;
}
export async function sendStatusReport(
startedAt: Date,
config: Config | undefined,
stats: AnalysisStatusReport | undefined,
error?: Error
error?: Error,
trapCacheUploadTime?: number,
didUploadTrapCaches?: boolean
) {
const status = actionsUtil.getActionsStatus(
error,
@ -57,6 +62,11 @@ export async function sendStatusReport(
}
: {}),
...(stats || {}),
trap_cache_upload_duration_ms: trapCacheUploadTime || 0,
trap_cache_upload_size_bytes:
config && didUploadTrapCaches
? await getTotalCacheSize(config.trapCaches)
: 0,
};
await actionsUtil.sendStatusReport(statusReport);
}
@ -66,6 +76,8 @@ async function run() {
let uploadResult: UploadResult | undefined = undefined;
let runStats: QueriesStatusReport | undefined = undefined;
let config: Config | undefined = undefined;
let trapCacheUploadTime: number | undefined = undefined;
let didUploadTrapCaches = false;
util.initializeEnvironment(util.Mode.actions, pkg.version);
await util.checkActionVersion(pkg.version);
@ -163,8 +175,10 @@ async function run() {
await uploadDatabases(repositoryNwo, config, apiDetails, logger);
// Possibly upload the TRAP caches for later re-use
const trapCacheUploadStartTime = Date.now();
const codeql = await getCodeQL(config.codeQLCmd);
await uploadTrapCaches(codeql, config, logger);
trapCacheUploadTime = Date.now() - trapCacheUploadStartTime;
didUploadTrapCaches = await uploadTrapCaches(codeql, config, logger);
// We don't upload results in test mode, so don't wait for processing
if (util.isInTestMode()) {
@ -188,23 +202,58 @@ async function run() {
if (error instanceof CodeQLAnalysisError) {
const stats = { ...error.queriesStatusReport };
await sendStatusReport(startedAt, config, stats, error);
await sendStatusReport(
startedAt,
config,
stats,
error,
trapCacheUploadTime,
didUploadTrapCaches
);
} else {
await sendStatusReport(startedAt, config, undefined, error);
await sendStatusReport(
startedAt,
config,
undefined,
error,
trapCacheUploadTime,
didUploadTrapCaches
);
}
return;
}
if (runStats && uploadResult) {
await sendStatusReport(startedAt, config, {
...runStats,
...uploadResult.statusReport,
});
await sendStatusReport(
startedAt,
config,
{
...runStats,
...uploadResult.statusReport,
},
undefined,
trapCacheUploadTime,
didUploadTrapCaches
);
} else if (runStats) {
await sendStatusReport(startedAt, config, { ...runStats });
await sendStatusReport(
startedAt,
config,
{ ...runStats },
undefined,
trapCacheUploadTime,
didUploadTrapCaches
);
} else {
await sendStatusReport(startedAt, config, undefined);
await sendStatusReport(
startedAt,
config,
undefined,
undefined,
trapCacheUploadTime,
didUploadTrapCaches
);
}
}

View file

@ -124,6 +124,7 @@ test("status report fields and search path setting", async (t) => {
queriesInputCombines: false,
},
trapCaches: {},
trapCacheDownloadTime: 0,
};
fs.mkdirSync(util.getCodeQLDatabasePath(config, language), {
recursive: true,
@ -442,6 +443,7 @@ const stubConfig: Config = {
queriesInputCombines: false,
},
trapCaches: {},
trapCacheDownloadTime: 0,
};
for (const options of [

View file

@ -60,6 +60,7 @@ test.beforeEach(() => {
queriesInputCombines: false,
},
trapCaches: {},
trapCacheDownloadTime: 0,
};
});

View file

@ -360,6 +360,7 @@ test("load non-empty input", async (t) => {
debugDatabaseName: "my-db",
augmentationProperties: configUtils.defaultAugmentationProperties,
trapCaches: {},
trapCacheDownloadTime: 0,
};
const languages = "javascript";

View file

@ -183,6 +183,11 @@ export interface Config {
* If a key is omitted, then TRAP caching should not be used for that language.
*/
trapCaches: Partial<Record<Language, string>>;
/**
* Time taken to download TRAP caches. Used for status reporting.
*/
trapCacheDownloadTime: number;
}
/**
@ -1015,6 +1020,13 @@ export async function getDefaultConfig(
);
}
const { trapCaches, trapCacheDownloadTime } = await downloadCacheWithTime(
trapCachingEnabled,
codeQL,
languages,
logger
);
return {
languages,
queries,
@ -1030,12 +1042,27 @@ export async function getDefaultConfig(
debugArtifactName,
debugDatabaseName,
augmentationProperties,
trapCaches: trapCachingEnabled
? await downloadTrapCaches(codeQL, languages, logger)
: {},
trapCaches,
trapCacheDownloadTime,
};
}
async function downloadCacheWithTime(
trapCachingEnabled: boolean,
codeQL: CodeQL,
languages: Language[],
logger: Logger
) {
let trapCaches = {};
let trapCacheDownloadTime = 0;
if (trapCachingEnabled) {
const start = Date.now();
trapCaches = await downloadTrapCaches(codeQL, languages, logger);
trapCacheDownloadTime = Date.now() - start;
}
return { trapCaches, trapCacheDownloadTime };
}
/**
* Load the config from the given file.
*/
@ -1204,6 +1231,13 @@ async function loadConfig(
}
}
const { trapCaches, trapCacheDownloadTime } = await downloadCacheWithTime(
trapCachingEnabled,
codeQL,
languages,
logger
);
return {
languages,
queries,
@ -1219,9 +1253,8 @@ async function loadConfig(
debugArtifactName,
debugDatabaseName,
augmentationProperties,
trapCaches: trapCachingEnabled
? await downloadTrapCaches(codeQL, languages, logger)
: {},
trapCaches,
trapCacheDownloadTime,
};
}

View file

@ -58,6 +58,7 @@ function getTestConfig(tmpDir: string): Config {
debugDatabaseName: DEFAULT_DEBUG_DATABASE_NAME,
augmentationProperties: defaultAugmentationProperties,
trapCaches: {},
trapCacheDownloadTime: 0,
};
}

View file

@ -26,6 +26,7 @@ import {
import { Language } from "./languages";
import { getActionsLogger } from "./logging";
import { parseRepositoryNwo } from "./repository";
import { getTotalCacheSize } from "./trap-caching";
import {
checkActionVersion,
checkGitHubVersionInRange,
@ -65,6 +66,12 @@ interface InitSuccessStatusReport extends StatusReportBase {
tools_resolved_version: string;
/** Comma-separated list of languages specified explicitly in the workflow file. */
workflow_languages: string;
/** Comma-separated list of languages for which we are using TRAP caching. */
trap_cache_languages: string;
/** Size of TRAP caches that we downloaded, in megabytes. */
trap_cache_download_size_bytes: number;
/** Time taken to download TRAP caches, in milliseconds. */
trap_cache_download_duration_ms: number;
}
async function sendSuccessStatusReport(
@ -115,6 +122,9 @@ async function sendSuccessStatusReport(
tools_input: getOptionalInput("tools") || "",
tools_resolved_version: toolsVersion,
workflow_languages: workflowLanguages || "",
trap_cache_languages: Object.keys(config.trapCaches).join(","),
trap_cache_download_size_bytes: await getTotalCacheSize(config.trapCaches),
trap_cache_download_duration_ms: config.trapCacheDownloadTime,
};
await sendStatusReport(statusReport);

View file

@ -33,6 +33,7 @@ function getTestConfig(tmpDir: string): configUtils.Config {
debugDatabaseName: util.DEFAULT_DEBUG_DATABASE_NAME,
augmentationProperties: configUtils.defaultAugmentationProperties,
trapCaches: {},
trapCacheDownloadTime: 0,
};
}

View file

@ -87,6 +87,7 @@ const testConfigWithoutTmpDir: Config = {
trapCaches: {
javascript: "/some/cache/dir",
},
trapCacheDownloadTime: 0,
};
function getTestConfigWithTempDir(tmpDir: string): configUtils.Config {
@ -113,6 +114,7 @@ function getTestConfigWithTempDir(tmpDir: string): configUtils.Config {
javascript: path.resolve(tmpDir, "jsCache"),
ruby: path.resolve(tmpDir, "rubyCache"),
},
trapCacheDownloadTime: 0,
};
}

View file

@ -2,6 +2,7 @@ import * as fs from "fs";
import * as path from "path";
import * as cache from "@actions/cache";
import getFolderSize from "get-folder-size";
import * as actionsUtil from "./actions-util";
import { CodeQL, CODEQL_VERSION_BETTER_RESOLVE_LANGUAGES } from "./codeql";
@ -118,12 +119,19 @@ export async function downloadTrapCaches(
return result;
}
/**
* Possibly upload TRAP caches to the Actions cache.
* @param codeql The CodeQL instance to use.
* @param config The configuration for this workflow.
* @param logger A logger to record some informational messages to.
* @returns Whether the TRAP caches were uploaded.
*/
export async function uploadTrapCaches(
codeql: CodeQL,
config: Config,
logger: Logger
): Promise<void> {
if (!(await actionsUtil.isAnalyzingDefaultBranch())) return; // Only upload caches from the default branch
): Promise<boolean> {
if (!(await actionsUtil.isAnalyzingDefaultBranch())) return false; // Only upload caches from the default branch
const toAwait: Array<Promise<number>> = [];
for (const language of config.languages) {
@ -138,6 +146,7 @@ export async function uploadTrapCaches(
toAwait.push(cache.saveCache([cacheDir], key));
}
await Promise.all(toAwait);
return true;
}
export async function getLanguagesSupportingCaching(
@ -175,6 +184,23 @@ export async function getLanguagesSupportingCaching(
return result;
}
export async function getTotalCacheSize(
trapCaches: Partial<Record<Language, string>>
): Promise<number> {
const sizes = await Promise.all(
Object.values(trapCaches).map(async (cacheDir) => {
return new Promise<number>((resolve) => {
getFolderSize(cacheDir, (err, size) => {
// Ignore file system errors when getting the size. It's only used for telemetry anyway.
if (err) resolve(0);
resolve(size);
});
});
})
);
return sizes.reduce((a, b) => a + b, 0);
}
async function cacheKey(
codeql: CodeQL,
language: Language,

View file

@ -357,6 +357,7 @@ for (const [packs, expectedStatus] of ML_POWERED_JS_STATUS_TESTS) {
queriesInputCombines: false,
},
trapCaches: {},
trapCacheDownloadTime: 0,
};
t.is(util.getMlPoweredJsQueriesStatus(config), expectedStatus);