From fa7d7df2321001e2ff76c087e5ba63315efd8415 Mon Sep 17 00:00:00 2001 From: Levi Bostian Date: Wed, 3 Apr 2024 07:01:51 -0500 Subject: [PATCH] fix: gracefully handle race condition between parallel builds I am receiving build failures when I run 2 parallel builds that both run setup-mint when they startup. You can reproduce this scenario by creating 2 workflows that get triggered when a PR is opened and both workflows run setup-mint action. 1 will succeed with saving the cache, the other will fail early and give error message "Unable to reserve cache with key -macOS-X64-irgaly/setup-mint-master, another job may be creating this cache." I believe this is a race condition between the parallel builds where they both attempt to reserve the github actions cache using the same cache key. One way to handle this scenario is to not fail the action when this race condition occurs, since you only need 1 of the parallel builds to save the cache. The other build can skip saving and move on. References: https://github.com/oven-sh/setup-bun/issues/45 - experiencing the same issue as this person https://github.com/actions/cache/issues/485#issuecomment-744145040 - explaining the idea of how caches are reserved --- src/main.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main.ts b/src/main.ts index 18c7738..1c2e898 100644 --- a/src/main.ts +++ b/src/main.ts @@ -34,6 +34,17 @@ function pathContains(parent: string, child: string): boolean { return !path.relative(parent, child).startsWith("../") } +// Caching may throw errors for legitimate reasons that should not fail the action. +// Example: Race condition between multiple github runners where both try to save cache with the same key at the same time. +// You only need 1 of the runners to save the cache. The other runners can gracefully ignore the error and continue running. +async function saveCache(paths: string[], key: string): Promise { + try { + await cache.saveCache(paths, key) + } catch (error) { + core.warning(`Failed to cache ${key}. Error thrown: ${error}`) + } +} + async function main() { try { const mintDirectory = core.getInput('mint-directory') @@ -79,7 +90,8 @@ async function main() { await execute('swift', ['build', '-c', 'release'], `${temp}/Mint`) fs.copyFileSync(`${temp}/Mint/.build/release/mint`, '/usr/local/bin/mint') } - await cache.saveCache(mintPaths, mintCacheKey) + + await saveCache(mintPaths, mintCacheKey) } if (hasMintfile && bootstrap) { const mintDirectory = (process.env['MINT_PATH'] || '~/.mint').replace(/^~\//, `${os.homedir()}/`) @@ -145,9 +157,9 @@ async function main() { } } } - await cache.saveCache(mintDependencyPaths, mintDependencyCacheKey) + await saveCache(mintDependencyPaths, mintDependencyCacheKey) if (mintBinaryNeedsCache) { - await cache.saveCache(mintBinaryPaths, mintBinaryCacheKey) + await saveCache(mintBinaryPaths, mintBinaryCacheKey) } } }