This page walks you through your first cached function, from a running Redis server to sync and async usage, and helps you pick an eviction policy.
-
A Redis server, e.g. via Docker:
docker run -it --rm -p 6379:6379 redis:alpine
-
The library installed in your Python environment:
pip install redis_func_cache[hiredis]
In-process caches like the standard library's functools.cache are private to a single Python process: every worker re-computes and re-stores the same values, and the cache vanishes on restart. This library uses Redis as the backend, so the cache is:
- Distributed: all processes and machines behind your application share one cache — a result computed once is visible to every worker.
- Persistent across restarts: cached results survive process restarts and deploys (until evicted or expired).
- Cluster-ready: dedicated policies keep each cache's two Redis keys in the same hash slot, so the cache works on Redis Cluster out of the box.
- Atomic and highly available: every read/write is a single Lua script executed atomically by Redis; running Redis with replication/Sentinel gives you the usual Redis high-availability story for free.
from redis import Redis
from redis_func_cache import RedisFuncCache as Cache
pool = Redis.ConnectionPool.from_url("redis://")
factory = lambda: Redis.from_pool(pool)
cache = Cache("quickstart", maxsize=128, ttl=300, factory=factory)
@cache
def get_exchange_rate(base: str, quote: str) -> float:
print("fetching from remote API ...") # only printed on a cache miss
... # call the real API here
return 7.25
get_exchange_rate("USD", "CNY") # cache miss: the function executes, the line is printed
get_exchange_rate("USD", "CNY") # cache hit: served from Redis, nothing is printedWe create a Redis client (via a factory, recommended for concurrent use), then a RedisFuncCache instance — no policy argument, so it uses the default lru_t_policy (time-based LRU) — and decorate get_exchange_rate with it.
The first call executes the function and stores its result in Redis; the second call with the same arguments is served from Redis — the function body never runs.
It works almost the same as the standard library's functools.lru_cache, except that the cache lives in Redis and is therefore shared by every process and machine connecting to the same Redis.
To decorate async functions, supply an async Redis client via the factory argument — the example is the same shape as the sync one above:
import asyncio
from redis.asyncio import Redis as AsyncRedis
from redis_func_cache import RedisFuncCache as Cache
pool = AsyncRedis.ConnectionPool.from_url("redis://")
factory = lambda: AsyncRedis.from_pool(pool)
cache = Cache("quickstart-async", maxsize=128, ttl=300, factory=factory)
@cache
async def get_exchange_rate(base: str, quote: str) -> float:
print("fetching from remote API ...") # only printed on a cache miss
... # await the real API call here
return 7.25
async def main():
await get_exchange_rate("USD", "CNY") # cache miss: the function executes
await get_exchange_rate("USD", "CNY") # cache hit: served from Redis
with asyncio.Runner() as runner:
runner.run(main())❗ Attention:
- When a
RedisFuncCacheis created with an async Redis client, it can only be used to decorate async functions. These async functions will be decorated with an asynchronous wrapper, and the I/O operations between the Redis client and server will be performed asynchronously.- Conversely, a synchronous
RedisFuncCachecan only decorate synchronous functions. These functions will be decorated with a synchronous wrapper, and I/O operations with Redis will be performed synchronously.
The default policy suits most caches, but you can specify any built-in policy when creating the cache:
from redis import Redis
from redis_func_cache import RedisFuncCache, fifo_policy, lfu_policy, rr_policy
pool = Redis.ConnectionPool.from_url("redis://")
factory = lambda: Redis.from_pool(pool)
# FIFO (First In, First Out)
fifo_cache = RedisFuncCache("my-fifo-cache", fifo_policy, factory=factory)
# LFU (Least Frequently Used)
lfu_cache = RedisFuncCache("my-lfu-cache", lfu_policy, factory=factory)
# Random Replacement
rr_cache = RedisFuncCache("my-rr-cache", rr_policy, factory=factory)Available policies:
lru_t_policy(Default, recommended): Time-based LRU, offers the best balance of performance and accuracy for most use cases. It is the default when thepolicyargument is omitted.fifo_policy: First in, first outgdsf_policy: Greedy-Dual-Size — evicts the smallest benefit per byte (frequency × cost / size); supports thecostdecorator kwarg (see considerations)hyperbolic_policy: Hyperbolic caching (LFU with aging) — frequency and recency in one score, so stale hot entries age outlfu_policy: Least frequently usedlru_policy: Least recently used (more precise but slower than LRU-T)lru_tr_policy: LRU-T with random admission — rejects ~half of the new insertions to blunt scan pollution; per-functionreject_poverride (requires Redis ≥ 7.0; see considerations)mru_policy: Most recently usedrr_policy: Random remove
ℹ️ Info:
Explore the source code in the directorysrc/redis_func_cache/policiesfor more details.