Commit Graph
4 Commits
Author SHA1 Message Date
Chris Lu 2d41f4ca8b s3: route non-conditional suspended DELETE off the DLM
A suspended DELETE removes the null version (the main object entry) and adds a
delete marker. Now that suspended PUT routes on the object key, a DELETE left on
the distributed lock wouldn't serialize against it (the per-path entry lock and
the distributed lock are independent). Route it on the object key too.

FinalizeVersionedWrite gains an optional delete_path it removes under the same
lock before flipping the pointer, so "delete null + add marker" is one atomic
step on the owner. createDeleteMarker threads it through (empty for the
versioning-enabled marker path). The DeleteObjectHandler routes a no-versionId
suspended delete when there is no If-Match — that condition targets the main
object, not the .versions pointer the op evaluates, so a conditional suspended
delete stays on the lock path. Object-lock can't apply (it requires versioning
enabled, never suspended).
2026-05-23 10:17:45 -07:00
Chris Lu d7854ce88e s3: keep object-lock buckets fully on the distributed lock
objectWriteOwner now returns "" for object-lock (WORM) buckets, so versioned
PutObject / copy / delete-marker no longer route them off the lock. Routed writes
serialize on the owner's per-path entry lock while retention-checked deletes use
the distributed lock, and those two locks don't serialize against each other; an
object-lock bucket that routed some writes and not others would split-brain on
the same object. Retention enforcement is gateway-side and not part of the
per-path-locked filer ops, so the whole bucket stays on the distributed lock and
remains internally consistent. The now-redundant gate is dropped from
routedObjectOwner.
2026-05-23 10:08:22 -07:00
Chris Lu a99c8214bb s3: route versioned COPY and delete-marker off the DLM
Both create a unique version/marker file plus an atomic latest-pointer flip —
the same shape as versioned PutObject — so both now reuse FinalizeVersionedWrite
instead of the distributed lock.

finalizeCopyDestination (VersioningEnabled) and createDeleteMarker take an
optional owner + condition; when the .versions owner is known they run the
demote + pointer flip (and the precondition) atomically on that owner via
routedVersionedFinalize, rolling back the version/marker file on failure and
surfacing a precondition miss as 412. CopyObjectHandler and DeleteObjectHandler
take the routed path for versioning-enabled destinations and skip the lock;
suspended versioning, version-specific deletes, metadata-only self-copy, and the
lifecycle marker path keep the existing lock-based path.

No proto or filer changes — reuses the op from the versioned-PutObject PR.
2026-05-23 09:48:25 -07:00
Chris Lu 2b8349f9e9 s3: route versioned PutObject finalize off the DLM (object-key serialization)
A versioned write's only contended mutation is the .versions directory's latest
pointer; the version file itself goes to a unique <object>/.versions/<versionId>
path. Add a FinalizeVersionedWrite filer op that, under one exclusive lock on the
object key, evaluates the precondition against the current latest, stamps the
previous latest noncurrent (before the pointer flip so the lifecycle router
observes it), then merges the latest pointer / cached metadata into the .versions
entry. Key names are passed in, so the filer carries no S3 semantics.

Routing and the lock are keyed on the object (objectWriteOwner + lock_key), the
same key normal and suspended writes use, so all writes to one object resolve the
same owner and serialize on the same lock regardless of versioning state — a
versioned and a non-versioned write to the same object can't race on different
owners during a versioning-state change.

The gateway routes a versioned PutObject's finalize to that owner and tells
putToFiler the version path is unique, so it skips the object write lock and the
gateway precondition (the op does both atomically). When the owner is unknown or
the condition can't reduce to one primitive, it stays on the lock path; on op
error it returns InternalError. Versioned COPY, delete markers, suspended
versioning, and multipart completion still use the lock and adopt the same op as
follow-ups.
2026-05-23 09:45:23 -07:00