Cache¶
A cache remembers the results of slow work — a heavy query, an API call — so repeat requests get an instant answer instead of doing the work again.
EndoCore ships a small cache with in-memory and Redis backends, plus a
@cached decorator.
Configure & use¶
from endocore import configure_cache, get_cache
configure_cache("memory") # process-local, TTL-aware
# or: configure_cache("redis", client=redis_client) # pip install "endocore[redis]"
cache = get_cache()
cache.set("user:1", {"name": "Ada"}, ttl=60) # ttl in seconds (optional)
cache.get("user:1", default=None)
cache.has("user:1")
cache.delete("user:1")
cache.incr("hits", amount=1) # atomic-ish counter
cache.clear()
If you never call configure_cache, get_cache() returns a default in-memory
cache.
@cached decorator¶
Memoize a function's result (sync or async) with an optional TTL:
from endocore import cached
@cached(ttl=30)
async def expensive(user_id: int):
return await slow_lookup(user_id)
@cached(ttl=300, key=lambda a, b: f"sum:{a}:{b}")
def add(a, b):
return a + b
Inject the cache¶
Via DI, either with the CacheExtension or a
provider:
# extensions.py
from endocore.extensions import CacheExtension
extensions = [CacheExtension(backend="memory")]
Redis backend¶
# extensions.py
from endocore.extensions import RedisExtension, CacheExtension
extensions = [
RedisExtension(url="redis://localhost:6379/0"),
CacheExtension(backend="redis"), # uses the Redis client above
]
Values are pickled; keys are namespaced with a prefix (endocore: by default).
Sign cached values if Redis isn't fully trusted
pickle.loads() on whatever bytes Redis returns is only as safe as Redis
itself — anything that can write that key (an exposed/misconfigured
instance, a compromised neighboring service) gets code execution on the
next cache read. Pass secret= to HMAC-sign every value on write and
verify it on read; a missing or wrong signature is treated as a cache
miss, never an exception:
extensions = [
RedisExtension(url="redis://localhost:6379/0"),
CacheExtension(backend="redis", secret=env("SECRET_KEY")),
]
Without secret=, values are unsigned (as before) and a warning is
raised once per cache instance.
Rate limiting
rate_limit_middleware(..., redis_client=...) shares one limit across
every worker process instead of counting per-process — see
Middleware.