Skip to main content

A composable retry policy framework with classification, jitter strategies, and observability hooks.

Project description

reflexio

CI codecov PyPI Version

Composable, low-overhead retry policies with pluggable classification, per-class backoff strategies, and structured observability hooks.
Designed for services that need predictable retry behavior and clean integration with metrics/logging.

Documentation

Installation

From PyPI:

uv pip install reflexio
# or
pip install reflexio

Quick Start

from reflexio.policy import RetryPolicy
from reflexio.classify import default_classifier
from reflexio.strategies import decorrelated_jitter

policy = RetryPolicy(
    classifier=default_classifier,
    strategy=decorrelated_jitter(max_s=10.0),
)

def flaky():
    # your operation that may fail
    ...

result = policy.call(flaky)

Decorator quick start

from reflexio import retry, default_classifier
from reflexio.strategies import decorrelated_jitter

@retry  # defaults to default_classifier + decorrelated_jitter(max_s=5.0)
def fetch_user():
    ...

# Or customize classifier/strategies
@retry(
    classifier=default_classifier,
    strategy=decorrelated_jitter(max_s=3.0),
)
def fetch_user_custom():
    ...

# Context manager for repeated calls with shared hooks/operation
policy = RetryPolicy(classifier=default_classifier, strategy=decorrelated_jitter(max_s=3.0))
with policy.context(operation="batch") as retry:
    retry(fetch_user)

Async quick start

import asyncio
from reflexio import AsyncRetryPolicy, default_classifier
from reflexio.strategies import decorrelated_jitter

async_policy = AsyncRetryPolicy(
    classifier=default_classifier,
    strategy=decorrelated_jitter(max_s=5.0),
)

async def flaky_async():
    ...

asyncio.run(async_policy.call(flaky_async))

Why reflexio?

Most retry libraries give you either:

  • decorators with a fixed backoff model, or
  • one global strategy for all errors.

reflexio gives you something different:

✔ Exception → coarse error class mapping

Provided via default_classifier.

✔ Per-class strategy dispatch

Each ErrorClass can use its own backoff logic.

✔ Dependency-free strategies with jitter

decorrelated_jitter, equal_jitter, token_backoff.

✔ Deadlines, max attempts, and separate caps for UNKNOWN

Deterministic retry envelopes.

✔ Clean observability hook

Single callback for:
success, retry, permanent_fail, deadline_exceeded, max_attempts_exceeded, max_unknown_attempts_exceeded.

Error Classes & Classification

PERMANENT
CONCURRENCY
RATE_LIMIT
SERVER_ERROR
TRANSIENT
UNKNOWN

Classification rules:

  • Explicit reflexio error types
  • Numeric codes (err.status or err.code)
  • Name heuristics
  • Fallback to UNKNOWN

Metrics & Observability

def metric_hook(event, attempt, sleep_s, tags):
    print(event, attempt, sleep_s, tags)

policy.call(my_op, on_metric=metric_hook)

Backoff Strategies

Strategy signature:

(attempt: int, klass: ErrorClass, prev_sleep: Optional[float]) -> float

Built‑ins:

  • decorrelated_jitter()
  • equal_jitter()
  • token_backoff()

Per-Class Example

policy = RetryPolicy(
    classifier=default_classifier,
    strategy=decorrelated_jitter(max_s=10.0),  # default
    strategies={
        ErrorClass.CONCURRENCY: decorrelated_jitter(max_s=1.0),
        ErrorClass.RATE_LIMIT: decorrelated_jitter(max_s=60.0),
        ErrorClass.SERVER_ERROR: equal_jitter(max_s=30.0),
    },
)

Deadline & Attempt Controls

policy = RetryPolicy(
    classifier=default_classifier,
    strategy=decorrelated_jitter(),
    deadline_s=60,
    max_attempts=8,
    max_unknown_attempts=2,
)

Development

uv run pytest

Examples (in docs/snippets/)

  • Sync httpx demo: uv pip install httpx then uv run python docs/snippets/httpx_sync_retry.py
  • Async httpx demo using AsyncRetryPolicy: uv pip install httpx then uv run python docs/snippets/httpx_async_retry.py
  • Async worker loop with retries: uv run python docs/snippets/async_worker_retry.py
  • Decorator usage (sync + async): uv run python docs/snippets/decorator_retry.py
  • FastAPI proxy with metrics counter: uv pip install "fastapi[standard]" httpx then uv run uvicorn docs.snippets.fastapi_downstream:app --reload
  • FastAPI middleware with per-endpoint policies: uv pip install "fastapi[standard]" httpx then uv run uvicorn docs.snippets.fastapi_middleware:app --reload
  • PyODBC + SQLSTATE classification example: uv pip install pyodbc then uv run python docs/snippets/pyodbc_retry.py
  • requests example: uv pip install requests then uv run python docs/snippets/requests_retry.py
  • asyncpg example: uv pip install asyncpg and set ASYNC_PG_DSN, then uv run python docs/snippets/asyncpg_retry.py
  • Pyperf microbenchmarks: uv pip install .[dev] then uv run python docs/snippets/bench_retry.py

Docs site

  • Build/serve locally: uv pip install .[docs] then uv run mkdocs serve
  • Pages: docs/index.md, docs/usage.md, docs/observability.md, docs/recipes.md with runnable snippets in docs/snippets/.

Versioning

Semantic Versioning.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

reflexio-0.2.2.tar.gz (79.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

reflexio-0.2.2-py3-none-any.whl (14.3 kB view details)

Uploaded Python 3

File details

Details for the file reflexio-0.2.2.tar.gz.

File metadata

  • Download URL: reflexio-0.2.2.tar.gz
  • Upload date:
  • Size: 79.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for reflexio-0.2.2.tar.gz
Algorithm Hash digest
SHA256 f9dabac69c94e52afddbb6e71d176f1dec9d4ada64a09354ec5138f9f5898cef
MD5 5b7be9bd820e36d3853c7cee3e115145
BLAKE2b-256 c0a6a683d1699862948ddb79773dea8fc3a7bc61a70eef4b525dcf1af1ce4dae

See more details on using hashes here.

File details

Details for the file reflexio-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: reflexio-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 14.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for reflexio-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 49418064927a1cb24cf9ae0c5d8c4a437a105e8d40e96298b5c7b17c28c7c9ec
MD5 a1739ae82a854bf2c4b51b3c1e2ba4b0
BLAKE2b-256 caf042052c16a2c372bd2d1e1537f6743973dc82e879f53ac120a3aea52b22b6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page