Keep a nearby copy with an expiry story
Picture
On a cache hit, return the saved value. On a miss, read the source of truth and fill the cache. That is cache-aside.
Predict
Does a long TTL guarantee that cached data is correct until it expires?
Walk through the small example
No, the source can change earlier. TTL bounds retention, not agreement with every intervening write.
Mental model and runnable Python model
State the cache’s owner, key, TTL, eviction policy, and stale-read tolerance. Cache-aside is simple, but invalidating after a write can race with an earlier slow reader that repopulates an old value. Versioned keys, conditional fills, or stronger coordination may be needed. Write-through and write-behind move different consistency and failure responsibilities into the cache. Separate CDN caching of suitable responses from per-user application caches. Negative caching and hot-key protection also need bounded lifetimes.
What the function promises: TTLCache uses caller-supplied monotonic times and caches successful loader results, including None. It is single-threaded, unbounded, and not a complete invalidation protocol.
class TTLCache:
def __init__(self, ttl):
if ttl <= 0:
raise ValueError("TTL must be positive")
self.ttl, self.entries = ttl, {}
def get(self, key, now, load):
if key in self.entries:
value, expires = self.entries[key]
if now < expires:
return value
value = load(key)
self.entries[key] = (value, now + self.ttl)
return value
def invalidate(self, key):
self.entries.pop(key, None)
source = {"a": 1}
cache = TTLCache(5)
assert cache.get("a", 0, source.__getitem__) == 1
source["a"] = 2
assert cache.get("a", 4, source.__getitem__) == 1
assert cache.get("a", 5, source.__getitem__) == 2
Work and storage: Expected O(1) cache lookups; misses pay source cost. Memory grows with keys unless eviction or expiry cleanup is added.
When to recognize it: If many requests miss together, all may hit the database. Single-flight work, jittered expiry, or controlled stale serving can reduce that stampede.
Check before applying it: Cache-aside needs a stated stale-read and invalidation policy, especially under concurrent writes.
Change one rule
A slow miss started before an update, then fills the cache after invalidation. Can stale data reappear?
See what changes
Yes. Invalidate-after-write alone does not prevent that race. Use a version or generation check on fills, or another protocol matching the required freshness guarantee.
Connect
Choose storage from reads and writes: The database remains authoritative under a defined caching protocol.
LRU Cache: LRU chooses what to evict; TTL and invalidation decide freshness.