Vetora logo
✍️Caching

Write-Through Cache

Write-through caching ensures every write goes to both the cache and the database synchronously. The cache is always consistent with the database, eliminating stale reads at the cost of higher write latency.

Overview

Write-through caching is a pattern where every write operation updates both the cache and the underlying database synchronously before returning success to the caller. Unlike cache-aside, which only populates the cache on reads, write-through ensures the cache always contains the latest version of any data that has been written. This eliminates the primary weakness of cache-aside -- stale data caused by writes that bypass the cache -- by making the cache a mandatory participant in the write path.

The key advantage of write-through is consistency. Because the cache is updated atomically with the database write, any subsequent read from the cache returns the latest value. There is no staleness window, no eventual consistency gap, and no need for complex TTL tuning to balance freshness against hit rate. This makes write-through ideal for workloads where read-after-write consistency is critical -- user profile updates that must be immediately visible, inventory counts that must reflect the latest decrement, or session data that must be current across all serving nodes.

The primary disadvantage is write latency. Every write operation incurs two synchronous writes: one to the cache and one to the database. If the cache write takes 1ms and the database write takes 5ms, every write takes at least 6ms instead of 5ms. More significantly, write-through caches data that may never be read. If an application writes a million records but only 10,000 are ever requested, 990,000 cache entries consume memory without providing any benefit. This is why write-through is almost always combined with TTL-based expiration to evict cold data and with cache-aside on the read path to avoid caching data that is written but never read.

Write-through caching works best when combined with other patterns. A common architecture uses write-through for the write path (ensuring cache consistency) and cache-aside for the read path (ensuring only requested data remains cached). Adding TTL ensures that data written but never read is eventually evicted, freeing memory. This hybrid approach provides the consistency benefits of write-through without the memory waste of caching all writes indefinitely. DynamoDB Accelerator (DAX) is a production example of transparent write-through caching that intercepts DynamoDB writes and keeps the cache synchronized automatically.

Key Points
  • 1Every write updates the cache and database synchronously. The write is considered complete only after both the cache and database have confirmed the update, ensuring they are always in sync.
  • 2Write-through eliminates stale reads because the cache is always updated as part of the write operation. There is no staleness window, unlike cache-aside where the cache and database can diverge between writes.
  • 3Write latency is approximately doubled because each write involves two synchronous operations. For latency-sensitive write paths, this overhead may be unacceptable, and write-back (async) may be more appropriate.
  • 4Write-through caches data that may never be read, wasting memory. Combining with TTL-based expiration evicts cold entries, and pairing with cache-aside on reads ensures the cache naturally prioritizes hot data.
  • 5Failure handling is complex: if the cache write succeeds but the database write fails (or vice versa), the cache and database are inconsistent. Implementations must handle partial failures with rollback or retry logic.
  • 6Transparent write-through (like DynamoDB DAX) intercepts writes at the SDK level, making the caching layer invisible to the application. This reduces implementation complexity but limits control over cache behavior.
Simple Example

The Secretary and Filing Cabinet

Imagine a secretary who keeps a notepad on the desk (cache) and a filing cabinet in the back room (database). With write-through, every time someone gives the secretary a new document, the secretary writes it on the notepad AND files it in the cabinet before confirming receipt. Anyone asking for the document later can check the notepad first and always find the latest version. The downside: every document submission takes longer because the secretary must do two things before saying 'done,' and the notepad fills up with documents nobody ever asks to see again.

Real-World Examples

Amazon DynamoDB Accelerator (DAX)

DAX is a fully managed, transparent write-through cache for DynamoDB. When an application writes to DynamoDB through the DAX client, DAX writes to the cache and DynamoDB simultaneously. Subsequent reads are served from the DAX cache with microsecond latency instead of DynamoDB's single-digit millisecond latency. DAX handles cache invalidation automatically and supports TTL for evicting cold entries. It is completely transparent -- applications use the same API they use for DynamoDB.

Hibernate ORM (L2 Cache)

