Skip to content
Free sample

The four strategies

Every caching pattern answers two questions: who populates the cache, and when is the source of truth updated relative to it.

Cache-aside

The application checks the cache, and on a miss reads the database and populates the cache itself. The cache has no knowledge of the database.

def get_user(user_id):
    cached = cache.get(f"user:{user_id}")
    if cached is not None:
        return cached
    user = db.query("SELECT * FROM users WHERE id = %s", user_id)
    cache.set(f"user:{user_id}", user, ttl=300)
    return user

This is the default for a reason: if the cache is down, every request simply becomes a database read. Degraded, not broken.

Read-through

The cache sits inline. The application only talks to the cache, which fetches from the database on a miss. Logic moves out of the application and into the cache layer.

Cleaner call sites, but the cache is now on the critical path — if it is unavailable, reads fail entirely rather than falling back.

Write-through

Writes go to the cache, which synchronously writes to the database before acknowledging. The cache is never stale.

The cost is write latency: every write pays for both hops. Worth it when reads vastly outnumber writes and stale reads are unacceptable.

Write-behind

Writes land in the cache and are flushed to the database asynchronously.

Fastest writes available, and the only strategy that can lose acknowledged data. A crash between acknowledgement and flush loses whatever was buffered.