python
41 lines · 6 steps
A retry decorator with exponential backoff
A configurable decorator that re-runs a flaky function, waiting longer between each failed attempt.
Explained by
highlit
1import random
2import time
3import logging
4from functools import wraps
5
6logger = logging.getLogger(__name__)
7
8
9def retry_with_backoff(
10 max_attempts=5,
11 base_delay=0.5,
12 max_delay=30.0,
13 backoff_factor=2.0,
14 jitter=True,
15 exceptions=(Exception,),
16):
17 def decorator(func):
18 @wraps(func)
19 def wrapper(*args, **kwargs):
20 delay = base_delay
21 for attempt in range(1, max_attempts + 1):
22 try:
23 return func(*args, **kwargs)
24 except exceptions as exc:
25 if attempt == max_attempts:
26 logger.error(
27 "%s failed after %d attempts: %s",
28 func.__name__, attempt, exc,
29 )
30 raise
31 sleep_for = min(delay, max_delay)
32 if jitter:
33 sleep_for = random.uniform(0, sleep_for)
34 logger.warning(
35 "%s attempt %d/%d failed (%s); retrying in %.2fs",
36 func.__name__, attempt, max_attempts, exc, sleep_for,
37 )
38 time.sleep(sleep_for)
39 delay *= backoff_factor
40 return wrapper
41 return decorator
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A three-layer nested function lets a decorator accept configuration arguments while still wrapping the target.
- 2Exponential backoff with a cap and jitter spreads out retries so a failing dependency isn't hammered in lockstep.
- 3Re-raising on the final attempt preserves the original exception so callers still see real failures.
Related explainers
python
import argparse import sys from pathlib import Path
Building a subcommand CLI with argparse
cli
argparse
subcommands
Intermediate
6 steps
go
package main import ( "errors"
Parsing and validating CLI flags in Go
cli-parsing
validation
error-handling
Intermediate
8 steps
java
public class ThumbnailProcessor { private static final int MAX_CONCURRENCY = 4;
Bounded parallel thumbnail rendering in Java
concurrency
thread-pool
futures
Intermediate
7 steps
typescript
type RetryOptions = { retries?: number; timeoutMs?: number; baseDelayMs?: number;
Retry with timeout and backoff in TypeScript
promises
retry
exponential-backoff
Intermediate
10 steps
python
from collections.abc import Mapping from typing import Any, Iterator
Flattening nested config into dotted keys
recursion
generators
tree-traversal
Intermediate
7 steps
python
import csv import io from datetime import datetime
Streaming a CSV export in Flask
streaming
generators
csv
Intermediate
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/a-retry-decorator-with-exponential-backoff-explained-python-0cf9/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.