Laravel Caching: Tags, Stampede Locks &amp; Cache-Aside | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://www.msaied.com/public) - [Home](https://www.msaied.com/public)
- [Projects](https://www.msaied.com/public/projects)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [About](https://www.msaied.com/public#about)

           [  Contact](https://www.msaied.com/public#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://www.msaied.com/public)
- [ProjectsCase studies](https://www.msaied.com/public/projects)
- [ArticlesEngineering notes](https://www.msaied.com/public/articles)
- [CertificatesCredentials](https://www.msaied.com/public/certificates)
- [AboutHow I work](https://www.msaied.com/public#about)
- [ContactGet in touch](https://www.msaied.com/public#contact)

  [Start a conversation](https://www.msaied.com/public#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. Laravel Caching Strategies: Tags, Stampede Prevention, and Cache-Aside at Scale

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

 Beyond simple remember() calls: how to use cache tags for granular invalidation, prevent cache stampedes with atomic locks, and implement the cache-aside pattern cleanly in Laravel services.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://www.msaied.com/public#person) Published 3 Jul 2026 · Updated 3 Jul 2026 · 3 min read

ShareCopy linkCopied

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

  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)

 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.

- [laravel](https://www.msaied.com/public/articles?search=laravel)
- [caching](https://www.msaied.com/public/articles?search=caching)
- [performance](https://www.msaied.com/public/articles?search=performance)
- [redis](https://www.msaied.com/public/articles?search=redis)

 Frequently asked questions 
---------------------------

  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.

   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.

   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.

   ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)About the author
----------------

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

[About](https://www.msaied.com/public#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleEnvKit: A Free Local Development Stack for Laravel on Windows and macOS](https://www.msaied.com/public/articles/envkit-a-free-local-development-stack-for-laravel-on-windows-and-macos) [Next articleLaravel Reverb: Building Presence Channels with Per-User State and Typed Events](https://www.msaied.com/public/articles/laravel-reverb-building-presence-channels-with-per-user-state-and-typed-events)  

   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)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://www.msaied.com/public#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/740/cce86edc21eddcbdd2f2454fadaf9c70.png)  · 3 min read### The Pipeline Pattern in Laravel: Custom Pipelines Beyond Middleware

5 Oct 2026 ](https://www.msaied.com/public/articles/the-pipeline-pattern-in-laravel-custom-pipelines-beyond-middleware-1) [ ![](https://cdn.msaied.com/739/2d6897fdcdcf090613f96f72a64b8a78.png)  · 4 min read### MySQL Full-Text Search in Laravel: Indexes, Relevance Scoring, and Boolean Mode

4 Oct 2026 ](https://www.msaied.com/public/articles/mysql-full-text-search-in-laravel-indexes-relevance-scoring-and-boolean-mode) [ ![](https://cdn.msaied.com/738/073696a3fefe18bec825beec5ac658f5.png)  · 4 min read### Laravel Queue Rate-Limited Middleware: Throttling Jobs Without Losing Work

4 Oct 2026 ](https://www.msaied.com/public/articles/laravel-queue-rate-limited-middleware-throttling-jobs-without-losing-work) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://www.msaied.com/public#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://www.msaied.com/public/sitemap.xml)
