s3api: make conditional mutations atomic and AWS-compatible (#8802)
* s3api: serialize conditional write finalization * s3api: add conditional delete mutation checks * s3api: enforce destination conditions for copy * s3api: revalidate multipart completion under lock * s3api: rollback failed put finalization hooks * s3api: report delete-marker version deletions * s3api: fix copy destination versioning edge cases * s3api: make versioned multipart completion idempotent * test/s3: cover conditional mutation regressions * s3api: rollback failed copy version finalization * s3api: resolve suspended delete conditions via latest entry * s3api: remove copy test null-version injection * s3api: reject out-of-order multipart completions * s3api: preserve multipart replay version metadata * s3api: surface copy destination existence errors * s3api: simplify delete condition target resolution * test/s3: make conditional delete assertions order independent * test/s3: add distributed lock gateway integration * s3api: fail closed multipart versioned completion * s3api: harden copy metadata and overwrite paths * s3api: create delete markers for suspended deletes * s3api: allow duplicate multipart completion parts
This commit is contained in:
@@ -82,8 +82,17 @@ type S3ApiServer struct {
|
||||
embeddedIam *EmbeddedIamApi // Embedded IAM API server (when enabled)
|
||||
stsHandlers *STSHandlers // STS HTTP handlers for AssumeRoleWithWebIdentity
|
||||
cipher bool // encrypt data on volume servers
|
||||
newObjectWriteLock func(bucket, object string) objectWriteLock
|
||||
}
|
||||
|
||||
type objectWriteLock interface {
|
||||
StopShortLivedLock() error
|
||||
}
|
||||
|
||||
const (
|
||||
objectWriteLockTTL = 15 * time.Second
|
||||
)
|
||||
|
||||
func NewS3ApiServer(router *mux.Router, option *S3ApiServerOption) (s3ApiServer *S3ApiServer, err error) {
|
||||
return NewS3ApiServerWithStore(router, option, "")
|
||||
}
|
||||
@@ -182,6 +191,21 @@ func NewS3ApiServerWithStore(router *mux.Router, option *S3ApiServerOption, expl
|
||||
cipher: option.Cipher,
|
||||
}
|
||||
|
||||
if len(option.Filers) > 0 {
|
||||
objectWriteLockClient := cluster.NewLockClient(option.GrpcDialOption, option.Filers[0])
|
||||
s3ApiServer.newObjectWriteLock = func(bucket, object string) objectWriteLock {
|
||||
lockKey := fmt.Sprintf("s3.object.write:%s", s3ApiServer.toFilerPath(bucket, object))
|
||||
owner := fmt.Sprintf("s3api-%d", s3ApiServer.randomClientId)
|
||||
lock := objectWriteLockClient.NewShortLivedLock(lockKey, owner)
|
||||
if err := lock.AttemptToLock(objectWriteLockTTL); err != nil {
|
||||
// The initial acquisition already succeeded with the default short TTL.
|
||||
// Renewal to a longer TTL is opportunistic to cover slower metadata paths.
|
||||
glog.Warningf("objectWriteLock: failed to extend lock TTL for %s: %v", lockKey, err)
|
||||
}
|
||||
return lock
|
||||
}
|
||||
}
|
||||
|
||||
// Set s3a reference in circuit breaker for upload limiting
|
||||
s3ApiServer.cb.s3a = s3ApiServer
|
||||
|
||||
|
||||
Reference in New Issue
Block a user