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
typescript
import { NestFactory } from '@nestjs/core'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { ValidationPipe } from '@nestjs/common'; import { ApiProperty } from '@nestjs/swagger';
Wiring validation and Swagger docs in NestJS
validation
openapi
decorators
Intermediate
8 steps
python
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status from pydantic import BaseModel, EmailStr from sqlalchemy.orm import Session
Building a signup endpoint in FastAPI
dependency-injection
request-validation
background-tasks
Intermediate
8 steps
javascript
const ROLE_PERMISSIONS = { admin: ['users:read', 'users:write', 'billing:read', 'billing:write'], manager: ['users:read', 'billing:read'], member: ['users:read'],
Role-based permissions middleware in Express
authorization
middleware
rbac
Intermediate
9 steps
ruby
class TemplateInterpolator PLACEHOLDER = /\{\{\s*([\w.]+)\s*\}\}/ def initialize(strict: false)
Interpolating templates with dotted keys in Ruby
regex
string-interpolation
hash-traversal
Intermediate
6 steps
python
from django import forms from django.utils import timezone from .models import Reservation
Multi-field validation in a Django ModelForm
form validation
cross-field validation
modelform
Intermediate
7 steps
python
from django import template from django.urls import reverse, NoReverseMatch from django.utils.html import format_html
Active nav-link template tags in Django
template tags
url routing
active state
Intermediate
7 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.