jonnify·redis /redis-patterns/caching/client-side-caching/client-tracking

R1.04.1 · Client-side caching — dive 1

CLIENT TRACKING.

The server keeps a record of which keys each connection holds, so it can warn that connection the moment one changes.

Grounding: real CLIENT TRACKING · echo_cache/table.ex (the RESP3 subscription) · echo_cache/coherence.ex (the channel).

Read a key, watch the record fill. Select a key the connection reads next; the slot fills and the table on the right shows what the server has recorded.
conn 7 near cache the server tracking table instr:AAPL flag:halt cfg:limits
conn 7 holdssince
readout · Read a key to record it.
§1

The server-side record

source · CLIENT TRACKING

Turn tracking on for a connection with CLIENT TRACKING ON. From then on, every key the connection reads is noted by the server in a tracking table: the key, and the connections holding it. The table is the whole mechanism. When a key is modified — by this connection or any other — the server looks up who holds it and sends each of them an invalidation. The next dive covers that push; this one is about how the record is built.

The default record is exact: the server remembers the precise keys a connection read, so an invalidation arrives only for keys the connection actually holds. Exactness has a cost — the server stores a table proportional to the number of distinct keys tracked across all connections. That is the memory the server spends to keep every near cache honest.

tracking table
The server-side record of which keys each connection has read; the basis for invalidation.
tracked key
A key a connection holds locally and the server has noted; a change to it triggers a push.
RESP3
The protocol revision whose push frames carry the invalidation message on the same connection.
redirect
An option to route invalidations to a second connection, for clients that read on RESP2.
§2

Default vs broadcast

source · BCAST

There are two ways to keep the record. Default mode tracks the exact keys each connection read — precise, but the server holds a per-key table. Broadcast mode (CLIENT TRACKING ON BCAST PREFIX instr:) tracks prefixes instead: the server keeps no per-key table and instead notifies every broadcast subscriber whenever any key under a registered prefix changes. Cheaper for the server, but a connection may receive invalidations for keys it never read.

The choice is a trade between server memory and notification precision. Compare them below: pick a mode and read which one each side pays for.

Pick a tracking mode. What each side pays.
readout · Pick a mode to read what the server and the client each pay for.

Default mode spends server memory to be precise. Broadcast spends precision to save memory. The workload — how many distinct hot keys, how wide the prefix — decides which is cheaper.

§3

The near cache in EchoCache

echo_cache/table.ex (init)

Picture the Exchange Platform's instrument-catalog read path with a near cache in front of it. A gateway reads an instrument record often and the record changes rarely. With a near cache on, the table holds the instrument in local L1 ETS and lets the server carry the change notification; later reads are memory lookups. The first read still falls through to Valkey for the value, and the table populates both the L2 row (ecc:{instruments}:<id>) and the local copy. Every later read of the unchanged instrument stays in the process.

EchoCache rides the broadcast shape over plain pub/sub. The table opens a RESP3 connection at start and subscribes to its coherence channel — the real init path:

# echo/apps/echo_cache/lib/echo_cache/table.ex (init)
{:ok, sub} =
  Connector.start_link(
    Keyword.get(opts, :connector, [])
    |> Keyword.merge(protocol: 3, push_to: self(), heartbeat_ms: 0)
  )

:ok = Connector.subscribe(sub, Coherence.channel(table_str))   # ecc:{<table>}:coh

The channel name derives from the table name and nothing else — Coherence.channel("instruments") is ecc:{instruments}:coh. A writer that changes an instrument publishes on that channel; the next dive follows the push.

the application · the cache keyspace

One hash-tagged key, one cluster slot

The L2 row a near cache falls through to is the cache's own key form — ecc:{<table>}:<id> — a fresh prefix beside emq:, with the table name hash-tagged so every key of one cache lands in one cluster slot. The first read fills both layers under a TTL with a single atomic command:

# the L2 fill, value and TTL set atomically (id is a 14-byte branded id)
SET ecc:{instruments}:<id> <version+value> PX 300000

Notes on ValkeyThe broadcast subscription rides plain pub/sub, which is at-most-once — a message is delivered once if at all. A lost notification costs one TTL of staleness, the floor under the loss, which is why a risk surface rides the durable job lane instead — valkey.io/topics/pubsub.

CLIENT TRACKING

The server records which keys a connection holds, so it can warn that connection when one changes. Default mode is exact and table-backed; broadcast trades a table for prefix subscriptions.

in EchoCache

The table reads a hot instrument key into L1 ETS and keeps a local copy. A RESP3 subscription on ecc:{<table>}:coh carries the change; the next dive applies it newer-wins.

Tracking is a server-kept record of who holds what. It is the half of the pattern that makes a near cache safe to trust.

§4

References

sources · related

Sources

Related in this course