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).
This commit is contained in:
Chris Lu committed 2026-05-23 10:17:45 -07:00
1 parent d7854ce88e
commit 2d41f4ca8b
9 files changed
+56 -12

No files matched your search

@@ -98,10 +98,11 @@ func versionedPointerExtended(versionId, versionFileName string, versionEntry *f
// version file and skips the object write lock and gateway precondition. A
// transport/in-band error maps to InternalError (the client retries); there is
// no silent non-atomic fallback.
func (s3a *S3ApiServer) routedVersionedFinalize(owner pb.ServerAddress, bucket, object, versionId, versionFileName string, versionEntry *filer_pb.Entry, cond *filer_pb.WriteCondition) s3err.ErrorCode {
func (s3a *S3ApiServer) routedVersionedFinalize(owner pb.ServerAddress, bucket, object, versionId, versionFileName string, versionEntry *filer_pb.Entry, cond *filer_pb.WriteCondition, deletePath string) s3err.ErrorCode {
set, del := versionedPointerExtended(versionId, versionFileName, versionEntry)
req := &filer_pb.FinalizeVersionedWriteRequest{
LockKey: s3a.toFilerPath(bucket, object),
DeletePath: deletePath,
VersionsDir: s3a.toFilerPath(bucket, object+s3_constants.VersionsFolder),
SetExtended: set,
DeleteExtended: del,
@@ -148,7 +149,7 @@ func (s3a *S3ApiServer) versionedAfterCreate(r *http.Request, bucket, object, ve
cond, condOk := buildWriteCondition(r)
if owner != "" && condOk {
return func(versionEntry *filer_pb.Entry) s3err.ErrorCode {
return s3a.routedVersionedFinalize(owner, bucket, object, versionId, versionFileName, versionEntry, cond)
return s3a.routedVersionedFinalize(owner, bucket, object, versionId, versionFileName, versionEntry, cond, "")
}, true
}
return func(versionEntry *filer_pb.Entry) s3err.ErrorCode {