Skip to content

fix(queue): lease messages with FOR UPDATE SKIP LOCKED - #240

Merged
ralyodio merged 1 commit into
masterfrom
fix/queue-read-skip-locked
Sep 25, 2026
Merged

ralyodio merged 1 commit into
masterfrom
fix/queue-read-skip-locked

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

What changed

Queue.read() in packages/shared/lib/queue.js selected the visible candidates and then bumped their vt in a second statement inside the transaction. On libSQL the single multiplexed connection plus the in-process write lock serialised readers, so that was safe. On the Postgres pool (#239) two concurrent readers of one queue see the same candidates and both lease them.

Running the DB-bound suites against a real Postgres for the first time (the meshhook.com cutover, 2026-09-25) failed exactly one test: Queue system integration > concurrent consumers > never hands the same message to two readers (36 leases of 10 messages). The select now ends with for update skip locked, so a reader locks the rows it is about to lease and skips rows another reader holds.

Production today runs one orchestrator with one Worker per queue, so the race did not bite there; this closes it before a second consumer ever exists.

Test

TEST_DATABASE_URL=postgres://... pnpm exec vitest run src/queue packages/shared/lib/auth.test.js: 110 passed, 0 failed (was 109 / 1).

🤖 Generated with Claude Code

… never share one

Queue.read() selected the visible candidates and then bumped their vt in a
second statement. On libSQL the single multiplexed connection and the
in-process write lock serialised readers, so that was safe; on the Postgres
pool (#239) two concurrent readers see the same candidates and both lease
them. The integration test 'never hands the same message to two readers'
failed against a real Postgres (36 leases of 10 messages). The select now
locks the rows it is about to lease and skips rows another reader holds.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

11 finding(s)

HIGH/CRITICAL: 5 | MEDIUM: 3 | LOW: 3

Severity Rule Location
HIGH secret-database-url apps/web/.env.example:9
HIGH secret-database-url docs/Environment-Setup.md:46
HIGH secret-database-url docs/Environment-Setup.md:132
HIGH secret-slack-webhook docs/WEBHOOK_CONFIGURATION.md:145
HIGH secret-generic-credential src/nodes/README.md:271
MEDIUM sql-template-interpolation apps/web/src/routes/api/secrets/[id]/+server.js:80
MEDIUM sql-template-interpolation apps/web/src/routes/api/workflows/[id]/+server.js:141
MEDIUM sql-template-interpolation src/queue/test-helpers.js:52
LOW sql-template-interpolation docs/Turso-Migration.md:70
LOW secret-generic-credential src/nodes/webhook.test.js:287
LOW sql-template-interpolation workers/orchestrator.test.js:52

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 5a20d2c into master Sep 25, 2026
4 checks passed
@ralyodio
ralyodio deleted the fix/queue-read-skip-locked branch September 25, 2026 13:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant