InterviewPrepKit

Home / Coding / Agent Coding / Reliability & Retries / Retry with Exponential Backoff

Retry with Exponential Backoff

medium 00:00
Solving tips
  • Backoff grows the wait between attempts (base * 2**attempt) so you stop hammering a service that is already struggling.
  • Inject the sleep function so tests stay fast and deterministic — never call real time.sleep in gradeable code.
  • max_retries is the number of RETRIES, so the function calls fn up to max_retries + 1 times before giving up.

Build a retry wrapper with exponential backoff — the single most common reliability primitive around a flaky call (an LLM API, a tool HTTP request, a rate-limited endpoint). The interviewer wants to see correct attempt counting, a growing backoff, and a sleep you can inject so the behavior is deterministic and testable.

How backoff works

You call fn(). If it succeeds, you return its result. If it raises, you wait a bit and try again — and each wait is longer than the last so you back off a service that is under pressure.

For retry attempt attempt (0-indexed: the first retry is attempt = 0), the wait is:

delay = base_delay * 2**attempt + jitter
jitter = 0.1 * base_delay * attempt

The jitter here is deterministic (a fixed function of attempt) so the test can assert exact delays. In production you would randomize it to avoid thundering-herd sync, but a gradeable exercise keeps it reproducible.

You sleep by calling the injected sleep(delay) — not time.sleep — so tests run instantly and can record every delay.

Task

Complete retry(fn, max_retries, base_delay, sleep):

  1. Attempt fn(). On success, return its result immediately.
  2. On exception, if any retries remain, compute delay = base_delay * 2**attempt + 0.1 * base_delay * attempt, call sleep(delay), then try again.
  3. After the final attempt fails (a total of max_retries + 1 calls to fn), re-raise the last exception.

Example

calls = []
delays = []

def flaky():
    calls.append(1)
    if len(calls) < 3:          # fail twice, then succeed
        raise ValueError("boom")
    return "ok"

def sleep(s):
    delays.append(s)

retry(flaky, max_retries=3, base_delay=1.0, sleep=sleep)  # -> "ok"
# len(calls) == 3
# delays == [1.0, 2.1]   # attempt 0: 1*1 + 0; attempt 1: 1*2 + 0.1

Constraints

  • Do not call time.sleep or any real timer — use the injected sleep.
  • max_retries counts retries, not total attempts: fn is invoked at most max_retries + 1 times.
  • If max_retries == 0, call fn exactly once and let any exception propagate (no sleep).
  • Do not swallow the final failure — re-raise the exception from the last attempt unchanged.

Write your solution, then hit Run tests to check it — or get a mock grade from the AI coach.

The coach remembers this session — revise your code and ask again, and it grades your progress. It gives hints, not the answer.
Report a bug