From e270ab65b332f122da9fe489af8b28ee32e359a4 Mon Sep 17 00:00:00 2001 From: Poorna K Date: Thu, 16 Dec 2021 15:34:55 -0800 Subject: [PATCH] fix: healing of replication delete markers (#13933) A corner case can occur where the delete-marker was propagated but the metadata could not be updated on the primary. Sending a RemoveObject call with the Delete marker version would end up permanently deleting the version on target. Instead, perform a Stat on the delete-marker version on target and redo replication only if the delete-marker is missing on target. --- cmd/bucket-replication.go | 8 +++----- cmd/data-scanner.go | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cmd/bucket-replication.go b/cmd/bucket-replication.go index 7a82c0c61..b3e012146 100644 --- a/cmd/bucket-replication.go +++ b/cmd/bucket-replication.go @@ -517,8 +517,8 @@ func replicateDeleteToTarget(ctx context.Context, dobj DeletedObjectReplicationI } return } - // early return if already replicated delete marker for existing object replication - if dobj.DeleteMarkerVersionID != "" && dobj.OpType == replication.ExistingObjectReplicationType { + // early return if already replicated delete marker for existing object replication/ healing delete markers + if dobj.DeleteMarkerVersionID != "" && (dobj.OpType == replication.ExistingObjectReplicationType || dobj.OpType == replication.HealReplicationType) { if _, err := tgt.StatObject(ctx, tgt.Bucket, dobj.ObjectName, miniogo.StatObjectOptions{ VersionID: versionID, Internal: miniogo.AdvancedGetOptions{ @@ -526,10 +526,8 @@ func replicateDeleteToTarget(ctx context.Context, dobj DeletedObjectReplicationI }}); isErrMethodNotAllowed(ErrorRespToObjectError(err, dobj.Bucket, dobj.ObjectName)) { if dobj.VersionID == "" { rinfo.ReplicationStatus = replication.Completed - } else { - rinfo.VersionPurgeStatus = Complete + return } - return } } diff --git a/cmd/data-scanner.go b/cmd/data-scanner.go index db1e72675..2857994e4 100644 --- a/cmd/data-scanner.go +++ b/cmd/data-scanner.go @@ -1269,6 +1269,7 @@ func (i *scannerItem) healReplicationDeletes(ctx context.Context, o ObjectLayer, DeleteMarker: roi.DeleteMarker, }, Bucket: roi.Bucket, + OpType: replication.HealReplicationType, } if roi.ExistingObjResync.mustResync() { doi.OpType = replication.ExistingObjectReplicationType