Skip to content

test(sim): mock the database as a transport, with injected rollbacks - #114

Merged
kyscott18 merged 1 commit into
mainfrom
test/sim-db-transport
Nov 2, 2026
Merged

kyscott18 merged 1 commit into
mainfrom
test/sim-db-transport

Conversation

@kyscott18

@kyscott18 kyscott18 commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

The sim's database mock (makeSimDbTransport) now wraps a DbTransport and sits under the runtime's real makeDbClient (#112), the way makeSimTransport sits under layerRpc. Before, it wrapped the finished Db, so the runtime's retry policy never ran in the sim. Latency, io gating and the no-clock/no-random rules inside a transaction are unchanged. makeDbClient is now exported.

Injected errors. SIM.db.errorRate is picked per seed from [0, 0.01, 0.05]. With that probability, a transaction rolls back after f ran. The runtime sees DbError({ sql: "COMMIT", notApplied: true }), as on a serialization failure at COMMIT, and runs f again from the start. That exercises the client's retries and the "f can run more than once" contract on Db.

  • Each client attempt draws once, keyed by the statements it ran and its attempt count.
  • At most 0.05, five failed attempts in a row have probability about 3×10⁻⁷ per transaction, so running out of retries is negligible.
  • Only transactions draw errors: in the sim, the runtime issues no statements outside them.
  • Not injected: "applied, then outcome unknown" (notApplied: false). That stops the runtime, so it only makes sense once the sim simulates restarts.

Flakes stay out of the simulation. A real database failure with notApplied retries inside io, on the real clock (exponential from 10 ms, 5 times), then dies, as chain.ts does for Anvil. Only drawn rollbacks reach the runtime, so a flake can't move virtual time or the runtime's seeded draws.

  • A drawn rollback leaves io as InjectedRollback, which the flake retry passes by, and becomes the DbError only after the latency.
  • A flake that runs f again records its statements from scratch and reuses that attempt's draw.

Checked (Postgres, sequential, on a937227):

  • bun test with DATABASE_URL: 97 pass.
  • Seeds 0xe 0x11 0x41 0xa0 pass and draw no rollbacks. 0xe, 0x41 and 0xa0 match main exactly (virtual time, submissions, blocks, batches).
  • With a local reorgRate: [0] patch, seed 0x01 (rate 0.05) passes full validation with 51 injected rollbacks.
  • Before the rebase, a throwaway wrapper failed the real transport at random (Math.random, before and after f). Runs of 0x01 that absorbed 105 and 130 flakes were identical to the clean run.

🤖 Generated with Claude Code

@kyscott18
kyscott18 force-pushed the test/sim-db-transport branch from 251d2c2 to 174406f Compare November 2, 2026 21:22
@kyscott18
kyscott18 marked this pull request as ready for review November 2, 2026 21:23
@kyscott18
kyscott18 changed the base branch from feat/db-pglite to main November 2, 2026 21:24
The simulation's database mock wraps a `DbTransport`, under the runtime's
real `makeDbClient`, as `sim-rpc.ts` sits under `layerRpc`. The runtime's
retry policy now runs in the simulation.

`SIM.db.errorRate` ([0, 0.01, 0.05]) is how often a transaction rolls back
after `f` ran, as on a serialization failure at `COMMIT` (`notApplied`),
so the client runs `f` again from the start. Each client attempt draws
once, keyed by the statements it ran and how many times it ran them.

A flake of the real database retries inside `io`, on the real clock, then
dies, as `chain.ts` does for Anvil, so only drawn rollbacks reach the
runtime and a flake moves neither virtual time nor the runtime's draws. A
drawn rollback leaves `io` as `InjectedRollback`, which the flake retry
passes by, and reaches the client as a `DbError` after the latency.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyscott18
kyscott18 force-pushed the test/sim-db-transport branch from 174406f to bd82647 Compare November 2, 2026 21:28
@kyscott18
kyscott18 merged commit 3ef7c9e into main Nov 2, 2026
7 checks passed
@kyscott18
kyscott18 deleted the test/sim-db-transport branch November 2, 2026 21:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant