Write-Through Caching: Synchronous Cache Updates on Every Write
In this tutorial, you will learn about Write. We cover key concepts, practical examples, and best practices to help you master this topic.
Write-through caching updates the cache synchronously with every write to the backing store. This ensures the cache always contains the latest data, providing strong consistency at the cost of higher write latency since both the cache and the database must acknowledge before responding to the client.
sequenceDiagram
participant C as Client
participant App as Application
participant Cache as Cache (Redis)
participant DB as Database
C->>App: PUT /resource
App->>DB: UPDATE resource
DB-->>App: OK
App->>Cache: SET resource:123 data
Cache-->>App: OK
App-->>C: 200 OK
What You'll Learn
- Write-through pattern principles and implementation
- Atomic write-through with database transactions
- Trade-offs: write latency vs read consistency
- When to use write-through vs write-behind vs write-around
Why It Matters
Write-through caching is the safest caching Strategy for data that must be immediately consistent. It prevents the common bug where reading immediately after writing returns stale cached data.
Real-World Use
A banking API uses write-through caching for account balances. When a transfer deducts from an account, the database and cache update atomically. Any subsequent read of the balance returns the correct post-transfer amount, preventing users from seeing outdated balances.
Write-Through Implementations
Basic Write-Through Cache
async function writeThrough(key, data, writeToDB, ttl = 3600) {
await writeToDB(data);
await cache.set(key, JSON.stringify(data), ttl);
}
// Usage
await writeThrough(
`user:${userId}`,
{ name, email, avatar },
(data) => db.query('UPDATE users SET ? WHERE id = ?', [data, userId])
);
Expected output:
Both DB and cache are updated. If either fails, the operation is considered failed (no partial update without transaction).
Write-Through with Database Transaction
async function writeWithTransaction(table, id, data, cacheKey) {
const connection = await db.getConnection();
try {
await connection.beginTransaction();
await connection.query(`UPDATE ${table} SET ? WHERE id = ?`, [data, id]);
const [rows] = await connection.query(`SELECT * FROM ${table} WHERE id = ?`, [id]);
await cache.set(cacheKey, JSON.stringify(rows[0]), 3600);
await connection.commit();
} catch (err) {
await connection.rollback();
await cache.del(cacheKey);
throw err;
} finally {
connection.release();
}
}
Expected output:
Transaction ensures atomicity: if cache write fails, DB write is rolled back. If DB write fails, cache is not updated. Cache key is also deleted on error.
Write-Through with Cache Expiry on Write
async function writeThroughWithVersion(key, data, writeFn) {
const version = Date.now().toString(36);
const versionedKey = `${key}:${version}`;
await writeFn(data);
// Set new data
await cache.setEx(versionedKey, 3600, JSON.stringify(data));
// Update pointer to latest version
await cache.setEx(`ptr:${key}`, 3600, version);
// Delete old version asynchronously
const oldVersion = await cache.get(`ptr:${key}:old`);
if (oldVersion) {
cache.del(`${key}:${oldVersion}`).catch(() => {});
}
await cache.setEx(`ptr:${key}:old`, 60, version);
}
Expected output:
Version keys allow atomic cache updates. Readers atomically get the pointer then the data. Old versions are cleaned up asynchronously.
Common Mistakes
- Using write-through for write-heavy workloads where most data is rarely read — you pay write penalty for no read benefit.
- Not wrapping cache and DB updates in a transaction, risking cache being updated while DB write fails.
- Implementing write-through without error handling — if the cache is down, the DB write should still succeed.
- Setting excessively long TTLs for write-through cached data, causing stale reads after DB modifications bypass the cache.
- Using write-through for every endpoint without considering whether the data is read frequently enough to justify the write overhead.
Practice Questions
- How does write-through caching differ from cache-aside on writes?
- What consistency guarantee does write-through provide?
- How do you handle write-through when the cache server is unreachable?
- Why is write-through not suitable for all data types?
- What is the write amplification factor in write-through caching?
Challenge
Design a write-through cache for an inventory management system. Stock levels change in real-time via multiple services. Ensure that a stock read immediately following a stock update returns the correct value across all services.
FAQ
Mini Project
Implement write-through caching for a product inventory API. Every stock update writes through to both PostgreSQL and Redis. Add a transaction wrapper that rolls back both on failure. Write a test that verifies read-after-write consistency by measuring zero stale reads in 1000 iterations.
What's Next
Continue with Write-Around Caching to understand write-around and write-behind patterns.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro