In-memory, thread-safe cache for FastAPI, Django, and any Python
backend.
TTL with lazy expiration, a memoize decorator, and built-in metrics โ no Redis, no broker, no
database.
pip install rust-py-cache
Rust handles storage, locking, and expiration. Python stays simple.
Reads and writes hit a sharded DashMap in process โ no network hop, no disk. A "mini Redis"
living inside your app.
set(key, value, ttl=60) in seconds. Expired keys are dropped on access, or swept in bulk with
cleanup_expired().
Values are pickle-serialized, so dicts, lists, dataclasses, sets โ anything picklable โ
round-trip unchanged.
Per-shard locks plus AtomicU64 counters mean you share one Cache across threads
with no global lock on the hot path.
@cache.cached(ttl=60) caches a function's return keyed by its arguments โ and even a
None result is cached, not recomputed.
stats() returns hits, misses, sets,
deletes, expired, and size โ observability for free.
set / get / delete / exists / keys /
clear / len(cache) โ exactly the methods you'd reach for.
Drop a Cache in front of a slow query or report. Per-process, no broker, no Redis instance to
run.
Storage, TTL, and counters compile to a native .so extension against the stable ABI. No Rust
needed to use it.
Call the core API, memoize a function, or wire it into your framework.
from rust_py_cache import Cache
cache = Cache()
# Values can be any picklable Python object.
cache.set("user:1", {"name": "Roberto"})
cache.get("user:1") # {'name': 'Roberto'}
cache.get("missing", default=0) # 0 โ no KeyError
cache.exists("user:1") # True (honors TTL)
cache.delete("user:1") # True if it existed
cache.keys() # ['...']
len(cache) # number of entries
cache.stats() # {'hits', 'misses', 'sets', 'deletes', 'expired', 'size'}
from rust_py_cache import Cache
cache = Cache()
# ttl in seconds (int or float); None = never expires.
cache.set("token", "abc", ttl=30)
cache.set("forever", 42) # no expiration
cache.set("bad", 1, ttl=0) # ValueError: ttl must be > 0
# Lazy expiration: an expired key is dropped on access...
cache.get("token") # None once 30s have passed
# ...or you can sweep every expired entry at once:
removed = cache.cleanup_expired() # -> count removed
from rust_py_cache import Cache
cache = Cache()
# Memoize by arguments โ runs once per distinct call.
@cache.cached(ttl=60)
def fib(n):
return n if n < 2 else fib(n-1) + fib(n-2)
fib(30) # computed once, then served from cache
# Custom key: a fixed string, or a callable of the args.
@cache.cached(ttl=300, key=lambda uid: f"user:{uid}")
def load_user(uid):
return db.query(uid)
from fastapi import FastAPI
from rust_py_cache import Cache
app = FastAPI()
cache = Cache()
@app.get("/users/{user_id}")
def get_user(user_id: int):
key = f"user:{user_id}"
cached = cache.get(key)
if cached is not None:
return {"source": "cache", "data": cached}
user = load_from_db(user_id) # slow path
cache.set(key, user, ttl=30)
return {"source": "db", "data": user}
# views.py
from django.http import JsonResponse
from rust_py_cache import Cache
cache = Cache()
def product_detail(request, product_id):
key = f"product:{product_id}"
data = cache.get(key)
if data is None:
product = Product.objects.get(pk=product_id)
data = {"id": product.id, "name": product.name}
cache.set(key, data, ttl=60)
return JsonResponse(data)
# Invalidate when the row changes:
@receiver(post_save, sender=Product)
def drop_cache(sender, instance, **kwargs):
cache.delete(f"product:{instance.id}")
Every operation is counted with lock-free atomics. Inspect the cache at any time.
hits read foundmisses read emptysets writedeletes removeexpired TTL sweptAtomicU64 lock-freeO(1) snapshotthread-safe shared{
"hits": 128,
"misses": 7,
"sets": 135,
"deletes": 3,
"expired": 12,
"size": 120
}
# sweep every expired entry, return how many left
12
Rust stores and expires. Python serializes and calls. You inspect.
Storage is a concurrent DashMap<String, CacheEntry>; values are opaque
Vec<u8>
bytes that Python pickles. The native .so is compiled once via
maturin +
PyO3, against the stable ABI (abi3-py310) โ one wheel covers Python
3.10โ3.13+.
Your users just pip install โ no Rust toolchain, no Redis, no message broker required.
Install from PyPI. No Rust, no compilers, no configuration.