Client-Side Caching With Tracking: Keeping Redis Data in Application Memory
AI generated
SET
TTL
Redis · RESP3 · Client-Side Caching
Client-Side Caching With Tracking
keeping Redis data directly in application memory

Every Redis access costs a network round trip, even when the value has not changed since the last query. Client-side caching flips this principle around: frequently read values move into the application's local memory, and Redis actively reports through the tracking protocol as soon as a cached key changes. That eliminates the round trip for many reads entirely, without the application risking stale data. How the tracking protocol works technically, where broadcasting and default mode differ, and where the practical limits lie, is what this article covers.

12 min read CLIENT TRACKING · RESP3 Redis 7 · Redis 8 · Invalidation

1. The baseline problem: every read costs a round trip

A classic Redis access always travels over the network, even when the requested value has sat unchanged in the Redis server's memory for milliseconds. On a product detail page that reads several cached fragments from Redis per call, such as pricing data, availability, and category assignment, these round trips quickly add up to noticeable latency, especially when the application and the Redis server are not on the same machine and every round trip carries network overhead and TLS handshake cost.

An obvious idea is to keep frequently read values directly in the application's own memory and only consult Redis on a cache miss. The catch is classic cache invalidation: how does the application know that a locally held value has since become stale because another process changed the underlying key in Redis? This is exactly where the tracking protocol comes in.

2. How the RESP3 tracking protocol sends invalidating messages

With CLIENT TRACKING ON, a connection enables server-side tracking for itself. From that point on, Redis remembers which keys this client requested through read commands like GET or MGET and internally builds a mapping from key to interested clients. When one of these keys changes, whether through SET, DEL, EXPIRE, or any other write command, Redis proactively sends an invalidation message to the affected client without the client having to ask.

Technically this relies on the RESP3 protocol, which, unlike RESP2, supports genuine push messages that can arrive outside the normal request-response cycle over the same connection. The application must use its client library so that it recognizes incoming invalidation messages, whether on a separate RESP3 push connection or directly on the main connection, and immediately removes the affected local cache entry.


# Enable tracking for the current connection (requires RESP3)
redis-cli -3
> CLIENT TRACKING ON
OK
> GET product:4711:price
"49.90"

# When another client changes the key, a push message arrives:
# >3
# $10
# invalidate
# *1
# $18
# product:4711:price

3. Default mode: server-side tracking per client

In default mode, Redis individually remembers, for each tracked client, which keys it last read. This mapping lives in an invalidation table in server memory and grows with the number of distinct keys that active clients query. The advantage is precision: a client only receives invalidations for keys it actually read itself, which keeps message traffic minimal.

The downside is memory usage on the Redis server itself, which grows with the number of concurrently tracked clients and the size of their distinct key sets. With very many active connections each reading a broad and varied set of keys, this table can create noticeable memory pressure, which is why default mode suits a manageable number of long-lived connections with a stable access pattern best, such as application server processes with a permanently open Redis connection.

4. Broadcasting mode: invalidation by prefix instead of per key

Broadcasting mode, enabled via CLIENT TRACKING ON BCAST PREFIX product:, skips individual per-client key tracking entirely. Instead, the client subscribes to one or more key prefixes, and Redis sends invalidation messages for every changed key within those prefixes to all clients subscribed to that prefix, regardless of whether the individual client ever actually read that particular key.

This drastically reduces server-side memory requirements, since no per-key-to-per-client mapping needs to be maintained, only a list of subscribed prefixes. The trade-off is potentially higher message volume: with a broad prefix like product:, clients also receive invalidations for products they never loaded into their local cache at all. For workloads with clearly scoped, topically matching prefixes and many concurrent clients, broadcasting is therefore often the more practical choice.


# Enable broadcasting mode with two prefixes
redis-cli -3
> CLIENT TRACKING ON BCAST PREFIX product: PREFIX category:
OK

# Optional: use a separate redirect connection for push messages
> CLIENT TRACKING ON REDIRECT 42 BCAST PREFIX product:
OK

5. Application integration using a PHP client as an example

In practice, tracking is usually wrapped in a small cache facade: a local array or a PSR-16-compatible in-memory cache holds values, while a dedicated Redis connection in RESP3 mode listens for invalidation messages in the background and removes the matching local entry on every incoming message. For PHP-based applications with a per-request process model, tracking pays off mainly when a long-lived worker process, for example under RoadRunner or Swoole, serves multiple requests in sequence using the same local cache, since classic PHP-FPM discards process memory after every request anyway.

For short-lived PHP-FPM processes, tracking therefore brings barely any benefit, because the local cache never outlives a single request in the first place. It looks different for daemon processes, such as a consumer that runs continuously and repeatedly reads the same reference data, like category trees or pricing rules that change rarely but unpredictably.


{
  "pattern": "long-lived worker process",
  "local_cache": "in-memory array keyed by redis key",
  "tracking_connection": "separate RESP3 connection, BCAST mode",
  "on_invalidate_push": "remove matching key from local_cache",
  "on_cache_miss": "fetch from redis, populate local_cache"
}

6. Practical limit: memory usage on the client side

Client-side caching shifts memory demand from the central Redis server onto every single application process. In horizontally scaled applications with many parallel worker processes, this means the same cached value can potentially sit separately in every process's memory instead of centrally once in Redis. For small, frequently read reference data this is unproblematic, but for large objects or very many distinct keys, memory usage across all processes can add up quickly, trading the saved network round trip for increased memory pressure on the application servers.

