Fault-tolerance primitives — retries, circuit breakers, timeouts, fallbacks, and health checks

Overview

attune.resilience is a small toolkit of fault-tolerance primitives for code that calls flaky things (LLM APIs, networks, subprocesses). Five patterns, each in its own module and exported from attune.resilience:

Each pattern has a decorator (the common case) and the underlying class/function for programmatic use.

Concepts

retry

@retry(...) re-invokes a function on exception with exponential backoff. Knobs: max_attempts (3), backoff_factor (2.0), initial_delay (1.0), max_delay (60.0), jitter (True), retryable_exceptions (which exceptions to retry — default all), and an on_retry callback. RetryConfig holds the same settings as an object (get_delay(attempt) computes the wait); retry_with_backoff(func, *a, config=...) runs a call imperatively.

circuit breaker

@circuit_breaker(name=..., failure_threshold=5, reset_timeout=60.0, half_open_max_calls=3) trips OPEN after failure_threshold consecutive failures, short-circuiting calls (raising CircuitOpenError) until reset_timeout elapses, then probes in HALF_OPEN. CircuitState is CLOSED / OPEN / HALF_OPEN. The underlying CircuitBreaker class exposes record_success / record_failure / reset / get_stats / get_time_until_reset; get_circuit_breaker(name) looks one up from the shared registry by name.

timeout

@timeout(seconds, error_message=None, fallback=None) bounds a call, raising ResilienceTimeoutError (or returning fallback) on overrun. For awaiting a coroutine with a bound, use with_timeout(coro, seconds, fallback_value=None).

fallback

@fallback(*fallback_funcs, default=None) runs the alternates in order when the decorated function raises. with_fallback(primary, fallbacks, default=None) builds the same chain programmatically — note it returns an async wrapper (await it), whereas the @fallback decorator runs synchronously on a sync target (it returns a coroutine wrapper for an async def). Fallback(name, functions, default_value) is the underlying class (add / execute).

health checks

HealthCheck aggregates named checks. Register each as a decorator@hc.register(name, timeout=10.0, critical=False) over a check function — then run_all() (async) or run_all_sync() returns a SystemHealth (status, checks, to_dict()). HealthStatus is HEALTHY / DEGRADED / UNHEALTHY / UNKNOWN.