R8.01.3 · Kernel tuning — dive 3
Durable on a crash, never silently losing work.
A job store has two quiet ways to lose work: it crashes and discards the writes it acknowledged, or it fills up and evicts the keys it was holding. These settings close both gaps — the store fails loudly instead of quietly.
Grounding: infra/valkey/conf/valkey.conf (the persistence, memory, and network blocks, verbatim) · bcs.8.md B8.2 (the one-second loss bound, reject-don’t-evict) · the source kernel-tuning guide (the listen-queue and file-descriptor figures).
Static map — a crash → appendfsync everysec (loss ≈ 1s) ·
a full store → noeviction (a rejected write, not a dropped key) · a SIGTERM →
shutdown-timeout 10 (the log flushes) · a burst → a raised
tcp-backlog/somaxconn (the connection is accepted).
The append-only log and the loss bound
valkey.conf · persistence blockThe first question of durability is one number: after a crash, how much acknowledged work is gone. With an append-only log turned on and an fsync cadence of once a second, the answer is bounded and small.
appendonly yes makes the volume hold an append-only file — every write that changes the
keyspace is appended to a log on disk. appendfsync everysec flushes that log to the disk once a
second on a background thread. The manuscript states the bound directly: “with appendonly on and
appendfsync everysec, the volume holds an append-only log and the loss bound after a crash is roughly one
second of writes, not a whole snapshot interval.” A snapshot-only store loses everything written
since the last snapshot — minutes of work; the append-only log narrows that window to about one
second.
aof-use-rdb-preamble yes lets the periodic rewrite start from a compact binary preamble
rather than replaying every command, so the rewrite is fast and the file stays small. save ""
turns snapshotting off on purpose: with the append-only file as the single source of durability, there is no
reason to keep a second snapshot fork competing for the machine. One fork source, one durability mechanism.
The copy-on-write spike of that one rewrite fork is dive 1’s subject, not repeated here.
# ---- Persistence: AOF is the single source of durability ---- # One fork source. AOF everysec bounds worst-case loss to about one second. appendonly yes appendfsync everysec no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb aof-use-rdb-preamble yes save "" ← RDB off: it does not compete for a second fork
Notes on ValkeyThe append-only file is replayed at startup to rebuild
the in-memory dataset; appendfsync everysec is the balance Valkey recommends — durability
close to always at a fraction of its fsync cost —
valkey.io/topics/persistence.
An append-only log with a once-a-second fsync turns “how much did the crash cost” from an open question into a bound: about one second of writes, never a whole snapshot interval.
Reject, do not evict
valkey.conf · the memory guardrailThe second way a job store loses work is memory pressure. When a store fills up it must choose: throw old keys away to make room, or refuse the new write. For a cache the first is fine — an evicted key is only a future miss. For a queue it is a silent data-loss bug.
maxmemory 512mb is a loud guardrail, not a working ceiling — the live dataset is
single-digit megabytes, so 512 sits far above it and leaves the rest of the box for fork copy-on-write and
client buffers. maxmemory-policy noeviction is the load-bearing line. The config’s own
comment names the trade: “noeviction means a runaway keyspace REJECTS writes loudly instead of
being OOM-killed silently.” The manuscript states the principle: “a queue that silently
drops keys is a queue that silently loses work, so memory pressure must surface as write errors and
alerts.” Under noeviction, a write that would exceed maxmemory returns an
error to the caller — a signal to alert on — rather than discarding a job that something already
enqueued.
maxmemory-clients 64mb caps the memory a single client’s buffers may hold, so one slow
or stuck consumer cannot drive the whole instance into that pressure on its own.
# ---- Memory: a loud guardrail, not a working ceiling ---- # noeviction means a runaway keyspace REJECTS writes loudly # instead of being OOM-killed silently. maxmemory 512mb maxmemory-policy noeviction maxmemory-clients 64mb
Notes on ValkeyThe eviction policies (the allkeys-lru
family and the rest) decide what to drop when a write would exceed maxmemory;
noeviction is the one policy that drops nothing and returns an error instead —
valkey.io/topics/lru-cache.
For a cache, eviction is a feature; for a job store it is data loss. noeviction makes
memory pressure a rejected write and an alert, not a key that quietly disappears.
Fail loudly
valkey.conf · the network/shutdown blockRejecting writes under memory pressure is one half of failing loudly. The other half is refusing to acknowledge data the store cannot promise to keep, and giving the log a chance to flush on the way down.
propagation-error-behavior panic is the strictest possible posture, and the config states why
in one line: “fail the write rather than acknowledge data that may not survive.” If a
write cannot be propagated where it must go, the server fails the write rather than returning a success the
data does not back. A false acknowledgement is worse than a visible error — the caller that hears
“OK” stops worrying about the job; an error makes it retry.
shutdown-timeout 10 covers the orderly exit. On a SIGTERM — the signal a
deploy or a platform sends to stop the process — the server is given ten seconds to flush the
append-only file before the platform forces it down. The config’s comment: “give the AOF a
chance to flush on SIGTERM.” Without that window, a rolling deploy could discard the last
unflushed second on every restart.
# valkey.conf — fail loudly rather than acknowledge un-survivable data. propagation-error-behavior panic # fail the write, never a false OK shutdown-timeout 10 # flush the AOF on SIGTERM before forced exit
A store that returns a success it cannot back is the worst kind of quiet failure.
panic refuses the false acknowledgement; shutdown-timeout 10 keeps a deploy from
eating the last unflushed second.
Staying connected
kernel-tuning.md.txt · TCP stack & file descriptorsA dropped connection is dropped work, so the network settings take the same posture as the persistence ones: accept the burst rather than drop it. These are the connection-survival half of the source’s kernel-tuning guide.
tcp-backlog sets the listen queue for incoming connections. The source’s kernel-tuning
guide warns that the Linux default is 128, so during a traffic spike new connections are
dropped before the server ever sees them. The effective backlog is
min(tcp-backlog, somaxconn) — both the application’s tcp-backlog and
the kernel’s net.core.somaxconn must be raised, or whichever is smaller caps the queue.
tcp-backlog lives in valkey.conf; somaxconn and the other knobs are kernel
settings, set on the host:
# /etc/sysctl.conf — the KERNEL half of the connection-survival pair net.core.somaxconn=65536 # raise the kernel's accept queue to match net.ipv4.tcp_max_syn_backlog=65536 # systemd unit — the file-descriptor ceiling; each connection costs one fd LimitNOFILE=65536
The source sets somaxconn to 65536 so the kernel’s queue is never the
limit, and raises the file-descriptor ceiling to 65536 because each connection consumes one
file descriptor and a server’s maxclients is bounded by the descriptors available to it.
Back in valkey.conf, tcp-keepalive 300 sends a keepalive every 300 seconds so a load balancer or
firewall does not silently reap a long-lived idle connection, and the pub/sub output buffer is bounded so a
slow subscriber is dropped rather than allowed to grow the server’s memory without limit.
# ---- Network ---- port 6390 tcp-backlog 511 ← effective = min(tcp-backlog, somaxconn) tcp-keepalive 300 # ---- Client output buffers ---- # A slow subscriber is dropped rather than growing memory without limit. client-output-buffer-limit pubsub 32mb 8mb 60
Notes on ValkeyBoth ends of the listen queue have to be raised
together — the server’s tcp-backlog and the kernel’s somaxconn;
whichever is smaller wins, which is why a high tcp-backlog alone does nothing on a stock host
— valkey.io/topics/admin.
A durable store fails loudly: bound crash loss with an append-only log, reject writes on memory pressure rather than evict, refuse un-survivable acknowledgements, and accept a connection burst rather than drop it.
The real valkey.conf fronts the codemojex queue’s emq:{q}: keyspace with
appendfsync everysec + noeviction + panic, and the host raises
somaxconn/LimitNOFILE to 65536.
Why a job store chooses this engine
EchoMQ keeps its jobs in the braced emq:{q}: keyspace on this exact Valkey. The manuscript
frames the durability choice for the queue in one passage:
noeviction ← a queue that drops keys loses work appendfsync everysec ≈ 1s loss bound after a crash backlog + fd limits ← a burst of clients is accepted, not dropped
The EchoMQ protocol depth — the lane consumers, the leases, the Lua — is the dedicated /echomq course; this page stays on the settings.
Same posture, three layers: the log bounds the loss, noeviction and
panic refuse to lose or fake work, and the raised backlog and descriptor ceiling keep a burst of
clients from being dropped at the door.
Beneath the volatile tier
bcs.5.md · the persistence floorThe settings above bound the queue’s crash loss to about one second. That is the right floor for a
volatile work tier, but it is not the whole durability story. Deep history and off-box durability live one
tier down, on the persistence floor: when the bus trims a stream, EchoStore.StreamArchive folds
the trimmed segments into EchoStore.Graft’s append-only CubDB at a reserved high page
range, and a streamer ships those pages on to Tigris object storage — deep history without resident
memory. The volatile tier keeps the last second; the durable floor keeps everything, addressable beside the
live tail.
The persistence floor
That floor is its own surface — the durability dial from ETS to Valkey to CubDB and on to Tigris. The /echo-persistence course carries it; this page stays on the volatile tier’s valkey.conf.
The queue’s Valkey bounds loss to about a second; the persistence floor beneath it keeps the rest, off-box and addressable — two tiers, one durability dial.
Recap — fail loudly
return → R8 · Production & OperationsA durable job store is one that fails loudly. The append-only log with appendfsync everysec
bounds crash loss to about a second; noeviction makes memory pressure a rejected write and an
alert instead of a silently dropped key; propagation-error-behavior panic refuses to
acknowledge data that may not survive; shutdown-timeout 10 flushes the log on the way down; and
a raised tcp-backlog/somaxconn pair and file-descriptor ceiling accept a connection
burst rather than drop it. Every one of these settings is in the real valkey.conf (or the host sysctls
beside it) that fronts the codemojex queue. This is the last dive of the module — the recap returns to
the chapter.
References
sources · relatedSources
- Valkey — Persistence — the append-only file and the
everysecposture; a roughly one-second loss bound after a crash. - Valkey — SHUTDOWN — the orderly stop that
shutdown-timeoutbounds, flushing the append-only file onSIGTERM. - Redis — Persistence — RDB and AOF, the fsync policies, and the durability trade the append-only file makes.
- Redis — Documentation — the production guidance: overcommit, the listen backlog, file-descriptor limits, and the startup warnings.
- Valkey — Administration — the listen-queue and file-descriptor figures: the 128 default,
somaxconn65536, andLimitNOFILE65536.
Related in this course
- R8.01 · Kernel tuningthe module hub
- R8.01.2 · Latency spikesthe previous dive — swap, threads, defrag
- R8 · Production & Operationsthe chapter
- /echo-persistencethe persistence floor beneath the queue
- /bcs · Production on FlyValkey on a Fly machine