Application Caching¶
A cache keeps a reusable copy closer to the consumer to reduce latency, load, or cost. It trades freshness and complexity for reuse. Before adding one, measure the underlying operation and determine whether an index, query change, batch, or simpler local computation would solve the cause directly.
Common access strategies¶
| Strategy | Read and write behavior | Important consequence |
|---|---|---|
| Cache-aside | Application loads on a miss and invalidates or updates after a write | Application owns race handling |
| Read-through | Cache abstraction loads missing data | Loader behavior becomes part of the cache contract |
| Write-through | Write completes in cache and backing store synchronously | Higher write latency, simpler read freshness |
| Write-behind | Cache acknowledges before asynchronous persistence | Data-loss and ordering policy is required |
Time-to-live limits age; maximum size limits space. Neither proves freshness. Versioned keys and event-driven invalidation can narrow stale windows but add coordination. Every design should state whether stale data is permitted and what happens when the cache is unavailable.
Correct keys and values¶
Keys must include all dimensions that affect the response, especially tenant, authorization scope, locale, representation version, and query parameters. Cached mutable values can leak changes between callers; prefer immutable snapshots or defensive copies. Treat negative results separately because a short absence lifetime may be appropriate.
Stampedes and concurrent loads¶
When a popular entry expires, many callers may reload it simultaneously. This cache stampede can overload the source. Possible controls include request coalescing, bounded single-flight loading, randomized expiry, refresh-ahead, and serving an explicitly bounded stale value. Each needs a timeout and a policy for loader failure; a lock held around an external call can become another outage.
Spring method caching¶
Spring's @Cacheable provides declarative method caching through an abstraction.
In the common proxy mode, only calls that pass through the proxy are intercepted;
self-invocation does not activate caching. Key expressions, cache manager,
serialization, expiry, and eviction remain application decisions. Never assume
that adding the annotation defines consistency.
@Cacheable(cacheNames = "catalog", key = "#sku")
public ProductView findProduct(String sku) {
return catalogClient.fetch(sku);
}
Test and observe¶
- test miss, hit, expiry, eviction, invalidation, and source failure;
- exercise concurrent misses for the same key;
- measure hit ratio, load latency, evictions, size, and load failures;
- avoid user IDs or cache keys as unbounded metric labels;
- verify that cache failure degrades according to the stated contract.
See the official Spring cache abstraction.