Skill Tracer
Skills › gstack › investigate › race.md

race.md

27 lines · investigate/sections/race.md at 3f79d81

Open on GitHub
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 2
3## Race recipe
4
5A race is two actors touching shared state with an ordering nobody enforces.
6Make it deterministic before fixing it.
7
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.

  1. 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.
  2. Forced interleaving. Inject an await or 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.
  3. 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.
  4. 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.
Raw lines 3 to 27