Skip to content

Commit c35adfb

Browse files
authored
feat: add cache-restore-timeout to cap entire restore operation (#156)
The existing segmentTimeoutInMs (5 min) only covers individual download segments. If the extraction phase (gtar/unzstd) hangs, there is no timeout — we observed a macOS runner hang for 1h25m on cache extraction. Add a cache-restore-timeout input (in milliseconds, default 0/disabled) that wraps the entire restoreCache() call with Promise.race, covering both download and extraction.
1 parent 3156a5f commit c35adfb

7 files changed

Lines changed: 52 additions & 7 deletions

File tree

action.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ inputs:
4545
description: Cache repositories based on MODULE.bazel/WORKSPACE
4646
required: false
4747
default: "false"
48+
cache-restore-timeout:
49+
description: Timeout in milliseconds for each cache restore operation (download + extraction). Set to 0 to disable.
50+
required: false
51+
default: "0"
4852
token:
4953
description: GitHub token to query Bazelisk releases
5054
required: false

config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,15 @@ if (externalCacheConfig) {
133133
}
134134
}
135135

136+
const cacheRestoreTimeoutMs = parseInt(core.getInput('cache-restore-timeout')) || 0
137+
136138
const token = core.getInput('token')
137139
core.exportVariable('BAZELISK_GITHUB_TOKEN', token)
138140

139141
export default {
140142
baseCacheKey,
141143
cacheSave,
144+
cacheRestoreTimeoutMs,
142145
bazeliskCache: {
143146
enabled: core.getBooleanInput('bazelisk-cache'),
144147
files: [`${moduleRoot}/.bazelversion`],

dist/main/index.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128381,12 +128381,15 @@ if (externalCacheConfig) {
128381128381
}
128382128382
}
128383128383

128384+
const cacheRestoreTimeoutMs = parseInt(getInput('cache-restore-timeout')) || 0
128385+
128384128386
const token = getInput('token')
128385128387
exportVariable('BAZELISK_GITHUB_TOKEN', token)
128386128388

128387128389
/* harmony default export */ const config = ({
128388128390
baseCacheKey,
128389128391
cacheSave,
128392+
cacheRestoreTimeoutMs,
128390128393
bazeliskCache: {
128391128394
enabled: getBooleanInput('bazelisk-cache'),
128392128395
files: [`${moduleRoot}/.bazelversion`],
@@ -128581,20 +128584,36 @@ async function index_restoreCache(cacheConfig) {
128581128584
await (0,promises_namespaceObject.setTimeout)(delay)
128582128585

128583128586
startGroup(`Restore cache for ${cacheConfig.name}`)
128587+
const name = cacheConfig.name
128584128588
try {
128585128589
const hash = await glob_hashFiles(cacheConfig.files.join('\n'))
128586-
const name = cacheConfig.name
128587128590
const paths = cacheConfig.paths
128588128591
const restoreKey = `${config.baseCacheKey}-${name}-`
128589128592
const key = `${restoreKey}${hash}`
128590128593

128591128594
core_debug(`Attempting to restore ${name} cache from ${key}`)
128592128595

128593-
const restoredKey = await restoreCache(
128596+
const restorePromise = restoreCache(
128594128597
paths, key, [restoreKey],
128595-
{ segmentTimeoutInMs: 300000 } // 5 minutes
128598+
{ segmentTimeoutInMs: 300000 } // 5 minutes per download segment
128596128599
)
128597128600

128601+
let restoredKey
128602+
if (config.cacheRestoreTimeoutMs > 0) {
128603+
const ac = new AbortController()
128604+
const timeoutPromise = (0,promises_namespaceObject.setTimeout)(config.cacheRestoreTimeoutMs, null, { signal: ac.signal })
128605+
.then(() => { throw new Error(`Timed out restoring ${name} cache after ${config.cacheRestoreTimeoutMs}ms`) })
128606+
timeoutPromise.catch(() => {})
128607+
128608+
try {
128609+
restoredKey = await Promise.race([restorePromise, timeoutPromise])
128610+
} finally {
128611+
ac.abort()
128612+
}
128613+
} else {
128614+
restoredKey = await restorePromise
128615+
}
128616+
128598128617
if (restoredKey) {
128599128618
info(`Successfully restored cache from ${restoredKey}`)
128600128619

dist/main/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/post/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127583,12 +127583,15 @@ if (externalCacheConfig) {
127583127583
}
127584127584
}
127585127585

127586+
const cacheRestoreTimeoutMs = parseInt(getInput('cache-restore-timeout')) || 0
127587+
127586127588
const token = getInput('token')
127587127589
exportVariable('BAZELISK_GITHUB_TOKEN', token)
127588127590

127589127591
/* harmony default export */ const config = ({
127590127592
baseCacheKey,
127591127593
cacheSave,
127594+
cacheRestoreTimeoutMs,
127592127595
bazeliskCache: {
127593127596
enabled: getBooleanInput('bazelisk-cache'),
127594127597
files: [`${moduleRoot}/.bazelversion`],

dist/post/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

index.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,27 @@ async function restoreCache(cacheConfig) {
164164

165165
core.debug(`Attempting to restore ${name} cache from ${key}`)
166166

167-
const restoredKey = await cache.restoreCache(
167+
const restorePromise = cache.restoreCache(
168168
paths, key, [restoreKey],
169-
{ segmentTimeoutInMs: 300000 } // 5 minutes
169+
{ segmentTimeoutInMs: 300000 } // 5 minutes per download segment
170170
)
171171

172+
let restoredKey
173+
if (config.cacheRestoreTimeoutMs > 0) {
174+
const ac = new AbortController()
175+
const timeoutPromise = setTimeout(config.cacheRestoreTimeoutMs, null, { signal: ac.signal })
176+
.then(() => { throw new Error(`Timed out restoring ${name} cache after ${config.cacheRestoreTimeoutMs}ms`) })
177+
timeoutPromise.catch(() => {})
178+
179+
try {
180+
restoredKey = await Promise.race([restorePromise, timeoutPromise])
181+
} finally {
182+
ac.abort()
183+
}
184+
} else {
185+
restoredKey = await restorePromise
186+
}
187+
172188
if (restoredKey) {
173189
core.info(`Successfully restored cache from ${restoredKey}`)
174190

0 commit comments

Comments
 (0)