Skip to content

Commit 2bc7388

Browse files
frristclaude
andcommitted
docs(s3-versioning): make the top-MST value a keyed union
Adopt the review's value-union suggestion: a single-version key stores its bare ObjectManifest CID (the pre-versioning one-block layout), and a key gains an ObjectLeaf, wrapped in a "/objectleaf/0" keyed envelope, at its first supersession. Unversioned keys never pay the leaf indirection, and the envelope makes the format self-describing, so future revisions take a new key instead of a data reset. Also from the review: drop the incorrect claim that re-serializing an inlined manifest mints a new CID (cbor-gen encoding is deterministic), restate the pointer-vs-inline tradeoff as leaf weight, rename displacement to supersession, clarify that RemoveBlob releases the space's claim while Piri owns physical deletion, and point promotion's first use at §7.2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 6296728 commit 2bc7388

2 files changed

Lines changed: 182 additions & 99 deletions

File tree

docs/architecture.md

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,10 @@ non-obvious parts:
107107

108108
**Versioning.** Buckets carry a versioning state — `Unversioned` (default), `Enabled`, or
109109
`Suspended`.
110-
- Versions of a key are grouped under its `ObjectLeaf`: the current version inline (single-seek
111-
reads), noncurrent versions in a per-key sub-MST ordered **newest-first**, version-scoped reads
112-
direct seeks — see *Versioned storage* below.
110+
- A key with a single version stores its manifest directly (one-block reads); once superseded it
111+
gains an `ObjectLeaf` grouping its versions: the current version inline, noncurrent versions in
112+
a per-key sub-MST ordered **newest-first**, version-scoped reads direct seeks — see *Versioned
113+
storage* below.
113114
- `ListObjects` (V1/V2) returns only the current, non-delete-marked version per key (one head per
114115
key, skip to the next). `ListObjectVersions` walks all leaves. Delete markers are skipped by the
115116
former, surfaced by the latter (with the `x-amz-delete-marker` response header).
@@ -119,9 +120,11 @@ non-obvious parts:
119120

120121
**Versioned storage (the per-key version tree).** The full design is
121122
**[`s3-versioning.md`](./s3-versioning.md)**. In brief: the top MST is keyed by the **plain object
122-
key**; each leaf is an `ObjectLeaf` `{current, prev, nullSeq}` where `current` is the head version
123-
(single-seek reads) and `prev` is a per-key sub-MST of noncurrent versions keyed newest-first by
124-
the inverted per-bucket ordinal (`seq`, from `buckets.next_version_seq`). `seq` (ordering,
123+
key**. A single-version key's value is its bare `ObjectManifest` CID; a superseded key's value is
124+
an `ObjectLeaf` `{current, prev, nullSeq}`, wrapped in a `"/objectleaf/0"` keyed envelope so the
125+
two forms are told apart exactly, where `current` is the head version (single-seek reads) and
126+
`prev` is a per-key sub-MST of noncurrent versions keyed newest-first by the inverted per-bucket
127+
ordinal (`seq`, from `buckets.next_version_seq`). `seq` (ordering,
125128
internal) and `version_id` (identity, the client handle — a ULID token, or `"null"`) are
126129
**separate fields**, so the null version's replace-in-place semantics fall out instead of needing
127130
a sentinel. Object keys need no escaping and no terminator, and fit `MaxKeyBytes` as-is; the
@@ -163,7 +166,9 @@ validates/echoes them, independent of the internal sha256 content address.
163166

164167
The catalog is the per-bucket namespace: the MST plus the object manifests it points at.
165168

166-
**The MST** maps each composite key to a manifest CID. It is the forked, go-cid-only MST. It is the
169+
**The MST** maps each plain object key to its value block — the bare manifest for a
170+
single-version key, an `ObjectLeaf` once superseded ([§3](#3-the-s3-layer)). It is the forked,
171+
go-cid-only MST. It is the
167172
**source of truth for bucket state**, not merely a local index: because it is a content-addressed,
168173
self-verifying Merkle structure shipped to Forge (the catalog plane), a bucket's entire namespace is
169174
recoverable and portable from the network — the property a plain Postgres table (fast local index, but
@@ -185,7 +190,9 @@ depends on the single- vs multi-shard split in [§8](#8-retrieval-addressing-whe
185190
MST (bucket)
186191
leaf key = "photos/cat.jpg" (plain object key)
187192
188-
└──▶ CID ──▶ ObjectLeaf ← per-key version group (s3-versioning.md §2)
193+
└──▶ CID ──▶ single version: the bare ObjectManifest (below) — one block, one fetch
194+
──▶ superseded key: ObjectLeaf ← per-key version group, under the
195+
"/objectleaf/0" envelope (s3-versioning.md §2)
189196
├ current { seq, versionId, manifest CID } (the head version, inline)
190197
├ prev CID of the per-key sub-MST of noncurrent versions (nil if none)
191198
└ nullSeq a noncurrent null version's seq (0 = none)

0 commit comments

Comments
 (0)