Add telemetry for restoring dependency caches

This commit is contained in:
Michael B. Gale
2025-09-23 11:25:22 +01:00
parent 665891b4f2
commit 11480e326c
5 changed files with 78 additions and 14 deletions

View File

@@ -84,20 +84,44 @@ async function makeGlobber(patterns: string[]): Promise<glob.Globber> {
return glob.create(patterns.join("\n"));
}
/** Enumerates possible outcomes for cache hits. */
export enum CacheHitResult {
/** We were unable to calculate a hash for the key. */
NoHash = "no-hash",
/** No cache was found. */
Miss = "miss",
/** The primary cache key matched. */
Exact = "exact",
/** A restore key matched. */
Partial = "partial",
}
/** Represents results of trying to restore a dependency cache for a language. */
export interface DependencyCacheRestoreStatus {
hit: CacheHitResult;
download_size_bytes?: number;
download_duration_ms?: number;
}
/** A partial mapping from languages to the results of restoring dependency caches for them. */
export type DependencyCacheRestoreStatusReport = Partial<
Record<Language, DependencyCacheRestoreStatus>
>;
/**
* Attempts to restore dependency caches for the languages being analyzed.
*
* @param languages The languages being analyzed.
* @param logger A logger to record some informational messages to.
* @param minimizeJavaJars Whether the Java extractor should rewrite downloaded JARs to minimize their size.
* @returns A list of languages for which dependency caches were restored.
* @returns A partial mapping of languages to results of restoring dependency caches for them.
*/
export async function downloadDependencyCaches(
languages: Language[],
logger: Logger,
minimizeJavaJars: boolean,
): Promise<Language[]> {
const restoredCaches: Language[] = [];
): Promise<DependencyCacheRestoreStatusReport> {
const status: DependencyCacheRestoreStatusReport = {};
for (const language of languages) {
const cacheConfig = getDefaultCacheConfig()[language];
@@ -114,6 +138,7 @@ export async function downloadDependencyCaches(
const globber = await makeGlobber(cacheConfig.hash);
if ((await globber.glob()).length === 0) {
status[language] = { hit: CacheHitResult.NoHash };
logger.info(
`Skipping download of dependency cache for ${language} as we cannot calculate a hash for the cache key.`,
);
@@ -131,21 +156,29 @@ export async function downloadDependencyCaches(
)}`,
);
const start = performance.now();
const hitKey = await actionsCache.restoreCache(
cacheConfig.paths,
primaryKey,
restoreKeys,
);
const download_duration_ms = Math.round(performance.now() - start);
const download_size_bytes = Math.round(
await getTotalCacheSize(cacheConfig.paths, logger),
);
if (hitKey !== undefined) {
logger.info(`Cache hit on key ${hitKey} for ${language}.`);
restoredCaches.push(language);
const hit =
hitKey === primaryKey ? CacheHitResult.Exact : CacheHitResult.Partial;
status[language] = { hit, download_duration_ms, download_size_bytes };
} else {
status[language] = { hit: CacheHitResult.Miss };
logger.info(`No suitable cache found for ${language}.`);
}
}
return restoredCaches;
return status;
}
/**

View File

@@ -23,7 +23,10 @@ import {
} from "./caching-utils";
import { CodeQL } from "./codeql";
import * as configUtils from "./config-utils";
import { downloadDependencyCaches } from "./dependency-caching";
import {
DependencyCacheRestoreStatusReport,
downloadDependencyCaches,
} from "./dependency-caching";
import {
addDiagnostic,
flushDiagnostics,
@@ -102,6 +105,7 @@ async function sendCompletedStatusReport(
toolsSource: ToolsSource,
toolsVersion: string,
overlayBaseDatabaseStats: OverlayBaseDatabaseDownloadStats | undefined,
dependencyCachingResults: DependencyCacheRestoreStatusReport | undefined,
logger: Logger,
error?: Error,
) {
@@ -151,6 +155,7 @@ async function sendCompletedStatusReport(
await getTotalCacheSize(Object.values(config.trapCaches), logger),
),
overlayBaseDatabaseStats,
dependencyCachingResults,
);
await sendStatusReport({
...initWithConfigStatusReport,
@@ -351,6 +356,7 @@ async function run() {
}
let overlayBaseDatabaseStats: OverlayBaseDatabaseDownloadStats | undefined;
let dependencyCachingResults: DependencyCacheRestoreStatusReport | undefined;
try {
if (
config.overlayDatabaseMode === OverlayDatabaseMode.Overlay &&
@@ -562,7 +568,7 @@ async function run() {
codeql,
);
if (shouldRestoreCache(config.dependencyCachingEnabled)) {
await downloadDependencyCaches(
dependencyCachingResults = await downloadDependencyCaches(
config.languages,
logger,
minimizeJavaJars,
@@ -714,6 +720,7 @@ async function run() {
toolsSource,
toolsVersion,
overlayBaseDatabaseStats,
dependencyCachingResults,
logger,
error,
);
@@ -736,6 +743,7 @@ async function run() {
toolsSource,
toolsVersion,
overlayBaseDatabaseStats,
dependencyCachingResults,
logger,
);
}

View File

@@ -286,6 +286,7 @@ const testCreateInitWithConfigStatusReport = test.macro({
undefined,
1024,
undefined,
undefined,
);
if (t.truthy(initWithConfigStatusReport)) {

View File

@@ -13,6 +13,7 @@ import {
} from "./actions-util";
import { getAnalysisKey, getApiClient } from "./api-client";
import { parseRegistriesWithoutCredentials, type Config } from "./config-utils";
import { DependencyCacheRestoreStatusReport } from "./dependency-caching";
import { DocUrl } from "./doc-url";
import { EnvVar } from "./environment";
import { getRef } from "./git-utils";
@@ -497,6 +498,8 @@ export interface InitWithConfigStatusReport extends InitStatusReport {
overlay_base_database_download_size_bytes?: number;
/** Time taken to download the overlay-base database, in milliseconds. */
overlay_base_database_download_duration_ms?: number;
/** Stringified JSON object representing information about the results of restoring dependency caches. */
dependency_caching_restore_results: string;
/** Stringified JSON array of registry configuration objects, from the 'registries' config field
or workflow input. **/
registries: string;
@@ -522,6 +525,7 @@ export async function createInitWithConfigStatusReport(
configFile: string | undefined,
totalCacheSize: number,
overlayBaseDatabaseStats: OverlayBaseDatabaseDownloadStats | undefined,
dependencyCachingResults: DependencyCacheRestoreStatusReport | undefined,
): Promise<InitWithConfigStatusReport> {
const languages = config.languages.join(",");
const paths = (config.originalUserInput.paths || []).join(",");
@@ -570,6 +574,9 @@ export async function createInitWithConfigStatusReport(
overlayBaseDatabaseStats?.databaseSizeBytes,
overlay_base_database_download_duration_ms:
overlayBaseDatabaseStats?.databaseDownloadDurationMs,
dependency_caching_restore_results: JSON.stringify(
dependencyCachingResults ?? {},
),
query_filters: JSON.stringify(
config.originalUserInput["query-filters"] ?? [],
),