Across processes (Redis)¶
A circuit_breaker lives in one process. Behind a load balancer with 40 workers, each worker discovers a dead dependency on its own, and a rate_limit(100) lets 4,000 calls through. nopanic-redis keeps that state in Redis, so the fleet trips together and shares one quota.
pip install nopanic-redis
import redis
from nopanic_redis import redis_circuit_breaker, redis_rate_limit
r = redis.Redis() # redis.asyncio.Redis() for async functions
payments = redis_circuit_breaker(r, name="payments", min_calls=20, reset_timeout=15.0)
openai_quota = redis_rate_limit(r, name="openai", rate=500, per=60.0, max_wait=5.0)
@openai_quota
@payments
def charge(order): ...
Semantics¶
- Same model as the local policies.
redis_circuit_breakertakes the parameters ofcircuit_breaker(failure threshold over a bucketed sliding window,min_calls,reset_timeout,half_open_max_calls,on);redis_rate_limitbehaves likerate_limit(waiting,max_wait,RateLimited.retry_after, deadlines, refunds for cancelled waiters). - Identity is the name. Every instance with the same
nameandprefixis the same breaker or bucket, whichever process created it. Keys carry a Redis Cluster hash tag, so one policy's keys share a slot. - One atomic script per decision. A breaker costs two round trips per call (admit, record), a rate limit one.
- Wall-clock time. Decisions use the callers'
time.time()(injectable viaclock=). Keep hosts NTP-synced; skew only shifts window boundaries slightly. - Redis down is not your outage. By default (
fail_open=True) calls go through unguarded and abreaker.store_error/ratelimit.store_errorevent is emitted.fail_open=Falserejects withCircuitOpen/RateLimitedinstead. - Client flavour decides call flavour. A
redis.Redisclient serves sync functions, aredis.asyncio.Redisclient async ones. To guard both, create two instances with the same name: they share state. get_state()andreset()act fleet-wide (they return awaitables with an async client). Streams are not supported.
Combine freely with local policies: a local bulkhead per process and a shared redis_rate_limit per fleet is a common pairing.