BytewizeJoin the waitlist

Invalidation strategies

TTLs, write-through, and why invalidation is one of the two hard problems.

5 minute lesson

Every cache is a copy. Copies go stale.

When the database changes, the cached value doesn't know. Until something removes or refreshes it, readers get the old answer. How long that can last is your staleness window.

Key terms

TTL
Time to live: how long a value may be kept, in a cache or a store, before it expires.
invalidation
Removing or refreshing a cached value when its source changes.
staleness window
How long readers can see an old value after the source changes.

There are only two hard things in computer science: cache invalidation and naming things.

Phil Karlton

Strategy 1: let it expire

Each entry carries a time-to-live (TTL). When it expires, the next read misses and reloads from the database.

  1. t = 0 s

    A read misses, loads $19 from the database and caches it for 60 s.

    • Cache$19fresh
    • Database$19source of truth
    1. 0 s · cached
    2. 20 s · DB change
    3. 60 s · expires

    fresh

  2. t = 20 s

    The price changes to $24 in the database. The cache still says $19.

    • Cache$19stale
    • Database$24source of truth
    1. 0 s · cached
    2. 20 s · DB change
    3. 60 s · expires

    fresh, then stale

  3. t = 60 s

    The entry expires. The next read caches $24. Readers saw a stale price for 40 s.

    • Cache$24fresh
    • Database$24source of truth
    1. 0 s · cached
    2. 20 s · DB change
    3. 60 s · expires

    fresh, then stale

Check yourself

Prices are cached with a 5-minute TTL. A price changes. What's the worst case for shoppers?

  • They see the old price for up to 5 minutes
  • They get an error until the entry expires
  • They see the new price immediately
  • The price update fails while the entry is cached
Show the answer
  1. They see the old price for up to 5 minutes

TTL caps how long a stale value can live (here, 5 minutes). It doesn't cause errors or block writes.

A TTL bounds staleness. It doesn't remove it.

Strategy 2: delete on write

products.ts
async function updatePrice(id, price) {
  await db.product.update({
    where: { id }, data: { price },
  });
  await cache.del(`product:${id}`);
}
  1. Lines 2 to 4: Write the source of truth. Update the database first. If this fails, the cache still matches what is stored.
  2. Line 5: Then drop the cached copy. Delete the key instead of writing the new value. The next read misses and loads the fresh price.
  3. Line 5: Why not delete first?. A read between the delete and the write would re-cache the old price. Keep a TTL as a safety net for races and missed deletes.

Pick the right strategy

TTL only

expire

Staleness
Up to the TTL
Write cost
None
Use when
A bit stale is fine: feeds, counts

Delete on write

invalidate

Staleness
Near zero
Write cost
1 extra call
Use when
You own every write path

Write-through

update

Staleness
Near zero
Write cost
Every write hits the cache
Use when
Data is read right after it's written
Check yourself

After writing a new price to the database, the service deletes the cache key instead of writing the new price into the cache.

Why delete rather than update?

  • Deleting a key is faster than writing one
  • Two writers updating the cache can finish in the wrong order and leave the older price there; a delete just makes the next read reload
  • Caches can't store values that change
  • Updating the cache would turn off the key's TTL
Show the answer
  1. Two writers updating the cache can finish in the wrong order and leave the older price there; a delete just makes the next read reload

If two requests write the price, each then updating the cache, the cache writes can arrive in the opposite order to the database writes. The cache then holds the older price until the TTL. Deleting has no such race: the next read loads whatever the database holds.

Delete on write, don't update: a delete can't leave an older value behind.

Recall

What should you pair with delete-on-write, and why?

Show the answer

A TTL. If a delete is ever missed (a crash, a race, a forgotten write path), the stale entry still expires on its own.

Recap

  • TTL bounds staleness. It never removes it.
  • Delete the cache key after the database write, not before.
  • Pair delete-on-write with a TTL as a safety net.

Start with one byte

Join the waitlist and get the beta invite when it opens.

  1. Bit1U
  2. Nibble2U
  3. Byte4U
  4. Kilobyte8U
One unit more at every level