mirror of
https://github.com/pgsty/minio.git
synced 2026-11-10 17:45:32 +03:00
fix(pools): an approved ILM version expiry deletes every copy
A version can have copies in two pools, for example after an interrupted decommission or rebalance. DeleteObject left ILM expiries out of the reconciled delete of an addressed version, so a lifecycle version expiry deleted only the copy that latest-key routing chose. The other copy stayed readable; for a tiered version, expireTransitionedObject then deleted the remote object that copy still referenced; and a migration of the pool holding it left the expired version behind and failed its completion check. With delete replication on, the replication completion's own delete had reconciled the copies and hidden this. Admit an ILM version expiry that the pools layer approved under the lock with the versions in all pools (CheckedAllPools, and not a restored-copy expiry) to deleteObjectReconciled. It removes the secondary copies before the primary and stops at the first failure, so the remote object is only deleted after every copy is gone. Each set still guards the protection the pools approved. Marker creation, restored-copy expiry, free-version cleanup and delete-all keep their own paths; the bulk noncurrent purge already reconciled. Tests: with copies in two pools, a hot and a tiered version expiry remove both copies, and the tiered one deletes its remote object once; when the secondary copy cannot be deleted the expiry fails, both copies and the remote object stay; with replication configured the tiered expiry queues nothing; decommissioning the pool that holds the second copy completes and leaves no expired version. All fail without the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Feng Ruohang <rh@vonng.com>
This commit is contained in:
@@ -1324,10 +1324,15 @@ func (z *erasureServerPools) DeleteObject(ctx context.Context, bucket string, ob
|
||||
|
||||
// Resolve a physical purge by its addressed version even on a replica
|
||||
// receiver: latest-key routing can select a different pool or miss copies.
|
||||
// Marker creation and specialized movement/scanner operations retain their
|
||||
// existing routing.
|
||||
// An ILM version expiry that the pools approved above, under this lock and
|
||||
// with the versions in all pools, removes every copy of the version too:
|
||||
// a copy left in another pool would stay readable, and a tiered one would
|
||||
// outlive the remote object its expiry deletes next. Marker creation,
|
||||
// restored-copy expiry and specialized movement/scanner operations retain
|
||||
// their existing routing.
|
||||
approvedExpiry := opts.Expiration.Expire && opts.Expiration.CheckedAllPools && !opts.Transition.ExpireRestored
|
||||
reconcileVersion := opts.VersionID != "" && !opts.DataMovement &&
|
||||
(!opts.ReplicationRequest || opts.isVersionPurge()) && !opts.Expiration.Expire && !opts.InclFreeVersions
|
||||
(!opts.ReplicationRequest || opts.isVersionPurge()) && (!opts.Expiration.Expire || approvedExpiry) && !opts.InclFreeVersions
|
||||
if !z.SinglePool() && (opts.CheckPrecondFn != nil || reconcileVersion) {
|
||||
return z.deleteObjectReconciled(ctx, bucket, object, opts)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user