2.3 KiB
Caching Best Practices
Use Cache::remember() Instead of Manual Get/Put
Atomic pattern prevents race conditions and removes boilerplate.
Incorrect:
$val = Cache::get('stats');
if (! $val) {
$val = $this->computeStats();
Cache::put('stats', $val, 60);
}
Correct:
$val = Cache::remember('stats', 60, fn () => $this->computeStats());
Use Cache::flexible() for Stale-While-Revalidate
On high-traffic keys, one user always gets a slow response when the cache expires. flexible() serves slightly stale data while refreshing in the background.
Incorrect: Cache::remember('users', 300, fn () => User::all());
Correct: Cache::flexible('users', [300, 600], fn () => User::all()); — fresh for 5 min, stale-but-served up to 10 min, refreshes via deferred function.
Use Cache::memo() to Avoid Redundant Hits Within a Request
If the same cache key is read multiple times per request (e.g., a service called from multiple places), memo() stores the resolved value in memory.
Cache::memo()->get('settings'); — 5 calls = 1 Redis round-trip instead of 5.
Use Cache Tags to Invalidate Related Groups
Without tags, invalidating a group of entries requires tracking every key. Tags let you flush atomically. Only works with redis, memcached, dynamodb — not file or database.
Cache::tags(['user-1'])->flush();
Use Cache::add() for Atomic Conditional Writes
add() only writes if the key does not exist — atomic, no race condition between checking and writing.
Incorrect: if (! Cache::has('lock')) { Cache::put('lock', true, 10); }
Correct: Cache::add('lock', true, 10);
Use once() for Per-Request Memoization
once() memoizes a function's return value for the lifetime of the object (or request for closures). Unlike Cache::memo(), it doesn't hit the cache store at all — pure in-memory.
public function roles(): Collection
{
return once(fn () => $this->loadRoles());
}
Multiple calls return the cached result without re-executing. Use once() for expensive computations called multiple times per request. Use Cache::memo() when you also want cross-request caching.
Configure Failover Cache Stores in Production
If Redis goes down, the app falls back to a secondary store automatically.
'failover' => ['driver' => 'failover', 'stores' => ['redis', 'database']],