Comparison
How grelmicro compares to the focused libraries that cover one Pattern each.
Most Python libraries in this space are point solutions: one library for caching, another for rate limiting, another for distributed locks, another for retries, another for circuit breaking, another for health checks. Each ships its own config story, its own lifecycle, its own backend client, its own logging shape.
grelmicro's unique value is the opposite: one toolkit that ships every microservice resilience and infrastructure Pattern behind a unified API. One config contract. One lifecycle (async with micro:). One Redis client shared by Cache, Lock, RateLimiter, CircuitBreaker. One Postgres client shared by Lock, LeaderElection, TaskLock. One way to reconfigure at runtime. One way to declare health checks.
If you only need a single Pattern, a focused library is often the right pick and this page will say so. If you need two or more, the toolkit story wins by removing four or five config dialects from your codebase.
Tone: factual. Where the focused alternative is the better fit, this page says so.
TL;DR
| If you only need this | Use this | Pick grelmicro when you also need |
|---|---|---|
| Async cache decorator over Redis | aiocache or fastapi-cache |
Opt-in per-key stampede protection (lock=True), type-safe TTLCache[T], Postgres backend (planned, see #167) |
| FastAPI rate limiter | slowapi or fastapi-limiter |
Sliding-window algorithm, structured RateLimitResult for retry-after headers, swap Memory and Redis with the same API |
| Async circuit breaker | pybreaker or aiobreaker |
Reconfigurable thresholds, frozen Pydantic config, structured logging context, distributed backend (planned, see #163) |
Retry with @retry(stop=, wait=) |
tenacity |
A Retry that shares the same config + reconfigure shape as the rest of grelmicro |
| Redis distributed lock | aioredlock |
Same Lock primitive across Redis, PostgreSQL, SQLite, Kubernetes, in-memory. Adapter-agnostic protocol |
/healthz endpoint |
hand-rolled FastAPI handler | Concurrent check execution, per-check TTL cache, /livez + /readyz + /healthz triple, critical vs non-critical, FastAPI router included |
The pattern across every row: pick the focused library when you only need that one Pattern. Pick grelmicro when you need two or more, with a single config and lifecycle story across all of them.
The unique value: one toolkit, every pattern
What you get from a single import:
| Pattern | grelmicro class | Backends |
|---|---|---|
| Distributed lock | Lock |
Redis, PostgreSQL, SQLite, Kubernetes Lease, Memory |
| Leader election | LeaderElection |
same as Lock |
| Scheduled-task lock | TaskLock |
same as Lock |
| Cache decorator + TTL store | Cache, TTLCache[T], @cached |
Redis, Memory (Postgres planned) |
| Rate limiter | RateLimiter (token bucket, sliding window) |
Redis, Memory |
| Circuit breaker | CircuitBreaker |
Memory (distributed planned) |
| Retry | Retry, @retry |
n/a (in-process) |
| Health checks | HealthChecks + /livez /readyz /healthz router |
n/a |
| Scheduled tasks | Tasks, TaskRouter, @interval |
n/a |
| Structured logging | grelmicro.log (JSON, LOGFMT, PRETTY, AUTO) |
n/a |
| Tracing | grelmicro.trace (@instrument, span, add_context) |
OpenTelemetry |
All of them share:
- One config contract. Every Pattern reads
GREL_{PATTERN}_{NAME}_*env vars, or accepts a kwargs constructor, or accepts a PydanticConfig. Three paths, identical shape across Patterns. - One lifecycle.
async with Grelmicro(uses=[...]):opens every backend, every component, every Pattern. No per-librarystartup()/shutdown()ceremony. - One Redis / Postgres / SQLite client. Cache, Lock, RateLimiter on the same Redis don't open three connections. The shared client is a Provider; Patterns receive Adapters.
- One reconfigure protocol. Every Pattern that owns runtime thresholds (
CircuitBreaker,RateLimiter,Retry,Lock) supportsreconfigure(new_config)without restart. - One logging context. Every Pattern logs through the same structured format, with the same field names.
No focused library in the Python ecosystem covers more than two of these Patterns. The closest thing in other ecosystems is the resilience-pipeline class of libraries (Java, .NET), and none of those bundle distributed locks, caches, leader election, scheduled tasks, and health checks into the same toolkit.
If you build microservices on FastAPI today, grelmicro is the missing batteries.
Cache
@cached decorator + TTLCache over Redis or in-memory.
| Axis | aiocache |
fastapi-cache |
grelmicro Cache |
|---|---|---|---|
| Backends | Memory, Redis, Memcached | Memory, Redis, Memcached, DynamoDB | Memory, Redis (Postgres at 1.0, see #167) |
| Decorator | @cached |
@cache |
@cached |
Type-safe Cache[T] |
no | no | yes (TTLCache[T] plus PydanticSerializer(T)) |
| Per-key stampede protection | local lock via lock_value |
none | opt-in via lock=True (off by default). Distributed lock planned (see #235) |
| Serializers | several built-in | json, binary | JsonSerializer, PydanticSerializer, PickleSerializer |
| FastAPI integration | manual | first-class | works with any async framework, no FastAPI coupling |
Pick aiocache if you only need the cache primitive and want Memcached. Pick fastapi-cache if you want the most FastAPI-native surface and DynamoDB. Pick grelmicro when you also need a distributed Lock, RateLimiter, or CircuitBreaker on the same Redis client and want one config story across all of them.
Rate Limiter
Cap requests per window with token bucket or sliding window, per key.
| Axis | slowapi |
fastapi-limiter |
aiolimiter |
grelmicro RateLimiter |
|---|---|---|---|---|
| Algorithm | fixed/sliding window | fixed window | token bucket | token bucket, sliding window |
| Backends | Memory, Redis, Memcached, MongoDB | Redis only | in-process only | Memory, Redis (Postgres + SQLite at 1.0, see #164, #173) |
| Async-first | partial (Flask-Limiter port) | yes | yes | yes |
| Result shape for retry-after | string parsing | string parsing | none | RateLimitResult(allowed, limit, remaining, retry_after, reset_after) |
| Reconfigurable at runtime | no | no | no | yes (reconfigure(new_config)) |
| FastAPI integration | first-class | first-class | manual | works in any async framework |
Pick slowapi if you want fixed-window per-route limiting on FastAPI and a wider backend choice. Pick aiolimiter for the smallest in-process token bucket. Pick grelmicro when you also need a distributed Lock or Cache on the same backend, when you need precise sliding-window semantics, or when you need a structured retry-after result without parsing strings.
Circuit Breaker
Half-open / open / closed states, with thresholds and ignore lists.
| Axis | pybreaker |
aiobreaker |
grelmicro CircuitBreaker |
|---|---|---|---|
| Async-first | sync-first, has async wrapper | yes | yes |
| Storage | in-memory, Redis (via plugin) | in-memory | in-memory (Memory backend), distributed protocol at 1.0 (see #163) |
| Decorator + async CM | decorator + sync CM | decorator + async CM | decorator + async CM |
| Frozen Pydantic config | no | no | yes (CircuitBreakerConfig) |
| Live reconfigure | no | no | yes (reconfigure(new_config)) |
| Structured logging context | no | no | yes (the breaker logs name, state, last_error) |
| Listener hooks | yes (add_listener) |
basic | structured logs are first-class, explicit listener API post-1.0 |
Pick pybreaker if you have a sync-first codebase or want listener hooks today. Pick aiobreaker if you want a small async-only breaker with no other dependencies. Pick grelmicro when you also need a Retry or Lock that shares the same config story, or when you need to swap thresholds at runtime without restart.
Retry
Decorator and async context manager with stop / wait / retry conditions.
| Axis | tenacity |
backoff |
grelmicro Retry |
|---|---|---|---|
| Stop conditions | rich (stop_after_attempt, stop_after_delay, ...) |
basic | attempts= (count). Time-based stop planned. |
| Wait strategies | rich (wait_exponential, wait_chain, ...) |
exponential, fibonacci, constant | exponential, constant, linear, fibonacci, random |
| Retry condition | retry_if_* factories, \|/& operators |
exception type or predicate | Match.exception(...), Match.result(...), Match.exception_message(...), Match.exception_cause(...), plus not_* twins, \|/& operators |
| Async support | yes | yes | yes |
| Sync support | yes | yes | yes (decorator works on both) |
| Frozen config + reconfigure | no | no | yes |
| Shares filter DSL with other resilience Patterns | n/a | n/a | yes (Match ships with Retry, reused across the resilience module) |
tenacity is the right pick for the most advanced stop and wait vocabulary. Pick grelmicro Retry when you want a smaller surface, result-based retry out of the box (Match.result(None)), and a filter DSL (Match) shared with the other resilience Patterns in the same library.
Distributed Lock
Mutex across processes, replicas, or hosts.
| Axis | aioredlock |
redis-py lock |
hand-rolled Postgres advisory lock | grelmicro Lock |
|---|---|---|---|---|
| Algorithm | Redlock | SET NX with Lua release | pg_advisory_xact_lock |
SET NX + Lua release in Redis, advisory locks in Postgres, coordination.k8s.io/v1 Lease with resourceVersion CAS in Kubernetes |
| Backends | Redis only | Redis only | Postgres only | Redis, Postgres, SQLite, Kubernetes, Memory |
| Async-first | yes | partial | depends on driver | yes |
| Token-based release | yes | yes | n/a (transaction-scoped) | yes (token derived from worker id + task or thread id, deterministic so re-acquire extends the lease) |
| Reentrant detection | manual | no | n/a | yes (raises LockReentrantError) |
| Idempotent acquire (lease extension) | partial | manual | n/a | yes (the same token re-acquire extends the lease) |
from_thread adapter for blocking code |
no | no | n/a | yes |
| Reconfigurable lease and retry | no | no | n/a | yes |
| Same primitive backs leader election and task lock | no | no | n/a | yes (LeaderElection and TaskLock share the protocol) |
Pick aioredlock if you want the Redlock algorithm specifically. Pick a hand-rolled pg_advisory_lock if Postgres is your only backend and you only need one lock kind. Pick grelmicro when you want one Lock primitive across multiple backend choices, plus LeaderElection and TaskLock on the same protocol.
Health Checks
Liveness, readiness, and aggregate endpoints for orchestrators and load balancers.
| Axis | hand-rolled FastAPI handler | py-healthcheck |
grelmicro HealthChecks |
|---|---|---|---|
| Concurrent check execution | manual asyncio.gather |
no | yes (asyncio.TaskGroup) |
| Per-check timeout | manual | no | yes |
| Per-check TTL cache + single-flight | manual | no | yes (default cache_ttl=1.0) |
| Critical vs non-critical | manual | no | yes (non-critical never flips /readyz) |
/livez + /readyz + /healthz triple |
hand-rolled | no | yes (FastAPI router included) |
?exclude query |
manual | no | yes |
Verbose details gated by Depends(...) |
manual | no | yes (show_details=Depends(fn)) |
Pick a hand-rolled handler if you only need one boolean endpoint. Pick grelmicro when you want the orchestrator-grade triple (/livez, /readyz, /healthz) with concurrent execution, caching, and details gated by a FastAPI dependency.
What grelmicro is NOT
A few categories the comparison page does not cover, because grelmicro does not compete in them:
| Category | Use this instead |
|---|---|
| HTTP framework | FastAPI, Starlette, Litestar |
| Embedded server | Uvicorn, Hypercorn, Granian |
| Message broker abstraction | FastStream, aio_pika |
| Background workers (queues) | Celery, dramatiq, taskiq |
| ORM | SQLAlchemy, SQLModel, tortoise-orm |
| Auth | Authlib, authx, fastapi-users |
| Service mesh / discovery | Istio, Linkerd, Kubernetes DNS, Consul |
| API gateway | Envoy, Nginx, Kong |
grelmicro fills the gap between "I picked FastAPI for HTTP" and "I need a real distributed lock, rate limit, circuit breaker, cache, leader election, scheduled tasks, and health checks". It does not try to replace the rows above.