jonnify·redis /redis-patterns/caching/workshop

R1.07 · Caching — the chapter workshop

Cache the instrument catalog.

The chapter capstone: take the six patterns and put EchoCache in front of one real consumer read — the Exchange Platform’s instrument catalog, the reference data the gateway checks on every order.

Grounding: echo/apps/echo_cache (EchoCache.Table, Coherence) · echo/apps/exchange (Exchange.Gateway) · content/bcs4.1–4.2.md · bcsA.md — every figure verbatim from a committed record.

The catalog read path, in four layers. Select a layer to read which EchoCache surface it adds and the property it secures.
1 · cache-asideEchoCache.Table.fetch/3 2 · keep it consistentCoherence.broadcast / enqueue · newer? 3 · hardensingle-flight flights · jittered TTL 4 · measureEchoCache.Table.stats/1 counters L0 · Valkey — the engine under all four
readout · Select a layer. Cache-aside is the base; the three layers above it keep it consistent, absorb a herd, and report the hit rate — each one correct on its own.

Static verdicts — 1 cache → fetch/3 (:hit | :l2 | :fill) · 2 consistent → broadcast/enqueue + newer? · 3 harden → single-flight + jittered TTL · 4 measure → stats/1 counters.

§1

The consumer, and the read path in layers

exchange/gateway.ex · echo_cache/table.ex

The Exchange Platform’s gateway validates every order against a catalog of tradeable instruments. Exchange.Gateway.parse_place/1 turns an untrusted order into a typed command or one closed error, and the very first check is the instrument: it calls parse_instrument/1, which answers {:error, :unknown_instrument} for an instrument the venue does not list. That instrument lookup is reference data — read on every order, changed rarely — which is exactly the read-heavy, write-light shape cache-aside is built for. Fronting it with EchoCache.Table is the workshop.

The read path stacks four layers, each adding one move taught earlier in the chapter, and each leaving the read correct on its own. The base layer is a cache-aside read: EchoCache.Table.fetch/3 checks L1 ETS, then on a miss runs a single-flight fill — GET ecc:{instruments}:<id>, the loader, then SET … PX — and returns {:ok, value, source} with source :hit | :l2 | :fill. The next layer keeps the cache consistent: an instrument change runs Coherence.broadcast/4 on the fast lane or Coherence.enqueue/5 on the at-least-once job lane, and Coherence.newer?/2 resolves a late message newer-wins.

The third layer hardens the instrument every order touches — EchoCache does this by construction: the single-flight flights map coalesces a herd onto one fill, and a jittered TTL (ttl ± ttl·jitter) keeps a cohort from expiring together. The fourth layer measures: EchoCache.Table.stats/1 returns the counter snapshot — hits, misses, fills, l2_hits, coalesced, swept, full_skips, sweeps, coh_applied, coh_stale plus live size — so the hit rate is a number, not a guess. Nothing here is new: each layer is a pattern from R1.01–R1.06, applied to one real surface. The work is the assembly.

The catalog read path is cache-aside at the base, with three layers stacked on top: keep it consistent on a change, let the single-flight and jitter absorb a herd, and read the hit rate off the counters. Each layer is one move from earlier in the chapter.

§2

The three stages

cache → consistent → harden & measure

The workshop is three dives. Each takes one layer and builds it on the real consumer surface, ending with the read path one step closer to production. Take them in order: the cache must exist before it can be kept consistent, and it must be consistent before its hit rate means anything.

§3

Walk the build

echo_cache/table.ex · coherence.ex

Pick a stage to see which EchoCache surface it adds to the read path and what the cache can do once it lands. The surface grows as the stages stack; the readout names the new calls and the property the layer secures.

Build the read path. Pick a stage; the readout names the surface so far and what it gains.
the assembled read path

Cache-aside at the base, coherence on a change, the single-flight and jitter under a herd, and counters reporting the hit rate — four moves layered into one read path, each correct on its own.

its EchoCache application

EchoCache.Table in front of the Exchange Platform’s instrument catalog: fetch fills on a miss, broadcast/enqueue keep it consistent, the jittered TTL and single-flight absorb a herd, and stats reports the hit rate.

Three stages, four layers, one read path. The workshop turns the chapter’s patterns into a real consumer’s catalog cache — cached, consistent, hardened, and measured.

the application · the read path on a measured floor

EchoCache against live Valkey

The figures the workshop reproduces are committed, from the EchoCache gate against the live engine — the L1 hit cost and the lane prices, figure by figure.

content/bcs4.1.md · bcs4.2.md · the EchoCache gate, against live Valkeyfrozen
L1 ETS hit                    1311621 reads/s   ← 762 ns each
L2 GET                        31 us          ← the L1 hit is 40 times cheaper
coherence · broadcast lane     72 us          ← fire-and-forget, at-most-once
coherence · job lane           148 us         ← at-least-once: 2.1 times the latency

Notes on ValkeyEvery L2 fill is SET … PX — value and expiry in one atomic command — so the second layer expires itself even if every node forgets; a separate EXPIRE would leave an immortal key if the process died between the two — valkey.io/commands/set.

§4

References

sources · related

Sources

Related in this course