🐍
Code Snippet — July 10, 2026Async Retry with Exponential Backoff, Jitter & Circuit Breaker
A production-grade
@retry decorator for
async def — zero dependencies:
import asyncio, functools, random
_budget, _circuit = {}, {}
def retry(max_attempts=3, backoff_base=1.0, backoff_max=30.0,
jitter='random', circuit_breaker=None, budget=None,
retry_on=Exception, on_retry=None):
def decorator(func):
key = f'{func.__module__}.{func.__qualname__}'
@functools.wraps(func)
async def wrapper(*a, **kw):
for attempt in range(1, max_attempts+1):
try:
r = await func(*a, **kw)
if circuit_breaker: _circuit[key]=0
if budget: _budget[key]=budget
return r
except Exception as e:
if not isinstance(e, retry_on): raise
if attempt >= max_attempts: raise
if circuit_breaker:
_circuit[key] = _circuit.get(key,0)+1
if _circuit[key]>=circuit_breaker:
raise RuntimeError('Open') from e
if budget:
if _budget.get(key,0)<=0: raise RuntimeError('Exhausted') from e
_budget[key] -= 1
d = min(backoff_base*2**(attempt-1), backoff_max)
if jitter=='random': d=random.uniform(0,d)
elif jitter=='full': d=random.uniform(0,backoff_max)
if on_retry: on_retry(attempt,d,e)
await asyncio.sleep(d)
raise e
return wrapper
return decorator
💡 jitter='full' prevents thundering herds. budget=50 caps retries. circuit_breaker=10 stops after consecutive failures. Replaces bare try/except + time.sleep() — the #1 async anti-pattern.