Laravel cookbook · Intermediate
Cache expensive data with stale-while-revalidate
Cache expiry causes a stampede of slow recomputations at the same moment.
Approach
Use Cache::flexible(): values are fresh for the first period, then served stale while being recomputed in the
background (after the response), until the second period expires. Use Cache::remember for simple TTLs and
invalidate explicitly on writes.
Solution
<?php
namespace App\Queries;
use App\Models\Order;
use Illuminate\Support\Facades\Cache;
class DashboardStats
{
public function get(int $teamId): array
{
// Fresh for 5 minutes; between 5 and 30 minutes, serve stale and refresh after the response.
return Cache::flexible("dashboard:team:{$teamId}", [300, 1800], fn () => [
'revenue' => Order::where('team_id', $teamId)->where('status', 'paid')->sum('total_cents'),
'orders' => Order::where('team_id', $teamId)->count(),
]);
}
public function forget(int $teamId): void
{
Cache::forget("dashboard:team:{$teamId}");
}
}
Pitfalls
- Caching per-user data under a shared key.
- Using the
filecache with several servers; use Redis or the database store. - Caching Eloquent models with loaded relations, which serializes large object graphs; cache arrays or IDs.
Variations
Cache::lock()around recomputation prevents stampedes forremembercaches.- Use cache tags (Redis) to flush groups of related keys.
Further reading: Stale while revalidate caching
Reference: https://laravel.com/docs/13.x/cache#swr
More Laravel recipes
- Authorize actions with policies
- Call external APIs with timeouts, retries and fakes
- Catch N+1 queries with strict Eloquent mode
- Delete old records automatically with prunable models
- Dispatch events and listeners after the transaction commits
- Issue scoped API tokens with Sanctum
- Make queued jobs reliable with retries, backoff and uniqueness
- Prevent race conditions with transactions and row locks