Copilot commented on code in PR #3187:
URL: https://github.com/apache/dubbo-go/pull/3187#discussion_r2749397734


##########
cluster/cluster/failback/cluster_test.go:
##########
@@ -145,27 +145,28 @@ func TestFailbackRetryFailed(t *testing.T) {
        mockFailedResult := &result.RPCResult{Err: perrors.New("error")}
        invoker.EXPECT().Invoke(gomock.Any(), 
gomock.Any()).Return(mockFailedResult)
 
-       //
-       var wg sync.WaitGroup
-       retries := 2
-       wg.Add(retries)
-
-       // add retry call that eventually failed.
-       for i := 0; i < retries; i++ {
-               invoker.EXPECT().Invoke(gomock.Any(), 
gomock.Any()).DoAndReturn(func(context.Context, base.Invocation) result.Result {
-                       // with exponential backoff, retries happen with 
increasing intervals starting from ~1s
-                       wg.Done()
-                       return mockFailedResult
-               })
-       }
+       // Use atomic counter to safely track retries across goroutines.
+       // With exponential backoff and randomization factor, timing is 
non-deterministic.
+       var retryCount int64
+       targetRetries := int64(2)
+
+       // add retry calls that eventually failed.
+       invoker.EXPECT().Invoke(gomock.Any(), 
gomock.Any()).DoAndReturn(func(context.Context, base.Invocation) result.Result {
+               atomic.AddInt64(&retryCount, 1)
+               return mockFailedResult
+       }).MinTimes(int(targetRetries))
 
        // first call should failed.
        result := clusterInvoker.Invoke(context.Background(), 
&invocation.RPCInvocation{})
        require.NoError(t, result.Error())
        assert.Nil(t, result.Result())
        assert.Empty(t, result.Attachments())
 
-       wg.Wait()
+       // Wait for at least targetRetries to complete
+       for atomic.LoadInt64(&retryCount) < targetRetries {
+               time.Sleep(100 * time.Millisecond)
+       }
+
        time.Sleep(time.Second)

Review Comment:
   Using a fixed sleep here reintroduces timing sensitivity and can still be 
flaky on slow/loaded CI runners. Prefer waiting on the condition you actually 
need (e.g., require.Eventually until taskList.Len() >= 1, with a timeout) and 
remove the unconditional sleep.
   ```suggestion
        // Wait until we've seen at least targetRetries and the task has been 
re-queued.
        require.Eventually(t, func() bool {
                return atomic.LoadInt64(&retryCount) >= targetRetries &&
                        clusterInvoker.taskList.Len() >= int64(1)
        }, 5*time.Second, 50*time.Millisecond)
   ```



##########
cluster/cluster/failback/cluster_test.go:
##########
@@ -145,27 +145,28 @@ func TestFailbackRetryFailed(t *testing.T) {
        mockFailedResult := &result.RPCResult{Err: perrors.New("error")}
        invoker.EXPECT().Invoke(gomock.Any(), 
gomock.Any()).Return(mockFailedResult)
 
-       //
-       var wg sync.WaitGroup
-       retries := 2
-       wg.Add(retries)
-
-       // add retry call that eventually failed.
-       for i := 0; i < retries; i++ {
-               invoker.EXPECT().Invoke(gomock.Any(), 
gomock.Any()).DoAndReturn(func(context.Context, base.Invocation) result.Result {
-                       // with exponential backoff, retries happen with 
increasing intervals starting from ~1s
-                       wg.Done()
-                       return mockFailedResult
-               })
-       }
+       // Use atomic counter to safely track retries across goroutines.
+       // With exponential backoff and randomization factor, timing is 
non-deterministic.
+       var retryCount int64
+       targetRetries := int64(2)
+
+       // add retry calls that eventually failed.
+       invoker.EXPECT().Invoke(gomock.Any(), 
gomock.Any()).DoAndReturn(func(context.Context, base.Invocation) result.Result {
+               atomic.AddInt64(&retryCount, 1)
+               return mockFailedResult
+       }).MinTimes(int(targetRetries))
 
        // first call should failed.
        result := clusterInvoker.Invoke(context.Background(), 
&invocation.RPCInvocation{})
        require.NoError(t, result.Error())
        assert.Nil(t, result.Result())
        assert.Empty(t, result.Attachments())
 
-       wg.Wait()
+       // Wait for at least targetRetries to complete
+       for atomic.LoadInt64(&retryCount) < targetRetries {
+               time.Sleep(100 * time.Millisecond)
+       }

Review Comment:
   The polling loop can hang indefinitely if retries never reach targetRetries 
(e.g., scheduler stalls, logic regression, or gomock expectation mismatch). 
Please add a bounded wait (e.g., require.Eventually with a reasonable timeout 
and tick interval, or a context deadline) so the test fails fast instead of 
blocking CI.
   ```suggestion
        // Wait for at least targetRetries to complete, but bound the wait to 
avoid hanging tests.
        require.Eventually(t, func() bool {
                return atomic.LoadInt64(&retryCount) >= targetRetries
        }, 5*time.Second, 100*time.Millisecond)
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to