mirror of
https://github.com/pgsty/minio.git
synced 2026-10-31 04:50:40 +03:00
fix(storage): reconcile ordinary version DELETE across pools
Delete every copy of an explicitly addressed UUID, null version or delete marker under the pool lock. Preserve retention and replication callbacks, report unreadable pools and cleanup failures, and keep movement, incoming replication, expiration and free-version cleanup on their existing paths. Retain the separately developed general DELETE repair and replace its access-mover-only coverage with a real interrupted rebalance copy followed by HTTP deletion. Cover unqualified directory-marker DELETE and document the existing pool-order-dependent 503 behavior that this makes consistent. Signed-off-by: Feng Ruohang <rh@vonng.com>
This commit is contained in:
@@ -287,33 +287,47 @@ func (z *erasureServerPools) retireReplicaCopies(ctx context.Context, bucket, ob
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteObjectConditional evaluates the condition once against the logical
|
||||
// deleteObjectReconciled evaluates any condition once against the logical
|
||||
// version, then removes all its copies under the same lock as pooled writers.
|
||||
func (z *erasureServerPools) deleteObjectConditional(ctx context.Context, bucket, object string, opts ObjectOptions) (ObjectInfo, error) {
|
||||
func (z *erasureServerPools) deleteObjectReconciled(ctx context.Context, bucket, object string, opts ObjectOptions) (ObjectInfo, error) {
|
||||
copies, err := z.objectPoolInfos(ctx, bucket, object, opts)
|
||||
if err != nil {
|
||||
return ObjectInfo{}, err
|
||||
}
|
||||
primary := copies[0]
|
||||
if opts.CheckPrecondFn(primary.ObjInfo) {
|
||||
if opts.CheckPrecondFn != nil && opts.CheckPrecondFn(primary.ObjInfo) {
|
||||
return ObjectInfo{}, PreConditionFailed{}
|
||||
}
|
||||
opts.CheckPrecondFn = nil
|
||||
opts.NoLock = true
|
||||
if opts.EvalRetentionBypassFn != nil || opts.EvalMetadataFn != nil {
|
||||
versions, err := z.metadataPoolInfos(ctx, bucket, object, opts)
|
||||
if err != nil {
|
||||
return ObjectInfo{}, err
|
||||
logical := primary.ObjInfo
|
||||
var gerr error
|
||||
if logical.DeleteMarker {
|
||||
// Markers can be deleted by version ID. Match the set layer's
|
||||
// callback inputs instead of rejecting them as metadata updates.
|
||||
gerr = toObjectErr(errMethodNotAllowed, bucket, object)
|
||||
if opts.VersionID == "" || opts.DeleteMarker {
|
||||
gerr = toObjectErr(errFileNotFound, bucket, object)
|
||||
}
|
||||
} else {
|
||||
versions, err := z.metadataPoolInfos(ctx, bucket, object, opts)
|
||||
if err != nil {
|
||||
return ObjectInfo{}, err
|
||||
}
|
||||
logical = mergedPoolObjectInfo(versions)
|
||||
}
|
||||
logical := mergedPoolObjectInfo(versions)
|
||||
// Keep the retention gate first. These callbacks independently evaluate
|
||||
// the logical version and run once before any deletion; the handler only
|
||||
// sweeps metadata's transition state after a successful delete.
|
||||
if opts.EvalRetentionBypassFn != nil {
|
||||
if err := opts.EvalRetentionBypassFn(logical, nil); err != nil {
|
||||
if err := opts.EvalRetentionBypassFn(logical, gerr); err != nil {
|
||||
return ObjectInfo{}, err
|
||||
}
|
||||
opts.EvalRetentionBypassFn = nil
|
||||
}
|
||||
if opts.EvalMetadataFn != nil {
|
||||
decision, err := opts.EvalMetadataFn(&logical, nil)
|
||||
decision, err := opts.EvalMetadataFn(&logical, gerr)
|
||||
if err != nil {
|
||||
return ObjectInfo{}, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user