Skip to content

Commit a607451

Browse files
frristclaude
andauthored
feat(s3): implement bucket versioning (per-key version tree) (#66)
* feat(s3): implement bucket versioning (per-key version tree) Implements docs/s3-versioning.md: the ObjectLeaf/VersionNode data model with a per-key prev MST (newest-first by inverted seq), per-bucket seq allocation under the commit lock, strict ULID version ids carrying the seq in the low 64 entropy bits, the §5 write rule behind PUT / Copy / CompleteMultipartUpload / delete-marker insertion, version resolution for reads with marker 404/405 semantics and x-amz-delete-marker emission, version-scoped deletes with promotion, DeleteObjects dispatch, per-version blob_refs claims, ListObjectVersions with pagination, resumption, and delimiter grouping, Put/GetBucketVersioning, and the versioned DeleteBucket guard. Response version ids gate on the versioning state read under the commit lock, so a racing PutBucketVersioning cannot mint an id the response then omits. Tests: unit coverage for token classification, every displacement row, prev-null eviction, promotion, per-version claim add/release, list pagination and delimiter grouping, and the bare-leaf invariant; itest gains the versioning conformance categories (pass/xfail-ratcheted, run with the versioned teardown conf) plus the never-versioned VD listing group. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * itest: promote the versioning partition to pass (delete-release era) With the full release chain in place — ingot /blob/remove (FIL-588), hilt#37's blob.Remove write-set grant, and smelt#19's piri blob/release + blob/reject seed delegations — every body-storing versioning row passes its bucket teardown. The unexpected-pass ratchet flagged all 40: the 7 ListObjectVersions rows, the VD listing row, and 32 Versioning rows move to the pass tables. The one remaining XFail (DeleteObject_non_existing_objects) needs object-lock-enabled bucket creation, which ingot does not model. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(s3): store single-version keys as bare manifests behind a value union Adopt the revised design of docs/s3-versioning.md: the top-MST value is a keyed union. A key holding one version stores its ObjectManifest CID directly — the pre-versioning one-block layout — and gains an ObjectLeaf, wrapped in a "/objectleaf/0" envelope, at its first retained supersession, keeping it from then on (invariant 6). Unversioned keys never pay the leaf indirection, and readers dispatch on the envelope exactly: an unknown envelope key is an error, never a zero-filled cross-type decode. bucket gains the hand-written envelope codec (EnvelopedLeaf) and the read-side dispatch (ObjectValue); commitVersion, resolveVersion, the scoped and unversioned deletes, listWalk, and ListObjectVersions all handle both value forms; displaced/displacement becomes superseded/ supersession to match the spec. The rewritten bare-key structural test now asserts the value form through ObjectValue — the old direct ObjectLeaf decode would zero-fill a manifest and pass vacuously. Verified: unit suite, go vet, make gen no-op, and the full itest conformance partition against the smelt stack. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(s3): encode catalog values as a cborgen keyed union Adopt the revised §2.1: every value block is the keyed union — manifests under "/objectmanifest/0", leaves under "/objectleaf/0" — replacing the hand-written envelope codec and the bare-manifest fallback. ValueUnion is cborgen-generated from omitempty pointer arms; the strict ObjectValue / EnvelopedManifest / EnvelopedLeaf wrappers require exactly one arm, so a block carrying no known union key (a newer format, or a pre-union bare manifest) fails loudly instead of zero-fill decoding. Every manifest block read/write goes through the enveloped form, keeping a version's identity CID one encoding everywhere it appears. The ListObjectVersions resumption comment now spells out why token markers need no existence check (their seq is a position; only positionless null markers fall back to re-emission). Verified: unit suite (union round-trip, unknown-key and pre-union-block rejection), go vet, stable make gen, and the full itest conformance partition against the smelt stack. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 6a7d9e6 commit a607451

26 files changed

Lines changed: 3286 additions & 295 deletions

CLAUDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,8 @@ forge-mode daemon. Two tiers:
172172
- **`make itest` — integration** (`itest/`, build tag `itest`, Docker):
173173
boots the smelt Forge stack with THIS working tree's binary mounted over
174174
the published image.
175-
- **`versity_{bucket,object,multipart}_test.go`** — the S3 conformance
176-
partition: per upstream versitygw group, a curated pass table (every case
175+
- **`versity_{bucket,object,multipart,versioning}_test.go`** — the S3
176+
conformance partition: per upstream versitygw group, a curated pass table (every case
177177
must pass) and an XFail table (known-failing, reported as SKIP; an
178178
*unexpected pass* fails the test — the cue to promote the row). One
179179
shared stack serves all categories (`TestForgeVersity`).

0 commit comments

Comments
 (0)