DEV Community

Cover image for Taming Cache Stampede in NestJS with RedisX
Suren Krmoian
Suren Krmoian

Posted on

Taming Cache Stampede in NestJS with RedisX

Taming Cache Stampede in NestJS with RedisX

Cache stampede, or the thundering herd problem, is that pesky issue when a cache expires, and suddenly, your database gets bombarded by a swarm of requests. Not fun. In NestJS, there's a nifty way to tackle this using NestJS RedisX.

What's the Deal with Cache Stampede?

Imagine this: your app’s cache entry expires. Every request that comes in wants to hit the database at the same time. Chaos, right? The trick here is to make sure only one request actually goes through to the database, and the rest just chill until the first request updates the cache.

Enter NestJS RedisX

NestJS RedisX is like a Swiss Army knife for Redis in NestJS. One of its coolest features? Built-in cache stampede protection.

Setting It Up

Let's see how to set this up and save your database from overload.

  1. Get the Packages First thing, install RedisX and the cache plugin:
   npm install @nestjs-redisx/core @nestjs-redisx/cache
Enter fullscreen mode Exit fullscreen mode
  1. Configure Redis in Your AppModule Use RedisModule.forRootAsync to set things up with async config (thanks to ConfigService):
   import { Module } from '@nestjs/common';
   import { ConfigModule, ConfigService } from '@nestjs/config';
   import { RedisModule } from '@nestjs-redisx/core';
   import { CachePlugin } from '@nestjs-redisx/cache';

   @Module({
     imports: [
       ConfigModule.forRoot({ isGlobal: true }),
       RedisModule.forRootAsync({
         imports: [ConfigModule],
         inject: [ConfigService],
         plugins: [
           CachePlugin.registerAsync({
             inject: [ConfigService],
             useFactory: (config: ConfigService) => ({
               l1: { enabled: true, maxSize: 1000, ttl: 60 },
               defaultTtl: config.get('CACHE_TTL', 300),
               stampede: { enabled: true },
             }),
           }),
         ],
         useFactory: (config: ConfigService) => ({
           clients: {
             host: config.get('REDIS_HOST', 'localhost'),
             port: config.get('REDIS_PORT', 6379),
           },
         }),
       }),
     ],
   })
   export class AppModule {}
Enter fullscreen mode Exit fullscreen mode
  1. Decorate Your Services with @Cached Now, slap the @Cached decorator on your service methods that fetch data:
   import { Injectable } from '@nestjs/common';
   import { Cached } from '@nestjs-redisx/cache';

   @Injectable()
   export class ProductService {
     @Cached({ key: 'product:{0}', ttl: 3600, stampede: { enabled: true } })
     async getProduct(id: string): Promise<Product> {
       return this.db.findProduct(id);
     }
   }
Enter fullscreen mode Exit fullscreen mode

The Magic Behind It

With the @Cached decorator and the stampede option enabled, your app ensures that only one request hits the database. The rest wait their turn, and everyone gets their data once the cache is updated. It’s like a well-behaved queue at the supermarket checkout.

NestJS RedisX doesn't just handle cache stampedes. It's a full-on toolkit integrating Redis with features like distributed locks, rate limiting, and observability tools. Dive deeper into the official docs or check out the GitHub repo for more.

Top comments (0)