# Cache Pattern Guide The Cache pattern caches the result of an expensive operation for a configurable duration, reducing load on backends and serving a stale-but-known-good value when a refresh fails. ## When to Use - Expensive lookups (remote configuration, reference data) that don't need to be up-to-the-second - Reducing load on a backend that would otherwise be called on every request - Degrading gracefully to the last known good value when a refresh fails ## When NOT to Use - Data that must always be fresh (e.g. account balances, one-time tokens) - Caching mutable class instances the policy would need to own - prefer interfaces or records for `TResult` - As a substitute for a proper distributed cache when multiple processes must share the same cached value ## Quick Start ```pascal uses Murphy.Policy.Cache; var Policy: ICachePolicy; Value: string; begin Policy := TCacheBuilder .Handle([]) .Expire(TTimeSpan.FromMinutes(5)) .Build; Value := Policy.Execute(function: string begin Result := FetchExpensiveData; end); end; ``` ## Common Scenarios ### Serving Stale Data on Failure ```pascal function GetRemoteConfig: string; var Policy: ICachePolicy; begin Policy := TCacheBuilder .Handle(ENetworkError) // Serve the last cached value on this failure .Expire(TTimeSpan.FromMinutes(5)) .Build; Result := Policy.Execute( function: string begin Result := FetchConfigFromServer; end); end; ``` ### Manually Invalidating the Cache ```pascal var Policy: ICachePolicy; begin Policy := TCacheBuilder.Handle([]).Expire(TTimeSpan.FromMinutes(10)).Build; // Force the next call to refresh, regardless of expiry Policy.Invalidate; end; ``` ### Caching a Record ```pascal type TUserProfile = record Name: string; Email: string; end; var Policy: ICachePolicy; Profile: TUserProfile; begin Policy := TCacheBuilder .Handle(Exception) .Expire(TTimeSpan.FromMinutes(2)) .Build; Profile := Policy.Execute( function: TUserProfile begin Result := FetchUserProfile; end); end; ``` ### One Policy per Cached Value ```pascal type TCacheService = class private FUserCache: ICachePolicy; FSettingsCache: ICachePolicy; public constructor Create; end; constructor TCacheService.Create; begin // Each cached operation needs its own policy instance FUserCache := TCacheBuilder.Handle(Exception).Expire(TTimeSpan.FromMinutes(5)).Build; FSettingsCache := TCacheBuilder.Handle(Exception).Expire(TTimeSpan.FromHours(1)).Build; end; ``` ## Best Practices 1. **One instance per operation** - a `ICachePolicy` caches a single value; don't share one instance across unrelated calls 2. **Filter refresh failures with `Handle()`** - only the exception types passed there fall back to the stale value; anything else re-raises 3. **Prefer interfaces or records for `TResult`** - class instances are not owned or freed by the policy 4. **Choose expiry based on staleness tolerance** - not on how expensive the operation is 5. **Use `Invalidate` after a known write** - force a fresh read right after data changes upstream ## See Also - [Cache API Reference](../API-Reference/Cache-Policy.md) - [Combining Patterns](Combining-Patterns.md) --- [Back to Index](../Home.md)