Relationships
#3418 v3 first clone on Linux is 2.6x slower than v2: batch the catalog refresh during catch-up
Opened by stack72 · 10/13/2026· Shipped 10/14/2026
Background (read this first)
swamp is reworking its datastore layer (tracking: swamp-club#2865). In the new format, v3, every change is a commit in a single-writer SQLite commits.db. Content is stored as content-addressed chunks, and each checkout rebuilds its files from the commits. v3 is opt-in today for filesystem datastores (swamp-club#3279), and swamp-uat's filesystem-v3 backend tests it in CI.
Phase 4 is "solo mode on buckets":
- the CLI is the single writer of a local
commits.db; - Litestream replicates it to the bucket;
- chunks go to the bucket through
storage/1; - a fenced writer lease makes sure only one machine writes.
Done so far:
- A, swamp-club#3357: replication cost on a bucket model, choosing an idle sidecar. See
design/enablers/datastore-litestream.md, section "Phase 4A". - B, swamp-club#3358: download, verify and cache a pinned Litestream (0.5.17), dormant:
src/infrastructure/datastore_v3/litestream/litestream_binary_provider.ts. - C, swamp-club#3359: the fenced writer lease (
src/domain/datastore_v3/writer_lease.ts,src/infrastructure/datastore_v3/v3_writer_lock.ts). Every CLI write command takes a new fence, and serve keeps one for its lifetime.commits.dbkeeps a store-widewriter_fence.
The decisions are in design/enablers/datastore-litestream.md, under "Decision" and "What changes for Phase 4D and 4E", and in design/enablers/datastores.md, under the "v3 …" sections.
The gap
swamp-club#3337 removed the per-file fsync from v3's catch-up. On macOS a fresh checkout of 1,000 items fell from 26.0 s to 1.6 s, 1.45× v2. Linux still lags.
swamp-uat's Remote Datastore Benchmarks job (run 38477551054, ubuntu x86_64, release 20261013.220021.0-sha.a3fab18f, 3 samples), "first clone: setup + data list (1000 items)":
| Backend | p50 | p95 |
|---|---|---|
| filesystem (v2) | 1,894 ms | 1,924 ms |
| filesystem-v3 | 4,845 ms (2.6×) | 5,738 ms |
#3337's PR measured 1.9 s against 0.9 s (2.1×) in a linux-arm64 container on a Mac, and named two causes:
- overlay-filesystem
mkdir, which may be a container artifact; - the catalog's per-version SQLite transaction: the catch-up refreshes the query catalog (
.swamp/_catalog_v3.db, through v2'sCatalogStore) one version at a time, each in its own transaction.
Batching that touches v2's CatalogStore, so #3337 left it out.
Goal
Bring a Linux v3 first clone close to v2: within 1.5× on the CI job's first-clone line, as macOS is.
- Profile first, on a Linux host or the CI runner, not a container on a Mac. Say where the time goes before changing anything.
scripts/bench_read_model_catch_up.tsbenches the catch-up in-process. - If the catalog dominates: add a batched path to
CatalogStore, so one catch-up page refreshes the catalog in one transaction, and use it from the v3 read model.- v2 keeps calling the existing per-version path, unchanged.
- Apply the v2 gate: v2 suites unedited, and the route recorder identical.
- Keep #3337's invariants:
- the cursor never runs ahead of durable files;
- the boot-id check stays;
- the barrier on platforms without a boot id stays.
Tests
- Existing suites pass unchanged: the read-model crash safety, shared keys, the v2 oracle, the reference-model property test, and
CatalogStore's. - New: the batched catalog path gives the same catalog as the per-version path for the same commits (a property test over random commit sequences).
- The figure: put the swamp-uat benchmark's filesystem-v3 and filesystem first-clone lines in the PR, from a Linux run against the branch build if possible.
Acceptance
- Linux filesystem-v3 first clone (1,000 items) is within 1.5× of filesystem v2 on swamp-uat's benchmark job.
- v2's behaviour and performance don't change.
Blocked by: nothing. Runs in parallel with D1. Phase 4's dogfood and benchmark item (I) waits on it.
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.