Cache-Aside Invalidation
Cache-aside invalidation keeps cached data aligned with the source of truth by removing or refreshing a cache entry when the underlying data changes. The application explicitly manages the cache rather than relying on the database to update it automatically.
In the cache-aside pattern, the application checks the cache first. On a cache miss, it reads the data from the database or another source of truth, returns it, and usually stores the result in the cache for later requests.
When application code changes the underlying data, it must also deal with the corresponding cache entry. The common invalidation approach is to update the source of truth and then delete the affected cache key. The next read misses the cache and loads fresh data from the source.
Deletion is often preferred to trying to modify cached objects because it avoids duplicating complicated update logic in the cache layer. However, there can be a short period where concurrent readers or writers observe different versions, so the exact ordering and consistency guarantees depend on the system design.
A useful design is to treat the database as authoritative and the cache as disposable derived state. Cache expiration, versioned keys, explicit refreshes, and event-driven invalidation are alternatives or complements when stronger freshness or operational requirements exist.
Key points
- The source of truth remains authoritative; the cache contains derived data.
- A cache hit serves the cached value without querying the database.
- A cache miss reads the source, then commonly populates the cache.
- After a write, deleting the related cache entry is a common invalidation strategy.
- Concurrent operations can still create stale-data windows unless synchronization or stronger consistency mechanisms are added.
Interview questions
What is cache-aside invalidation?
Cache-aside invalidation is a technique for keeping a cache from serving known-stale data after the underlying data changes. The application updates the source of truth and invalidates the corresponding cache entry. A later read misses the cache and loads the current value from the source. This keeps cache management in the application rather than requiring the database to know about the cache.
Why would you delete a cache entry after updating the database instead of updating the cached value directly?
Deleting the entry makes the cache value disposable and avoids duplicating business-update logic in two places. The next read obtains the value from the authoritative source and repopulates the cache. This is simple and reduces the chance that cache-update logic produces a different result from database-update logic. The trade-off is that the next request pays the cost of a cache miss.
What happens if a cache invalidation and a database update occur concurrently with a read?
The result depends on the ordering of the operations. For example, if a reader observes the old database value after an invalidation, it can repopulate the cache with stale data. Similarly, a reader may observe an older cached value while a write is in progress. Systems that require tighter guarantees may use locking, versioning, coordinated write ordering, or event-based mechanisms.
What are the main trade-offs of cache-aside invalidation in a distributed Java service?
It is simple and keeps the cache decoupled from the source of truth, but correctness becomes harder when multiple application instances share a distributed cache. Network failures can cause invalidation messages or cache operations to fail, leaving stale entries. Concurrent updates can also create ordering problems, particularly when different nodes process writes independently. A production design should define acceptable staleness and choose mechanisms such as TTLs, versioned values, retries, events, or coordination accordingly.