Laravel cookbook · Intermediate

Cache expensive data with stale-while-revalidate

Cache expiry causes a stampede of slow recomputations at the same moment.

Create a free account to save this page, take notes and track your progress. Sign in

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 file cache 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 for remember caches.
  • 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

Was this page helpful?