Glossary
Cache invalidation
Caching speeds up reads; invalidation defines the boundary of their correctness. It deliberately connects a change at the source, a derived view, and an acceptable delay.
Short definition
Invalidating a stored view after data changes.
A cache contains a copy of a result, not authoritative data. Invalidation ensures that the copy is no longer used after a product, price, translation, permission, or another input from which it was created changes. It can mean deleting a specific key, incrementing a version in the key name, waiting for TTL expiration, or switching to a newly prepared cache namespace.
There is no single correct policy for every value. A public catalogue can tolerate a few minutes of delay, while an inventory confirmation or access permission must use the current source when making a decision. The goal is not perfectly fresh caching at any cost; it is to state precisely where an older value is acceptable and what happens after a change or failure.
What it is used for
Keeping reads fast without silently returning outdated information
Invalidation belongs with every cached view that can change.
- changing a product’s name, price, availability, or image in an online-store catalogue
- revoking or changing user permissions when the cache contains a derived access view
- refreshing an external-integration response after processing a webhook or import
- replacing a serialised format when a new application version is deployed
- switching an index, configuration, or translation without manually deleting an unknown number of keys
Practical example
Changing a product price across several cached views
An administrator changes a product price. The database transaction writes the new price and stores a ProductPriceChanged event in the outbox. A worker then removes the product-detail key and increments the catalogue version for the given store. The next visit to the detail page loads it from the database, while product listings gradually read the namespace with the new version.
The event may arrive twice or after a retry, so the operation must be safe to repeat. If the worker is unavailable, the TTL on old items limits the duration of the discrepancy. At the same time, the application does not use this catalogue view to complete a payment or reserve inventory, where current transactional data must be verified.
$this->connection->transactional(function () use ($productId, $price): void {
$this->productRepository->changePrice($productId, $price);
$this->outbox->record(new ProductPriceChanged($productId));
});
// Worker: opakované provedení je v pořádku.
$this->cache->delete(sprintf('product:v3:%s', $productId));
$this->cacheVersion->increment('catalog:store:cz');
How it works
Changing the source and invalidating the view should be one monitored process
A safe flow commits the business change in the source of truth first. Only then does it invalidate or mark as stale the values derived from that source.
- Transactional change The application stores a price, status, or other business value in the authoritative database. If the change fails, the cache must not pretend that it succeeded.
- Impact analysis The design identifies the views affected by the change: product detail, category listing, search result, HTTP response, or aggregate overview.
- Invalidation or new version The application deletes specific keys, invalidates a tag, writes a new namespace version, or sends an event to a worker. The choice depends on scope and reliability.
- Next read The next request encounters a miss or already reads a key with the new version. It creates a current view from the source and stores it with defined validity.
- Monitoring and recovery Metrics show increased misses and regeneration time. If a failure occurs between the database and cache, there must be a retry, reconciliation, or safe TTL fallback.
Main approaches
Deletion, TTL, and versioning address different dependencies.
The choice of mechanism depends on the scope of the change and knowledge of the affected keys.
TTL as a safety net
Expiration automatically limits how long a key can survive even when invalidation is lost. It does not guarantee an immediate change, and using the same TTL for every value rarely makes sense.
Exact key deletion
If the product-detail key is known, the application can remove it after a change. This is simple and precise, but insufficient when one product affects thousands of catalogue views.
Tags and dependencies
A tag or group can remove several values, such as everything that depends on a product or category. It requires disciplined tagging and attention to the cost of bulk deletion.
Namespace versioning
A key can contain a catalogue or format version. Incrementing it immediately directs new reads away from old keys; the old ones disappear later through TTL. This suits broad changes and deployments.
Asynchronous invalidation
A ProductChanged event can clear search results or a CDN outside the database transaction. It must be deliverable, retryable, and able to recover a missed step, or permanent differences will arise.
Benefits and limitations
Greater freshness requires more careful design and operational coordination.
Benefits
- a fast view remains useful after source data changes
- versioning makes it possible to replace a format or broad dataset safely
- TTL limits the impact of a lost event or invalidation error
- a traceable event separates a transactional write from expensive cache regeneration
Risks and mistakes
- deleting only the product detail leaves a stale price in a category, search result, or CDN
- invalidation before the database transaction commits can trigger regeneration from old data
- a global flush for every small change removes the cache benefit and can overload the source
- an asynchronous event without retries and checks permanently separates the cache from the source of truth
- without concurrency control, many requests regenerate the same expensive value at once
Scope of use
First define the acceptable age of information, then choose the mechanism.
In a frequently changing catalogue, a product detail can use a short TTL and exact invalidation while facets refresh asynchronously. For a large reindex or serialisation change, a namespace version is more practical than finding every historical key. The design should define the maximum acceptable delay for each view.
A critical write, such as decrementing inventory or authorizing a refund, should not wait for cache invalidation. The final check runs against current data in a transaction. A cache can improve user-facing reads but should not be the only basis for allowing a business operation to proceed.
What to keep in mind
Connect changes, keys, and failure behaviour through tests.
A good design can show what is removed after one product changes and what happens when the cache backend or queue is unavailable.
- write the invalidation only after the business change commits successfully, or use an outbox for reliable event delivery
- describe every view dependent on one data source, not just the first cache key found
- combine active invalidation with TTL as protection against a missed step
- use versioning or well-designed tags for large key groups instead of an uncontrolled global flush
- measure misses after invalidation, regeneration time, worker errors, and value age
- test duplicate events, cache outages, and the race between a write and a concurrent read
Common questions
How to avoid silent data staleness
Is TTL the same as invalidation?
No. TTL automatically limits a value’s age but does not react immediately to a specific business change. Invalidation targets a view after a known change; TTL is often the safety net if that step fails.
Is deleting everything after a change best?
Usually not. A global flush causes a surge in misses and source load. It is better to identify the specific keys, tag, or view version that the change actually affects.
Must invalidation happen inside the database transaction?
It must not happen before the transaction commits successfully. Reliable asynchronous delivery often uses an outbox stored in the same transaction as the business change.
Can the cache briefly differ from the database after a change?
Yes, if the design deliberately permits eventual consistency. It must define the maximum duration, catch-up mechanism, and places where the cache is not used at all for critical decisions.
How I address operational consistency
I connect faster reads with events, retries, and the source of truth.
For e-commerce and integration applications, I design cached views so every data change has a clear impact, safe retries, and a traceable recovery path.