npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nestjs-pg-cron-lock

v0.1.2

Published

Distributed cron job locking for NestJS using PostgreSQL advisory locks. Ensures only one instance runs a cron job across multiple servers. No Redis required.

Readme

nestjs-pg-cron-lock

Distributed cron job locking for NestJS using PostgreSQL advisory locks. Ensures only one instance runs a cron job across multiple servers. No Redis required.

The Problem

When running multiple instances of a NestJS app (e.g. behind a load balancer), every instance executes every @Cron() job. This means duplicate emails, double-processing, and race conditions.

Without this package, you write this in every service:

@Cron(CronExpression.EVERY_MINUTE)
async processOrders() {
  const queryRunner = this.dataSource.createQueryRunner();
  await queryRunner.connect();
  try {
    await queryRunner.startTransaction();
    const [{ locked }] = await queryRunner.query(
      `SELECT pg_try_advisory_xact_lock($1) AS locked`,
      [839271],
    );
    if (!locked) {
      await queryRunner.commitTransaction();
      return;
    }
    // ... actual business logic ...
    await queryRunner.commitTransaction();
  } catch (error) {
    await queryRunner.rollbackTransaction();
  } finally {
    await queryRunner.release();
  }
}

With this package, one decorator:

@Cron(CronExpression.EVERY_MINUTE)
@WithAdvisoryLock('process-orders')
async processOrders() {
  // just your business logic
}

Installation

npm install nestjs-pg-cron-lock

Setup

Import AdvisoryLockModule in your root module. It auto-discovers the TypeORM DataSource from your app:

import { AdvisoryLockModule } from 'nestjs-pg-cron-lock';

@Module({
  imports: [
    TypeOrmModule.forRoot({ ... }),
    AdvisoryLockModule,
  ],
})
export class AppModule {}

Usage

Decorator (recommended)

Add @WithAdvisoryLock() to any method, typically alongside @Cron():

import { Cron, CronExpression } from '@nestjs/schedule';
import { WithAdvisoryLock } from 'nestjs-pg-cron-lock';

@Injectable()
export class OrdersService {
  @Cron(CronExpression.EVERY_MINUTE)
  @WithAdvisoryLock('process-pending-orders')
  async processPendingOrders() {
    // Only one instance runs this, even with 10 replicas
  }

  @Cron(CronExpression.EVERY_10_MINUTES)
  @WithAdvisoryLock('send-reminder-emails')
  async sendReminderEmails() {
    // Same here, no duplicates
  }
}

String keys are auto-hashed to PostgreSQL lock IDs. You can also pass a numeric ID directly:

@WithAdvisoryLock(839271)  // explicit lock ID

Programmatic

For more control, inject AdvisoryLockService directly:

import { AdvisoryLockService } from 'nestjs-pg-cron-lock';

@Injectable()
export class PaymentsService {
  constructor(private readonly advisoryLockService: AdvisoryLockService) {}

  @Cron(CronExpression.EVERY_MINUTE)
  async processPayments() {
    await this.advisoryLockService.withLock(
      AdvisoryLockService.hashKey('process-payments'),
      async () => {
        // protected logic
      },
    );
  }
}

How It Works

  1. Before your method runs, a PostgreSQL transaction is started
  2. pg_try_advisory_xact_lock(lockId) attempts to acquire the lock. This is non-blocking
  3. If the lock is held by another instance, the method is skipped silently
  4. If acquired, your method runs. The lock is released when the transaction ends

Advisory locks are scoped to the database connection, so they work across any number of app instances connected to the same PostgreSQL database. No additional infrastructure needed.

API

@WithAdvisoryLock(key: string | number)

Method decorator. Wraps the method in an advisory lock.

  • String key: Auto-hashed to a 32-bit integer using FNV-1a
  • Numeric key: Used directly as the PostgreSQL advisory lock ID

AdvisoryLockService

Injectable service for programmatic usage.

  • withLock(lockId: number, fn: () => Promise<void>): Promise<void> - Acquire lock and run callback
  • static hashKey(key: string): number - Hash a string to a lock ID

AdvisoryLockModule

Global module. Import once in your root module. Requires TypeORM DataSource to be available.

Requirements

  • NestJS 10+ or 11+
  • TypeORM 0.3+
  • PostgreSQL

License

MIT