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. [  Why Basic Caching Breaks Under Load ](#why-basic-caching-breaks-under-load)
2. [  Tagged Caches for Precise Invalidation ](#tagged-caches-for-precise-invalidation)
3. [  Scoping Tags to Tenants ](#scoping-tags-to-tenants)
4. [  Preventing Cache Stampedes with Atomic Locks ](#preventing-cache-stampedes-with-atomic-locks)
5. [  The Cache-Aside Pattern with Eloquent ](#the-cache-aside-pattern-with-eloquent)
6. [  Probabilistic Early Expiry (XFetch) ](#probabilistic-early-expiry-xfetch)
7. [  Takeaways ](#takeaways)

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

  #laravel   #caching   #performance   #redis  

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

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

       Table of contents

1. [  01   Why Basic Caching Breaks Under Load  ](#why-basic-caching-breaks-under-load)
2. [  02   Tagged Caches for Precise Invalidation  ](#tagged-caches-for-precise-invalidation)
3. [  03   Scoping Tags to Tenants  ](#scoping-tags-to-tenants)
4. [  04   Preventing Cache Stampedes with Atomic Locks  ](#preventing-cache-stampedes-with-atomic-locks)
5. [  05   The Cache-Aside Pattern with Eloquent  ](#the-cache-aside-pattern-with-eloquent)
6. [  06   Probabilistic Early Expiry (XFetch)  ](#probabilistic-early-expiry-xfetch)
7. [  07   Takeaways  ](#takeaways)

 Why Basic Caching Breaks Under Load
-----------------------------------

Most Laravel developers start with `Cache::remember()` and call it done. That works fine at low traffic, but three failure modes emerge at scale: **tag-based invalidation becomes impossible**, **cache stampedes** hammer the database when a popular key expires, and **stale reads** sneak in when you skip the cache-aside pattern. Let's fix all three.

---

Tagged Caches for Precise Invalidation
--------------------------------------

Redis and Memcached drivers support cache tags, letting you group related keys and flush them atomically without knowing every individual key.

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

// Flush everything for tenant 42 on plan change
Cache::tags('tenant:42')->flush();

// Flush only product listings, not user data
Cache::tags('products')->flush();

```

> **Warning:** The `database` and `file` drivers do not support tags. If you switch drivers in tests, tag calls silently no-op. Pin your test environment to `redis` or use `array` with a tag-aware wrapper.

### Scoping Tags to Tenants

In a multi-tenant app, prefix every tag with the tenant identifier so a flush never bleeds across boundaries:

```php
final class TenantCache
{
    public function __construct(private readonly int $tenantId) {}

    public function tags(string ...$groups): Repository
    {
        $scoped = array_map(
            fn(string $g) => "tenant:{$this->tenantId}:{$g}",
            $groups
        );
        return Cache::tags($scoped);
    }
}

```

---

Preventing Cache Stampedes with Atomic Locks
--------------------------------------------

When a high-traffic key expires, dozens of workers race to rebuild it simultaneously. The result: a thundering herd that overwhelms your database.

Laravel's `Cache::lock()` gives you an atomic mutex:

```php
function getPopularFeed(int $userId): array
{
    $key = "feed:{$userId}";

    if ($cached = Cache::get($key)) {
        return $cached;
    }

    // Only one worker rebuilds; others wait up to 5 s then re-read
    return Cache::lock("lock:{$key}", seconds: 10)
        ->block(5, function () use ($key, $userId): array {
            // Double-check after acquiring the lock
            if ($cached = Cache::get($key)) {
                return $cached;
            }

            $data = FeedBuilder::for($userId)->build();
            Cache::put($key, $data, now()->addMinutes(5));
            return $data;
        });
}

```

The double-check inside the lock body is critical — without it, every queued worker rebuilds the cache after the first one finishes.

---

The Cache-Aside Pattern with Eloquent
-------------------------------------

Cache-aside means your application — not the cache — is responsible for loading on miss and writing on update. Laravel's `remember()` handles reads, but writes need explicit invalidation:

```php
final class ProductRepository
{
    private const TTL = 3600;

    public function find(int $id): Product
    {
        return Cache::tags('products')->remember(
            "product:{$id}",
            self::TTL,
            fn() => Product::with('variants')->findOrFail($id)
        );
    }

    public function save(Product $product): void
    {
        $product->save();
        // Invalidate immediately; next read repopulates
        Cache::tags('products')->forget("product:{$product->id}");
    }
}

```

Avoid writing to the cache inside `save()` — you risk a race condition where the stale write lands after a concurrent reader has already stored fresh data.

---

Probabilistic Early Expiry (XFetch)
-----------------------------------

For keys that are expensive to rebuild, you can proactively refresh before expiry using the XFetch algorithm — recompute with a probability that increases as the key ages:

```php
function xfetch(string $key, int $ttl, Closure $compute, float $beta = 1.0): mixed
{
    [$value, $expiry, $delta] = Cache::get($key) ?? [null, 0, 0];

    $now = microtime(true);
    if ($value === null || $now - $delta * $beta * log(random_int(1, PHP_INT_MAX) / PHP_INT_MAX) >= $expiry) {
        $start = microtime(true);
        $value = $compute();
        $delta = microtime(true) - $start;
        $expiry = $now + $ttl;
        Cache::put($key, [$value, $expiry, $delta], $ttl + 60);
    }

    return $value;
}

```

This is a niche tool — reach for it only when a stampede lock's blocking latency is unacceptable.

---

Takeaways
---------

- Use **cache tags** to group related keys and flush them by domain concept, not by guessing key names.
- Always **scope tags to tenants** in multi-tenant apps to prevent cross-tenant invalidation.
- Implement the **double-checked lock pattern** with `Cache::lock()->block()` to eliminate stampedes.
- Prefer **cache-aside with explicit invalidation** over writing to the cache inside save operations.
- XFetch is a last resort for extremely expensive computations where blocking is not acceptable.

 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&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) 

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

  3 questions  

     Q01  Does Cache::tags() work with the file or database driver in Laravel?        No. Tag support is only available on the Redis and Memcached drivers. Using tags with the file or database driver will throw a BadMethodCallException. In test environments, use the Redis driver or swap to a tag-aware in-memory store. 

      Q02  What is the difference between a cache stampede lock and a regular mutex?        Cache::lock() in Laravel is backed by an atomic Redis SET NX operation, making it safe across multiple workers and servers. A regular PHP mutex only protects a single process. The block() method lets waiting workers re-use the freshly populated cache value instead of each rebuilding it independently. 

      Q03  When should I write to the cache inside a save method vs. just invalidating?        Prefer invalidation (forget) over writing inside save. Writing risks a race where a stale value overwrites a fresher one set by a concurrent reader. Only write eagerly if cache misses are extremely expensive and you can guarantee ordering — which is rarely possible without additional coordination. 

  Continue reading

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

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

 [ ![Typed Enums as First-Class Domain Citizens in Laravel with PHP 8.3](https://cdn.msaied.com/282/71a8fc3e4cf4239b1bf6d38d57e0b985.png) laravel php8.3 enums 

### Typed Enums as First-Class Domain Citizens in Laravel with PHP 8.3

Go beyond simple enum labels. Learn how to attach behaviour, implement interfaces, and use backed enums as Elo...

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

 24 Jun 2026     3 min read  

  Read    

 ](https://msaied.com/articles/typed-enums-as-first-class-domain-citizens-in-laravel-with-php-83) [ ![RAG in Laravel: pgvector, Embeddings, and Retrieval-Augmented Generation in Practice](https://cdn.msaied.com/281/8d2ac57c0e69d3ff9f1e68faf0e4d10c.png) laravel ai pgvector 

### RAG in Laravel: pgvector, Embeddings, and Retrieval-Augmented Generation in Practice

Build a production-ready RAG pipeline in Laravel using pgvector, OpenAI embeddings, and a clean retrieval laye...

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

 24 Jun 2026     4 min read  

  Read    

 ](https://msaied.com/articles/rag-in-laravel-pgvector-embeddings-and-retrieval-augmented-generation-in-practice) [ ![Ship AI with Laravel: Failover, Queues, and Middleware for AI Agents](https://cdn.msaied.com/283/f0a6d6a6f22d9131bacb96bae1bfc10b.png) Laravel AI Agents Queues 

### Ship AI with Laravel: Failover, Queues, and Middleware for AI Agents

Learn how to make Laravel AI agents production-ready with automatic provider failover, background queue proces...

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

 24 Jun 2026     3 min read  

  Read    

 ](https://msaied.com/articles/ship-ai-with-laravel-failover-queues-and-middleware-for-ai-agents) 

   [  ![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)
