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.
This commit is contained in:
Chris Lu
2026-05-23 10:08:22 -07:00
parent 8acf21e4d1
commit d7854ce88e
2 changed files with 11 additions and 3 deletions
+2 -3
View File
@@ -28,9 +28,8 @@ func (s3a *S3ApiServer) routedObjectOwner(bucket, object string) (pb.ServerAddre
if enabled, err := s3a.isVersioningEnabled(bucket); err != nil || enabled {
return "", false
}
if locked, err := s3a.isObjectLockEnabled(bucket); err != nil || locked {
return "", false
}
// objectWriteOwner already excludes object-lock buckets (they stay fully on
// the distributed lock), returning "" for them.
owner := s3a.objectWriteOwner(bucket, object)
if owner == "" {
return "", false
@@ -43,6 +43,15 @@ func (s3a *S3ApiServer) objectWriteOwner(bucket, object string) pb.ServerAddress
if s3a.objectWriteLockClient == nil {
return ""
}
// Object-lock (WORM) buckets keep ALL of their writes on the distributed
// lock. Retention enforcement is a gateway-side check that the per-path-locked
// filer ops don't perform, so routing only some of such a bucket's writes
// would split serialization between the entry lock and the distributed lock
// (they don't serialize against each other). Excluding the bucket entirely
// keeps it internally consistent.
if locked, err := s3a.isObjectLockEnabled(bucket); err != nil || locked {
return ""
}
return s3a.objectWriteLockClient.PrimaryForKey("s3.object.write:" + s3a.toFilerPath(bucket, object))
}