ASYNC STATE · LOST UPDATES

Async state: prevent a lost update

Let parallel branches reserve books, stock handling and packing against one shared budget, and watch overlapping awaits erase each other’s debits.

Computed experiment · sixty inspection seconds

Your experiment

A cart budget of 1,000 is shared by parallel branches that select books (420), stock handling (380) and packing (150), starting one second apart. Each reads the balance, awaits its booking tool for two seconds and writes the balance minus its cost. The bookings cost 950, so 50 should remain. Read what is stored, then change the latency, the branch count and the write pattern.

Play replays the computed run over sixty inspection seconds; Next event steps, Restart rewinds, and Inspection time scrubs. Any input change recomputes the run and rewinds it without autoplay.

Event trace

    Computed data

    Final metrics

    Deterministic schedule: branch i starts at i seconds; a write that lands at the same instant as a read is applied first; compare-and-set retries re-read and re-apply immediately. ADK applies each event’s state delta, so two branches writing one key resolve last-writer-wins; this page models that timing, not ADK’s internals. No model or booking service is called.

    One balance, several awaits

    Each branch reads the shared budget, awaits a booking tool, then writes. Write pattern chooses how it writes: the new balance computed from its own earlier read, a compare-and-set that only succeeds if the version it read is still current, its own cost key that one later step sums, or no parallelism at all. Parallel branches adds the support branch (90) as the fourth. Booking tool latency is how long each await lasts; branches start one second apart. Retries after a version conflict applies to compare-and-set only.

    The opening run: 430 stored, 50 real

    Catalog reads 1,000 at 0 s and writes 580 at 2 s. Stock read 1,000 at 1 s, before that write, so at 3 s it writes 620 and erases the order debit. Car read 580 at 2 s and writes 430 at 4 s, erasing the stock debit. All three bookings exist and cost 950, but the stored balance says 430 is left: the stock’s 380 has vanished. Nothing failed and nothing was logged as an error.

    Where the race lives

    Set Booking tool latency to 1 s and the stored balance is 50: each write lands before the next branch reads, so the awaits no longer overlap. At 1.5 s the race is back (430), and at 3 s two debits are lost and 850 is stored. Set Parallel branches to 4: the support makes the bookings cost 1,040, yet the store shows 530, so a 40 overspend goes unnoticed. The bug is not the code of any one branch; it is a read that is stale by the time its await ends.

    Failure boundary: what each fix costs

    Compare-and-set with one retry stores 50 and, with four branches, refuses the support instead of overspending. With zero retries the stock write fails on a version conflict after its booking was made: an orphaned booking and a balance of 430. Own keys plus one summing step also store 50, but with four branches the over-budget support is only found at the merge and must be cancelled. Sequential agent is correct and slowest: 6 s for three branches, 8 s for four, against 4 s and 5 s in parallel.

    Reading the run and its limits

    Play replays each branch’s write, the stored balance and the outcome over sixty inspection seconds; the table shows what each branch read, what it wrote and what that did to the others. Next event, Restart, Speed and Inspection time control the replay, and any input change recomputes it. The schedule is fixed and the retries are instant; real timings vary run to run, which is exactly why a race that passes a test once can still ship. Give each parallel branch its own key and combine in one step.

    Views are my own and do not represent any employer, past or present.

    Search