Retry With Jitter (anti retry storm)
The retry pattern that adds controlled randomness to exponential backoff to break client synchronisation and avoid shock amplification during partner outage.
Problem
An AS2 partner goes down for 5 minutes. During these 5 minutes, 50 EDI clients try to send, fail, and trigger their retry. If all use pure exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 64s), at the moment the partner recovers, the 50 retry almost simultaneously — the partner is immediately re-saturated and falls again. This is the retry storm or thundering herd: the cure becomes worse than the disease.
Forces
- Retries are necessary: without retry, any transient failure = lost message.
- Exponential backoff alone is insufficient: it synchronises clients on discrete instants.
- Randomness must be quality pseudo-random: Math.random suffices, no need for cryptographic.
- Retry aggregate must be bounded: after N attempts, abandon and route to Dead Letter Channel.
- Idempotency must be guaranteed: a retry sends the same message, so the server must dedupe.
Solution
Add a random jitter factor to the retry interval. Three main variants documented in the reference AWS article (Marc Brooker, 2015):
- Exponential Backoff + Full Jitter:
sleep = random(0, min(cap, base * 2^attempt)). The reference case: distribution uniform over the entire interval. - Equal Jitter:
sleep = min(cap, base * 2^attempt) / 2 + random(0, min(cap, base * 2^attempt) / 2). Compromise that guarantees a minimum but stays partially random. - Decorrelated Jitter:
sleep = min(cap, random(base, sleep * 3)). Iterative, good statistical behaviour in simulation.
Modern libraries (AWS SDK, Resilience4j, Polly .NET, Tenacity Python) integrate Full Jitter by default.
Three retry strategies: 1. Pure exponential (BAD — retry storm) attempt 1: T+1s attempt 2: T+2s attempt 3: T+4s attempt 4: T+8s → all clients retry together: shock amplified 2. Exponential + Full Jitter (Marc Brooker, AWS 2015) attempt N: random(0, min(MAX, BASE * 2^N)) attempt 1: random(0, 2s) → e.g. 1.3s attempt 2: random(0, 4s) → e.g. 2.8s attempt 3: random(0, 8s) → e.g. 5.1s attempt 4: random(0, 16s) → e.g. 14.7s → distribution is uniform over the interval, breaks synchronisation 3. Decorrelated Jitter (AWS queue variant) sleep = min(cap, random(BASE, sleep * 3)) → progresses ergodically, balances exploration / convergence
EDI implementation
Concrete case: 50 simultaneous AS2 sends to the same partner,
timeout 30s. The partner returns 503 + Retry-After 60. Without
jitter: at T+60s, 50 clients retry, hit the same second, partner
re-saturated. With Full Jitter: each client retries after
random(0, 120s), uniformly distributed over 2 minutes. The
partner takes 50 spread-out requests, recovers normally, success
rate climbs back. Java code with Resilience4j:
RetryConfig config = RetryConfig.custom().maxAttempts(5).intervalFunction(IntervalFunction.ofExponentialRandomBackoff(1000, 2.0, 30000)).build();.
AS2-specific: also respect Retry-After when
provided, and combine with
Circuit
Breaker to open the circuit after failure threshold. For
time-constrained fiscal flows (ZATCA clearance, India IRP),
document maximum retry window in the SLA.
Anti-patterns
- Exponential retry without jitter: retry storm stays possible, especially in multi-instance architectures.
- Retry without cap: 100 attempts, 1h accumulated, message that will never go out → blocks the queue indefinitely.
- Retry without idempotency: the partner receives the same order 5 times, processes 5 times → 5 deliveries, accounting disaster.
- Retry of everything: retrying a 401 Unauthorized error is pointless, retry should depend on error type (transient vs permanent).
- Jitter too small: random(0, 100ms) on a 60s exponential sleep breaks nothing — jitter must be of the same order of magnitude as the sleep.
- No associated circuit breaker: if the outage lasts 10h, continuing to retry uselessly loads the network and amplifies costs.
Related patterns
- Retry, exponential backoff and circuit breaker — general parent pattern.
- Circuit Breaker — complementary pattern to bound attempts.
- Idempotency — property required for safe retry.
- Dead Letter Channel — destination of message after retry exhaustion.
- Rate Limiter — upstream pattern that prevents retry storms.
Sources
- Brooker M. — Exponential Backoff And Jitter, AWS Architecture Blog, 2015. Reference article that establishes the interest of jitter mathematically. aws.amazon.com/blogs — exponential-backoff-and-jitter
- Nygard M. — Release It! Design and Deploy Production-Ready Software, Pragmatic Bookshelf, 2nd ed. 2018. Timeouts and Circuit Breaker patterns.
- RFC 5681 — TCP Congestion Control (IETF, September 2009). The exponential-backoff reference in the IP stack. rfc-editor.org/rfc/rfc5681
- RFC 7231 §7.1.3 — HTTP/1.1 Retry-After header. rfc-editor.org/rfc/rfc7231
- Google SRE Workbook — Cascading Failures. Chapter dedicated to retry storms and their prevention. sre.google/workbook — cascading-failures
- Resilience4j Documentation. Reference Java library with native Full Jitter and Decorrelated Jitter implementation. resilience4j.readme.io