From a93e4769bcdbe4cc622b06fa354e31570a2d8bdc Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 15 Sep 2026 17:36:11 +0800 Subject: [PATCH 1/2] issue 10429: sync the calls to IsConstrained Signed-off-by: Lyndon-Li --- changelogs/unreleased/10533-Lyndon-Li | 1 + pkg/exposer/vgdp_counter.go | 5 +++ pkg/exposer/vgdp_counter_test.go | 54 ++++++++++++++++++++------- 3 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 changelogs/unreleased/10533-Lyndon-Li diff --git a/changelogs/unreleased/10533-Lyndon-Li b/changelogs/unreleased/10533-Lyndon-Li new file mode 100644 index 000000000..b2006f276 --- /dev/null +++ b/changelogs/unreleased/10533-Lyndon-Li @@ -0,0 +1 @@ +Fix issue #10429, sync the calls to IsConstrained \ No newline at end of file diff --git a/pkg/exposer/vgdp_counter.go b/pkg/exposer/vgdp_counter.go index 1f9850085..cb6ccd422 100644 --- a/pkg/exposer/vgdp_counter.go +++ b/pkg/exposer/vgdp_counter.go @@ -2,6 +2,7 @@ package exposer import ( "context" + "sync" "sync/atomic" "github.com/cockroachdb/errors" @@ -23,6 +24,7 @@ type dynamicQueueLength struct { } type VgdpCounter struct { + lock sync.Mutex client ctlclient.Client allowedQueueLength int @@ -164,6 +166,9 @@ func (w *VgdpCounter) initListeners(ctx context.Context, mgr manager.Manager) er } func (w *VgdpCounter) IsConstrained(ctx context.Context, log logrus.FieldLogger) bool { + w.lock.Lock() + defer w.lock.Unlock() + id := atomic.LoadUint64(&w.duState.changeID) if id != w.duCacheState.changeID { duList := &velerov2alpha1api.DataUploadList{} diff --git a/pkg/exposer/vgdp_counter_test.go b/pkg/exposer/vgdp_counter_test.go index 725b5f8d6..6666f0ed0 100644 --- a/pkg/exposer/vgdp_counter_test.go +++ b/pkg/exposer/vgdp_counter_test.go @@ -1,6 +1,8 @@ package exposer import ( + "sync" + "sync/atomic" "testing" "github.com/stretchr/testify/assert" @@ -19,23 +21,23 @@ import ( func TestIsConstrained(t *testing.T) { tests := []struct { name string - counter VgdpCounter + counter *VgdpCounter kubeClientObj []client.Object getErr bool expected bool }{ { name: "no change, constrained", - counter: VgdpCounter{}, + counter: &VgdpCounter{}, expected: true, }, { name: "no change, not constrained", - counter: VgdpCounter{allowedQueueLength: 1}, + counter: &VgdpCounter{allowedQueueLength: 1}, }, { name: "change in du, get failed", - counter: VgdpCounter{ + counter: &VgdpCounter{ allowedQueueLength: 1, duState: dynamicQueueLength{0, 1}, }, @@ -43,7 +45,7 @@ func TestIsConstrained(t *testing.T) { }, { name: "change in du, constrained", - counter: VgdpCounter{ + counter: &VgdpCounter{ allowedQueueLength: 1, duState: dynamicQueueLength{0, 1}, }, @@ -54,7 +56,7 @@ func TestIsConstrained(t *testing.T) { }, { name: "change in dd, get failed", - counter: VgdpCounter{ + counter: &VgdpCounter{ allowedQueueLength: 1, ddState: dynamicQueueLength{0, 1}, }, @@ -62,7 +64,7 @@ func TestIsConstrained(t *testing.T) { }, { name: "change in dd, constrained", - counter: VgdpCounter{ + counter: &VgdpCounter{ allowedQueueLength: 1, ddState: dynamicQueueLength{0, 1}, }, @@ -73,7 +75,7 @@ func TestIsConstrained(t *testing.T) { }, { name: "change in pvb, get failed", - counter: VgdpCounter{ + counter: &VgdpCounter{ allowedQueueLength: 1, pvbState: dynamicQueueLength{0, 1}, }, @@ -81,7 +83,7 @@ func TestIsConstrained(t *testing.T) { }, { name: "change in pvb, constrained", - counter: VgdpCounter{ + counter: &VgdpCounter{ allowedQueueLength: 1, pvbState: dynamicQueueLength{0, 1}, }, @@ -92,7 +94,7 @@ func TestIsConstrained(t *testing.T) { }, { name: "change in pvr, get failed", - counter: VgdpCounter{ + counter: &VgdpCounter{ allowedQueueLength: 1, pvrState: dynamicQueueLength{0, 1}, }, @@ -100,7 +102,7 @@ func TestIsConstrained(t *testing.T) { }, { name: "change in pvr, constrained", - counter: VgdpCounter{ + counter: &VgdpCounter{ allowedQueueLength: 1, pvrState: dynamicQueueLength{0, 1}, }, @@ -111,7 +113,7 @@ func TestIsConstrained(t *testing.T) { }, { name: "change in du, pvb, not constrained", - counter: VgdpCounter{ + counter: &VgdpCounter{ allowedQueueLength: 3, duState: dynamicQueueLength{0, 1}, pvbState: dynamicQueueLength{0, 1}, @@ -123,7 +125,7 @@ func TestIsConstrained(t *testing.T) { }, { name: "change in dd, pvr, constrained", - counter: VgdpCounter{ + counter: &VgdpCounter{ allowedQueueLength: 1, ddState: dynamicQueueLength{0, 1}, pvrState: dynamicQueueLength{0, 1}, @@ -178,3 +180,29 @@ func TestIsConstrained(t *testing.T) { }) } } + +func TestVgdpCounterConcurrentIsConstrained(t *testing.T) { + scheme := runtime.NewScheme() + require.NoError(t, velerov1api.AddToScheme(scheme)) + require.NoError(t, velerov2alpha1api.AddToScheme(scheme)) + + counter := &VgdpCounter{ + allowedQueueLength: 10, + client: fake.NewClientBuilder().WithScheme(scheme).Build(), + } + + var wg sync.WaitGroup + for i := 0; i < 4; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for j := 0; j < 200; j++ { + atomic.AddUint64(&counter.duState.changeID, 1) + atomic.AddUint64(&counter.ddState.changeID, 1) + counter.IsConstrained(t.Context(), velerotest.NewLogger()) + } + }() + } + wg.Wait() +} + From 66d54af22c033b0f5ddafec07f30811b2df8d59f Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 15 Sep 2026 17:46:32 +0800 Subject: [PATCH 2/2] issue 10429: sync the calls to IsConstrained Signed-off-by: Lyndon-Li --- pkg/exposer/vgdp_counter_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/exposer/vgdp_counter_test.go b/pkg/exposer/vgdp_counter_test.go index 6666f0ed0..621f97cf1 100644 --- a/pkg/exposer/vgdp_counter_test.go +++ b/pkg/exposer/vgdp_counter_test.go @@ -205,4 +205,3 @@ func TestVgdpCounterConcurrentIsConstrained(t *testing.T) { } wg.Wait() } -