mirror of
https://github.com/pgsty/minio.git
synced 2026-09-26 04:45:59 +03:00
Merge pull request #142 from pgsty/fix/issue-141-resync-dispatch-scope
fix: scope resync worker dispatch to the target being resynced
This commit is contained in:
@@ -3029,6 +3029,19 @@ func resyncResultFor(rinfos replicatedInfos, arn string, roi ReplicateObjectInfo
|
|||||||
return st
|
return st
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// objectNeedsResyncForARN reports whether roi must be resynced for target arn
|
||||||
|
// specifically. The resync worker pool is scoped to a single target (opts.arn),
|
||||||
|
// so an object that only qualifies for a different target must be skipped here:
|
||||||
|
// admitting it would replicate it for arn's peers only, leaving arn absent from
|
||||||
|
// the per-object result, which resyncResultFor then (correctly, but
|
||||||
|
// misleadingly) counts as a failure for arn - an object arn was never
|
||||||
|
// responsible for. Only opts.arn carries this resync's ResetID, and that reset
|
||||||
|
// is already folded into its per-target decision, so the per-target check both
|
||||||
|
// scopes dispatch and honors the reset.
|
||||||
|
func objectNeedsResyncForARN(roi ReplicateObjectInfo, arn string) bool {
|
||||||
|
return roi.ExistingObjResync.mustResyncTarget(arn)
|
||||||
|
}
|
||||||
|
|
||||||
// resyncBucket resyncs all qualifying objects as per replication rules for the target
|
// resyncBucket resyncs all qualifying objects as per replication rules for the target
|
||||||
// ARN
|
// ARN
|
||||||
func (s *replicationResyncer) resyncBucket(ctx context.Context, objectAPI ObjectLayer, heal bool, opts resyncOpts) {
|
func (s *replicationResyncer) resyncBucket(ctx context.Context, objectAPI ObjectLayer, heal bool, opts resyncOpts) {
|
||||||
@@ -3196,7 +3209,12 @@ func (s *replicationResyncer) resyncBucket(ctx context.Context, objectAPI Object
|
|||||||
}
|
}
|
||||||
lastCheckpoint = ""
|
lastCheckpoint = ""
|
||||||
roi := getHealReplicateObjectInfo(res.Item, rcfg)
|
roi := getHealReplicateObjectInfo(res.Item, rcfg)
|
||||||
if !roi.ExistingObjResync.mustResync() {
|
// Scope dispatch to this resync's target: the worker pool is for
|
||||||
|
// opts.arn, so skip objects that only need resync for a different
|
||||||
|
// target (each target has its own resync). Without this, a cross-target
|
||||||
|
// object leaves opts.arn absent from its per-object result and is
|
||||||
|
// miscounted as an opts.arn failure.
|
||||||
|
if !objectNeedsResyncForARN(roi, opts.arn) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
|
|||||||
@@ -647,3 +647,67 @@ func TestResyncResultFor(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestObjectNeedsResyncForARN asserts the resync dispatch is scoped to the
|
||||||
|
// target being resynced. The worker pool runs for a single target (opts.arn),
|
||||||
|
// so an object that only qualifies for a different target must be skipped: with
|
||||||
|
// A/B rules and a resync of A, an object that needs replication only for B must
|
||||||
|
// not be admitted to A's worker. Otherwise (after outcome-based classification)
|
||||||
|
// A would be absent from that object's result and miscounted as an A failure.
|
||||||
|
func TestObjectNeedsResyncForARN(t *testing.T) {
|
||||||
|
const (
|
||||||
|
arnA = "arn:minio:replication::id:bucket"
|
||||||
|
arnB = "arn:minio:replication::id2:bucket"
|
||||||
|
)
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
decision ResyncDecision
|
||||||
|
arn string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "target must resync",
|
||||||
|
decision: ResyncDecision{targets: map[string]ResyncTargetDecision{arnA: {Replicate: true}}},
|
||||||
|
arn: arnA,
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "object qualifies for B only, resyncing A",
|
||||||
|
decision: ResyncDecision{targets: map[string]ResyncTargetDecision{arnB: {Replicate: true}}},
|
||||||
|
arn: arnA,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "A present but not replicating, B replicating, resyncing A",
|
||||||
|
decision: ResyncDecision{targets: map[string]ResyncTargetDecision{
|
||||||
|
arnA: {Replicate: false},
|
||||||
|
arnB: {Replicate: true},
|
||||||
|
}},
|
||||||
|
arn: arnA,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "object qualifies for both, resyncing A",
|
||||||
|
decision: ResyncDecision{targets: map[string]ResyncTargetDecision{
|
||||||
|
arnA: {Replicate: true},
|
||||||
|
arnB: {Replicate: true},
|
||||||
|
}},
|
||||||
|
arn: arnA,
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no resync decision",
|
||||||
|
decision: ResyncDecision{},
|
||||||
|
arn: arnA,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
roi := ReplicateObjectInfo{Name: "obj", Bucket: "bucket", ExistingObjResync: tc.decision}
|
||||||
|
if got := objectNeedsResyncForARN(roi, tc.arn); got != tc.want {
|
||||||
|
t.Fatalf("objectNeedsResyncForARN(arn=%s) = %v, want %v", tc.arn, got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user