R8.05 · Production & Operations — module hub
Uber: resilience & staggered sharding.
Uber built an integrated Redis cache in front of Docstore — 40 million reads per second at a 99% cache hit rate. The headline is not the throughput; it is what Uber did so a sick cache never took the database down with it. Three resilience techniques worth taking apart: staggered sharding, the sliding-window circuit breaker, and graceful degradation.
Grounding: the Uber source pack — How Uber Uses Integrated Redis Cache to Serve
40M Reads/Second (ByteByteGo) — for the case study; the as-built
echo/apps/echo_wire/lib/echo_mq/connector.ex (the @backoff_min/@backoff_max
reconnect, the :disconnected fail-not-replay, the max_pending bound) for the
applied half. The bus is Valkey-only; Uber’s own Redis/Docstore is the case study, quoted as theirs.
Static facts — staggered shard → misaligned schemes spread a cluster’s failure · circuit breaker → count errors per node, short-circuit a fraction, trip · degrade → a miss or short-circuit falls through to Docstore, slower but served.
The system: an integrated cache over Docstore
ByteByteGo · Uber integrated cacheDocstore is Uber’s distributed database. A read against it is durable and correct but pays the full database cost; at Uber’s scale, repeating identical reads against Docstore is expensive in both latency and load. Uber’s answer was an integrated Redis cache — a layer in front of Docstore that the application reads through transparently. On a hit the value comes back from Redis in memory; on a miss the read falls through to Docstore and the result populates the cache.
The wins were the cache-aside wins at scale: lower latency, less database load, lower cost. A documented use case ran at over 6 million reads per second with a 99% cache hit rate. The title figure is the aggregate: 40 million reads per second served from the integrated cache.
But a cache in front of a database changes the failure model. When the cache is healthy it absorbs the load; when the cache is sick, the traffic it was absorbing lands somewhere — and where it lands decides whether a cache problem stays a cache problem or becomes a database outage. The rest of the module is the three things Uber did so the second never happened.
Uber’s integrated cache is the lens. The reader is building a system on a wire too — EchoMQ, backed by Valkey — so each technique below lands twice: the Uber fact, and the move it becomes on the connector. The thread is same goals, different mechanisms.
The three resilience techniques
ByteByteGo · Circuit Breaker patternStaggered sharding — misalign the two sharding schemes. Uber sharded the Redis cluster using a scheme different from the database sharding scheme. The reason is the failure model: if Redis and Docstore were sharded the same way, a single Redis cluster going down would dump all of its missed traffic onto the one Docstore shard that backed it — a hot shard, a correlated failure that can take the database shard down too. By deliberately misaligning the schemes, a single Redis cluster’s load spreads across multiple database shards instead of concentrating on one. Misalignment turns a correlated failure into a spread one.
The sliding-window circuit breaker. When a Redis node is down, every get and set routed to it pays a latency penalty before it fails — and at millions of requests per second, that penalty compounds. Uber added a sliding-window circuit breaker per node: count errors per node in each time bucket, sum the counts over the window’s width, and once the error count crosses a threshold, short-circuit a fraction of the requests to that node. If errors keep accumulating, the breaker trips: no requests go to the node at all until the window passes and the counts age out. The circuit breaker is the canonical resilience pattern — fail fast against a known-bad dependency rather than wait for each call to time out.
Graceful degradation. A Redis miss or a short-circuited request falls through to Docstore. The read is slower than a cache hit, but it is served — degraded, not failed. The combined effect was measured: P75 latency down about 75%, P99.9 latency down over 67%, and latency spikes limited. The same use case at over 6M reads/second failed over successfully to a remote region when its primary region had trouble — the cache going away in one region degraded to the remote path rather than taking the read down.
Three techniques, three goals: do not concentrate a failure, do not keep calling a sick dependency, and stay available when a dependency is gone. Each maps onto an EchoMQ analogue below.
The applied half — the BCS bus
echo_wire/connector.ex · echo_mq/keyspace.exUber’s integrated cache is the lens; the system the reader is building is EchoMQ, backed by Valkey. The honest framing is same goals, different mechanisms. Uber’s three techniques aim at three goals: do not concentrate a failure (staggered sharding), do not keep calling a sick dependency (the circuit breaker), and stay available when a dependency is gone (graceful degradation). The EchoMQ connector reaches the same three goals by different means.
Staggered sharding spreads a cache cluster’s failure across many database shards; the sliding-window circuit breaker stops hammering a node that is failing; graceful degradation falls through to the durable store so a read is served even when the cache is gone.
Placement is the {q} hashtag — a queue’s keys hash to one of 16384 slots
(R8.03). Not-hammering-a-sick-dependency is the connector’s bounded in-flight depth:
max_pending (default 10_000) answers :overloaded. Staying
available is fail-not-replay (:disconnected, never re-run) plus supervised reconnect with
capped jittered backoff (@backoff_min 100 → @backoff_max 2_000).
One guard holds the parallel straight. The connector’s max_pending →
:overloaded bound is backpressure / a bounded in-flight queue, not a circuit
breaker. A circuit breaker tracks a dependency’s error rate over a window and
stops calling it; the max_pending bound caps how many requests can be in flight at
once and refuses the overflow. They share a goal — do not pile work onto a struggling path —
but the mechanisms differ: backpressure is a depth limit, a circuit breaker is an error-rate trip. The
circuit-breaker concept in this module is Uber’s; echo_mq’s analogues are
backpressure, fail-fast, and reconnect-backoff.
# max_pending caps how many pipelines can be in flight; the overflow is refused, not buffered
def handle_call({:pipeline, _}, _from, %{pending_n: n, max_pending: max} = s) when n >= max do
emit([:emq, :connector, :overload], %{pending: n}, %{label: s.label})
{:reply, {:error, :overloaded}, %{s | overloads: s.overloads + 1}}
end
The worked consumer is codemojex. Every guess goes through
EchoMQ.Connector, so a guess inherits these properties for free: if the wire is saturated the
caller gets a fast :overloaded rather than an unbounded queue, and if Valkey drops the socket
the in-flight guess fails :disconnected (a fast, honest error the caller can retry) while the
connector reconnects with backoff. The game stays responsive under trouble instead of hanging on a sick
wire.
/echomq/queue
The connector behind the backpressure and reconnect tie-backs — the in-flight FIFO, the
:overloaded bound, the lease, the lanes.
Persistence, pooling & failover
The READONLY-reconnect failover and pool sizing in full — the material this module frames through Uber’s resilience lens.
door · the durability floor/echo-persistence
Where a served value survives a crash — the durability dial beneath the volatile cache and queue tiers.
Notes on ValkeyA Valkey primary failover surfaces to a client as a
-READONLY error: a replica not yet promoted refuses writes with
-READONLY You can’t write against a read only replica. The owned wire has no
special -READONLY handler — its recovery is the general one: the socket drops
and the supervised connector reconnects, re-fencing each time. Treat -READONLY as the Valkey
failover signal, not an echo surface —
valkey.io/topics/latency.
The three dives
shard → break → degradeThe module follows the resilience arc — place the failure, then break the circuit, then degrade gracefully. Read them in order.
Staggered sharding
The Redis sharding scheme deliberately different from the database’s, so one
cluster’s failure spreads across many DB shards — no hot shard; the BCS echo — the
{q} hashtag and 16384 slots (R8.03).
Circuit breakers
The sliding-window circuit breaker — per-node error counts, short-circuit a fraction,
trip until the window passes; the BCS echo — max_pending → :overloaded
backpressure, the in-process cousin.
Graceful degradation
Fall through to Docstore on a miss or short-circuit; P75 −75%, P99.9 −67%, remote-region failover; the BCS echo — capped-jittered-backoff reconnect and fail-not-replay.
Three dives, one integrated cache: do not let the cache and the database fail together, stop hammering a node that is down, and keep serving when it is gone — the Uber case study and the BCS connector read side by side.
R8.05 is the second of the chapter’s case studies. Return to the Production & Operations chapter to continue, or open the Queue pillar for the depth behind the connector, the bound, and the lease.
References
sources · relatedSources
- Uber Engineering — How Uber Serves Over 40 Million Reads Per Second Using an Integrated Cache — the primary engineering account of CacheFront: sharding Redis by partition key (distinct from the Docstore sharding) to avoid a hot shard, the sliding-window circuit breaker, and the 40M reads/second figures.
- ByteByteGo — How Uber Uses Integrated Redis Cache to Serve 40M Reads/Second — the integrated cache over Docstore, staggered sharding, the sliding-window circuit breaker, graceful degradation, and the 40M reads/s / 99% hit / remote-region-failover figures.
- Microsoft Learn — Circuit Breaker pattern — the canonical resilience pattern: fail fast against a known-bad dependency rather than wait for each call to time out.
- Valkey — Cluster specification — the
{…}hashtag and CRC16 mod 16384 hash slots behind the staggered-sharding tie. - Valkey — Diagnosing latency issues — failover and reconnect behaviour; the
-READONLYsignal the connector recovers from by reconnecting.
Related in this course
- R8.05.1 · Staggered shardingspread the failure, no hot shard
- R8.05.2 · Circuit breakersstop hammering a sick node
- R8.05.3 · Graceful degradationfall through, stay available
- R8.02 · Persistence, pooling & failoverthe failover this frames through Uber
- R8.03 · Pinterest: task queues & partitioningthe
{q}hashtag and CRC16 slot - /echomq · Queuethe connector, the bound, the lease