DEV Community

Cover image for Keep It Synchronized: Distributed Locks in NestJS with RedisX
Suren Krmoian
Suren Krmoian

Posted on

Keep It Synchronized: Distributed Locks in NestJS with RedisX

Distributed locking can be a lifesaver when you're juggling multiple instances of an application. You know how it goes—everyone wants a piece of the pie, and without some rules, things get messy. Let’s talk about how to keep the peace with distributed locks in NestJS using RedisX. We'll focus on making sure those locks don't run out of steam and how to prevent deadlocks.

The Need for Distributed Locks

Imagine this: multiple instances of your app trying to update the same data at the same time. Chaos, right? Race conditions, data getting all tangled up, maybe even your system throwing a fit. Distributed locks are like the bouncers of your code, making sure only one instance gets through the door to that critical section at a time.

RedisX to the Rescue

NestJS RedisX is the toolkit you didn't know you needed for handling Redis in your app. It’s got this great @nestjs-redisx/locks package that makes setting up Redis-backed locks a breeze. Auto-renewal? Check. Deadlock prevention? Double check. These features are your safety net in a production environment.

Getting Started with RedisX Locks

First things first, you gotta install some packages:

npm install @nestjs-redisx/core @nestjs-redisx/locks
Enter fullscreen mode Exit fullscreen mode

Next up, configure your Redis module in AppModule like so:

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { RedisModule } from '@nestjs-redisx/core';
import { LocksPlugin } from '@nestjs-redisx/locks';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    RedisModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      plugins: [
        LocksPlugin.registerAsync({
          imports: [ConfigModule],
          inject: [ConfigService],
          useFactory: (config: ConfigService) => ({
            defaultTtl: config.get('LOCK_DEFAULT_TTL', 30000),
            retry: {
              maxRetries: config.get('LOCK_MAX_RETRIES', 3),
              initialDelay: config.get('LOCK_RETRY_DELAY', 100),
            },
            autoRenew: { enabled: config.get('LOCK_AUTO_RENEW', true) },
          }),
        }),
      ],
      useFactory: (config: ConfigService) => ({
        clients: {
          type: 'single',
          host: config.get('REDIS_HOST', 'localhost'),
          port: config.get('REDIS_PORT', 6379),
        },
      }),
    }),
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

Using Locks in Your Services

Now, let's put those locks to work. With RedisX set up, you can use the @WithLock decorator to handle locks around your service methods. It does the heavy lifting of acquiring and releasing locks for you.

import { Injectable } from '@nestjs/common';
import { WithLock } from '@nestjs-redisx/locks';

@Injectable()
export class PaymentService {
  @WithLock({ key: 'payment:{0}', ttl: 10000 })
  async processPayment(orderId: string): Promise<Payment> {
    const order = await this.orderRepository.findById(orderId);
    if (order.status === 'paid') {
      throw new PaymentAlreadyProcessedError(orderId);
    }
    const result = await this.paymentGateway.charge(order);
    await this.orderRepository.update(orderId, { status: 'paid' });
    return result;
  }
}
Enter fullscreen mode Exit fullscreen mode

Keeping It Smooth with Auto-Renewal and Deadlocks

RedisX’s lock plugin is smart about keeping locks alive during long operations. Auto-renewal extends the lock's TTL as needed. Plus, if something goes wrong and the lock-holder crashes, deadlock prevention ensures the lock will eventually expire, letting others have a go.

Wrapping It Up

Using RedisX for distributed locking in NestJS not only makes life easier but also boosts your system's reliability. You get to focus on the fun parts of building, while RedisX takes care of the nitty-gritty of keeping things in order. Happy coding!

Top comments (0)