Cache¶
pachifast ships a small key/value cache with expiry, used internally for short-lived data. By default it lives in the memory of the process; point it at Redis to share it between workers and hosts.
What uses the cache today¶
| Feature | Key (before prefix) | Lifetime |
|---|---|---|
Activity log: the per-superuser choice "do not record my requests" (audit.disable_activity_log) |
activity-skip:<user id> |
30 seconds |
WebSocket tickets, only when events_ticket_store=cache |
ws-ticket:<sha-256 of the ticket> |
30 seconds |
See Events and activity log. The cache is a good fit for small, short-lived, disposable values; do not use it as a database.
Choose a backend¶
MemoryCache (default) |
RedisCache |
|
|---|---|---|
| Selected when | cache_url is empty |
cache_url is set |
| Shared between workers and hosts | No, one cache per process | Yes |
| Bounded | Yes, cache_max_entries |
By Redis |
| Expiry | Per entry, checked on access | Redis' own (millisecond precision) |
| Extra dependency | None | pachifast[redis] |
In memory¶
No configuration is required. The cache holds at most cache_max_entries entries (default 10000). When it is full, expired entries are dropped first, then the least recently used ones (LRU).
PACHIFAST_CACHE_MAX_ENTRIES=5000
Redis¶
Install the optional extra and set the URL:
uv add "pachifast[redis]"
PACHIFAST_CACHE_URL=redis://localhost:6379/0
PACHIFAST_CACHE_PREFIX=myapp:
| Setting | Default | Description |
|---|---|---|
cache_url |
empty | Empty means in-memory. Otherwise a redis://, rediss:// (TLS) or unix:// URL. Any other value is rejected at startup. |
cache_prefix |
pachifast: |
Prepended to every key. Use a different prefix per application when several share one Redis. |
cache_max_entries |
10000 |
Size limit of the in-memory cache. Ignored with Redis. |
Requirements and notes:
- Any Redis version: atomic read-and-delete is a
MULTI/EXECtransaction (noGETDEL, which needs Redis 6.2). - If
cache_urlis a Redis URL but theredispackage is not installed, the first call toget_cache()raises an error that tells you to installpachifast[redis]. - Put credentials in the URL through an environment variable or
.env, not in a committed YAML file.
All settings can also be given in the YAML file (cache_url, cache_prefix, cache_max_entries). See Configuration and the settings reference.
Use it from your code¶
from pachifast.cache import get_cache
cache = get_cache()
await cache.set("report:42", "ready", ttl=60) # seconds
value = await cache.get("report:42") # "ready", or None when missing or expired
once = await cache.pop("job:7") # read and delete atomically
await cache.delete("report:42") # no error if missing
get_cache() returns the shared instance. It is rebuilt automatically if cache_url, cache_prefix or cache_max_entries change. reset_cache() forgets the instance (useful in tests).
The interface, pachifast.cache.Cache, is deliberately small:
| Method | Description |
|---|---|
await get(key) -> str \| None |
The value, or None if missing or expired. |
await set(key, value, ttl) |
Store a string for ttl seconds. |
await delete(key) |
Forget the key. |
await pop(key) -> str \| None |
Atomic read and delete: with concurrent callers only one gets the value. |
await close() |
Release connections (closes the Redis pool; does nothing in memory). |
Notes:
- Keys and values are strings. Serialise structured data yourself (for example with
json.dumps). - Keys are namespaced by the backend: with Redis the stored key is
<cache_prefix><key>. Choose your own keys so they do not collide with the ones listed above. get_cache()is the only supported way to obtain a cache; the classesMemoryCacheandRedisCacheare exported for tests and advanced use.
Failure behaviour¶
| Situation | Behaviour |
|---|---|
| Redis unreachable while reading or writing the activity-log choice | A warning is logged; the setting is read from the database instead, and the request is not affected. |
Redis unreachable while issuing or redeeming a WebSocket ticket (events_ticket_store=cache) |
The request fails with a server error. Use events_ticket_store=database if you cannot depend on the cache. |
Invalid cache_url scheme |
The application refuses to start with a validation error. |
| Your own code | Calls raise the Redis client's exceptions: handle them as you would for any network call. |
Several workers¶
With the in-memory backend every worker has its own cache. That is harmless for the activity-log choice (worst case, each worker reads the database once every 30 seconds), but it breaks WebSocket tickets stored in the cache: a ticket issued by one worker cannot be redeemed by another. The simplest options are:
- keep the default
events_ticket_store=database, which works across workers and hosts without Redis; or - set
cache_urlto a Redis server and useevents_ticket_store=cache.
See Deployment for running several workers.