Skip to content

HDDS-16082. [Ozone versioning] [T5] SUSPENDED semantics - #10955

Open
symious wants to merge 23 commits into
apache:HDDS-15728from
symious:HDDS-16082
Open

HDDS-16082. [Ozone versioning] [T5] SUSPENDED semantics#10955
symious wants to merge 23 commits into
apache:HDDS-15728from
symious:HDDS-16082

Conversation

@symious

@symious symious commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

Please only review commits starts with "T5".

Sub-task Scope Acceptance
T5.1 PUT null-slot overwrite SUSPENDED PUT locates and replaces the null slot no version accumulation; old null-version data reclaimed; Enabled-era versions unaffected.the new record becomes the current version even when the null slot was noncurrent
T5.2 DELETE null marker SUSPENDED DELETE writes an isNullVersion=true marker overwrites the existing null slot; historical versions stay readable/deletable by versionId
T5.3 pre-versioning keys, zero migration keys written before enabling versioning interpreted as null versions no data rewrite of any kind; versionId=null addressing hits the legacy record
T5.4 MPU version retention multipart upload completion follows the same version-retention path as PUT the superseded version is demoted to the versionedKeyTable instead of being dropped — it stays readable by versionId and its blocks are neither leaked nor reclaimed; the completed key carries a generated versionId; a SUSPENDED completion takes the null slot with the same semantics as T5.1

What is the link to the Apache JIRA

https://issues.apache.org/jira/browse/HDDS-16082

How was this patch tested?

unit test

symious and others added 23 commits July 16, 2026 11:06
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…equest

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Key names in OBJECT_STORE buckets contain '/' verbatim, so a '/' separator
interleaves a key's versions with those of keys nested under it, breaking
the single-seek promotion and the merged ListObjectVersions order.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rename NULL_VERSION_ID to UNSET_VERSION_ID: 0 is the unset value of the
optional proto field, not the id of the null version. A null version carries
a normally generated id and is identified by isNullVersion, so that a null
created between two versioned writes orders as the middle version rather
than the oldest.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The reclaim branches skipped S3-versioned buckets only because the legacy
isVersionEnabled flag is kept in sync with an ENABLED status. Depend on the
status directly, so that dropping that sync cannot strand a version record
by reclaiming the blocks it still refers to.

OMKeyCommitRequestWithFSO is left alone: isS3VersioningEnabled() requires the
OBJECT_STORE layout, so the check is structurally false on the FSO path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
KeyArgs gains versionId and nullVersion: a null version carries a normally
generated id like any other version, so the null slot needs a selector of its
own rather than a reserved id. The current version is checked before the
versionedKeyTable, so naming it costs no extra read, and the null slot is found
by a bounded scan of the key's version prefix.

Addressing a delete marker by version is reported as KEY_IS_DELETE_MARKER
rather than KEY_NOT_FOUND: S3 answers 405 for it and 404 only for a read that
lands on a current marker without naming a version. The status mapping itself
belongs to the gateway.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DELETE ?versionId= is the only delete that destroys data on a versioned bucket:
the version leaves the versionedKeyTable and its blocks go to the deletedTable,
which stays the single path through which version blocks are reclaimed. The null
slot is addressed by attribute, so it is found by the same bounded prefix scan
the read path uses.

Addressing the current version is rejected for now: removing it has to promote
the next-newest version to keep the keyTable authoritative, which T4.3 adds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
keyTable holds the current version of every key that still has one, so removing
the current version has to hand the place over: one seek on the key's version
prefix yields the newest remaining version, which moves back into the keyTable
in the same WriteBatch as the delete. The record travels unchanged - promotion
is positional, and a version keeps the identity it was created with. When no
version survives, the key disappears entirely.

Deleting a current delete marker this way is exactly S3's restore-an-object
flow: the version the marker superseded becomes current again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nded

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant