Write Behind
Write-behind (also called write-back) makes every write go to the cache only and returns to the caller immediately. The cache then propagates the write to the database asynchronously, often batched and throttled. Writes are blindingly fast — the caller never waits for the DB — but the cache is now the source of truth for a brief window, and if the cache crashes before flushing, recent writes are lost. Write-behind is the highest-throughput, highest-risk caching strategy.
Foundational.
How it works
In write-behind (write-back), the application writes only to the cache. The write returns immediately — sub-millisecond for an in-memory store. A background worker (or the cache itself, if it has native write-behind support) then flushes the write to the database asynchronously, often batched and throttled to smooth DB load.
This is the mirror image of write-through. Write-through prioritizes freshness at the cost of write latency. Write-behind prioritizes write latency at the cost of durability. The cache is the system of record for the brief window between the cache write and the DB flush. If the cache crashes in that window, the writes it held are gone.
The classic use case is a view counter on a popular video. The true count lives in a database eventually, but no one wants a video play to block on a DB write. The cache (Redis) holds the counter, increments it in microseconds, and a background job flushes counts to the DB every few seconds. If Redis crashes between flushes, you lose a few seconds of view counts — annoying, but not catastrophic, and the alternative (a synchronous DB write per play) would not scale to YouTube's traffic.
The other major use case is write coalescing. If the same key is written 1000 times in a second (e.g., a counter being incremented 1000 times), write-behind can flush just the final value once per second — collapsing 1000 DB writes into 1. This is the strategy that lets Redis act as a write-amplification reducer, not just a read cache.
Durability is the central question.
Write-behind's risk is concentrated in one event: cache crash before flush. If Redis holds a write for 5 seconds before flushing, and Redis crashes (or is restarted, or fails over) at second 4, that write is gone — there is no DB record of it, and there is no cache record of it either (the cache is gone).
Three properties determine how bad this is in practice:
- Flush interval. A 1-second flush loses at most 1 second of writes. A 60-second flush loses up to a minute. Shorter flushes mean more DB write load (less batching) but tighter durability.
- Cache persistence. If the cache itself is persistent (Redis AOF with
appendfsync always, or Redis RDB snapshots every N seconds), a cache crash can recover recent writes from disk. Persistence turns a write-behind cache into something closer to write-through with extra steps — but it blunts the data-loss risk dramatically. - Write idempotency. If every write carries a unique ID and the flusher is idempotent (re-applying the same write twice is safe), then a crash mid-flush can be retried safely. If writes are not idempotent (raw increments with no operation log), a crash mid-flush means the DB and the recovered cache disagree forever.
The hard rule is: write-behind is acceptable only when the value of the writes you might lose is less than the cost of the latency you save. View counts: yes. Bank balances: absolutely not.
A read from a write-behind cache returns the most recent write — which the database may not have seen yet. If a second service reads from the database directly (bypassing the cache), it sees a stale value, and the two services disagree. This is the same shape as replication lag, and it has the same remedies: read-your-writes consistency (the writer always reads from the cache), session stickiness, or accepting eventual consistency. Never mix direct DB reads with write-behind cache reads in the same code path without a clear story for the staleness window.
Write coalescing — the hidden superpower.
Write-behind's killer feature is not just fast writes, it is write coalescing. If the same key is updated 1000 times in 5 seconds (a counter being incremented, a sensor reporting a value), the flusher only needs to persist the final value once. 1000 logical writes collapse into 1 physical DB write.
This is enormous for write-amplification-prone workloads:
- Counters: a popular video's play count might increment 10,000 times per second. The DB only needs to know the count once per second, not 10,000 times.
- Telemetry: a metrics pipeline might receive 100,000 events/sec. Aggregating in the cache (HyperLogLog, sorted sets, sums) and flushing a single aggregate per minute reduces DB load by 100,000x.
- Hot keys: a session's last-seen timestamp can be updated on every page load and flushed every 30 seconds — the DB never sees the per-page-load writes.
Coalescing is what makes write-behind structurally different from write-through with async retry. Write-through-with-async-retry flushes every logical write exactly once (no coalescing); the latency saving is just moving the cache SET off the critical path. Write-behind coalesces multiple writes into one — the throughput saving is multiplicative, not additive.
When to use write-behind:
- Write-heavy workloads where individual writes don't matter much. Counters, view counts, telemetry, metrics, sensor data.
- When write coalescing gives you a 10-100,000x DB load reduction. Anywhere the same key is written many times per second.
- When the cache is durable (Redis AOF) and the flush interval is short. A 1-second AOF-persisted write-behind cache has a 1-second data-loss window under catastrophic failure — acceptable for many non-financial workloads.
- When downstream consumers can tolerate lag. Dashboards, analytics, leaderboards — these don't need real-time precision.
When NOT to use write-behind:
- Financial transactions, balances, orders. A crash means real money lost.
- Any system that needs to read from the DB directly. The lag between cache and DB breaks correctness for direct DB readers.
- When writes are not idempotent and there is no operation log. A crash mid-flush leaves the DB and the recovered cache permanently inconsistent.
- When you cannot afford the operational complexity. Write-behind requires monitoring flush lag, handling flush failures, and replay on cache recovery. If you don't have an SRE culture, use cache-aside or write-through.
Your write-behind cache has a 10-second flush interval. Redis crashes at second 7 of an interval and restarts empty (no AOF). What is the consequence?
Pick one answer.
A video's view counter is incremented 10,000 times per second. The cache flushes to the DB once per second. How many DB writes per second does this produce, and why is this acceptable when the cache alone could not handle the load?
Pick one answer.
You have two services reading the same data. Service A reads from the write-behind cache. Service B reads directly from the database. What problem will you observe, and what is the standard fix?
Pick one answer.
Engineering mental model
Mental model. Think of Write Behind as a deliberate boundary in a system. The boundary exists because something becomes harder to manage when everything is done in one place: latency, scale, failure isolation, consistency, cost, or team ownership. The useful question is not “what does Write Behind mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”
Before choosing Write Behind, name the workload, the critical user path, the dominant bottleneck, the failure you are trying to absorb, and the trade-off you are willing to accept. If you cannot name those five things, the design is probably premature.
// Pseudocode
request = receive()
result = write_behind(request)
return result
// Production questions:
// 1. What happens on timeout?
// 2. Can this operation be retried safely?
// 3. What is the bottleneck?Back-of-the-envelope reasoning
Numerical lens: write down traffic, payload size, read/write ratio, peak multiplier and durability target before choosing a component. The numbers should justify the architecture.
Interactive thought experiment: Write Behind
Change the variables below and predict what breaks first in Write Behind. The production lab can later reuse these same inputs.
Change one variable at a time. Predict the failure mode first, then move the slider and see whether your mental model matches the simplified system response.
If you are stuck on Write Behind, start by drawing the request path and marking every network hop, stateful component, queue, cache and failure boundary. Then estimate where the system will saturate.
You increase traffic by 10× in a system using Write Behind. What should you inspect first?
Pick one answer.
Which statement is the safest engineering habit when using Write Behind?
Pick one answer.
You have dashboards for traffic, latency, errors and saturation. You can change the architecture, but every change has operational cost.
Production scenario: your system uses Write Behind, traffic suddenly spikes, and p99 latency doubles. What is your first move?
Interview drill
Answer this without notes: When would you choose Write Behind, and when would you intentionally avoid it? Mention at least one bottleneck it addresses, one failure mode it introduces, and one alternative. Then quantify the workload you are designing for.
For Write Behind, reason in this order: what data is hottest, where can it live closer to the caller, what makes it stale, and what happens on a miss or cache failure. A cache is an optimization boundary, not the source of truth.
Numerical sanity check
A useful first-order model is cache_load = request_rate × (1 - hit_rate). If traffic is 20,000 req/s and the hit rate is 90%, roughly 2,000 req/s still reaches the origin before considering misses caused by expiration or eviction.
Do not optimize for a memorized definition. Reason from the workload and failure mode.
You have a read-heavy Write Behind path. Traffic doubles overnight. What metric would you inspect first, and what would convince you to add another cache layer?
Pick one answer.
What you gain, what you pay
- +Write latency is essentially the cache write latency — sub-millisecond for in-memory stores.
- +Write coalescing collapses N writes to the same key into one DB write (10-100,000x reduction).
- +DB load is smoothed and batched — no spikes from write bursts.
- +Throughput scales with the cache, not the DB — millions of writes/sec on commodity hardware.
- +Reads from the cache return the freshest possible value (more recent than the DB).
- −Cache crash before flush = DATA LOSS for unflushed writes — the defining risk.
- −DB lags the cache by the flush interval — direct DB readers see stale data.
- −Cache must be persistent (AOF/RDB) to mitigate data loss, adding cache-side latency.
- −Operationally complex: monitor flush lag, handle flush failures, replay on cache recovery.
- −Not idempotent-safe without an operation log — crash mid-flush leaves DB and cache permanently inconsistent.
How this breaks in production
- Cache crash before flush → unflushed writes lost permanently (no DB record).
- Flush failure storm → cache accumulates writes faster than the DB can absorb, growing unboundedly.
- Mixed read paths (cache + direct DB) → staleness inconsistency between services.
- Non-idempotent writes during partial flush → DB and recovered cache disagree forever.
- Cache failover to a stale replica → reads return values older than the primary had before the crash.
- Flush interval grows under DB pressure → lag spikes, breaking latency-sensitive consumers.
Don't fall into these traps
- •Using write-behind for financial or transactional data — a crash means real money lost.
- •Setting a long flush interval (minutes) without AOF persistence — enormous data-loss window.
- •Allowing some services to read directly from the DB while others use the cache — silent staleness.
- •Assuming writes are idempotent when they are not — crash recovery produces permanent inconsistency.
- •Not monitoring flush lag — a stalled flusher can accumulate hours of unflushed writes silently.
- •Treating write-behind as a drop-in replacement for write-through — the durability and consistency semantics are fundamentally different.
Real systems using this
How real systems implement this
- YouTube view counters — View increments are coalesced in a Redis-backed write-behind layer; the canonical counter is flushed to a sharded DB periodically. Brief data loss on cache failure is acceptable because exact view counts are not financially material.
- Instagram / Facebook like counters — Likes are written to a cache (memcache/Redis), aggregated, and flushed to the durable tier asynchronously. The exact count is eventually consistent — viewers may see slightly different counts for a few seconds.
- statsd / Etsy metrics pipeline — statsd aggregates counters and timers in memory and flushes to a backend (Graphite, etc.) every 10 seconds by default — a textbook write-behind pattern tuned for write coalescing.
- Redis with persistence for write-behind (AOF appendfsync everysec) — Redis configured with AOF everysec and a write-behind flusher gives a 1-second data-loss window under catastrophic failure — the standard configuration for non-financial write-heavy workloads.
Practice saying it out loud
- Q1Design a view counter for a video platform with 1B views/day. How do you make the DB survive the write load?
- Q2When is write-behind the right choice? When is it the wrong choice? Give an example of each.
- Q3Your write-behind cache just crashed with a 5-second flush interval and no persistence. What did you lose, and how do you prevent it next time?
- Q4A second service reads the count from the DB directly and disagrees with the service reading from the cache. Why? How do you fix it?
- Q5How is write-behind's lag window similar to replication lag? How would you monitor it?
Further reading & references
Core explanations are original NO CAP material. External references are provided for deeper study and standards.
What next?
Mark as understood once the mental model clicks.
Next recommended
Cache Aside (Lazy Loading)