FFORGE//RS
← Roadmap

distributed systems / LEVEL 5

Both transactions preserved the invariant

EST.50 MIN
01

THEORY / RETRIEVAL

What to restore

  • Recognize write skew across disjoint rows under snapshot isolation
  • Retry the complete SERIALIZABLE transaction after retryable SQLSTATE classes
  • Keep one command identity and an exact total-attempt budget
  • Avoid retrying permanent or domain failures without explicit evidence

A stable snapshot can still violate a predicate invariant

PostgreSQL Repeatable Read gives each transaction a stable snapshot, but concurrent transactions can still form a serialization anomaly. If two doctors both observe two people on call and each updates only their own row, both disjoint writes can commit while the cross-row invariant becomes false. Locking only the row each transaction writes does not coordinate the predicate they both read.

Serializable turns the anomaly into an application decision

PostgreSQL Serializable admits only commits compatible with some serial order. When dependency monitoring cannot prove such an order, one transaction fails with SQLSTATE 40001. The application must discard every result from that attempt and rerun the complete decision transaction from a fresh snapshot, including the reads that chose which SQL and values to use.

Retry policy is narrower than error handling

A generic boundary can retry 40001 and, by explicit policy, deadlock 40P01. Unique violations and domain errors are normally returned immediately because they may be permanent; retry them only when application-specific evidence proves a transient concurrency cause. Bound total attempts, preserve the command or idempotency key, and place jittered delay outside this synchronous helper so overload does not synchronize retries.

CHECKPOINT

Two PostgreSQL Repeatable Read transactions each see two doctors on call, then update different doctor rows to off-call; both commit. Which response protects the at-least-one-on-call invariant?

ISOLATED RUST 1.96
src/lib.rsEDIT

02 / DEBUGGING

Find and fix the defect

Repair `run_serializable`. `max_attempts` is the total invocation budget, not the number of retries. Invoke the closure with the same command id every time; retry only SerializationFailure or DeadlockDetected while budget remains. Return success, UniqueViolation, Domain, or the final retryable error immediately when required. The closure represents a fresh complete SERIALIZABLE transaction on every invocation; async backoff and jitter belong to its caller.

Initializing editor…
CLOUD SANDBOXnetwork off · 256 MB · 12 s
1 / 64 KB
OUTPUT
Runner is waiting for a submission.