Skip to content
zit Artifact [3b5f8e1f5d]
Login

Artifact [3b5f8e1f5d]

Artifact 3b5f8e1f5da7571b30b52d484320be5a2daad052d455a5b459f31272387db7e4:


# zit 0.3.12

zit 0.3.12 makes sync announce only what its clusters do not already say.
A pull or push between two peers used to open with one `igot` line for
every held artifact — 904,898 lines, about 63 MB per direction, per round
on pkgsrc, whether or not anything had changed. zit now indexes the
cluster artifacts it holds, announces only the unclustered tail, and mints
clusters of its own where fossil does. The same no-op pull sends 4.7 KB.

No artifact, tag namespace, sync message, or on-disk format changed. The
cluster index lives in `.zit-cache/` and is rebuilt from the log if lost.

## Bounded announcement over the cluster index (zit#184)

A cluster artifact (M cards and a Z card, nothing else) is `igot` in bulk:
once a peer folds an artifact into a cluster it stops announcing it
individually, and a later cluster names the earlier one, so the whole
repository is reachable from the newest. zit consumed clusters on receipt
but never used them on send and never minted any. Now:

- Each sync open resolves a small index of the held clusters into what they
  already name (not announced again) and what they promise that this
  repository never collected (seeded as phantoms, so an interrupted pull
  resumes from what its own clusters announced — the recovery fossil gets
  from durable phantom rows, which zit does not write).
- The server announces held ∧ ¬clustered (fossil's `send_unclustered`);
  `pragma send-catalog` restores the full inventory (the RFC's resync
  state); `pragma req-clusters` emits every cluster's igot, enclosed per
  §5.3. A pushed cluster's unheld members are interned so the same reply
  begs for them.
- The client announces held ∧ ¬clustered; clone is exempt as before.
- A pull reply mints clusters — sorted, ≤800 M cards, md5 Z — once 100 or
  more held artifacts are unclustered, at fossil's point and thresholds. A
  repository born in zit clusters itself the first time anyone pulls from
  it.
- Promises a held cluster makes that the peer cannot keep (private or
  shunned upstream) are reported once as a warning; the pull still says
  `already in sync` and exits 0. They are requests the run made on its own
  initiative, not expectations the peer set — the same rule that already
  keeps a shunned phantom from failing a pull.
- Absent or stale index → the old full announcement. Bytes, never
  correctness.

Measured, no-op pull, release build, loopback:

| store | artifacts | clusters | request before | request after | warm pull |
|---|---|---|---|---|---|
| pkgsrc (fossil-imported, flat) | 904,898 | 2,439 | ~63 MB | 4,748 B | 5.4s |
| sqlite (fossil-imported) | 131,558 | 1,004 | ~9.2 MB | 1,183 B | 0.74s |
| zit-native, 161 artifacts | 161 | 0 → 1 minted | 161 igot | 201 B | — |

The index costs one walk of the store the first time a sync opens it
(pkgsrc 210s, sqlite 19s — once), then a ~100 KB sidecar adopted with no
walk. Evidence seam: `ZIT_SYNC_STATS=1` prints rounds, bytes each way,
clusters indexed, and rids walked.

## Compatibility

- Sync protocol unchanged; a fossil peer sees a client that announces its
  unclustered tail and a server that mints clusters — fossil's own shape.
- No on-disk or wire format changed. Fossil RFC revision 00 remains the
  target.
- No command or flag changed. One new stderr line on a pull whose clusters
  promise artifacts the peer cannot supply.

## Qualification

`zig build test`, `e2e`, `smoke` (Fossil interop against a real `fossil`),
`muscle` (41 differential steps), `git-import-e2e`, `git-export-e2e`, and
`dst`, plus the three-runner CI battery. The table's before figures are the
0.3.11 announcement volume (one 70-byte igot line per held artifact); the
after figures are `ZIT_SYNC_STATS` on the same stores.