@@ -434,31 +434,26 @@ def compute_next_delay(self) -> Optional[timedelta]:
434434 else :
435435 backoff_coefficient = self ._retry_policy .backoff_coefficient
436436
437- # Compute a deterministic "logical now" based on start time and accumulated delays,
438- # rather than wall-clock time, to avoid non-determinism during replay.
437+ # Compute the next delay and the logical start time of the next attempt
438+ # deterministically from accumulated delays, avoiding non-determinism during replay.
439+ # range(1, attempt_count + 1) sums all delays up to and including the one about to
440+ # be taken, so we're checking whether attempt N+1 would start within the timeout.
439441 total_elapsed_seconds = 0.0
440- for i in range (1 , self ._attempt_count ):
441- attempt_delay = (
442+ next_delay_f = 0.0
443+ for i in range (1 , self ._attempt_count + 1 ):
444+ next_delay_f = (
442445 math .pow (backoff_coefficient , i - 1 )
443446 * self ._retry_policy .first_retry_interval .total_seconds ()
444447 )
445448 if self ._retry_policy .max_retry_interval is not None :
446- attempt_delay = min (
447- attempt_delay ,
449+ next_delay_f = min (
450+ next_delay_f ,
448451 self ._retry_policy .max_retry_interval .total_seconds (),
449452 )
450- total_elapsed_seconds += attempt_delay
451- logical_now = self ._start_time + timedelta (seconds = total_elapsed_seconds )
452- if logical_now < retry_expiration :
453- next_delay_f = (
454- math .pow (backoff_coefficient , self ._attempt_count - 1 )
455- * self ._retry_policy .first_retry_interval .total_seconds ()
456- )
453+ total_elapsed_seconds += next_delay_f
457454
458- if self ._retry_policy .max_retry_interval is not None :
459- next_delay_f = min (
460- next_delay_f , self ._retry_policy .max_retry_interval .total_seconds ()
461- )
455+ logical_next_attempt_start = self ._start_time + timedelta (seconds = total_elapsed_seconds )
456+ if logical_next_attempt_start < retry_expiration :
462457 return timedelta (seconds = next_delay_f )
463458
464459 return None
0 commit comments