A sensible limit is therefore a hard cap on the number of locally held entries, combined with an LRU-style eviction strategy in the local cache itself, so tracking only applies to actually repeatedly read, compact values rather than accidentally becoming an unbounded shadow copy of the entire Redis instance.

7. Suitability for hot keys: where tracking has the biggest impact

Client-side caching delivers the biggest benefit for classic hot keys: keys that very many clients read extremely often but write only rarely. Typical examples are global configuration values, feature flags, current exchange rates, or category trees needed on every page but perhaps only changing a few times a day. For such values, tracking massively reduces server load, because a large share of reads never reach the Redis server at all and are served directly from application memory instead.

For values with high write frequency, such as intermediate cart states or live stock levels, the effect reverses: frequent invalidations mean the local cache entry rarely stays valid long enough to genuinely save the round trip, while additional message traffic for the invalidation itself is generated at the same time. Tracking is therefore suited specifically to read-dominant, low-change data, not as a blanket replacement for every cache access.

8. Error handling: connection drops and missed invalidations

A central risk in client-side caching is connection loss: if the tracking connection drops before the client could receive an invalidation message, the application has no way of knowing whether its local cache is still accurate. Redis handles this case with a special message carrying no key information, signaling that invalidations may have been missed. A robust implementation must react to this message by flushing the entire local cache rather than continuing selectively.

After a reconnect, the application should explicitly re-enable tracking and, in broadcasting mode, re-register its subscribed prefixes, since these are not automatically preserved across a reconnection. Anyone neglecting this error handling risks stale data in the local cache that, in the worst case, persists considerably longer than it would have without client-side caching at all.

9. Rollout strategy: migrating individual data classes step by step

A blanket switch of the entire application to client-side caching rarely makes sense. A step-by-step approach has proven effective: first identify concrete hot-key candidates using access statistics, for instance through redis-cli --hotkeys or custom monitoring, enable tracking on a trial basis for exactly that data class, and measure the effect on latency and server load under realistic traffic before rolling out further data classes.

It is also worth checking the client library in use: not every Redis library supports RESP3 tracking equally well, some already wrap push message handling conveniently, others require manual handling of the redirect connection. This check should come first in any rollout, before tracking is used in production at scale.

Criterion Default mode Broadcasting mode Practical relevance
Tracking scope Per client and key Per subscribed prefix Broadcasting saves server memory
Server memory Grows with clients × keys Only a prefix list per client Default risky with many clients
Message volume Only relevant invalidations Can include noise Broadcasting inefficient with broad prefixes
Suitability Few long-lived connections Many clients, clear prefixes Choose mode based on topology
Setup effort Simple to start Requires prefix design Broadcasting needs planning

Mironsoft

Cache layer setup and Magento Redis integration

Magento cache that isn't quite working or is misconfigured?

We set up Redis as a cache and session backend for Magento cleanly, tune memory usage and eviction strategies, and make sure full page cache and session storage work together reliably.

Redis Setup

Configure the cache, session, and FPC backend production-ready for Magento.

Memory Tuning

Match memory usage and eviction policies to the shop's actual load.

High Availability Setup

Set up Redis Sentinel or Cluster for resilient Magento environments.

10. Summary

Client-Side Caching With Tracking: The Essentials at a Glance

Basic principle

RESP3 push messages actively inform clients about changes to previously read keys, so local copies are invalidated instead of blindly reused.

Two modes

Default mode tracks precisely per client and key, broadcasting mode tracks more efficiently per prefix at the cost of potentially extra messages.

Limits

Memory usage shifts to every application process individually, connection drops require robust cache flushing instead of selective continuation.

Best fit

Read-dominant hot keys with rare changes, like feature flags or category trees, benefit the most; write-heavy data barely benefits at all.

11. FAQ: Client-Side Caching With Tracking: The Essentials at a Glance

1What is client-side caching with tracking in Redis?
A mechanism where applications keep frequently read Redis values in their own local memory, and Redis actively reports through the RESP3 protocol as soon as one of these values changes, so the local cache can be invalidated.
2Which protocol is a prerequisite for tracking?
Tracking requires RESP3, since only this protocol supports genuine push messages outside the normal request-response cycle. Tracking cannot be used with RESP2.
3How do default mode and broadcasting mode differ?
Default mode individually tracks the keys each client actually reads, while broadcasting mode instead subscribes to entire key prefixes and sends invalidations for every change within those prefixes.
4Which mode saves more memory on the Redis server?
Broadcasting mode, because it does not need to maintain a per-key-to-per-client mapping, only a list of subscribed prefixes per client.
5What kind of data is client-side caching best suited for?
Read-dominant hot keys with rare changes, such as feature flags, category trees, or global configuration values, where many clients frequently read the same value.
6Why does tracking make little sense for short-lived PHP-FPM processes?
Because the local cache is discarded after every request anyway, so it never lives long enough to actually benefit from the saved round trip. Long-lived worker processes benefit far more.
7What happens when the tracking connection drops?
After a reconnect, Redis sends a special message with no key information, indicating that invalidations may have been missed. A robust application must react by flushing the entire local cache.
8What practical memory limit does client-side caching have?
Memory usage shifts to every individual application process, so with many parallel workers the same value can end up sitting in memory multiple times instead of centrally once in Redis.
9Is tracking suitable for frequently written data like cart contents?
Not really. With high write frequency, local cache entries rarely stay valid long enough to save the round trip, while additional message traffic for invalidations is generated at the same time.
10How should client-side caching be introduced into an existing application?
Step by step: first identify hot-key candidates using access statistics, enable tracking on a trial basis for that data class, and measure the effect under realistic traffic before rolling out further data classes.