Guided Problem
MiniDock Build 13: Reclaim Only Unreachable Content
- Time
- 25m
- Level
- intermediate
- Artifacts
- not specified
Tags, References, and Garbage
Design claim: Mutable references and immutable content require reachability-based cleanup rather than deletion by name.
Starting model
- You can publish a complete release without retransmitting shared content or exposing partial state.
- The useful vocabulary at this point is deliberately small: image tag, mutable reference, live root, reachability.
Names move; content remains
Operators need movable release names and reclaimed storage without deleting layers still shared by another manifest. Distribution adds concurrency and remote failure to an immutable object graph. The useful question is when another client may safely observe a release.
The tempting shortcut is straightforward: delete every blob when its tag is removed. The happy path makes the shortcut appear atomic even though metadata and large byte transfers complete at different times. Tags point to manifests, manifests share configs and layers, and untagged manifests or active uploads may still make content reachable. Once a reader or retry can interleave with publication, visibility and durability need an explicit order.
Treat references as roots
Retagging latest from build 41 to build 42 rewrites one row in the reference store — the old manifest and its layers still exist, now possibly unreferenced ("dangling"). Multiply by hundreds of builds and the blob store fills with orphans. But per Lesson 8's DAG, you can never delete a blob just because one manifest stopped needing it — another image may share it. The only safe answer is reachability.
References are mutable roots over an immutable object graph, and garbage collection removes only objects unreachable from every protected root. The protocol separates authorization, object identity, transfer, and publication so each response has a precise meaning.
Read the architecture as a trust boundary around metadata and bytes, then identify the one operation that makes a graph visible.
flowchart TB
T1["root: tag api:2.2"] --> M2["manifest v2.2 ✓"]
C1["root: container c-42
(runs untagged build 41)"] --> M1["manifest v2.1 ✓"]
M2 --> BASE["base layer ✓
(shared — 2 parents)"]
M1 --> BASE
M2 --> APP2["app layer v2.2 ✓"]
M1 --> APP1["app layer v2.1 ✓"]
M0["manifest v2.0
(no root → dangling)"] --> BASE
M0 --> APP0["app layer v2.0 ✗ sweep"]
style M0 fill:#fff8ec,stroke:#f2ddb0
style APP0 fill:#fff8ec,stroke:#f2ddb0
It shows tags as roots over manifests and shared immutable objects. Its central claim is that content is reclaimable only when no live root or in-progress publication can reach it; the labels therefore describe authority rather than decorative grouping.
Mark first, sweep after the graph is stable
Garbage collection walks the DAG from every root: all tags in the reference store, plus every existing container (a container's image must survive even if untagged — Lesson 13's dashed edge). Everything reached is marked live; every unmarked blob is swept. The delicate part is concurrency: a push racing a GC could reference a blob mid-sweep, so real registries GC with writes paused or use grace periods for recent blobs.
Tag updates change root edges; mark walks manifests and descriptors; sweep removes unmarked content after writers are excluded or coordinated. At each message, ask what the client and registry can now prove about the object graph.
The second figure tests the same model in motion: it traces mark and sweep while preserving shared and publishing content.
sequenceDiagram
participant GC as GC
participant RS as Reference store
participant CS as Container store
participant BS as Blob store
GC->>RS: all tags → root manifests
GC->>CS: all containers → root images
GC->>BS: walk DAG from roots, mark reachable
BS-->>GC: marked: v2.1, v2.2 trees (base counted once)
GC->>BS: sweep unmarked (v2.0 manifest + its unique layer)
note over GC,BS: shared base survives —
it was reached from live roots
It traces mark and sweep while preserving shared and publishing content. The ordering is valid only when it continues to preserve the stated invariant under retries and interruption.
Shared content outlives any one tag
One tag is deleted while another manifest shares its base layer, so collection must retain the shared digest. Interruption may leave resumable work, but it must not leave a visible release whose referenced content cannot be served.
Content is reclaimable only when no live root or in-progress publication can reach it. The rule binds remote visibility to verified reachability rather than to connection success.
Let MiniDock reclaim only unreachable truth
MiniDock separates human release names from content lifetime and collects by graph reachability. MiniDock gains a distribution contract while placement remains a design decision.
What carries forward
- Content is reclaimable only when no live root or in-progress publication can reach it.
- Reclaim unreachable image objects without breaking tagged or active releases.
- The rejected shortcut remains a diagnostic: if the design starts depending on it again, the original constraint has probably been lost.
See what actually stuck.
Take the practice scenarios now.