EMZETT.
Login

Retry and exponential backoff: Repeating without overloading

In short: Trying a failed request again, but with growing pauses and some randomness so that an already stressed service is not put under even more pressure.

In more detail: In distributed systems calls often fail only briefly: network errors, overloaded server, restart. A new attempt then helps. But if every client repeats immediately and constantly, the problem is amplified (retry storm). That is why you wait longer and longer between attempts and spread the times randomly.

In Depth

Exponential backoff

The waiting time doubles: 1 s, 2 s, 4 s, 8 s … up to a maximum. This gives an overloaded service room to recover. An upper limit on the number of attempts (for example 5) prevents endless waiting.

Jitter

Without randomness all clients that failed at the same time would also ask again at the same time. Jitter mixes randomness into the waiting time, for instance wait = random(0, min(max, base * 2^attempt)) (“full jitter”). This spreads the load.

Example in JavaScript

async function withRetry(fn, attempts = 5) {
  for (let i = 0; ; i++) {
    try { return await fn(); }
    catch (e) {
      if (i >= attempts - 1 || !retryable(e)) throw e;
      const wait = Math.random() * Math.min(10000, 500 * 2 ** i);
      await new Promise((r) => setTimeout(r, wait));
    }
  }
}

When not to retry

Only temporary errors (timeout, 429, 502, 503) are retryable. With 400 or 404, repeating achieves nothing. Non-idempotent actions (triggering a payment) may only be repeated if an idempotency key prevents duplicate execution. With 429 or 503 the server often specifies a Retry-After header, which takes precedence. In addition, a circuit breaker protects by temporarily not asking at all after many failures.

See also: Circuit breaker and backpressure, Rate limiting, REST API design