R8.02 · Production & Operations — module hub
Persistence, pooling & failover.
Operating the bus as a record of truth in production is three guarantees, not one: it must survive a restart, serve under load, and ride a failover. R7.01 made the bus the record of truth; this module keeps it one.
Applies R7.01 redis-as-primary-database — reference the pattern, focus on the operations. Grounding: infra/valkey/conf/valkey.conf (the persistence policy, verbatim) · EchoMQ.Pool (echo/apps/echo_mq/lib/echo_mq/pool.ex) · EchoMQ.Connector (echo/apps/echo_wire/lib/echo_mq/connector.ex). The engine is Valkey 9.
Static verdicts — restart → appendfsync everysec bounds loss to
~1s · load → EchoMQ.Pool hides RTT over one command thread · failover →
EchoMQ.Connector reconnects on a jittered backoff and never replays an in-flight write.
The record into production
applies R7.01 · primary-databaseR7.01 made the case that the bus is the record of truth: the JOB HASH carries
state, attempts, and payload, and Postgres holds only what must be
transactional. This module takes that record into production and answers the operational question R7.01 left
open — how does it stay one.
A record of truth is not a data model; it is a data model plus three operational guarantees. It must survive a restart so the data outlives the process, serve under load so one server feeds many callers without a head-of-line stall, and ride a failover so a dropped wire recovers cleanly. Each guarantee lives on a different real surface, and this module reads each one. The pattern is R7.01; the subject here is the operations.
redis-as-primary-database in production: the record of truth must survive a restart, serve
under load, and ride a failover — three guarantees that turn a data model into a durable, available
record.
valkey.conf (appendonly yes + appendfsync everysec +
save "", the durability dial) · EchoMQ.Pool (N pipelined connectors hiding RTT
over a single-threaded server) · EchoMQ.Connector (capped-jittered-backoff reconnect,
in-flight fail-not-replay).
A record of truth is a data model plus three operational guarantees. The persistence policy keeps the record across a restart, the pool keeps it served under load, and the reconnect discipline keeps it reachable across a failover.
Survive a restart — the persistence policy
valkey.conf · persistence blockThe first guarantee is durability: the data outlives the process. The real valkey.conf makes the
append-only log the single source of durability. appendonly yes records every keyspace-changing
write to a log on disk; appendfsync everysec flushes that log on a background thread once a second,
bounding crash loss to about one second of writes; save "" turns snapshotting off so it never
competes for a second fork; aof-use-rdb-preamble yes keeps the rewrite fast.
propagation-error-behavior panic fails a write rather than acknowledge data that may not survive,
and maxmemory-policy noeviction rejects writes loudly under memory pressure instead of dropping a
job — the R7.01 tie.
Dive 1 takes this block apart line by line. It is distinct from R8.01’s
persistence-safe-settings, which is the kernel layer (the
vm.overcommit_memory sysctl for the save fork); here it is the Valkey persistence
policy itself. The two are halves of one posture — cross-reference, not duplicate.
# ---- Persistence: AOF is the single source of durability ---- appendonly yes appendfsync everysec ← ~1 s loss bound, fsync on a background thread aof-use-rdb-preamble yes ← compact preamble: a fast rewrite save "" ← RDB off: one fork source, not two dir /data propagation-error-behavior panic ← fail the write rather than ack lost data
Notes on ValkeyWith appendonly on and
appendfsync everysec, the append-only file is replayed at startup to rebuild the in-memory dataset,
and the loss bound after a crash is roughly one second of writes — the balance Valkey recommends, durability
close to always at a fraction of its fsync cost —
valkey.io/topics/persistence.
Serve under load — the pool
EchoMQ.Pool · pool.exThe second guarantee is throughput under concurrency. EchoMQ.Pool is a fixed pool of pipelined
connectors with lock-free round-robin dispatch: next/1 advances an :atomics counter
and a caller hits the next member; the default size: 4. The sharp production insight is the one
most pool sizing gets wrong. Valkey runs commands single-threaded — the real
valkey.conf sets io-threads 1 and its comment reads “command execution is
single-threaded.” So a pool does not buy N× server parallelism — it hides
round-trip latency by keeping the one command thread fed via pipelining.
Size for pipeline depth and RTT-hiding, not for cores; over-sizing only adds sockets the single thread
serializes anyway. Dive 2 derives that insight. It is the same EchoMQ.Pool R6.05 teaches as the
concurrency primitive — here it is sized for production rather than introduced.
# A fixed pool of pipelined connectors with lock-free round-robin # dispatch. Each member is already a pipeline, so a small pool # multiplies throughput without checkout ceremony. size = Keyword.get(opts, :size, 4) defp next(name) do {size, at} = :persistent_term.get({__MODULE__, name}) member(name, rem(:atomics.add_get(at, 1, 1), size) + 1) end
Notes on ValkeyA pipeline sends several commands without waiting for each reply, so the round-trip latency is paid once for the batch rather than per command — which is exactly what a single command thread needs to stay fed — redis.io/docs/…/pipelining.
Ride a failover — the reconnect discipline
EchoMQ.Connector · connector.exThe third guarantee is reachability across a disruption. When a primary fails over to a replica, a client
that keeps writing to the old node gets a Valkey -READONLY reply — the signal that a failover
happened. A resilient client must reconnect to the new primary. EchoMQ.Connector grounds the
reconnect discipline: supervised reconnect with capped jittered backoff, re-fencing on every
reconnect; in-flight callers failed with :disconnected on socket loss — never
replayed, because the connector cannot know what is idempotent; and an idle heartbeat that PINGs a quiet
wire so dead peers are noticed before the next caller pays for the discovery.
The shipped deploy is a standalone Fly node, so a failover here is a socket drop and a reconnect to the same
restarted server; the discipline is the same one a Sentinel- or Cluster-aware client uses to re-resolve a new
primary. Dive 3 is honest about the seam: the connector handles failover by socket-drop and
reconnect, not by parsing -READONLY — the -READONLY reply is the
signal (the pattern), the connector’s reconnect is the real grounding.
Notes on ValkeyA replica refuses writes and answers “You
can’t write against a read only replica”; a client that gets -READONLY has been
talking to a node that is no longer the primary, and should reconnect to the promoted one —
valkey.io/topics/replication.
Putting it together
one posture, three layersThe three surfaces are one posture seen from three layers. A restart hits the persistence policy: the
append-only log replays and the record is whole. A load spike hits the pool: the four pipelined connectors keep
the single command thread fed without one slow caller stalling the rest. A failover hits the connector: the
socket drops, in-flight callers get a clean :disconnected, and the supervised reconnect re-fences
and resumes. Because a pool member is an EchoMQ.Connector, the pool inherits the reconnect
discipline for free.
Why a job store chooses this engine
codemojex keeps its game and JOB state in Valkey (the bus and the store) while money lives in
Postgres — the R7.01 per-datum split. So Valkey’s AOF durability is exactly what carries in-flight
game and JOB state across a restart, and the EchoMQ.Pool fronts the bot workers’
notify queue, where an EchoMQ.Consumer drains the emq:{q}: keyspace. The persistence,
pool, and reconnect lines, side by side:
valkey.conf appendfsync everysec ← survive a restart (~1 s loss) EchoMQ.Pool size: 4, :atomics ← serve under load (RTT-hiding) EchoMQ.Connector backoff + fail-not-replay ← ride a failover (at-most-once)
The EchoMQ protocol depth — the lane consumers, the leases, the Lua — is the dedicated /echomq course; this module stays on the operations.
The three dives
restart → load → failoverEach dive takes one guarantee and the real surface that delivers it, in the arc survive → serve → ride: the engine’s persistence policy, the connector pool sized for a single-threaded server, and the connector’s reconnect discipline. Read them in order — durability starts.
RDB and AOF — the durability dial
The real valkey.conf persistence block, verbatim: AOF as the single source of
durability, everysec’s one-second loss bound, save "" retiring RDB.
Pool sizing — pipeline depth, not cores
EchoMQ.Pool and the single-threaded-server insight: size for the depth that keeps
one command thread fed across the round trip, not for cores it does not use.
READONLY, reconnect & failover
The -READONLY signal and EchoMQ.Connector’s reconnect discipline:
capped jittered backoff, re-fence, in-flight fail-not-replay.
Three dives, one record of truth: keep it across a restart, serve it under load, and reach it across a failover — the persistence policy, the pool, and the reconnect discipline working as one posture.
After R8.02 the chapter continues with the case studies — Pinterest, Twitter/X, and Uber
— then the capstone hands off to the dedicated EchoMQ course. Return to the
Production & Operations chapter to continue, open
the EchoMQ Proof pillar for the production evidence, or
Codemojex on Fly for where this valkey.conf actually runs.
References
sources · relatedSources
- Valkey — Persistence — the append-only file, the
everysecfsync policy, and the roughly one-second loss bound after a crash. - Valkey — Replication — primary/replica roles and the
-READONLYreply returned to a client after a failover. - Redis — Pipelining — why a pipeline hides round-trip latency on a single command thread, the premise behind pool sizing.
- Valkey — Sentinel — automatic failover and how a resilient client re-resolves the new primary after a
-READONLYreply.
Related in this course
- R7.01 · Redis as a primary databasethe pattern this module operates
- R8.01 · Kernel tuningthe host layer beneath this posture
- R6.05 · Worker concurrencythe same EchoMQ.Pool, as the primitive
- /echomq · the Proof pillarconformance, telemetry, production evidence
- /bcs · Production on Flywhere this valkey.conf runs
- /echo-persistencethe durability floor beyond AOF