mirror of
https://github.com/pgsty/minio.git
synced 2026-11-11 01:55:30 +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:
@@ -133,8 +133,9 @@ func TestPoolsLifecycleDuplicateLockMetadata(t *testing.T) {
|
||||
}
|
||||
for pool := range s.z.serverPools {
|
||||
_, err := s.z.serverPools[pool].GetObjectInfo(t.Context(), s.bucket, key, ObjectOptions{VersionID: id})
|
||||
// Single DeleteObject retains its existing first-copy dispatch.
|
||||
gone := tc.delete && (batch || pool == 0)
|
||||
// An approved expiry removes the version from every pool,
|
||||
// in a batch and as a single delete.
|
||||
gone := tc.delete
|
||||
if gone && !isErrVersionNotFound(err) || !gone && err != nil {
|
||||
t.Errorf("pool=%d err=%v wantGone=%t", pool, err, gone)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user