Session & feature-flag stores
Hot keys, read-mostly, latency-sensitive. Client-side caching turns Redis into an invalidated local map instead of a tax on every request.
cache=LRUCache(...) + GET/SET/EXPIRE
algor.ist experiment · live Redis
coredis is a fast, fully typed Redis client built on structured concurrency. Cluster, Sentinel, pipelines, client-side caching, streams, and OpenTelemetry — without the footguns of half-async ports.
import anyio
import coredis
from coredis.cache import LRUCache
async def main() -> None:
client = coredis.Redis(
cache=LRUCache(max_keys=4096),
decode_responses=True,
)
async with client:
await client.set("hello", "coredis")
async with client.pipeline() as pipe:
pipe.incr("hits")
name = pipe.get("hello") # Awaitable[str|None]
print(await name)
anyio.run(main, backend="asyncio")
Live against Redis
Each run executes real commands on a local Redis instance via this showcase’s
Python backend — coredis on one side, redis.asyncio on the other.
Numbers reflect this machine, this moment; the point is the shape of the difference.
Deep dives
Performance is one axis. Typing, topology, caching, and runtime flexibility are the reasons teams stop fighting their Redis client.
Feature matrix
| Capability | coredis | redis-py asyncio | legacy aioredis |
|---|---|---|---|
| Native async design | Yes (anyio) | Port of sync API | Unmaintained / merged |
| Strict type annotations | Full public API | Partial | Weak |
| Typed pipeline futures | Per-command | list from execute() | list |
| Client-side caching | TrackingCache | DIY | DIY |
| asyncio + trio | anyio | asyncio only | asyncio only |
| Cluster + Sentinel | First-class | Supported | Partial |
| Redis modules / Stack | Handbook support | Via raw commands | Limited |
| OpenTelemetry | coredis[otel] | External | — |
| Runtime type checks | beartype optional | — | — |
| Native extensions (wheels) | Bulk response speed | hiredis optional | hiredis |
Typical asyncio jobs
Hot keys, read-mostly, latency-sensitive. Client-side caching turns Redis into an invalidated local map instead of a tax on every request.
cache=LRUCache(...) + GET/SET/EXPIRE
INCR + EXPIRE or token buckets under concurrent workers. Typed commands and predictable pool behavior matter when every request touches Redis.
INCR · LUA · pipelines
Consumer groups, acks, reclaim of pending entries. High-level stream helpers beat yet another fragile XREADGROUP loop.
XREADGROUP · XACK · consumers
Live notifications in asyncio services. Clean subscribe abstractions and structured cancel scopes (anyio) avoid zombie listeners.
SUBSCRIBE · PSUBSCRIBE
Multi-key pitfalls and MOVED redirects should be the client’s problem. One API from laptop Redis to production cluster.
RedisCluster · pipelines
Trace Redis commands next to HTTP spans. Optional OTel integration without wrapping every call site by hand.
coredis[otel]
Try it
pip install coredis
# or
uv add coredis
import asyncio
import coredis
async def main():
r = coredis.Redis(decode_responses=True)
async with r:
await r.set("msg", "hi")
print(await r.get("msg"))
asyncio.run(main())
cd experiments/coredis
uv sync
# Redis on :6379 (or: docker compose up redis -d)
uv run uvicorn showcase.main:app --reload --port 8080
Official docs: coredis.readthedocs.io · Source: github.com/alisaifee/coredis