Write Through
Write-through caching makes every write go to the cache and the database synchronously, in the same request. The cache is therefore always fresh: there is never a stale-read window. The cost is higher write latency — every write now pays the round-trip to both stores — and a partial-failure window in which one write succeeds and the other fails. Write-through is the right choice when reads must never be stale and the write path can tolerate the extra milliseconds.
How it works
In write-through, the application writes to the cache and the database in the same critical path. The cache write and the DB write are both synchronous — the caller does not get an ACK until both have committed. As a result, the cache is a strict, always-fresh copy of the database rows it covers. A read can never return stale data, because every value in the cache was written there as part of the write that updated the database.
Contrast this with cache aside, where writes update the DB and invalidate the cache. In cache aside, the cache entry disappears on write and is repopulated only when someone reads it. Between the write and the next read, the entry is simply absent — that's fine. The staleness problem in cache aside is different: if the invalidation call fails, the cache keeps serving the old value until its TTL expires. Write-through eliminates that failure mode entirely because the new value is pushed into the cache, not lazily re-fetched.
The trade is on the write side. A write-through write is at least as slow as max(cache_write_latency, db_write_latency) if the writes are parallel, or cache_write + db_write if serial. Either way, you are paying for two synchronous writes per logical write. For most web workloads where reads dominate 10:1 or 100:1, this is a great trade — write latency is a small fraction of total traffic. For write-heavy workloads (analytics ingestion, telemetry, audit logs), the cost dominates and write-behind is usually a better fit.
Order matters: DB first, then cache.
The standard write-through ordering is: write the database first, then write the cache. If the cache write fails, you have not lost the canonical update — the DB has it, and the cache will simply be stale (or empty) until either a TTL fires, an invalidation pass catches it, or a read repopulates. If you write cache first and the DB write fails, the cache now holds a value that does not exist in the source of truth. Subsequent reads return a hallucination, and you cannot even detect the inconsistency from the cache side.
So the safe ordering is: DB write (commit) → cache SET. The cache write is best-effort from a correctness standpoint but synchronous from a latency standpoint. You pay the latency, you get the freshness, and if the cache write fails you fall back to cache-aside semantics (next read repopulates). Some implementations make the cache write fire-and-forget after the DB commits — that's actually a hybrid of write-through and write-behind, sometimes called write-around-with-publish, and it relaxes the freshness guarantee slightly.
Note also that "synchronous" does not mean "atomic." If the DB commits but the cache SET call times out, the cache is briefly stale. This is why every write-through system still sets a TTL — TTL is the safety net that bounds staleness even when the synchronous cache write fails.
Cache stampedes happen when a popular key expires and N concurrent readers all miss at the same time. In cache aside, hot keys expire on a TTL and a thundering herd restarts them. In write-through, the cache is updated on every write, so a hot key is refreshed as a side effect of writes — not as a side effect of reads. If a key is being written to, it never expires from disuse, because the writes keep it warm. The stampede can still happen for read-only hot keys (a viral post nobody is editing), but for read-write hot keys (a counter, a balance, a leaderboard), write-through is structurally stampede-proof.
The partial-failure window.
Write-through's signature failure mode is the partial write: the DB commits, but the cache write fails (network blip, Redis OOM, eviction storm). Now the cache holds either nothing (a future read will repopulate from the DB) or, worse, a stale value (if the cache had the old value and the SET to overwrite it failed). The standard mitigations:
- TTL on every key. Even write-through keys must have TTLs. A failed cache write means the cache may be stale; TTL bounds how long that staleness can persist. Without a TTL, a single failed SET means stale data forever.
- Retry the cache write asynchronously. After the DB commits, queue a best-effort retry of the cache SET on a background worker. If the first attempt failed, the retry usually catches up within milliseconds.
- Read-through fallback on critical reads. For reads that must be perfectly fresh (e.g., a balance display before a withdrawal), do a double read: cache first for speed, then verify against the DB if the operation is irreversible. This is rarely needed but is the gold standard for financial flows.
- Cache invalidation as a backup. If you cannot guarantee the SET succeeded, DEL the key on failure — forcing the next read to repopulate from the DB.
The deeper insight is that write-through does not eliminate inconsistency, it narrows the window. Cache aside's stale window is up to TTL duration (potentially minutes). Write-through's stale window is the duration of a failed SET plus the retry interval (usually milliseconds to seconds). That is a 100-1000x improvement, and it is the real reason to choose write-through.
When to use write-through:
- Read-freshness is critical. Bank balances, seat counts, inventory, session validity — anywhere stale reads cost real money or break correctness.
- Reads vastly outnumber writes. Write-through's extra write latency is amortized across many fast reads. A 10:1 read:write ratio is the floor; 100:1 is comfortable.
- You want to eliminate the cache stampede on write-heavy hot keys. Counters, leaderboards, real-time stats.
- You can afford the latency. A write that used to take 5 ms (DB only) now takes 5–7 ms (DB + cache SET). For most user-facing APIs this is invisible.
When NOT to use write-through:
- Write-heavy workloads (logs, telemetry, audit, analytics). Each write now costs two synchronous writes; throughput drops, and the cache adds little because writes don't read.
- When staleness is acceptable. Social feeds, recommendation previews, analytics dashboards — cache aside is simpler and faster.
- When you need ultra-low write latency. Real-time ad bidding, game state mutations — write-behind is the better choice, accepting its data-loss risk.
- When the cache and DB cannot agree on transaction semantics. If the DB write is transactional but the cache write isn't (Redis has no cross-system 2PC), there is always a partial-failure window — be honest about it.
In a write-through cache, the DB write succeeds but the cache SET fails due to a transient network error. The cache still holds the previous value. What reads see in the next few seconds?
Pick one answer.
Your team is choosing a cache strategy for a banking balance lookup that gets 1000 reads/sec and 5 writes/sec. Reads must never show a stale balance. Which strategy do you choose and why?
Pick one answer.
Why is the standard write-through ordering "DB write first, then cache SET" rather than the reverse?
Pick one answer.
Engineering mental model
Mental model. Think of Write Through 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 Through mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”
Before choosing Write Through, 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_through(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 Through
Change the variables below and predict what breaks first in Write Through. 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 Through, 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 Through. What should you inspect first?
Pick one answer.
Which statement is the safest engineering habit when using Write Through?
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 Through, traffic suddenly spikes, and p99 latency doubles. What is your first move?
Interview drill
Answer this without notes: When would you choose Write Through, 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 Through, 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 Through 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
- +Cache is always fresh on a hit — no stale reads in the common case.
- +Kills the cache stampede for write-heavy hot keys (writes keep the cache warm).
- +Narrows the stale window from TTL-duration (cache aside) to milliseconds (failed SET retry interval).
- +Reads are always fast and never block on DB repopulation for actively-written keys.
- +Simpler reasoning: "the cache reflects the DB" is true almost all the time, vs cache aside's "the cache may be stale."
- −Higher write latency — every write pays for two synchronous stores instead of one.
- −Partial-failure window: DB commits but cache SET fails → brief staleness until TTL/retry.
- −Lower write throughput — write-heavy workloads (logs, telemetry) get half the throughput.
- −Cache holds data even if it is never read again (no lazy population benefit of cache aside).
- −Cache and DB cannot be updated atomically (no cross-system 2PC) — "synchronous" is not "transactional."
How this breaks in production
- Partial write: DB commits, cache SET fails → stale cache until TTL expires or retry succeeds.
- Cache eviction storm: high write rate evicts other keys, causing read misses on previously-hot entries.
- Write amplification on the cache: every logical write becomes a cache write, even for keys that are never read.
- Latency cliff: if the cache or DB slows down, write latency doubles because both are in the critical path.
- Cross-system inconsistency during failover: if the cache fails over to a replica that hasn't seen the latest SETs, reads return stale data.
Don't fall into these traps
- •Skipping the TTL — "write-through keeps the cache fresh" is false under partial failure; TTL is the safety net.
- •Writing the cache before the DB — guarantees an unrecoverable inconsistency if the DB write fails.
- •Treating write-through as atomic — it is synchronous, not transactional; design for the partial-failure window.
- •Using write-through for write-heavy workloads — write latency and throughput collapse; use write-behind or skip the cache.
- •Not retrying failed cache SETs — a failed SET without retry leaves the cache stale for the full TTL.
- •Forgetting that read-only hot keys can still stampede — write-through protects write-heavy keys, not viral read-only content.
Real systems using this
How real systems implement this
- AWS DynamoDB DAX — DAX is a write-through cache for DynamoDB. Application writes go to DAX and DynamoDB synchronously; reads from DAX are always fresh. The classic use case is shopping cart and session state where stale reads cause user-visible bugs.
- Redis-backed session stores (e.g., GitHub's) — GitHub uses Redis as a write-through session store for high-value tokens: every login or refresh writes the session to Redis and the durable store together, so reads from Redis are guaranteed to reflect committed logins.
- Varnish with grace mode + PURGE on write — Some Varnish deployments implement a write-through pattern: writes to the origin trigger a PURGE plus a synchronous refresh, so the edge cache always reflects the latest published content.
- Apple's iTunes / App Store inventory — Inventory and pricing for the App Store use a write-through cache layer so that searches and product pages show fresh prices and availability without hitting the canonical inventory database for every read.
Practice saying it out loud
- Q1Design a cache for a banking balance lookup. Reads must never be stale. Walk me through your strategy and its failure modes.
- Q2Compare write-through and cache aside. When would you choose each? What is the partial-failure window for each?
- Q3Your write-through cache's SET call is taking 50 ms due to Redis load. Writes are now too slow. What do you do?
- Q4A cache SET fails after a successful DB write. Walk me through every state the cache can be in for the next 60 seconds and how you mitigate each.
- Q5Why does write-through still need a TTL? Isn't the cache always fresh?
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
Write Behind