Merge remote-tracking branch 'origin/main' into HEAD

Signed-off-by: Feng Ruohang <rh@vonng.com>

# Conflicts:
#	cmd/erasure-server-pool-decom.go
This commit is contained in:
Feng Ruohang
2026-11-02 12:29:08 +08:00
13 changed files with 892 additions and 62 deletions
+45 -1
View File
@@ -997,6 +997,9 @@ func (z *erasureServerPools) decommissionPool(ctx context.Context, idx int, pool
if decommissioned == len(fivs.Versions) {
stopFn := globalDecommissionMetrics.log(decomMetricDecommissionRemoveObject, idx, bi.Name, entry.name)
err := z.removeMovedVersions(ctx, bi.Name, entry.name, idx, fivs.Versions, expired)
if err == nil {
err = z.removeFreeVersions(ctx, bi.Name, entry.name, idx, fivs.FreeVersions)
}
stopFn(0, err)
auditLogDecom(ctx, "DecomDeleteObject", bi.Name, entry.name, "", err)
if err != nil {
@@ -1123,6 +1126,45 @@ func (z *erasureServerPools) decommissionInBackground(ctx context.Context, idx i
return nil
}
// removeFreeVersions removes the free versions fvs of object from the pool
// idx being decommissioned, as the scanner removes free versions: first the
// remote object of each, then the free version. A free version is not moved.
// In another pool it would be a delete marker on top of the object, and once
// this pool is gone nothing would remove its remote object. A remote object
// that is already gone is not an error.
func (z *erasureServerPools) removeFreeVersions(ctx context.Context, bucket, object string, idx int, fvs []FileInfo) error {
if len(fvs) == 0 {
return nil
}
for _, fv := range fvs {
err := deleteObjectFromRemoteTier(ctx, fv.TransitionedObjName, fv.TransitionVersionID, fv.TransitionTier)
if err != nil && !isErrObjectNotFound(err) && !isErrVersionNotFound(err) {
return fmt.Errorf("unable to remove the remote object %s of a free version of %s/%s from tier %s: %w",
fv.TransitionedObjName, bucket, object, fv.TransitionTier, err)
}
}
object = encodeDirObject(object)
lk := z.NewNSLock(bucket, object)
lkctx, err := lk.GetLock(ctx, globalOperationTimeout)
if err != nil {
return err
}
ctx = lkctx.Context()
defer lk.Unlock(lkctx)
for _, fv := range fvs {
_, err := z.serverPools[idx].DeleteObject(ctx, bucket, object, ObjectOptions{
VersionID: fv.VersionID,
InclFreeVersions: true,
NoLock: true,
NoAuditLog: true,
})
if err != nil && !isErrObjectNotFound(err) && !isErrVersionNotFound(err) {
return err
}
}
return nil
}
func (z *erasureServerPools) checkAfterDecom(ctx context.Context, idx int) error {
buckets, err := z.getBucketsToDecommission(ctx)
if err != nil {
@@ -1210,7 +1252,9 @@ func (z *erasureServerPools) checkAfterDecom(ctx context.Context, idx int) error
}
}
versionsFound += len(fivs.Versions) - ignored
// A free version left behind keeps a remote object that
// nothing removes once the pool is gone.
versionsFound += len(fivs.Versions) - ignored + len(fivs.FreeVersions)
}); err != nil {
return err
}