Cache Invalidation

Cache invalidation decides when cached API data can no longer be trusted and how it is refreshed or removed. A sound policy balances freshness, backend load, and the failure modes of coordinating a cache with its source of truth.

Cache invalidation is the policy for deciding when a cached value must no longer be served and what happens next. An API often caches database query results, computed responses, or data fetched from another service to reduce latency and backend load. Once the underlying data changes, a still-cached value may be stale.

A common strategy is to delete affected keys after a successful write. The next request misses the cache, fetches current data from the source of truth, and repopulates the cache. Another strategy is to update the cached value during the write; some systems instead expire entries after a time-to-live (TTL). These approaches can be combined.

The main challenge is determining which entries a change affects. A single entity might appear in detail responses, filtered lists, aggregates, and multiple tenants' views. Keys should encode relevant inputs, and invalidation may need to cover a group of related keys or use versioned namespaces.

Correctness also depends on ordering and failure handling. If the database update succeeds but cache invalidation fails, stale data can persist; if invalidation happens before a database transaction commits, a concurrent read can repopulate the cache with old data. Choose and document an acceptable staleness window, and use mechanisms such as transactional outboxes or short TTLs when reliable invalidation is important.

In distributed systems, invalidation messages may be delayed, duplicated, or lost. Make consumers safe to retry, monitor cache hit rates and stale-read behavior, and avoid assuming that deleting a key on one application instance clears every cache layer.

Key points

Interview questions

What is cache invalidation, and why do APIs need it?

Invalidation is the rule for retiring cached data that no longer represents the underlying source. It matters because stale responses can violate API correctness even when caching improves latency and reduces load. A typical approach removes affected keys after a successful write and lets the next read repopulate them. TTLs can bound how long stale data may remain if an invalidation is missed.

How would you invalidate a cached API response after an update?

After a successful database commit, remove the relevant cache key or keys; a later request can refill them from the committed state. Invalidating before commit is risky because a concurrent request might load the old database value back into the cache. A commit followed by a separate delete still has a failure window, so use a short TTL or reliable event delivery if that risk is unacceptable. The exact choice depends on the tolerated staleness and operational complexity.

How do you handle invalidation when one write affects many cached queries?

First identify every representation that depends on the changed record: detail keys, list or search results, aggregates, and tenant- or permission-specific variants. Broad deletion by a carefully designed namespace or version can be safer than maintaining fragile lists of individual keys, though it may reduce hit rate. Tags or dependency indexes can target related entries when the cache supports them. The strategy should be measured against write volume, cache size, and the cost of rebuilding entries.

How does invalidation differ from write-through caching?

The cache-aside pattern typically deletes a key after a successful write, leaving the next read to fetch and cache the value. Write-through caching updates the cache during the write path, which can provide a warm, current value but requires handling failures consistently. Deletion can be simpler when there are many derived keys, while write-through is useful when the updated value is known and frequently read. Both need explicit handling for partial failures and concurrent requests.

How would you make cache invalidation reliable across service failures?

A successful database write followed by a cache delete is not a single atomic transaction. If the process crashes between those actions, the stale cache entry remains; if invalidation is issued too early, a reader can refill from old data. A transactional outbox records an event in the same database transaction as the write, then a worker publishes it with retries. Idempotent consumers and a bounded TTL help manage duplicate or delayed delivery, but eventual consistency still needs to be acceptable.