Skip to content

fix: enforce per-vault ordering in event outbox poller - #1487

Merged
Junirezz merged 2 commits into
Junirezz:mainfrom
Ottones:fix/issue-1447-event-outbox-does-not-enforce-ordering-per
Oct 3, 2026
Merged

Junirezz merged 2 commits into
Junirezz:mainfrom
Ottones:fix/issue-1447-event-outbox-does-not-enforce-ordering-per

Conversation

@Ottones

@Ottones Ottones commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Overview

This PR fixes the event outbox poller so that events are delivered in per-vault order. Previously the poller ordered globally by createdAt, which allowed events from different vaults to interleave and let a slow webhook for one vault delay another vault's events — causing consumers that track vault.version to observe out-of-order state (e.g. version 5 before version 4).

The fix adds a monotonic sequence column scoped to each vaultId, orders polling by vaultId, createdAt, and asserts per-vault ordering in tests.

Related Issue

Changes

🗂️ Outbox schema

  • [MODIFY] backend/prisma/schema.prisma

    • Add sequence (monotonic per vaultId) to the event outbox model, with a unique constraint on (vaultId, sequence) and an index supporting vaultId, createdAt polling.
  • [ADD] backend/prisma/migrations/20260824204600_add_event_outbox/migration.sql

    • Migration adding the sequence column, backfilling existing rows per vaultId, and creating the per-vault unique + ordering indexes.

📤 Outbox writer

  • [MODIFY] backend/src/eventOutbox.ts
    • Assign the next per-vault sequence when enqueuing an event, so each vaultId gets a strictly incrementing sequence.

🔁 Poller

  • [MODIFY] backend/src/jobs/outboxPoller.ts
    • Order polling by vaultId, createdAt (and sequence) so events for a given vault are processed in enqueue order and are not reordered by retries or interleaving across vaults.

✅ Tests

  • [MODIFY] backend/src/__tests__/eventOutbox.test.ts
    • Assert sequence increments by 1 per vaultId.
    • Enqueue interleaved events for vault A and B and assert per-vault order is preserved for the consumer.
    • Regression coverage for single-vault ordering.

Verification Results

npm test -- backend/src/__tests__/eventOutbox.test.ts
✅ per-vault sequence increments by 1
✅ interleaved vault A/B events preserve per-vault order
✅ single-vault ordering unchanged (no regression)
Acceptance Criteria Status
Poller orders by vaultId, createdAt or uses per-vault queue with FOR UPDATE SKIP LOCKED partitioned by vaultId ✅ Poller orders by vaultId, createdAt (with sequence)
Add sequence monotonic per vaultId column to outbox and assert sequence increments by 1 per vault in test ✅ sequence column added; test asserts +1 per vaultId
Test: enqueue events for vault A and B interleaved and assert consumer sees per-vault order preserved ✅ Interleaved A/B test asserts per-vault order
No regression for single-vault ordering ✅ Single-vault ordering test passes

Closes #1447

@drips-wave

drips-wave Bot commented Sep 29, 2026

Copy link
Copy Markdown

@Ottones Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Junirezz
Junirezz merged commit 69bdca2 into Junirezz:main Oct 3, 2026
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.

Event outbox does not enforce ordering per vault causing webhook consumers to see out-of-order state

2 participants