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 thundering-herd stampedes with atomic locks, and implement the cache-aside pattern cleanly in Laravel.

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

ShareCopy linkCopied

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

  On this page +1. [Beyond remember(): Production Caching in Laravel](#beyond-coderemembercode-production-caching-in-laravel)
2. [Cache Tags for Granular Invalidation](#cache-tags-for-granular-invalidation)
3. [Stampede Prevention with Atomic Locks](#stampede-prevention-with-atomic-locks)
4. [The Cache-Aside Pattern as a First-Class Service](#the-cache-aside-pattern-as-a-first-class-service)
5. [Key Takeaways](#key-takeaways)

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

Most Laravel applications start with `Cache::remember()` and never look further. That works until you hit two classic production problems: stale data that can't be selectively invalidated, and a thundering herd that hammers your database the moment a popular key expires. This article tackles both, plus a clean implementation of the cache-aside pattern.

---

### Cache Tags for Granular Invalidation

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

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

// Flush everything for one product on update
Cache::tags("product:{$product->id}")->flush();

// Flush the entire products namespace on bulk import
Cache::tags('products')->flush();

```

Tag granularity is a design decision. Tag too broadly and you over-invalidate; tag too narrowly and you miss stale entries. A useful rule: one tag per aggregate root (`product:{id}`) plus one per collection type (`products`). Flush the aggregate tag on any mutation, the collection tag only on structural changes (create/delete).

---

### Stampede Prevention with Atomic Locks

When a high-traffic key expires, dozens of requests can simultaneously find a cache miss and all query the database. Laravel's `Cache::lock()` solves this with an atomic Redis lock.

```php
function getPopularProducts(): Collection
{
    $key = 'products:popular';
    $ttl = 300;

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

    // Only one process rebuilds; others wait briefly then re-read
    $lock = Cache::lock('lock:' . $key, 10);

    try {
        if ($lock->get()) {
            // Double-check after acquiring the lock
            $cached = Cache::get($key);
            if ($cached !== null) {
                return $cached;
            }

            $data = Product::popular()->get();
            Cache::tags('products')->put($key, $data, $ttl);
            return $data;
        }
    } finally {
        optional($lock)->release();
    }

    // Lock not acquired — serve a short-lived stale value or wait
    return Cache::tags('products')->remember($key, 5, fn () => Product::popular()->get());
}

```

The double-check after lock acquisition is critical. Without it, every queued request rebuilds the cache sequentially after the first one finishes.

---

### The Cache-Aside Pattern as a First-Class Service

Rather than scattering `Cache::remember()` calls across controllers and repositories, encapsulate the pattern in a dedicated service.

```php
final class CacheAside
{
    public function __construct(private readonly Repository $cache) {}

    /**
     * @template T
     * @param array $tags
     * @param Closure(): T $loader
     * @return T
     */
    public function get(string $key, array $tags, int $ttl, Closure $loader): mixed
    {
        $store = $tags
            ? $this->cache->tags($tags)
            : $this->cache;

        return $store->remember($key, $ttl, $loader);
    }

    public function invalidate(string ...$tags): void
    {
        $this->cache->tags($tags)->flush();
    }
}

```

Bind it as a singleton and inject it wherever you need caching. This keeps cache key construction and tag management in one place, making it trivial to audit or swap drivers.

```php
// In a repository
public function findPopular(): Collection
{
    return $this->cache->get(
        key: 'products:popular',
        tags: ['products'],
        ttl: 300,
        loader: fn () => Product::popular()->get(),
    );
}

```

---

### Key Takeaways

- **Use cache tags** on Redis/Memcached to enable surgical invalidation per aggregate and per collection.
- **Atomic locks** with a double-check pattern eliminate thundering-herd stampedes on high-traffic keys.
- **Cache-aside as a service** centralises key naming, tag assignment, and TTL policy — don't scatter `remember()` calls.
- Keep TTLs short on collection caches and longer on immutable or slowly-changing aggregate data.
- Always test cache behaviour with `Cache::fake()` in unit tests; use a real Redis instance in integration tests to verify tag flushing.

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

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

  Which cache drivers support tags in Laravel?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 is never acquired in the stampede prevention example?The fallback at the end of the function calls remember() with a very short TTL (5 seconds). This means non-lock-holding requests still get a value quickly, accepting a brief period of redundant queries rather than blocking indefinitely.

   Should cache key construction logic live in the model or a dedicated service?A dedicated service or repository is preferable. Embedding cache keys in Eloquent models couples persistence and caching concerns. A CacheAside service or a repository method keeps the logic in one auditable place and makes it easy to change key schemes without touching models.

   ![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 articleWhenMounted and BigInt Props in Inertia.js v3.8](https://www.msaied.com/public/articles/whenmounted-and-bigint-props-in-inertiajs-v38) [Next articleEloquent Custom Relations: Polymorphic Pivots, Has-One-Of-Many, and Raw Join Relations](https://www.msaied.com/public/articles/eloquent-custom-relations-polymorphic-pivots-has-one-of-many-and-raw-join-relations)  

   On this page
-------------

1. [Beyond remember(): Production Caching in Laravel](#beyond-coderemembercode-production-caching-in-laravel)
2. [Cache Tags for Granular Invalidation](#cache-tags-for-granular-invalidation)
3. [Stampede Prevention with Atomic Locks](#stampede-prevention-with-atomic-locks)
4. [The Cache-Aside Pattern as a First-Class Service](#the-cache-aside-pattern-as-a-first-class-service)
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)
