Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

Docs: rewrite the README and organize the guides

520470e19cd906bd6fff987e2d132d793ce56486 · Keenan Nemetz · 16d ago · parent c5902451

Docs: rewrite the README and organize the guides

Replace the long README with a landing page, split feature details into focused guides, and remove the roadmap and spike notes. Update comments that referenced them.

docs/spikes/ref-cas/README.md −96

@@ -1,96 +0,0 @@
1 # Spike A — metadata writes as CAS ref updates
2
3 **Question:** can issue/CR/ACL writes be commits on sidecar refs, and at what
4 throughput? The roadmap's core risk is that a comment becomes a `git update-ref`,
5 so this decides the Phase 2 schema shape.
6
7 ## Method
8
9 Each write produces a blob for one entity, folds it into the shard's tree, makes
10 a commit, and moves the ref with `git update-ref <ref> <new> <old>` — the old
11 value makes a lost race fail loudly instead of clobbering. A loser retries with
12 jittered backoff (capped at 200 attempts).
13
14 Four configurations, each run on a fresh repo:
15
16 | Mode | Refs | Writers | Batching |
17 |---|---|---|---|
18 | `shared-naive` | 1 | 32 | one commit per entity |
19 | `sharded-naive` | 256 | 32 | one commit per entity |
20 | `shared-batched` | 1 | 1 batcher | 64 entities per commit |
21 | `sharded-batched` | 256 | 8 batchers | 64 entities per commit |
22
23 Entities are ~450-byte JSON docs. Every run ends with a **verification pass that
24 counts tree entries in every ref**, so a silently dropped write cannot hide in a
25 good-looking throughput number. That check earned its place: the first version of
26 this harness lost 249 of 300 writes in the batched modes because a batcher
27 discarded work owned by a peer that had already exited. Lossless-looking
28 throughput was worthless; the write-verification pass caught it.
29
30 Hardware: 16-core Linux 7.0, 14 GB RAM, ext4 on local NVMe, git 2.53.
31
32 ## Results — 2,000 entities
33
34 | Mode | Entities/s | CAS retries/entity | p50 | p95 | p99 | Lost writes |
35 |---|---|---|---|---|---|---|
36 | `shared-naive` | **30** | 22.0 | 623 ms | 3,641 ms | 6,368 ms | 0 |
37 | `sharded-naive` | **1,240** | 0.06 | 23 ms | 45 ms | 62 ms | 0 |
38 | `shared-batched` | **1,425** | 0 | 41 ms/commit | 53 ms | 58 ms | 0 |
39 | `sharded-batched` | **6,966** | 0 | 6.7 ms/commit | 24 ms | 36 ms | 0 |
40
41 A single `git` invocation costs **1.10 ms** on this box, and a per-entity commit
42 costs 5–6 of them. That floor is what all of this is fighting.
43
44 ## Results — 20,000 entities
45
46 | Mode | Entities/s | p50 | Lost writes |
47 |---|---|---|---|
48 | `shared-batched` | **511** | 83 ms/commit | 0 |
49 | `sharded-batched` | **25,637** | 9.8 ms/commit | 0 |
50
51 This is the important pair. The single-ref design gets *worse* as it fills:
52 1,425/s at 2k entities collapses to 511/s at 20k, because every commit re-lists
53 and rewrites a tree that now holds 20,000 entries. That is O(n²) total work and
54 it has no floor. The sharded design instead gets faster (6,966 → 25,637/s),
55 because more entities per shard means batches fill completely.
56
57 ## Findings
58
59 1. **An unsharded metadata ref is not viable.** 30 writes/s with 22 retries per
60 write at 32 writers. Comment threads would visibly stall. Rejected outright.
61 2. **Sharding is the single biggest win** — 41× on its own (30 → 1,240/s) by
62 removing ref contention rather than adding hardware.
63 3. **Batching amortises the spawn cost** — 5.6× on top of sharding
64 (1,240 → 6,966/s), by turning 6 processes per entity into 6 per 64 entities.
65 4. **Composition matters more than scale-out.** Sharded + batched is 232× the
66 naive single-ref path on the same box, with zero retries.
67 5. **CAS is the right primitive.** Zero lost writes in every configuration,
68 including the pathological one. Nothing needed a lock server.
69 6. **Sharding bounds tree growth**, which is what keeps throughput flat instead
70 of degrading quadratically. This is the finding that constrains Phase 2.
71
72 ## Decision
73
74 - Metadata refs are **sharded** (256 shards, FNV-1a of entity id, matching
75 `shardOf` here). Non-negotiable: the 20k-entity run shows the alternative decays.
76 - Writers **batch** entity writes per shard on a short timer.
77 - Writes go through **one CAS commit helper**; no other code path may move a
78 metadata ref.
79 - Target of **≥500 writes/s sustained at 100k entities** is comfortably met with
80 headroom. Realistic issue traffic is orders of magnitude below this.
81
82 ## Caveats
83
84 - Throughput here excludes the derived-index update, which will add per-write
85 cost. Re-measure in Phase 2 once the index exists.
86 - Payloads are small. Large issue bodies or long comment threads will shift
87 numbers; the batching decision is what protects against that.
88 - Single box, warm page cache, local disk. Object storage over a network would
89 move the spawn cost line and should be re-measured if that's ever on the table.
90
91 ## Reproduce
92
93 ```sh
94 go run ./ref-cas -n 2000
95 go run ./ref-cas -n 20000 -modes shared-batched,sharded-batched
96 ```