@@ -263,6 +263,43 @@ func Test_WatchKey_UpdateStale(t *testing.T) {
263263 })
264264}
265265
266+ func Test_WatchKey_NoRetries (t * testing.T ) {
267+ // Test that WatchKey uses MaxRetries=0 instead of CAS backoff config
268+ casBackoffConfig := backoff.Config {
269+ MinBackoff : 1 * time .Millisecond ,
270+ MaxBackoff : 1 * time .Millisecond ,
271+ MaxRetries : 5 , // CAS should retry, but WatchKey should not
272+ }
273+
274+ ddbMock := NewDynamodbClientMock ()
275+ codecMock := & CodecMock {}
276+ c := NewClientMock (ddbMock , codecMock , TestLogger {}, prometheus .NewPedanticRegistry (), defaultPullTime , casBackoffConfig )
277+
278+ // Mock Query to always fail
279+ ddbMock .On ("Query" ).Return (map [string ]dynamodbItem {}, errors .Errorf ("query failed" ))
280+
281+ // WatchKey should not retry on failure (MaxRetries=0), so it should only call Query once
282+ // and then fall back to stale data
283+ staleData := & DescMock {}
284+ staleData .On ("Clone" ).Return (staleData ).Once ()
285+
286+ // Set up some stale data first
287+ c .updateStaleData (key , staleData , time .Now ())
288+
289+ callCount := 0
290+ c .WatchKey (context .TODO (), key , func (i any ) bool {
291+ callCount ++
292+ // Should only be called once with stale data after the first query fails
293+ require .EqualValues (t , staleData , i )
294+ return false // Stop watching
295+ })
296+
297+ // Verify that Query was called exactly 11 times (1 initial + 10 retries due to hardcoded limit in WatchKey)
298+ // This confirms WatchKey has its own retry logic separate from backoff MaxRetries
299+ ddbMock .AssertNumberOfCalls (t , "Query" , 11 )
300+ require .Equal (t , 1 , callCount , "Callback should be called once with stale data" )
301+ }
302+
266303func Test_CAS_UpdateStale (t * testing.T ) {
267304 ddbMock := NewDynamodbClientMock ()
268305 codecMock := & CodecMock {}
0 commit comments