Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions supabase/functions/_backend/triggers/on_version_update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ type ManifestCleanupEntry = {
/**
* Trash unreferenced R2 objects first (exist → move to deleted-after-7-days/,
* missing → ok), then delete that DB row. Never drop DB tracking before R2 is handled.
* Per-file work is committed, so a timeout mid-pass is safe to retry. Leftover rows
* after the normal retry budget are reclaimed by sweep_deleted_version_manifests.
* Incomplete work throws so the queue retries; already-trashed paths are idempotent.
*/
async function deleteManifest(c: Context, record: Database['public']['Tables']['app_versions']['Row']) {
Expand Down
8 changes: 4 additions & 4 deletions supabase/functions/_backend/triggers/queue_consumer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ const QUEUE_HTTP_TIMEOUT_MS = 15_000
const VERSION_QUEUE_HTTP_TIMEOUT_MS = 300_000 // large deleted manifests: trash then DB delete
const HEALTHCHECK_HTTP_TIMEOUT_MS = 8_000
export const MAX_QUEUE_READS = 5
const VERSION_QUEUE_MAX_READS = 30 // deleted manifests can need many partial trash/delete passes
const DISCORD_IGNORED_ERROR_CODES = new Set(['version_not_found', 'no_channel'])

export const messageSchema = z.object({
Expand Down Expand Up @@ -281,9 +280,10 @@ function getQueueHttpTimeoutMs(functionName: string): number {
return QUEUE_HTTP_TIMEOUT_MS
}

function getQueueMaxReads(queueName: string): number {
if (isVersionQueueFunction(queueName))
return VERSION_QUEUE_MAX_READS
function getQueueMaxReads(_queueName: string): number {
// Same budget for every queue. Large deleted-manifest cleanup already commits
// per file; leftover rows are reclaimed by sweep_deleted_version_manifests.
// Do not burn retries as a fake "progress" budget.
return MAX_QUEUE_READS
}

Expand Down
18 changes: 9 additions & 9 deletions tests/queue-consumer-message-shape.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ describe('queue_consumer legacy message compatibility', () => {
expect(__queueConsumerTestUtils__.getQueueVisibilityTimeout('cron_email')).toBe(120)
expect(__queueConsumerTestUtils__.getQueueVisibilityTimeout('on_version_update')).toBe(900)
expect(__queueConsumerTestUtils__.getQueueHttpTimeoutMs('on_version_update')).toBe(300_000)
expect(__queueConsumerTestUtils__.getQueueMaxReads('on_version_update')).toBe(30)
expect(__queueConsumerTestUtils__.getQueueMaxReads('on_manifest_create')).toBe(5)
expect(__queueConsumerTestUtils__.getQueueMaxReads('on_version_update')).toBe(MAX_QUEUE_READS)
expect(__queueConsumerTestUtils__.getQueueMaxReads('on_manifest_create')).toBe(MAX_QUEUE_READS)
expect(__queueConsumerTestUtils__.getQueueHttpTimeoutMs('cron_email')).toBe(15_000)
expect(__queueConsumerTestUtils__.shouldRunQueueSyncInBackground('on_manifest_create')).toBe(false)
expect(__queueConsumerTestUtils__.shouldRunQueueSyncInBackground('cron_email')).toBe(true)
Expand Down Expand Up @@ -237,29 +237,29 @@ describe('queue_consumer legacy message compatibility', () => {
)).toBe('continue')
})

it.concurrent('uses the version queue retry budget for Discord failure alerts', () => {
const versionRetryBudget = __queueConsumerTestUtils__.getQueueMaxReads('on_version_update')
it.concurrent('uses the shared queue retry budget for Discord failure alerts', () => {
const retryBudget = __queueConsumerTestUtils__.getQueueMaxReads('on_version_update')
const midRetry = {
cf_id: 'cf-version-mid',
error_code: 'manifest_cleanup_incomplete',
function_name: 'on_version_update',
function_type: 'supabase',
msg_id: 2,
payload_size: 10,
read_count: MAX_QUEUE_READS,
read_count: MAX_QUEUE_READS - 1,
status: 500,
status_text: 'Internal Server Error',
}
const exhausted = {
...midRetry,
cf_id: 'cf-version-done',
msg_id: 3,
read_count: versionRetryBudget,
read_count: retryBudget,
}

expect(versionRetryBudget).toBe(30)
expect(__queueConsumerTestUtils__.getActionableQueueFailures([midRetry], versionRetryBudget)).toEqual([])
expect(__queueConsumerTestUtils__.getActionableQueueFailures([exhausted], versionRetryBudget)).toEqual([exhausted])
expect(retryBudget).toBe(MAX_QUEUE_READS)
expect(__queueConsumerTestUtils__.getActionableQueueFailures([midRetry], retryBudget)).toEqual([])
expect(__queueConsumerTestUtils__.getActionableQueueFailures([exhausted], retryBudget)).toEqual([exhausted])
})

it.concurrent('alerts Discord after retry budget is exhausted', () => {
Expand Down
Loading