Course → Module 3: Storage, Databases & Caching

The Write Side of Caching

Session 3.8 covered the read path: cache-aside, read-through, and refresh-ahead. Those strategies answer one question: how does data get into the cache when someone reads it? This session covers the other half: what happens when someone writes new data?

The write path is where the hard trade-offs live. Every write creates two copies of truth: one in the cache, one in the database. The strategy you pick determines which copy updates first, whether they update together, and what happens when something fails between the two writes.

There are three core patterns: write-through, write-behind (also called write-back), and write-around. Each makes a different bet about what matters most.

Write-Through

In write-through caching, every write goes to both the cache and the database synchronously. The application writes to the cache, and the cache immediately writes to the database. The write is only acknowledged as successful after both stores have been updated.

sequenceDiagram participant App as Application participant Cache as Cache participant DB as Database App->>Cache: 1. Write data Cache->>DB: 2. Write to DB (sync) DB-->>Cache: 3. Acknowledge Cache-->>App: 4. Acknowledge Note over App,DB: Both stores updated before response

The guarantee is strong: cache and database are always consistent. If the database write fails, the entire operation fails, and the cache is not updated. There is no window where the cache holds data that the database does not.

The cost is latency. Every write now has the latency of two operations: writing to cache (sub-millisecond) plus writing to the database (typically 1-10ms for a relational database). For read-heavy workloads where writes are infrequent, this penalty is acceptable. For write-heavy workloads, it adds up quickly.

Write-Behind (Write-Back)

Write-behind flips the priority. The application writes to the cache, and the cache acknowledges immediately. The database is updated asynchronously, sometime later. The delay might be a few milliseconds, a few seconds, or batched into periodic flushes.

sequenceDiagram participant App as Application participant Cache as Cache participant DB as Database App->>Cache: 1. Write data Cache-->>App: 2. Acknowledge (fast) Note over Cache: Data queued for DB write Cache->>DB: 3. Async batch write (later) DB-->>Cache: 4. Acknowledge Note over Cache,DB: Gap between cache write and DB write

Write latency drops dramatically because the application only waits for the cache write. The database sees fewer, larger batch writes instead of many small ones, which can significantly reduce I/O pressure. If your application writes 1,000 updates per second to the same key, write-behind can collapse those into a single database write.

The risk is data loss. If the cache node crashes before the async write reaches the database, those pending writes are gone. The database is now behind. For some systems this is fine. For others, it is catastrophic.

Write-Around

Write-around skips the cache entirely on writes. The application writes directly to the database. The cache is only populated when data is read (through a read strategy like cache-aside or read-through).

sequenceDiagram participant App as Application participant Cache as Cache participant DB as Database App->>DB: 1. Write directly to DB DB-->>App: 2. Acknowledge Note over Cache: Cache not updated App->>Cache: 3. Later: read request Cache-->>App: 4. Cache miss App->>DB: 5. Fetch from DB DB-->>App: 6. Return data App->>Cache: 7. Populate cache

Write-around avoids cache pollution. If you write data that is unlikely to be read soon (log entries, audit records, analytics events), putting it in the cache wastes memory and may evict more frequently accessed data. By writing only to the database, the cache stays focused on hot data.

The downside is that a read immediately after a write will always be a cache miss. The data exists in the database but not in the cache, so the first read pays the full database round-trip cost. For workloads where writes are rarely followed by immediate reads, this is efficient. For workloads where "write then read" is common, write-around creates unnecessary latency.

Comparing the Three Strategies

Dimension Write-Through Write-Behind Write-Around
Write latency High (cache + DB sync) Low (cache only) Medium (DB only)
Read-after-write latency Low (data in cache) Low (data in cache) High (cache miss)
Consistency Strong Eventual Strong (DB is source of truth)
Data loss risk None Yes (if cache fails before flush) None
DB write load Same as without cache Reduced (batching) Same as without cache
Cache pollution Possible (write-heavy data cached) Possible Minimal
Implementation complexity Low High (async queue, retry logic) Low
Best for Financial, medical, config data High-throughput counters, metrics Logs, audit trails, cold writes

Write-through trades latency for consistency. Write-behind trades consistency for throughput. Write-around trades read-after-write speed for cache efficiency. No single strategy wins. The choice depends on what your system cannot afford to lose.

Combining Strategies

Production systems rarely use a single write strategy for everything. A banking application might use write-through for account balances (consistency is non-negotiable), write-behind for session activity tracking (high volume, eventual consistency is fine), and write-around for transaction audit logs (written once, read rarely).

The decision tree is straightforward. Ask three questions about each data type:

  1. Can you tolerate any data loss? If no, eliminate write-behind.
  2. Do reads typically follow writes? If yes, eliminate write-around.
  3. Is write throughput a bottleneck? If yes, consider write-behind with a durable queue (like Redis with AOF persistence) to reduce the data loss risk.
flowchart TD Start["New data type to cache"] --> Q1{"Can you tolerate
any data loss?"} Q1 -->|No| Q2{"Is write latency
critical?"} Q1 -->|Yes| Q3{"Do reads follow
writes immediately?"} Q2 -->|No| WT["Write-Through"] Q2 -->|Yes| WTQ["Write-Through +
async read replica"] Q3 -->|Yes| WB["Write-Behind"] Q3 -->|No| WA["Write-Around"] style Start fill:#2a2a2a,stroke:#c8a882,color:#ede9e3 style Q1 fill:#2a2a2a,stroke:#c8a882,color:#ede9e3 style Q2 fill:#2a2a2a,stroke:#c8a882,color:#ede9e3 style Q3 fill:#2a2a2a,stroke:#c8a882,color:#ede9e3 style WT fill:#2a2a2a,stroke:#6b8f71,color:#ede9e3 style WTQ fill:#2a2a2a,stroke:#6b8f71,color:#ede9e3 style WB fill:#2a2a2a,stroke:#6b8f71,color:#ede9e3 style WA fill:#2a2a2a,stroke:#6b8f71,color:#ede9e3

Systems Thinking Lens

Each write strategy creates a different feedback loop. Write-through creates a tight, synchronous loop: every write immediately confirms both stores are consistent. The system is predictable but rigid. Write-behind introduces delay into the loop. The application "thinks" the write succeeded, but the database has not confirmed it yet. This delay is a stock of pending writes that can grow if the database slows down. If that stock grows faster than it drains, the system eventually fails. Write-around decouples write and read paths entirely, creating two separate loops that only interact when a cache miss triggers a database read.

The key insight: consistency is not free. Every write strategy pays for it somewhere, either in latency, risk, or complexity. Your job is to decide where the payment is least painful for each data type in your system.

Further Reading

Assignment

For each scenario below, choose the most appropriate write caching strategy and explain your reasoning. Then answer the follow-up question.

  1. Banking transaction: A user transfers $500 between accounts. Which write strategy should update the cache? What happens if the chosen strategy fails mid-write?
  2. Social media like counter: A viral post receives 10,000 likes per second. Which write strategy handles this load? What is at stake if the counter is temporarily inaccurate?
  3. Audit log: Every API request generates a compliance record that regulators may review months later. Which write strategy fits? What is at stake if the cache evicts this data before it is ever read?

For each answer, state: (a) the strategy, (b) why the alternatives are worse, and (c) what failure looks like if your chosen strategy breaks.