add lock awareness

This commit is contained in:
darshanime 2026-07-08 15:29:01 +05:30
parent 7484b46911
commit 0dbe61b437
4 changed files with 28 additions and 8 deletions

View File

@ -172,6 +172,11 @@ describe('warpbuild snapshot cache', () => {
expect((await lookupSnapshot('123', SHA)).kind).toBe('miss') expect((await lookupSnapshot('123', SHA)).kind).toBe('miss')
}) })
it('maps 409 upload responses to locked (another job is uploading)', async () => {
stubFetch(409, {sub_code: 'FVE_GITMIRROR_LOCKED'})
expect((await requestUploadURL('123', SHA)).kind).toBe('locked')
})
it('maps 403 to disabled (backend kill switch)', async () => { it('maps 403 to disabled (backend kill switch)', async () => {
stubFetch(403, {sub_code: 'PDE_GITMIRROR_DISABLED'}) stubFetch(403, {sub_code: 'PDE_GITMIRROR_DISABLED'})
expect((await lookupSnapshot('123', SHA)).kind).toBe('disabled') expect((await lookupSnapshot('123', SHA)).kind).toBe('disabled')

15
dist/index.js vendored
View File

@ -41699,7 +41699,8 @@ const promises_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.ur
// Client for backend-core's /api/v1/git-mirrors endpoints, authed by the runner // Client for backend-core's /api/v1/git-mirrors endpoints, authed by the runner
// verification token. Contract: 200 = presigned URL; 404 = miss (upload after the // verification token. Contract: 200 = presigned URL; 404 = miss (upload after the
// stock fetch); 403 = unservable org (skip cache + upload); else = fall back. // stock fetch); 403 = unservable org (skip cache + upload); 409 = another job is
// uploading this snapshot (skip upload); else = fall back.
const API_TIMEOUT_MS = 10_000; const API_TIMEOUT_MS = 10_000;
function backend_api_baseUrl() { function backend_api_baseUrl() {
return (process.env['WARPBUILD_HOST_URL'] || '').replace(/\/+$/, ''); return (process.env['WARPBUILD_HOST_URL'] || '').replace(/\/+$/, '');
@ -41753,6 +41754,10 @@ async function requestUploadURL(repoKey, sha) {
core_debug(`[wb-cache] upload-url answered 403 (disabled)`); core_debug(`[wb-cache] upload-url answered 403 (disabled)`);
return { kind: 'disabled' }; return { kind: 'disabled' };
} }
if (res.status === 409) {
core_debug(`[wb-cache] upload-url answered 409 (locked)`);
return { kind: 'locked' };
}
core_debug(`[wb-cache] upload-url answered ${res.status}`); core_debug(`[wb-cache] upload-url answered ${res.status}`);
return { kind: 'error' }; return { kind: 'error' };
} }
@ -41974,9 +41979,11 @@ async function uploadSnapshot(settings) {
const size = (await external_fs_namespaceObject.promises.stat(tmpTar)).size; const size = (await external_fs_namespaceObject.promises.stat(tmpTar)).size;
const upload = await requestUploadURL(repoKey, sha); const upload = await requestUploadURL(repoKey, sha);
if (upload.kind !== 'ok') { if (upload.kind !== 'ok') {
info(upload.kind === 'disabled' info(upload.kind === 'locked'
? 'Snapshot cache is disabled by the backend; not uploading' ? 'Another job is already uploading this snapshot; skipping'
: 'Snapshot cache backend unavailable; not uploading'); : upload.kind === 'disabled'
? 'Snapshot cache is disabled by the backend; not uploading'
: 'Snapshot cache backend unavailable; not uploading');
return; return;
} }
const init = { const init = {

View File

@ -3,7 +3,8 @@ import * as core from '@actions/core'
// Client for backend-core's /api/v1/git-mirrors endpoints, authed by the runner // Client for backend-core's /api/v1/git-mirrors endpoints, authed by the runner
// verification token. Contract: 200 = presigned URL; 404 = miss (upload after the // verification token. Contract: 200 = presigned URL; 404 = miss (upload after the
// stock fetch); 403 = unservable org (skip cache + upload); else = fall back. // stock fetch); 403 = unservable org (skip cache + upload); 409 = another job is
// uploading this snapshot (skip upload); else = fall back.
const API_TIMEOUT_MS = 10_000 const API_TIMEOUT_MS = 10_000
@ -22,6 +23,7 @@ export type SnapshotLookup =
export type UploadURLResult = export type UploadURLResult =
| {kind: 'ok'; url: string} | {kind: 'ok'; url: string}
| {kind: 'disabled'} | {kind: 'disabled'}
| {kind: 'locked'}
| {kind: 'error'} | {kind: 'error'}
function baseUrl(): string { function baseUrl(): string {
@ -89,6 +91,10 @@ export async function requestUploadURL(
core.debug(`[wb-cache] upload-url answered 403 (disabled)`) core.debug(`[wb-cache] upload-url answered 403 (disabled)`)
return {kind: 'disabled'} return {kind: 'disabled'}
} }
if (res.status === 409) {
core.debug(`[wb-cache] upload-url answered 409 (locked)`)
return {kind: 'locked'}
}
core.debug(`[wb-cache] upload-url answered ${res.status}`) core.debug(`[wb-cache] upload-url answered ${res.status}`)
return {kind: 'error'} return {kind: 'error'}
} catch (error) { } catch (error) {

View File

@ -253,9 +253,11 @@ async function uploadSnapshot(settings: IGitSourceSettings): Promise<void> {
const upload = await api.requestUploadURL(repoKey, sha) const upload = await api.requestUploadURL(repoKey, sha)
if (upload.kind !== 'ok') { if (upload.kind !== 'ok') {
core.info( core.info(
upload.kind === 'disabled' upload.kind === 'locked'
? 'Snapshot cache is disabled by the backend; not uploading' ? 'Another job is already uploading this snapshot; skipping'
: 'Snapshot cache backend unavailable; not uploading' : upload.kind === 'disabled'
? 'Snapshot cache is disabled by the backend; not uploading'
: 'Snapshot cache backend unavailable; not uploading'
) )
return return
} }