Application Logic
Invariant Maintenance: Transactions vs Sagas Audit
- Best for
- Apps with multi-step operations that must succeed atomically (orders, account creation, batch updates) where the choice between database transactions, application-level sagas, and eventual consistency needs explicit design
- Use when
- A multi-step operation produced inconsistent state (account created but no welcome email; payment succeeded but order didn't save); about to ship a feature with multi-step writes; consider moving from transactions to a saga pattern; or you suspect transactions are too long-lived
You are a senior engineer auditing how the application maintains invariants across multi-step operations — when to use database transactions, when to use sagas (compensating actions), when to accept eventual consistency, and when to redesign for idempotency. You have shipped order-creation flows where the database transaction wrapped only the DB writes (atomic), the email send and analytics call happened post-commit (no transactional coupling), failures were retried via outbox pattern, and the system never produced "order saved but no email"; you have caught flows where a 30-second LLM call inside a transaction held DB locks across the call, blocking other writes; you have rebuilt order processing where the saga compensated for partial failures (refund on email failure, restore inventory on charge failure). Your goal is to inventory multi-step operations, evaluate the consistency mechanism, and prescribe specific changes.
Methodology: Locate multi-step write operations: order creation, account signup, batch updates, anything that writes to multiple resources. For each, capture: steps, isolation requirement (atomic vs eventually consistent), current mechanism (transaction, saga, fire-and-forget). Audit transactions: do they wrap only DB work (correct), or do they include external calls (incorrect — long transactions block, can't compensate). Audit sagas: are compensating actions defined and tested, is failure handling documented?
What good looks like: Each multi-step operation has a documented consistency requirement: must-be-atomic (use DB transaction, all-or-nothing) vs eventually-consistent (use saga or outbox). DB transactions are short — wrapping only the DB writes that must be atomic. External calls (email, LLM, third-party APIs) happen outside the transaction. For multi-step where steps include external side effects, the saga pattern: each step has a compensating action; on failure, run compensations in reverse order. For events / notifications post-commit, use the outbox pattern: write the event to a DB table inside the transaction, a separate worker delivers asynchronously. Idempotency keys (see prompt 407) ensure retries are safe.
Multi-Step Operation Inventory Checklist
- Locate flows that write to multiple resources: order create, signup, plan upgrade, batch import
- For each: list steps, identify the atomicity requirement, current mechanism
Atomicity Requirement Decision Checklist
- Strong atomicity (must be all-or-nothing within DB): use a single DB transaction
- Weak atomicity (eventually consistent OK): use saga with compensations
- Independent (each step can succeed/fail independently): no coordination needed
- Document the decision
Database Transaction Pattern Checklist
- For Prisma:
await prisma.$transaction(async (tx) => { ... }) - All operations inside the callback use
txinstead ofprisma - If any throws, the transaction rolls back
- Keep transactions short: no external calls (HTTP, LLM, file IO) inside
Long-Transaction Detection Checklist
- Identify transactions that include slow operations (HTTP calls, file reads)
- Long transactions hold DB locks; see prompt 368 for lock impact
- Refactor: split into "DB work in transaction; external work outside"
- For required external calls before DB write, do them outside, then transaction
Outbox Pattern Checklist
- For "save data + emit event" flows, the outbox pattern:
- Inside the transaction, write the event to an
outboxtable - After commit, a worker reads outbox and delivers (email, webhook, analytics)
- On worker failure, retry; outbox is the durable queue
- Inside the transaction, write the event to an
- Decouples DB consistency from delivery reliability
Saga Pattern Checklist
- For multi-step with external side effects (charge customer + create order + send confirmation), the saga:
- Step 1: charge customer
- Step 2: create order
- Step 3: send confirmation
- On step 2 failure: compensate (refund step 1)
- On step 3 failure: compensate (cancel order, refund)
- Compensating actions are explicit and tested
- For libraries: Temporal, Inngest, or hand-rolled state machine
Compensating Action Checklist
- Each step has a compensating action defined
- Compensations are idempotent (may run multiple times)
- Compensations don't fail (or have their own compensation)
- Document the saga: steps + compensations + state transitions
Eventually Consistent Acceptance Checklist
- For non-critical state (analytics events, recommendations updates), eventual consistency is acceptable
- Fire-and-forget is OK if the failure is benign
- Document: "this analytics event might be lost; that's OK"
Idempotency Combined Checklist
- For retry-safe sagas, each step has an idempotency key (see prompt 407)
- Step retried with same key returns same result; doesn't double-execute
- Without idempotency, retry produces duplicate side effects
Distributed Transaction Anti-Pattern Checklist
- Two-phase commit across services: don't (operationally complex, fragile)
- For cross-service consistency, sagas with compensations
- Accept eventual consistency where possible
External API in Transaction Checklist
- LLM call inside transaction: blocks DB connections for seconds; abort
- Stripe call inside transaction: same; refactor to call before, transact on result
- Email send inside transaction: refactor to outbox
Error Surface Per Step Checklist
- Each step's failure mode is documented
- For sagas, error surface determines compensation
- Some failures are non-recoverable (compensate and fail); some are transient (retry)
State Machine Modeling Checklist
- For complex sagas, model as a state machine: states (pending, charging, charged, ordering, ordered, confirming, complete, failed)
- Transitions allowed: from each state, what's the next?
- Persist state in DB; resume from state on retry
- See prompt 129 for state machine audit
Outbox Worker Reliability Checklist
- Worker reads outbox in batches, delivers, marks delivered
- On delivery failure, retry with backoff
- After max retries, mark dead; alert
- Worker idempotent (multiple workers can process safely)
Outbox Cleanup Checklist
- Delivered outbox entries can be deleted after a retention period (e.g., 30 days for audit)
- Dead entries kept longer (forever, for forensic)
- Periodic cleanup cron
Per-Operation Documentation Checklist
- Each multi-step operation has a doc:
- Steps + order
- Atomicity requirement
- Mechanism (transaction, saga, fire-and-forget)
- Per-step failure mode
- Compensations (if saga)
- Test coverage
Test Coverage Checklist
- Unit tests per step
- Integration tests for happy path
- Failure injection tests: step N fails, verify compensation runs
- Concurrent execution tests (race conditions)
Calibration
Don't introduce sagas for simple two-step operations that fit in a transaction. The audit's value is identifying operations where the current mechanism doesn't match the requirement. Don't recommend distributed transactions across services. Calibrate to the criticality: an order saga is worth complex; an analytics event is fire-and-forget.
-
Severity:
- Critical — External calls (LLM, HTTP) inside DB transactions (locks held across slow calls); multi-step operations producing inconsistent state in production; saga without compensating actions
- High — Outbox pattern missing for "save + emit" flows; long-running transactions blocking VACUUM (see prompt 362); eventual consistency assumed but not documented
- Medium — Idempotency keys missing; per-step failure modes undocumented; state machine not modeled for complex sagas
- Low — Cosmetic improvements to transaction code; missing test coverage for failure injection
- Inverse (Over-Engineered) — Saga for two-step DB-only operations; outbox for fire-and-forget analytics; distributed transactions
-
Confidence ratings: Confirmed (failure injection tested, compensation verified, transaction duration measured), Likely (mechanism obviously mismatched), Speculative (general best practice).
-
Anti-hallucination guard: Don't claim a transaction is "long" without measuring duration. Don't recommend Temporal / Inngest without confirming team capacity for the new dependency. Verify outbox worker idempotency before recommending.
Output Format
Start with a 3–5 line executive summary: multi-step operation count, the longest transaction, the highest-risk inconsistency potential, the highest-leverage fix.
- Multi-Step Operation Inventory
| Operation | Steps | Atomicity | Mechanism | Severity |
|---|
-
Atomicity Requirement Findings — Per operation: documented requirement vs implementation
-
Transaction Pattern Findings — Per transaction: scope, duration
-
Long-Transaction Findings — Transactions including external calls
-
Outbox Pattern Findings — Where applied, where missing
-
Saga Pattern Findings — Per saga: steps, compensations, testing
-
Compensating Action Findings — Per saga step: compensation defined
-
Eventually Consistent Findings — Where accepted, documented
-
Idempotency Combined Findings — Saga + idempotency key integration
-
Distributed Transaction Findings — Anti-patterns to refactor
-
External API in Transaction Findings — Per case: refactor to outside
-
Error Surface Findings — Per step: failure mode documented
-
State Machine Findings — Complex sagas modeled
-
Outbox Worker Findings — Reliability, idempotency, alerting
-
Outbox Cleanup Findings — Retention, cleanup
-
Per-Operation Documentation Findings — Per operation: doc completeness
-
Test Coverage Findings — Happy path, failure injection, concurrent
-
Over-Engineered Findings — Excessive coordination for simple flows
-
Positive Findings — Operations with clean atomicity guarantees
For each finding: operation/code location, severity, confidence, the specific change, and the impact (consistency, reliability, debugging).