Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
LaravelPHPJune 22, 20264 min read

Laravel Cache Warming: Predictive Pipelines with Redis Streams

Master Laravel cache warming using Redis Streams and Bloom Filters. Reduce database load and slash latency with this deterministic pre-computation pipeline.

LaravelRedisPerformanceCachingBackendPHP

We’ve all been there: a massive marketing push hits, and your database CPU spikes because your cache invalidation strategy is slightly too aggressive. I recently spent about three days refactoring a dashboard that was suffering from "cold start" latency, where the first request after an invalidation took roughly 450ms. It wasn't just slow; it was unpredictable.

To fix this, we moved away from reactive caching—waiting for a user to request data before computing it—and toward a deterministic cache warming pipeline. By combining Laravel’s event system with Redis Streams and Bloom Filters, we ensured the cache is almost always hot before the user even clicks the link.

Why Reactive Caching Fails at Scale

Reactive caching relies on the "Cache-Aside" pattern. You check the cache, miss, hit the database, and write back. In high-concurrency environments, this causes a stampede. If a popular resource expires, twenty requests might simultaneously trigger a heavy Eloquent query, effectively DDOSing your own read replica.

We initially tried simple job batching, but that didn't account for state. We were re-warming keys that didn't need it or missing keys that were about to become hot. That’s when we pivoted to a predictive model.

Implementing Predictive Cache Warming

To build a deterministic pipeline, you need to track what's "interesting" to the system. We use Bloom filters for efficient membership testing in high-cardinality data to quickly determine if a requested resource is a candidate for pre-computation.

Here is how the architecture looks:

  1. The Observer: A Laravel Event Listener intercepts model updates.
  2. The Stream: We push the "need to warm" signal into a Redis Stream.
  3. The Worker: A long-running Laravel queue worker consumes the stream and executes the heavy lifting.
  4. The Filter: Before warming, we check a Bloom filter to ensure we aren't wasting cycles on data that isn't frequently accessed.

The Redis Stream Implementation

Using Redis Streams (XADD) is superior to standard Laravel queues for this because it allows for consumer groups. If one worker dies, another picks up the exact state of the stream.

PHP
#6A9955">// Inside your Event Listener
use Illuminate\Support\Facades\Redis;

public function handle(ProductUpdated $event)
{
    Redis::xadd('cache-warming-stream', '*', [
        'resource_id' => $event->product->id,
        'type' => 'product_detail'
    ]);
}

By decoupling the "trigger" from the "computation," we ensure that our primary request cycle finishes in milliseconds. The heavy lifting happens asynchronously, and the cache is updated before the next user request hits.

Performance Engineering with Bloom Filters

The biggest risk in cache warming is "over-warming." You don't want to cache every single record in your database. This is where Bloom filters shine. Because they are probabilistic, they occupy very little memory while telling us if a key has a high probability of being requested.

If you’re interested in how to keep your job processing efficient, I’ve previously discussed Laravel Performance Optimization: Building Content-Aware Batching Pipelines to handle these bursts.

When the worker pulls an item from the stream, it performs a membership test:

PHP
if ($this->bloomFilter->exists($productId)) {
    $data = $this->expensiveCalculation($productId);
    Cache::put("product_{$productId}", $data, now()->addHours(2));
}

This prevents the worker from warming obscure, rarely-accessed data, keeping your cache hit ratio high and your memory usage predictable.

Trade-offs and Lessons Learned

We initially tried using standard Redis Sets to track hot keys, but the memory footprint grew too fast as our user base expanded. The Bloom filter approach reduced our memory overhead by about 70% compared to a standard set.

One caveat: Bloom filters have a false-positive rate. In our case, this is acceptable—the worst-case scenario is we cache a key that isn't actually hot, which is far better than the alternative of a slow database query during peak traffic.

If you're managing complex job dependencies, you might also find Laravel Job Queuing: Architecting Weighted Fair Queuing with Redis useful for prioritizing these warm-up jobs over background cleanup tasks.

Frequently Asked Questions

Does this increase Redis memory usage? Yes, but minimally. A Bloom filter with 100,000 items and a 1% error rate takes up roughly 120KB. Compare that to the several megabytes of RAM needed for a standard PHP array or Redis Set.

What happens if the Redis Stream grows too large? Use XTRIM to cap the stream length. We currently cap ours at 50,000 entries. Anything older than that is likely stale anyway.

Is this overkill for small apps? Probably. If your database handles the load easily, stick to standard Cache::remember(). This architecture is intended for applications where the cost of a cache miss is measured in seconds of downtime or significant user frustration.

Next time, I want to experiment with using Redis::eval() to handle the Bloom filter logic directly in Lua, which would shave off another 2–3ms of network round-trip time. It’s a minor gain, but at scale, those milliseconds add up to a much smoother experience.

Back to Blog

Similar Posts

LaravelPHPJune 22, 20264 min read

Laravel Middleware Request Collapsing for High-Concurrency APIs

Master Laravel middleware request collapsing to solve high-concurrency bottlenecks. Learn to implement deterministic memoization and batching for faster APIs.

Read more
LaravelPHP
June 22, 2026
4 min read

Laravel Job Queuing: Architecting Weighted Fair Queuing with Redis

Laravel Job Queuing often struggles with priority starvation. Learn how to architect a Weighted Fair Queuing system using Redis Sorted Sets for better throughput.

Read more
LaravelPHPJune 21, 20264 min read

Laravel Performance Optimization: Building Content-Aware Batching Pipelines

Laravel performance optimization through deferred execution and content-aware request batching. Learn to handle high-concurrency APIs without bottlenecking.

Read more