Report sizes of cache entries

Using the patched version of @actions/cache, we now report the total
size of cache entries restored/saved, as well as details of each one.
This commit is contained in:
Daz DeBoer 2021-10-30 12:17:41 -06:00
parent 3ba05ede1f
commit 472ac8a356
No known key found for this signature in database
GPG key ID: DD6B9F0B06683D5D
4 changed files with 63 additions and 26 deletions

View file

@ -5,7 +5,7 @@ describe('caching report', () => {
it('with one requested entry report', async () => { it('with one requested entry report', async () => {
const report = new CacheListener() const report = new CacheListener()
report.entry('foo').markRequested('1', ['2']) report.entry('foo').markRequested('1', ['2'])
report.entry('bar').markRequested('3').markRestored('4') report.entry('bar').markRequested('3').markRestored('4', 500)
expect(report.fullyRestored).toBe(false) expect(report.fullyRestored).toBe(false)
}) })
}) })
@ -22,13 +22,13 @@ describe('caching report', () => {
}) })
it('with restored entry report', async () => { it('with restored entry report', async () => {
const report = new CacheListener() const report = new CacheListener()
report.entry('bar').markRequested('3').markRestored('4') report.entry('bar').markRequested('3').markRestored('4', 300)
expect(report.fullyRestored).toBe(true) expect(report.fullyRestored).toBe(true)
}) })
it('with multiple restored entry reportss', async () => { it('with multiple restored entry reportss', async () => {
const report = new CacheListener() const report = new CacheListener()
report.entry('foo').markRestored('4') report.entry('foo').markRestored('4', 3300)
report.entry('bar').markRequested('3').markRestored('4') report.entry('bar').markRequested('3').markRestored('4', 333)
expect(report.fullyRestored).toBe(true) expect(report.fullyRestored).toBe(true)
}) })
}) })
@ -64,7 +64,7 @@ describe('caching report', () => {
const report = new CacheListener() const report = new CacheListener()
const entryReport = report.entry('foo') const entryReport = report.entry('foo')
entryReport.markRequested('1', ['2', '3']) entryReport.markRequested('1', ['2', '3'])
entryReport.markSaved('4') entryReport.markSaved('4', 100)
const stringRep = report.stringify() const stringRep = report.stringify()
const reportClone: CacheListener = CacheListener.rehydrate(stringRep) const reportClone: CacheListener = CacheListener.rehydrate(stringRep)
@ -85,7 +85,7 @@ describe('caching report', () => {
// Check type and call method on rehydrated entry report // Check type and call method on rehydrated entry report
expect(entryClone).toBeInstanceOf(CacheEntryListener) expect(entryClone).toBeInstanceOf(CacheEntryListener)
entryClone.markSaved('4') entryClone.markSaved('4', 100)
expect(entryClone.requestedKey).toBe('1') expect(entryClone.requestedKey).toBe('1')
expect(entryClone.requestedRestoreKeys).toEqual(['2', '3']) expect(entryClone.requestedRestoreKeys).toEqual(['2', '3'])

View file

@ -98,13 +98,15 @@ export class CacheEntryListener {
return this return this
} }
markRestored(key: string): CacheEntryListener { markRestored(key: string, size: number | undefined): CacheEntryListener {
this.restoredKey = key this.restoredKey = key
this.restoredSize = size
return this return this
} }
markSaved(key: string): CacheEntryListener { markSaved(key: string, size: number | undefined): CacheEntryListener {
this.savedKey = key this.savedKey = key
this.savedSize = size
return this return this
} }
} }
@ -149,7 +151,7 @@ export abstract class AbstractCache {
} }
core.saveState(this.cacheResultStateKey, cacheResult) core.saveState(this.cacheResultStateKey, cacheResult)
entryReport.markRestored(cacheResult) entryReport.markRestored(cacheResult.key, cacheResult.size)
core.info(`Restored ${this.cacheDescription} from cache key: ${cacheResult}`) core.info(`Restored ${this.cacheDescription} from cache key: ${cacheResult}`)
try { try {
@ -170,7 +172,7 @@ export abstract class AbstractCache {
cachePath: string[], cachePath: string[],
cacheKey: string, cacheKey: string,
cacheRestoreKeys: string[] = [] cacheRestoreKeys: string[] = []
): Promise<string | undefined> { ): Promise<cache.CacheEntry | undefined> {
try { try {
return await cache.restoreCache(cachePath, cacheKey, cacheRestoreKeys) return await cache.restoreCache(cachePath, cacheKey, cacheRestoreKeys)
} catch (error) { } catch (error) {
@ -214,18 +216,20 @@ export abstract class AbstractCache {
core.info(`Caching ${this.cacheDescription} with cache key: ${cacheKey}`) core.info(`Caching ${this.cacheDescription} with cache key: ${cacheKey}`)
const cachePath = this.getCachePath() const cachePath = this.getCachePath()
await this.saveCache(cachePath, cacheKey) const savedEntry = await this.saveCache(cachePath, cacheKey)
listener.entry(this.cacheDescription).markSaved(cacheKey) if (savedEntry) {
listener.entry(this.cacheDescription).markSaved(savedEntry.key, savedEntry.size)
}
return return
} }
protected async beforeSave(_listener: CacheListener): Promise<void> {} protected async beforeSave(_listener: CacheListener): Promise<void> {}
protected async saveCache(cachePath: string[], cacheKey: string): Promise<void> { protected async saveCache(cachePath: string[], cacheKey: string): Promise<cache.CacheEntry | undefined> {
try { try {
await cache.saveCache(cachePath, cacheKey) return await cache.saveCache(cachePath, cacheKey)
} catch (error) { } catch (error) {
if (error instanceof cache.ValidationError) { if (error instanceof cache.ValidationError) {
// Validation errors should fail the build action // Validation errors should fail the build action
@ -238,6 +242,7 @@ export abstract class AbstractCache {
core.warning(String(error)) core.warning(String(error))
} }
} }
return undefined
} }
protected debug(message: string): void { protected debug(message: string): void {

View file

@ -67,10 +67,10 @@ export class GradleUserHomeCache extends AbstractCache {
const cacheKey = fs.readFileSync(bundleMetaFile, 'utf-8').trim() const cacheKey = fs.readFileSync(bundleMetaFile, 'utf-8').trim()
listener.markRequested(cacheKey) listener.markRequested(cacheKey)
const restoredKey = await this.restoreCache([bundlePattern], cacheKey) const restoredEntry = await this.restoreCache([bundlePattern], cacheKey)
if (restoredKey) { if (restoredEntry) {
core.info(`Restored ${bundle} with key ${cacheKey} to ${bundlePattern}`) core.info(`Restored ${bundle} with key ${cacheKey} to ${bundlePattern}`)
listener.markRestored(restoredKey) listener.markRestored(restoredEntry.key, restoredEntry.size)
} else { } else {
core.info(`Did not restore ${bundle} with key ${cacheKey} to ${bundlePattern}`) core.info(`Did not restore ${bundle} with key ${cacheKey} to ${bundlePattern}`)
tryDelete(bundleMetaFile) tryDelete(bundleMetaFile)
@ -154,9 +154,11 @@ export class GradleUserHomeCache extends AbstractCache {
this.debug(`No change to previously restored ${bundle}. Not caching.`) this.debug(`No change to previously restored ${bundle}. Not caching.`)
} else { } else {
core.info(`Caching ${bundle} with cache key: ${cacheKey}`) core.info(`Caching ${bundle} with cache key: ${cacheKey}`)
await this.saveCache([artifactPath], cacheKey) const savedEntry = await this.saveCache([artifactPath], cacheKey)
if (savedEntry !== undefined) {
this.writeBundleMetaFile(bundleMetaFile, cacheKey) this.writeBundleMetaFile(bundleMetaFile, cacheKey)
listener.markSaved(cacheKey) listener.markSaved(savedEntry.key, savedEntry.size)
}
} }
for (const file of bundleFiles) { for (const file of bundleFiles) {

View file

@ -2,7 +2,7 @@ import {GradleUserHomeCache} from './cache-gradle-user-home'
import {ProjectDotGradleCache} from './cache-project-dot-gradle' import {ProjectDotGradleCache} from './cache-project-dot-gradle'
import * as core from '@actions/core' import * as core from '@actions/core'
import {isCacheDisabled, isCacheReadOnly} from './cache-utils' import {isCacheDisabled, isCacheReadOnly} from './cache-utils'
import {CacheListener} from './cache-base' import {CacheEntryListener, CacheListener} from './cache-base'
const BUILD_ROOT_DIR = 'BUILD_ROOT_DIR' const BUILD_ROOT_DIR = 'BUILD_ROOT_DIR'
const CACHE_LISTENER = 'CACHE_LISTENER' const CACHE_LISTENER = 'CACHE_LISTENER'
@ -54,11 +54,41 @@ export async function save(): Promise<void> {
} }
function logCachingReport(listener: CacheListener): void { function logCachingReport(listener: CacheListener): void {
core.info('---------- CACHING REPORT -------------') core.info(`---------- Caching Summary -------------
Restored Entries Count: ${getCount(listener.cacheEntries, e => e.restoredSize)}
Size: ${getSum(listener.cacheEntries, e => e.restoredSize)}
Saved Entries Count: ${getCount(listener.cacheEntries, e => e.savedSize)}
Size: ${getSum(listener.cacheEntries, e => e.savedSize)}`)
core.startGroup('Cache Entry details')
for (const entry of listener.cacheEntries) { for (const entry of listener.cacheEntries) {
core.info(`${entry.entryName} core.info(`Entry: ${entry.entryName}
Requested Key : ${entry.requestedKey ?? ''} Requested Key : ${entry.requestedKey ?? ''}
Restored Key : ${entry.restoredKey ?? ''} Restored Key : ${entry.restoredKey ?? ''}
Saved Key : ${entry.savedKey ?? ''}`) Size: ${formatSize(entry.restoredSize)}
Saved Key : ${entry.savedKey ?? ''}
Size: ${formatSize(entry.savedSize)}`)
} }
core.endGroup()
}
function getCount(
cacheEntries: CacheEntryListener[],
predicate: (value: CacheEntryListener) => number | undefined
): number {
return cacheEntries.filter(e => predicate(e) !== undefined).length
}
function getSum(
cacheEntries: CacheEntryListener[],
predicate: (value: CacheEntryListener) => number | undefined
): string {
return formatSize(cacheEntries.map(e => predicate(e) ?? 0).reduce((p, v) => p + v, 0))
}
function formatSize(bytes: number | undefined): string {
if (bytes === undefined || bytes === 0) {
return ''
}
return `${Math.round(bytes / (1024 * 1024))} MB (${bytes} B)`
} }