-
Notifications
You must be signed in to change notification settings - Fork 1
Cache
Marco Breveglieri edited this page Jul 20, 2026
·
1 revision
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.
- 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
- 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
uses Murphy.Policy.Cache;
var
Policy: ICachePolicy<string>;
Value: string;
begin
Policy := TCacheBuilder<string>
.Handle([])
.Expire(TTimeSpan.FromMinutes(5))
.Build;
Value := Policy.Execute(function: string begin Result := FetchExpensiveData; end);
end;function GetRemoteConfig: string;
var
Policy: ICachePolicy<string>;
begin
Policy := TCacheBuilder<string>
.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;var Policy: ICachePolicy<string>;
begin
Policy := TCacheBuilder<string>.Handle([]).Expire(TTimeSpan.FromMinutes(10)).Build;
// Force the next call to refresh, regardless of expiry
Policy.Invalidate;
end;type
TUserProfile = record
Name: string;
Email: string;
end;
var
Policy: ICachePolicy<TUserProfile>;
Profile: TUserProfile;
begin
Policy := TCacheBuilder<TUserProfile>
.Handle(Exception)
.Expire(TTimeSpan.FromMinutes(2))
.Build;
Profile := Policy.Execute(
function: TUserProfile
begin
Result := FetchUserProfile;
end);
end;type
TCacheService = class
private
FUserCache: ICachePolicy<string>;
FSettingsCache: ICachePolicy<string>;
public
constructor Create;
end;
constructor TCacheService.Create;
begin
// Each cached operation needs its own policy instance
FUserCache := TCacheBuilder<string>.Handle(Exception).Expire(TTimeSpan.FromMinutes(5)).Build;
FSettingsCache := TCacheBuilder<string>.Handle(Exception).Expire(TTimeSpan.FromHours(1)).Build;
end;-
One instance per operation - a
ICachePolicy<TResult>caches a single value; don't share one instance across unrelated calls -
Filter refresh failures with
Handle()- only the exception types passed there fall back to the stale value; anything else re-raises -
Prefer interfaces or records for
TResult- class instances are not owned or freed by the policy - Choose expiry based on staleness tolerance - not on how expensive the operation is
-
Use
Invalidateafter a known write - force a fresh read right after data changes upstream