Laravel Caching: Tags, Stampede Locks &amp; Cache-Aside | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Laravel Caching Strategies: Tags, Stampede Prevention, and Cache-Aside at Scale        On this page       1. [  Beyond remember(): Production Caching in Laravel ](#beyond-coderemembercode-production-caching-in-laravel)
2. [  1. Cache Tags for Granular Invalidation ](#1-cache-tags-for-granular-invalidation)
3. [  2. Preventing Cache Stampedes with Atomic Locks ](#2-preventing-cache-stampedes-with-atomic-locks)
4. [  3. The Cache-Aside Pattern in a Service Class ](#3-the-cache-aside-pattern-in-a-service-class)
5. [  Key Takeaways ](#key-takeaways)

  ![Laravel Caching Strategies: Tags, Stampede Prevention, and Cache-Aside at Scale](https://cdn.msaied.com/351/ac186f395c8b0f7ae2e8ff230c61e323.png)

  #laravel   #caching   #performance   #redis  

 Laravel Caching Strategies: Tags, Stampede Prevention, and Cache-Aside at Scale 
=================================================================================

     3 Jul 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Beyond remember(): Production Caching in Laravel  ](#beyond-coderemembercode-production-caching-in-laravel)
2. [  02   1. Cache Tags for Granular Invalidation  ](#1-cache-tags-for-granular-invalidation)
3. [  03   2. Preventing Cache Stampedes with Atomic Locks  ](#2-preventing-cache-stampedes-with-atomic-locks)
4. [  04   3. The Cache-Aside Pattern in a Service Class  ](#3-the-cache-aside-pattern-in-a-service-class)
5. [  05   Key Takeaways  ](#key-takeaways)

 Beyond `remember()`: Production Caching in Laravel
--------------------------------------------------

Most Laravel applications start with `Cache::remember()` and never look back. That works until you hit real traffic, shared cache keys across tenants, or a deployment that invalidates half your cache simultaneously. This article covers three concrete techniques that separate hobby caching from production caching.

---

1. Cache Tags for Granular Invalidation
---------------------------------------

Cache tags let you group related entries and flush them as a unit without touching unrelated keys. They require a tag-aware driver — Redis or Memcached.

```php
// Storing tagged entries
Cache::tags(['products', 'tenant:42'])->put(
    "product:{$product->id}",
    $product,
    now()->addHour()
);

// Retrieve with the same tag set
$product = Cache::tags(['products', 'tenant:42'])
    ->get("product:{$product->id}");

// Flush only tenant 42's product cache on a write
Cache::tags(['tenant:42', 'products'])->flush();

```

The key insight: tag `tenant:{id}` on every cache write scoped to that tenant. A single `flush()` call after a bulk import clears exactly what needs clearing — nothing more.

> **Gotcha:** `Cache::tags()->flush()` in Redis does not delete keys immediately; it invalidates the tag reference, making tagged keys unreachable. Key memory is reclaimed by Redis's eviction policy. Monitor `used_memory` if you flush large tag sets frequently.

---

2. Preventing Cache Stampedes with Atomic Locks
-----------------------------------------------

A stampede happens when a hot key expires and dozens of workers simultaneously miss the cache, all hitting the database at once. Laravel's `Cache::lock()` solves this cleanly.

```php
use Illuminate\Support\Facades\Cache;

function getExpensiveReport(int $id): array
{
    $key = "report:{$id}";
    $cached = Cache::get($key);

    if ($cached !== null) {
        return $cached;
    }

    // Only one worker rebuilds; others wait up to 5 seconds
    return Cache::lock("lock:{$key}", 10)->block(5, function () use ($key, $id) {
        // Re-check after acquiring the lock
        if ($hit = Cache::get($key)) {
            return $hit;
        }

        $data = DB::table('reports')->where('id', $id)->first();
        Cache::put($key, $data, now()->addMinutes(30));

        return $data;
    });
}

```

The double-check inside the lock is mandatory. Without it, every worker that was queued behind the lock rebuilds the cache anyway.

`block(5)` throws `LockTimeoutException` if the lock is not acquired within 5 seconds — catch it and fall back to a direct DB read rather than letting the request fail.

---

3. The Cache-Aside Pattern in a Service Class
---------------------------------------------

Inlining cache logic in controllers couples your caching strategy to your HTTP layer. Extract it into a dedicated repository or service:

```php
final class CachedProductRepository
{
    public function __construct(
        private readonly ProductRepository $inner,
        private readonly Repository $cache,
    ) {}

    public function findById(int $id): ?Product
    {
        return $this->cache
            ->tags(['products'])
            ->remember(
                "product:{$id}",
                now()->addMinutes(60),
                fn () => $this->inner->findById($id)
            );
    }

    public function invalidate(int $id): void
    {
        $this->cache->tags(['products'])->forget("product:{$id}");
    }
}

```

Bind it in a service provider:

```php
$this->app->bind(ProductRepository::class, function ($app) {
    return new CachedProductRepository(
        inner: $app->make(EloquentProductRepository::class),
        cache: $app->make('cache.store'),
    );
});

```

Now your controllers depend on `ProductRepository` — the caching layer is invisible to them and trivially swappable in tests.

---

Key Takeaways
-------------

- **Cache tags** enable surgical invalidation; always tag by tenant and entity type in multi-tenant apps.
- **Atomic locks with `block()`** prevent stampedes; always re-check the cache after acquiring the lock.
- **Cache-aside in a dedicated class** keeps controllers clean and makes the caching strategy testable in isolation.
- Catch `LockTimeoutException` and degrade gracefully — never let a lock failure become a 500.
- Prefer `now()->addMinutes()` over integer TTLs; it reads clearly and respects `Carbon` timezone config.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-caching-strategies-tags-stampede-prevention-and-cache-aside-at-scale-1&text=Laravel+Caching+Strategies%3A+Tags%2C+Stampede+Prevention%2C+and+Cache-Aside+at+Scale) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-caching-strategies-tags-stampede-prevention-and-cache-aside-at-scale-1) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  Which Laravel cache drivers support cache tags?        Only Redis and Memcached support cache tags. The file, database, and array drivers do not. If you call Cache::tags() on an unsupported driver, Laravel throws a BadMethodCallException at runtime. 

      Q02  What happens if the lock owner crashes before releasing the lock?        Laravel's cache locks accept a TTL (the first argument to Cache::lock()). If the owning process dies, the lock expires automatically after that many seconds, allowing another worker to acquire it. Always set a realistic TTL — slightly longer than your worst-case rebuild time. 

      Q03  Should I cache Eloquent models or plain arrays?        Prefer plain arrays or simple DTOs. Caching Eloquent models serializes relationship state, which can be stale or unexpectedly large. Reconstructing a model from a cached array via Model::make() or a DTO factory is safer and produces smaller cache payloads. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![Cursor Pagination, Chunked Iteration, and Lazy Collections at Scale in Laravel](https://cdn.msaied.com/355/3a6df23a2c16b740843260134fad7c63.png) laravel eloquent performance 

### Cursor Pagination, Chunked Iteration, and Lazy Collections at Scale in Laravel

Offset pagination breaks under large datasets. Learn how cursor pagination, chunked iteration, and lazy collec...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 3 Jul 2026     4 min read  

  Read    

 ](https://msaied.com/articles/cursor-pagination-chunked-iteration-and-lazy-collections-at-scale-in-laravel-1) [ ![Job Batching, Chaining, and Rate-Limited Middleware in Laravel Queues](https://cdn.msaied.com/353/89d47dc6b618d5435f9d7f333b75e922.png) laravel queues jobs 

### Job Batching, Chaining, and Rate-Limited Middleware in Laravel Queues

Go beyond basic dispatch: learn how to compose Laravel job batches with callbacks, chain dependent jobs safely...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 3 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/job-batching-chaining-and-rate-limited-middleware-in-laravel-queues-2) [ ![Laravel Reverb: Building Presence Channels with Per-User State and Typed Events](https://cdn.msaied.com/352/9b3c490b8303fdc84442671965a3ee8a.png) laravel reverb websockets 

### Laravel Reverb: Building Presence Channels with Per-User State and Typed Events

Presence channels in Laravel Reverb go far beyond simple pub/sub. Learn how to track per-user state, broadcast...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 3 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/laravel-reverb-building-presence-channels-with-per-user-state-and-typed-events) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
