Fix retry body replay and jitter panic; drive delays via internal/clock

Gate the retry decision on body rewindability: an idempotent request whose
body cannot be replayed (no GetBody) is now returned as-is instead of looping
with an empty body or surfacing a stale, already-drained response. Guard
ExponentialBackoff against rand.Int64N panicking when delay/2 rounds to zero.
Use internal/clock for inter-attempt delays so retry timing is consistent with
the rest of the codebase and testable without real sleeps.
This commit is contained in:
2026-05-23 13:47:18 +03:00
parent e8c4577c6f
commit 43d3ecfba1
4 changed files with 124 additions and 22 deletions

View File

@@ -44,8 +44,11 @@ func (b *exponentialBackoff) Delay(attempt int) time.Duration {
}
if b.withJitter {
jitter := time.Duration(rand.Int64N(int64(delay / 2)))
delay += jitter
// Guard against rand.Int64N panicking on a non-positive argument when
// delay is small enough that delay/2 rounds to zero.
if half := int64(delay / 2); half > 0 {
delay += time.Duration(rand.Int64N(half))
}
}
if delay > b.max {