Transactions — atomic { … }
Tenant-scoped interactive transactions. Snapshot isolation at block entry; first-committer-wins on the write-set at commit.
Shape
eel
atomic {
listings |> filter id == ${lid} && stock > 0 |> update { stock: stock - 1 }
orders |> insert { listing: ${lid}, buyer_ref: ${bref}, total: 129.00$, placed_at: now(), status: .pending }
}All pipelines in the block share one snapshot pin and one commit (or abort). Cross-tenant coordination stays app-layer sagas — atomics never span tenants.
Semantics
| Rule | Behavior |
|---|---|
| Snapshot pin | At begin, copy the version vector. Reads resolve against the pin, not the live head. |
| Read-your-own-writes | Reads also see this block's uncommitted dirty pages (live version > pin). |
| Buffered writes | Mutations append WAL Write frames under the txn id; pages stay dirty until commit. |
| Commit | Conflict check → one Commit frame → wal.sync → flush dirty pages → Meta. Same fsync path as batch commit. |
| Abort | Discard dirty pages; restore last durable heads (concurrent commits stay visible). WAL Abort for recovery. |
| Conflict | If any key this block wrote was committed by another block after this pin → typed conflict (conflict: true on HTTP). |
Crash recovery
| Crash point | On reopen |
|---|---|
After Begin + Writes, before Commit | Open txn discarded — zero effect from the block |
After Commit + WAL fsync | Replay applies all Writes; full txn visible |
Scope today
- In: single-process first-committer-wins (in-memory commit log keyed by DB path).
- Out (later): cross-process / after-reopen conflict detection; cross-tenant atomics.
Stress coverage
atomic_two_buyer, crash_mid_atomic, and atomic_vs_batch_latency live in eelden-stress.