Skip to content

feat(store): ADR 0003 G2 content-defined chunking (X6 winner K1) - #85

Merged
aien-spark merged 11 commits into
mainfrom
feat/adr0003-g2
Oct 31, 2026
Merged

aien-spark merged 11 commits into
mainfrom
feat/adr0003-g2

Conversation

@aien-spark

@aien-spark aien-spark commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

What changed

ADR 0003 step G2 (content-defined chunking, after experiment X6). Blobs above a threshold are cut into chunks by an in-house FastCDC-style gear chunker (no new crates) and stored in the segments once per ChunkDigest; the blob's identity stays its BlobDigest over the whole content. Chunked WAL v2 frames (flag 0x02), a chunk index and manifests rebuilt by replay and persisted in the checkpoint segment index (version 2 only when chunks exist), commit-time dedup against the chunk index and earlier records of the same batch, reads that reassemble chunks and verify the BlobDigest. StoreLimits.chunking holds the parameters; old whole-blob stores (A1 files, A2 segments) stay readable.

Base: main with A2 (#77) and B1 (#81) merged. Revision records (B1) still store their blobs whole (inline, never chunked).

Why

X6 fixed the rule before its runs (evidence/g2-content-defined-chunking/x6/RULE.md, commit ab9ccc0, sha256 dd4ed8f9...). Result (host-observed, shared host, one agent, 1,037 changes / 24,093 revisions / 433.8 MB of real AIEN history, 3 reps): K1 (T 16 KiB, avg 4 KiB) is the only eligible candidate: 20.7 % fewer log bytes than no chunking, 1.095x seconds per accepted change (limit 1.10, thin margin), 1.068x seconds per read. Seconds per accepted change 2.133 ms vs the 1.006 ms A2 flush bound (0.471 of bound at one agent; no chunking 0.516). Joules (SoC package meter only) are inside noise above idle. ChunkParams::X6_DEFAULT is K1. See x6/RESULTS.md and docs/G2_CONTENT_DEFINED_CHUNKING.md.

How verified

  • Acceptance: g2_every_blob_round_trips_byte_for_byte_with_unchanged_blob_digest, g2_dedup_between_two_revisions_follows_the_x6_rule (plus batch, parameter, damage, crash, index v2 and old-store tests in crates/synapse-store/src/g2_tests.rs).
  • Red before green: G2 tests do not build on A2; on A2 a 30 KB edit of a 430 KB blob appends 434,176 log bytes; 8 mutations each make tests fail (evidence/g2-content-defined-chunking/red-green/).
  • Local gate (not on CI, which is jammed): cargo fmt --all --check && cargo clippy --locked --workspace --all-targets -- -D warnings && cargo test --locked --workspace all pass.
  • Old stores: A1 fixtures and an A2 segment store written by the A2 code (tests/fixtures/g2-a2).

Deviations and limits

ChunkDigest lives in synapse-store, not synapse-core (avoids the parallel B1 envelope work). No format marker bump: pre-G2 builds fail closed on chunked frames and indexes. UNVERIFIED: K1 vs the ingest limit on a quiet host (margin is within noise), cold reads, multi-agent ingest with chunking, power loss. Tested again after rebasing onto main (342 workspace tests pass). NOT_RUN: crumb compile (synapse has no crumb.lock pin, and its root .crumb is already UNCOMPILED on the A2 baseline).

Fixes #45

🤖 Generated with Claude Code

drakestapleton and others added 5 commits October 16, 2026 05:53
…dup rule, decision)

ADR 0003 section 6.4 X6 for step G2 (#45): chunker definition, seven
chunked candidates plus the no-chunking control, the four AIEN repository
histories at pinned commits, the measurements, the deduplication rule G2 is
accepted against, and the decision criterion. Committed before any run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blobs above the chunk threshold are cut by a FastCDC-style gear chunker with
normalized chunking (the chunker fixed in evidence/g2-content-defined-chunking/x6/
RULE.md) and stored as chunks inside the segments, one copy per ChunkDigest. The
blob identity stays its BlobDigest over the whole content; reads reassemble the
chunks (contiguous runs in one read) and verify the BlobDigest.

- chunk.rs: ChunkDigest, gear table, cut rule, ChunkParams (StoreLimits.chunking,
  refused at open when invalid), manifests.
- WAL v2 chunked frames (flag 0x02): manifests, chunk table, chunk bytes; payload
  digest over the prefix, every chunk verified against its digest on recovery.
- Commit drops chunks already indexed and manifests of blobs already stored,
  counting records accepted earlier in the same batch (X6 rule section 5).
- Chunk index and manifests rebuilt by replay and persisted in the checkpoint
  segment index (version 2 only when it holds chunks; version 1 otherwise).
- Old stores stay readable: A1 fixtures and an A2 segment-store fixture written
  by the A2 code (generator under evidence/.../fixture-generator).
- Tests g2_*: round trip, dedup between revisions, inside a record and a batch,
  parameter changes, damage refusals, crash after batch sync, index v2 decoder.
- Red/green: red against A2 (behaviour probe and compile), 8 mutations each
  caught (evidence/g2-content-defined-chunking/red-green/).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mmitted)

`synapse-bench x6` reads the four corpus histories from bare clones (first-parent
commits, added or modified regular files, blob bytes provided the first time they
appear), runs the eight configurations three times with the order rotated per
repetition, and records raw per-change and per-read times, log and content bytes,
and one aien_spbm energy window per phase. `x6-report` applies the RULE.md section 6
criterion (unit-tested on constructed runs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sults

X6 (RULE.md sha256 dd4ed8f9..., unchanged) ran on real AIEN histories: K1 (T 16 KiB,
avg 4 KiB) is the only eligible candidate (20.7 % fewer log bytes, 1.095x seconds per
change, 1.068x per read). ChunkParams::X6_DEFAULT is K1. Adds raw.json, report.json,
RESULTS.md, manifest and checksums, and docs/G2_CONTENT_DEFINED_CHUNKING.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Drake Stapleton <drake@aienos.com>
…lobs)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Drake Stapleton <drake@aienos.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Drake Stapleton <drake@aienos.com>
@aien-spark aien-spark added the needs-rebase The rebase train could not rebase this onto main by itself label Oct 24, 2026
@aien-spark

Copy link
Copy Markdown
Collaborator Author

This was generated by AI during triage.

The rebase train (tools/aien-local-ci/rebase-train.sh) could not move this pull request onto the latest
main by itself: rebase onto origin/main conflicts. It resolves only .crumb files (generated by the crumb compiler) on its own.

Files that need a person (or an agent) to resolve:

  • crates/synapse-store/src/lib.rs

Rebase the branch onto main by hand, resolve these files, and push. The needs-rebase label was added;
the train tries again on every run and removes the label once a rebase goes through.

@aien-spark

Copy link
Copy Markdown
Collaborator Author

parked: GPU-stack focus; label returns when resumed

@aien-spark
aien-spark merged commit 2c2046c into main Oct 31, 2026
6 checks passed
@aien-spark
aien-spark deleted the feat/adr0003-g2 branch October 31, 2026 03:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-rebase The rebase train could not rebase this onto main by itself

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ADR 0003 G2: Content-defined chunking

2 participants