Skip to content

Commit 5516764

Browse files
committed
fix(file-search): keep the dispatch pass's file-then-queue lock order
Reaping before the backfill held the owner's queue row while the backfill took FOR SHARE on its unindexed files; a concurrent save holds its file while its trigger waits on that queue row, so the two could deadlock. The pass runs backfill then reap again, and a wake that conflicts with a row this transaction's backfill just stamped as a reconciliation clears the stamp, so a reaped claim stays ahead of the rotation.
1 parent ebb550c commit 5516764

1 file changed

Lines changed: 12 additions & 4 deletions

File tree

‎apps/sim/lib/workspace-files/search/dispatcher.ts‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,15 @@ async function enqueueOwners(
229229
)
230230
await (reconciliation
231231
? insert.onConflictDoNothing({ target })
232-
: insert.onConflictDoUpdate({ target, set: { updatedAt: transactionNow } }))
232+
: insert.onConflictDoUpdate({
233+
target,
234+
set: {
235+
updatedAt: transactionNow,
236+
// A row this pass's backfill just queued as a reconciliation is woken as if the wake had
237+
// queued it first; a stamp from an earlier transaction is always older than `now()`.
238+
lastDispatchedAt: sql`CASE WHEN ${fileSearchDispatchQueue.lastDispatchedAt} = ${transactionNow} THEN NULL ELSE ${fileSearchDispatchQueue.lastDispatchedAt} END`,
239+
},
240+
}))
233241
}
234242

235243
/**
@@ -660,13 +668,13 @@ export async function prepareWorkspaceFileSearchDispatch(
660668
}
661669

662670
const now = new Date()
663-
// Reaped owners are queued woken before the backfill page, whose wake-ups leave queued
664-
// owners untouched; the other order would file a reaped claim behind them.
671+
// The backfill locks files before the reap locks queue rows: a file save holds its file
672+
// while its trigger waits on the owner's queue row, so the other order can deadlock.
673+
const backfilledFiles = await runDispatchPhase('backfill', () => seedBackfillPage(tx, now))
665674
const { reaped: reapedClaims, abandoned: abandonedClaims } = await runDispatchPhase(
666675
'reap',
667676
() => reapStaleClaims(tx, now)
668677
)
669-
const backfilledFiles = await runDispatchPhase('backfill', () => seedBackfillPage(tx, now))
670678
const [{ active, cleanupBacklogged }] = await tx.execute<{
671679
active: number
672680
cleanupBacklogged: boolean

0 commit comments

Comments
 (0)