Skip to main content

Module packgen

Module packgen 

Source
Expand description

The substrate’s GitPackGen implement (PackGen) — pack generation over the real Find front (ObjectFront’s per-session SessionFind views) and the negotiation ack seam’s real predicate (the trait’s common_haves, ADR-014 §5) — substrate.md §object storage “Packs”; ADR-004 is authoritative for the pipeline.

§The pipeline (ADR-004, composed exactly)

run_pipeline (the module’s pipeline function) is the wave-2 doubles’ composition re-landed on the substrate Find (the POC-O packgen.rs reference shape):

peel wants to commit tips (tag chains walked; peeled tag objects kept)
  boundary_haves peeled identically (the closure-subtraction sets)
  → commit-ancestry walk (gix_traverse::commit::Simple, Parents::All,
    non-commit tips as-is)
  → count::objects_unthreaded (TreeContents) over the closure inputs
  [+ AsIs over the peel-exposed tag chains]
  → counted-object existence pre-check
  → entry::iter_from_counts → InOrderIter
  → bytes::FromEntriesIter (V2, sha1) → the sink

The two-stage closure is mandatory (ADR-004: TreeContents does not follow commit parents; feeding tip commits alone produces incomplete packs — POC-2’s course correction). Missing objects abort with StorageError::ObjectMissing naming the missing object (never a broken pack emitted downstream): every counted object is existence-checked before the byte pipeline opens, and the entry stage’s own missing_objects statistics remain the abort posture.

Generation is negotiation-agnostic: boundary sets in, pack out. The limits parameter is accepted and unused for v1 — the doubles’ own posture; ADR-009 budgets the receive side, and ADR-004 pins no generation-side size budget (max_pack_size is GitPackIngest territory).

§Both serving shapes (substrate.md §object storage “Packs”)

The mode selection is the substrate impl’s internal posture — the constructor level (the trade: a per-call selection would surface a mode parameter the frozen trait signature does not carry).

  • Direct (PackGen::direct): the generated pack streams through the sink, no storage round-trip.
  • Through-store (PackGen::through_store): the pack is put once into the store (the pack rides the canonical blob family — PACK_ALGORITHM below — with the store’s own tier dispatch routing big packs to the large tier), then served through read_range windows into the sink — every window’s out-of-band slice digest is re-derived and verified mid-flight (the store does not verify a range read; the front-line impl re-derives, the same posture ObjectFront’s loose reads carry). A request-digest PackCache lets repeat fetches ride the stored pack without regeneration; releases and evictions are pins the next sweep reclaims (PackCache::release_all is the PackHousekeeping consumer’s release path). Large tier required for through-store serving — a kv-only shape rejects over-threshold packs, the rejection riding the store’s own put-time kv-only overflow (mapped to StorageError::Io).

§Threading and the one bridge

The ADR-004 pipeline runs on tokio::task::spawn_blocking with owned handles moved in (backend.md §concurrency model: the permit stays wire-side, the impl’s threading invisible). Every sync→async hop inside the pipeline goes through ObjectFront::block_on — the odb-front task’s one-place bridge rule (SessionFind’s sync legs already own theirs). The through-store put rides the async path directly (generate’s own async context): the pipeline streams the pack through a bounded duplex pipe (PACK_PIPE_CAPACITY, the backpressure boundary) into Store::put_as — no unbounded buffer on the async path.

§Failure posture on the pack put

A generation that fails after the put began leaves the pack put’s fate to the stream: an early pipeline end closes the pipe and the put commits what it received — a truncated or empty orphan pack that holds its pin only across the call. The error is returned and any put-returned pin is dropped, so the orphan is sweep-eligible: a failed fetch is a failed fetch, not partial repo state (the substrate’s GC-decreed recovery shape). Nothing partial is ever served: the pack never reaches the sink on an error path, and a cached pack is inserted only once both the pipeline and the put succeeded. The put error wins over the pipeline’s pipe-closed noise — the honest cause (a kv-only over-threshold rejection, a backing failure) is what the caller sees.

Structs§

PackCache
The request-digest pack cache of the through-store shape: repeat fetches of the same (canonicalized) want/have set ride the stored pack without regeneration. Evictions (capacity bounded) and releases (PackCache::release_all) drop the entry’s pins — the sweep reclaims the pack blobs the next time it runs (substrate.md §object storage: “releases/evictions are pins the next sweep reclaims”).
PackGen
The substrate’s GitPackGen implement: ADR-004’s pipeline over the object front’s real Find, the ADR-014 §5 ack predicate, and one of the two serving shapes (constructor-level posture — through the frozen trait signature runs no mode parameter; the wire layer is unchanged either way).