Skills › gstack › investigate › race.md
race.md
27 lines · investigate/sections/race.md at 3f79d81
1<!-- AUTO-GENERATED from race.md.tmpl — do not edit directly -->
2<!-- Regenerate: bun run gen:skill-docs -->
<!-- AUTO-GENERATED from race.md.tmpl — do not edit directly --> <!-- Regenerate: bun run gen:skill-docs -->
Raw lines 1 to 23## Race recipe
5A race is two actors touching shared state with an ordering nobody enforces.
6Make it deterministic before fixing it.
81. **Timing probes.** Log a monotonic timestamp (`performance.now()`) and the
9 actor id at every read and write of the shared state (file, row, map, lock).
10 A race shows as interleaved writes or a read between another actor's
11 check and its write. Keep the probe output as evidence.
122. **Forced interleaving.** Inject an `await` or sleep at the suspected point
13 in one actor (behind a test-only hook or env var) so the other actor runs in
14 the window. The bug must now appear every time; if it does not, the window is
15 elsewhere.
163. **Concurrent-writer template.** Drive N writers at once and assert the
17 invariant, not the absence of an error:
18 ```ts
19 const results = await Promise.allSettled(Array.from({ length: N }, (_, i) => writer(i)));
20 // exactly one winner | all N applied | file or row intact | lock released and owner recorded
21 ```
22 Run with N ≥ 8 under the forced interleaving; the template becomes the
23 regression test with the interleaving still injected.
244. **Fix shape.** An atomic primitive: create with `wx`, compare-and-swap,
25 `UPDATE … WHERE old_value = ?`, a lock with an owner token and a stale-owner
26 rule. A check-then-act with a smaller window is not a fix. Rerun step 3
27 after the change and paste both outputs.
Race recipe
A race is two actors touching shared state with an ordering nobody enforces. Make it deterministic before fixing it.
- Timing probes. Log a monotonic timestamp (
performance.now()) and the actor id at every read and write of the shared state (file, row, map, lock). A race shows as interleaved writes or a read between another actor's check and its write. Keep the probe output as evidence. - Forced interleaving. Inject an
awaitor sleep at the suspected point in one actor (behind a test-only hook or env var) so the other actor runs in the window. The bug must now appear every time; if it does not, the window is elsewhere. - Concurrent-writer template. Drive N writers at once and assert the invariant, not the absence of an error: ``
ts const results = await Promise.allSettled(Array.from({ length: N }, (_, i) => writer(i))); // exactly one winner | all N applied | file or row intact | lock released and owner recorded`` Run with N ≥ 8 under the forced interleaving; the template becomes the regression test with the interleaving still injected. - Fix shape. An atomic primitive: create with
wx, compare-and-swap,UPDATE … WHERE old_value = ?, a lock with an owner token and a stale-owner rule. A check-then-act with a smaller window is not a fix. Rerun step 3 after the change and paste both outputs.