Caching¶
Added in version 3.9.0.
coredis allows using a client side cache to eliminate readonly requests to the redis server when a response is available in the local cache.
If enabled the clients will cache the responses for readonly commands by
key/command/arguments. The coredis clients accept any cache
implementing the AbstractCache
interface and will:
Cache responses for readonly commands acting on single keys (the docstring for the method will indicate whether it supports caching, for example
get()
).Return cached responses when they are found if the cache is returning healthy via the
healthy
propertyInvalidate the entire cache for a key if a non readonly command is called
If the cache implements
SupportsSampling
and returns aconfidence
value lower than100
the client will distrust the cached response(100-$confidence)%
of the time and validate the cached response against the actual response from the server. The result of the comparison will be provided to the cache through a call tofeedback()
and it is up to the cache implementation to decide what to do with this feedback.
Tracking Cache¶
coredis currently ships with an implementation of Server assisted client side caching
that can be used with both the standalone Redis
client or the RedisCluster
client. This implementation enables client tracking for the client so that the redis server
remembers which keys the client has requested and if the key is modified (whether mutated, deleted or expired)
sends a notification that the cache subscribes to to invalidate the cache.
Specifically NodeTrackingCache
contains the implementation for a
single node and ClusterTrackingCache
tracks all the nodes in a redis cluster.
For convenience a proxy class that automatically picks the right implementation based on the
client is available as TrackingCache
.
For example:
import asyncio
import coredis
from coredis.cache import TrackingCache
cached_client = coredis.Redis(cache=TrackingCache())
regular_client = coredis.Redis()
# or in cluster mode
# cached_client = coredis.RedisCluster("localhost", 7000, cache=InvalidatingCache())
# regular_client = coredis.RedisCluster("localhost", 7000)
async def test():
assert not await cached_client.get("fubar") # None response cached
await regular_client.set("fubar", "bar") # <- triggers a push message to cached_client
await asyncio.sleep(0.01)
assert b"bar" == await cached_client.get("fubar") # Cache should be invalidated
assert b"bar" == await cached_client.get("fubar") # Fetched from local cache
await cached_client.delete(["fubar"]) # Invalidates local cache immediately
assert not await cached_client.get("fubar")
asyncio.run(test())
TrackingCache
exposes a few configuration options to fine tune
the cache. Specifically the following constructor arguments might be of interest:
max_size_bytes
Maximum size in bytes that the cache should be allowed to grow to. The cache will periodically shrink the cache in an LRU manner until it is below the threshold.
max_keys
Maximum number of redis keys to track. This does not map directly to the number of cached entries as the cache maintains a per key, per command, per argument cache.
max_idle_time_seconds
Maximum time to tolerate no repsonse from the server. The cache instance will use the
PING
command to verify if the server is responsive even if no invalidation notifications have been received and if the threshold is breached the in memory cache will be reset and the cache marked unhealthy.confidence
Confidence % in the cache. The client will sample cached values based on the confidence and if the cached value is not the same as the actual response from the server the actual value will be returned and the tainted key invalidated.
dynamic_confidence
If set to
True
the cache will adjust it’s confidence based on sampled (sampling depends on the initial confidence value itself) validations.