Chapter 15 · Pipelining, Batching, Client Libraries, and Client-Side Caching
Client-Side Caching and Server-Assisted Invalidation: Freshness and Coherency Boundaries
Use RESP3 server-assisted client-side caching with invalidations while defining the freshness, reconnect, memory, authorization, and unsupported-command boundaries explicitly.
Learning outcomes
AtlasMart repeatedly reads product configuration that changes infrequently. A process-local cache can remove Redis round trips, but a plain dictionary becomes stale when another client writes the key. Redis server-assisted client-side caching (CSC) pairs a local cache with server tracking and invalidation messages so the client knows when cached replies are no longer valid.
Explain Redis tracking, RESP3 invalidation push messages, and the difference between a local cache hit and a Redis server read.
Use redis-py CacheConfig with protocol=3 and prove cache hits/invalidation using a second writer and bounded MONITOR observation.
Distinguish server CLIENT TRACKING modes from the subset currently implemented by redis-py built-in CSC.
Define disconnect/reconnect policy: cached data must not survive loss of the invalidation channel without an explicit safe rule.
Treat client-side caching as a performance layer, not authorization, durability, or a replacement for application freshness contracts.
All Chapter 15 mandatory labs reuse the disposable Chapter 01
environment: Redis Open Source 8.10.1 from pinned
Docker image redis:8.10.1, container
atlasmart-redis-ch01, standalone topology, host
endpoint 127.0.0.1:6379, TLS disabled only
because traffic remains on loopback, default ACL user
disabled, named academy-admin and
atlasmart-app users, logical database 0, AOF with
appendfsync everysec plus RDB snapshots,
persistent /data, and no explicit
maxmemory/eviction policy. Python work pins
redis-py 8.1.0. Chapter-specific fixtures stay
under atlasmart:ch15:.
1. Tracking turns cache freshness into a protocol contract
With tracking enabled, Redis remembers which keys a client connection read (or, in broadcast mode, which prefixes it subscribed to). When a tracked key changes, Redis sends an invalidation. With RESP version 3 (RESP3), those invalidations can arrive as push messages on the same connection. The client library removes the corresponding cached reply so the next read returns to Redis.
| Layer | Responsibility |
|---|---|
| Redis server | Track read keys/prefixes and emit invalidation signals |
| RESP3 connection | Carry ordinary replies plus push invalidations |
| Client library | Maintain local cache, process invalidations, evict entries |
| Application | Choose what is safe to cache and what to do on disconnect/staleness |
2. redis-py built-in client-side caching has explicit requirements
Current redis-py supports client-side caching from 5.1.0 onward.
The current Redis guide recommends Redis 7.4+ for compatibility
across Redis products. The Chapter 15 baseline uses Redis 8.10.1
and redis-py 8.1.0. The connection must use RESP3 and pass a
CacheConfig. We still specify
protocol=3 explicitly even when a future client
default changes.
import redisfrom redis.cache import CacheConfigcached = redis.Redis( host="127.0.0.1", port=6379, username="atlasmart-app", password="AtlasMart-App-Lab-Only-2026", protocol=3, cache_config=CacheConfig(), decode_responses=True, client_name="atlasmart-ch15-cache",)writer = redis.Redis(host="127.0.0.1", port=6379, username="atlasmart-app", password="AtlasMart-App-Lab-Only-2026", decode_responses=True)key = "atlasmart:ch15:l4:product:42:name"writer.set(key, "Camera v1")print("first", cached.get(key)) # server read + cache fillprint("second", cached.get(key)) # local cache hitwriter.set(key, "Camera v2") # triggers invalidationprint("after-write", cached.get(key)) # refreshes from Rediscached.close(); writer.close()
3. Observe cache hits with MONITOR—only in the disposable lab
MONITOR streams every command seen by a Redis
server and can expose sensitive traffic and impose overhead. Use
it only briefly on this isolated lab. Start it in one terminal,
run the previous Python program in another, then stop MONITOR.
The first GET should appear server-side; the
immediate second GET can disappear from MONITOR
because redis-py serves it locally. After the writer changes the
key, the next cached-client read should return to Redis.
docker exec -it -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin MONITOR# Run only the bounded Chapter 15 cache script, observe its SET/GET traffic, then press Ctrl+C immediately.# Never leave MONITOR running on production merely to count cache hits.
MONITOR shows server commands, not local cache operations. Missing second GET plus correct returned value is evidence that the client served it locally; it is not a throughput benchmark.
4. Prove why a naïve local dictionary is stale
Server-assisted invalidation matters because a process-local dictionary has no knowledge of remote writes. This deterministic comparison caches a value manually, modifies Redis through another client, then shows the stale dictionary alongside a fresh Redis read.
import redisr1 = redis.Redis(host="127.0.0.1", port=6379, username="atlasmart-app", password="AtlasMart-App-Lab-Only-2026", decode_responses=True)r2 = redis.Redis(host="127.0.0.1", port=6379, username="atlasmart-app", password="AtlasMart-App-Lab-Only-2026", decode_responses=True)key = "atlasmart:ch15:l4:naive"r1.set(key, "v1")local = {key: r1.get(key)}r2.set(key, "v2")print("naive_local", local[key])print("authoritative_redis", r1.get(key))# Expected: v1 versus v2. A local dict is not coherent merely because Redis is fast.
5. Disconnect is a freshness event
Invalidations are not a durable message log. If the invalidation connection disappears, the client must not keep serving entries that may have changed while it was disconnected. Current redis-py documentation states that if any connection in the client/pool disconnects, the client flushes all cached keys; later reads repopulate the cache. Applications should preserve that conservative policy rather than trying to “optimize” by retaining unknown-freshness entries.
# If your application detects a topology/network transition and needs an explicit safety reset:cache = cached.get_cache()cache.flush()# The next eligible read must go to Redis and rebuild local cache state.
A successful local cache hit proves only that the client believes the entry valid under its tracking state. It does not make Redis the source of truth for external databases, and it does not provide authorization or durable event delivery.
6. Server tracking has more modes than redis-py built-in CSC currently exposes
The Redis server supports default tracking, OPTIN,
OPTOUT, broadcasting (BCAST) with
optional PREFIX, redirection to another client ID,
and NOLOOP. Current redis-py built-in CSC does not
implement the server's opt-in/opt-out or broadcasting modes. Do
not assume that sending
CLIENT TRACKING BCAST manually means redis-py's
local cache will implement that mode correctly.
| Server option | Meaning | redis-py built-in CSC note |
|---|---|---|
| default | Track keys read by the connection | Supported path |
| OPTIN / OPTOUT | Per-next-command cache selection | Server supports; built-in redis-py CSC currently not implementing these modes |
| BCAST + PREFIX | Invalidate by prefixes without remembering reads | Server supports; not current built-in redis-py CSC mode |
| NOLOOP | Suppress invalidations caused by same connection | Server option; library integration must be verified |
| REDIRECT | Send invalidations to another connection ID | Useful for client implementations/pools; verify library behavior |
7. Manual tracking introspection is connection-local
CLIENT TRACKINGINFO reports tracking configuration
for the connection that asks. To study raw server behavior
independently of redis-py's cache abstraction, use an
interactive RESP3 redis-cli connection, enable tracking, inspect
it, read one key, and then modify that key from a second
terminal.
docker exec -it -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli -3 --user atlasmart-appCLIENT TRACKING ONCLIENT TRACKINGINFOGET atlasmart:ch15:l4:manual# Leave Terminal A open so RESP3 push invalidations can arrive.
SET atlasmart:ch15:l4:manual changed-by-terminal-b# Terminal A should receive an invalidation push for the tracked key.# Then run CLIENT TRACKING OFF in Terminal A before exiting.
8. Not every read is a useful cache candidate
Current redis-py client-side caching guidance excludes Redis
Time Series and probabilistic commands, nondeterministic reads
such as scans/random-member operations, and Redis Search
FT.* commands from its cacheable set. Even eligible
commands may be poor choices when data changes frequently or
working-set cardinality exceeds local cache memory.
| Workload | CSC fit | Reason |
|---|---|---|
| Hot product configuration | Often good | Read frequently, update infrequently |
| Rapid telemetry/time-series | Poor | High mutation rate; current redis-py excludes these command families |
| FT.SEARCH results | Not built-in cache candidate | Search commands currently excluded |
| Permission decision per user | High caution | Freshness and authorization risk can dominate performance benefit |
| Random/SCAN results | Poor/unsupported | Nondeterministic response semantics |
9. Security and tenancy remain separate
Tracking tells a client that a Redis reply changed; it does not decide whether one user is allowed to see another user's cached response. Local cache keys must include all authorization/tenant dimensions that affect the result, and the Redis ACL still governs server access. A process serving multiple principals must not collapse responses into one local cache entry merely because they share a Redis key.
10. Verification and cleanup
- redis-py was configured with explicit RESP3 and CacheConfig.
- The second identical read was observed as a local hit using short MONITOR evidence.
- A second writer invalidated the cached response and the next read returned the new value.
- The naïve dictionary example remained stale as expected.
- Disconnect/invalidation loss was treated as a reason to flush, not to trust stale entries.
UNLINK atlasmart:ch15:l4:product:42:name atlasmart:ch15:l4:naive atlasmart:ch15:l4:manual
11. Production judgment
Use client-side caching for hot, read-heavy, relatively stable data when local memory is bounded and stale-data risk is understood. Monitor local hit/miss/eviction rates, reconnects/cache flushes, invalidation volume, Redis tracking memory/CPU, and correctness tests that mutate data from another client. Treat failover/reconnect as a cache-coherency event. Test the exact client/version/topology because support differs across redis-py, Jedis, node-redis, go-redis, Cluster, Sentinel, and managed services.
Check your understanding
- What carries invalidations on a modern same-connection setup?
- Why must the cache be flushed after losing invalidations?
- Does CLIENT TRACKING provide authorization?
- Are Redis server BCAST/OPTIN modes automatically supported by redis-py built-in CSC?
- What does a missing second GET in MONITOR indicate?
Review the answers
RESP3 push messages.
Keys may have changed while the client could not hear invalidation messages, so their freshness is unknown.
No. ACL/application authorization and cache-key partitioning are separate controls.
No. The server supports them, but current redis-py built-in CSC does not implement those modes.
Together with a correct returned value, it is evidence the client served that eligible read locally.
12. Summary and next step
Server-assisted caching can eliminate even the remaining Redis read round trip, but only while invalidation state is trustworthy. Lesson 5 closes the chapter by benchmarking one realistic chatty workflow before and after pipelining with identical requests and explicit measurement discipline.
Authoritative references
- Redis Open Source 8.10 release notes
- Redis pipelining
- Using Redis commands
- Redis transactions
- redis-py guide
- redis-py pipelines and transactions
- redis-py production usage
- redis-py connection guide
- redis-py asynchronous usage
- redis-py error handling
- Connection pools and multiplexing
- Client-side caching introduction
- Client-side caching reference
- CLIENT TRACKING
- CLIENT TRACKINGINFO
- CLIENT CACHING
- CLIENT LIST
- CLIENT INFO
- CLIENT ID
- CLIENT SETNAME
- MONITOR
- INFO
- SLOWLOG
- Redis latency diagnosis
- Redis security
- Redis ACLs
- Redis Cluster specification
- Redis persistence
- Redis replication
- redis-py documentation
- redis-py 8.1.0 on PyPI
- Redis licenses