Hibernate's second-level cache supports write-through mode, where entity updates are written to both the cache (typically EHCache or Infinispan) and the database as part of the same transaction. This ensures that any session reading from the L2 cache sees the latest committed state. The trade-off is increased transaction commit time, but it eliminates the stale-read problems that plague application-managed cache-aside implementations.

AWS ElastiCache for Redis

Many AWS customers implement write-through patterns using ElastiCache for Redis, where the application writes to Redis and Aurora/RDS in the same code path. AWS documentation recommends this pattern for workloads requiring read-after-write consistency, such as user session management and shopping cart state. The application wraps both writes in error handling, falling back to database-only writes if Redis is temporarily unavailable.

Trade-Offs
AspectDescription
Consistency vs Write LatencyWrite-through guarantees cache-database consistency but doubles write latency because each write involves two synchronous operations. For a write-heavy workload doing 10,000 writes/sec, the additional 1-2ms per write translates to a significant aggregate latency increase and reduced write throughput.
Memory Efficiency vs CompletenessWrite-through caches all written data, regardless of whether it will ever be read. This ensures complete cache coverage but wastes memory on cold entries. TTL-based expiration and LRU eviction mitigate this, but they add complexity and risk evicting entries that will be needed soon.
Simplicity vs Failure HandlingThe write-through concept is simple to understand, but handling partial failures (cache writes succeeds, database fails, or vice versa) requires careful error handling, rollback logic, or retry mechanisms. Without these, the cache and database can diverge precisely when write-through is supposed to prevent divergence.
Read-After-Write Guarantee vs FlexibilityWrite-through provides strong read-after-write consistency, which is exactly what some workloads need. But this consistency is all-or-nothing -- you cannot selectively apply it to some writes and not others without maintaining two write paths, which increases code complexity.
Case Study

DynamoDB Accelerator (DAX) at a Financial Services Firm

Scenario

A financial services company used DynamoDB for real-time portfolio valuation, requiring sub-millisecond reads for portfolio positions that were updated thousands of times per second as market prices changed. Direct DynamoDB reads at 3-5ms were too slow for their trading dashboard, which needed to display positions updated within the last second.

Solution

The company deployed DAX as a transparent write-through cache in front of DynamoDB. All portfolio writes flowed through the DAX client, which updated both the DAX cache and DynamoDB synchronously. Reads were served from the DAX cache at microsecond latency. DAX's write-through behavior ensured that the trading dashboard always displayed the most recent portfolio values without any staleness window. TTLs were set to 10 minutes to evict positions for inactive portfolios.

Outcome

Read latency dropped from 3-5ms (DynamoDB) to 200-400 microseconds (DAX), a 10x improvement. Write latency increased by approximately 1ms (from 5ms to 6ms) due to the dual write, which was acceptable for the portfolio update workflow. The trading dashboard achieved real-time accuracy with no stale reads. The transparent integration meant zero application code changes beyond swapping the DynamoDB client for the DAX client.

Common Mistakes
  • Using write-through without TTL, causing the cache to fill with data written once and never read again. Always pair write-through with TTL-based expiration to evict cold entries and manage memory effectively.
  • Ignoring partial failure scenarios. If the cache write succeeds but the database write fails, the cache contains data that does not exist in the database. Implement retry logic or compensating writes to handle these edge cases.
  • Applying write-through to write-heavy workloads where most data is never read. In this scenario, write-through wastes memory and adds write latency with no read benefit. Write-back (async) or cache-aside on reads is a better fit.
  • Assuming write-through eliminates the need for cache invalidation. While it prevents stale data from the application's own writes, external processes (batch jobs, admin scripts, other services) that write directly to the database still bypass the cache. Pair with event-driven invalidation for complete consistency.
Related Concepts

See Write-Through Cache in action

Explore system design templates that use write-through cache and run traffic simulations to see how these concepts perform under real load.

Browse Templates

Simulate write-through cache consistency under load

Metrics to watch
write_latency_mscache_consistency_pctdb_write_load_rpsthroughput_rps
Run Simulation
Test Your Understanding

1What is the primary advantage of write-through caching over cache-aside?

2Why should write-through caching be combined with TTL-based expiration?

Deeper Reading