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

@beignet/provider-locks-redis

v0.0.52

Published

Redis-backed lock and lease provider for Beignet

Readme

@beignet/provider-locks-redis

Runtime: Beignet requires Node.js 22.12 or newer. Bun is optional.

Redis-backed LocksPort provider for Beignet applications.

The provider installs ctx.ports.locks using ioredis and Redis lease semantics:

  • acquire the lease and fencing token atomically with Redis Lua
  • renew only when the stored owner token matches
  • release only when the stored owner token matches
  • optional fencing tokens from Redis INCR inside the acquire script

createRedisLocksProvider(...) returns the stable RedisLocksProvider type. RedisLocksConfig describes its validated config; the Zod schema remains internal.

Install

bun add @beignet/provider-locks-redis @beignet/core ioredis

Register

// server/providers.ts
import { createRedisLocksProvider } from "@beignet/provider-locks-redis";

export const providers = [
  createRedisLocksProvider({
    prefix: "my-app:locks",
  }),
];

Set REDIS_LOCKS_URL for the default env-backed provider. You can also pass an existing Redis-compatible client to createRedisLocksProvider({ client }). Environment variables:

  • REDIS_LOCKS_URL (required unless you pass client)
  • REDIS_LOCKS_DB
  • REDIS_LOCKS_PREFIX
  • REDIS_LOCKS_CONNECT_TIMEOUT_MS
  • REDIS_LOCKS_SHUTDOWN_TIMEOUT_MS
  • REDIS_LOCKS_MAX_RETRIES_PER_REQUEST
  • REDIS_LOCKS_CONNECT_MAX_ATTEMPTS

Environment-backed numeric values must be non-negative integer strings. Pass numbers to the matching db, connectTimeoutMs, shutdownTimeoutMs, and maxRetriesPerRequest factory options. Both forms require JavaScript safe integers. Connection and shutdown timeouts cannot exceed 2,147,483,647 milliseconds, the runtime timer ceiling; shutdown timeouts must be positive. Lease waitMs must be an integer from 0 through that ceiling; retryDelayMs must be an integer from 1 through that ceiling.

beignet doctor --strict checks that installed Redis locks providers are registered in server/providers.ts and that REDIS_LOCKS_URL is present in app env examples or config when the env-backed provider is used.

Lease owner tokens use Web Crypto randomUUID() or getRandomValues() when available and securely fall back to node:crypto on supported Node runtimes. The direct createRedisLocks(...) adapter accepts createOwnerToken for deterministic tests; production wiring should keep the secure runtime default.

Use

const result = await ctx.ports.locks.acquire("schedule:daily-report", {
  ttlMs: 60_000,
  waitMs: 0,
});

if (!result.acquired) return;

try {
  await runDailyReport(ctx);
} finally {
  await result.lease.release();
}

Or use withLease(...):

await ctx.ports.locks.withLease(
  "outbox:drain",
  { ttlMs: 30_000, waitMs: 5_000 },
  async ({ lease }) => {
    await drainOutbox(ctx, { fencingToken: lease.fencingToken });
  },
);

Restore a handle in a later invocation with the persisted owner token and an explicit renewal TTL:

const lease = ctx.ports.locks.restore(key, ownerToken, {
  ttlMs: 60_000,
  expiresAt: persistedExpiresAt,
  fencingToken: persistedFencingToken,
});

Omit expiry or fencing metadata when it was not persisted; the adapter does not invent either value. Redis still verifies the owner token atomically on renew and release.

API

createRedisLocks(options)

Creates a LocksPort from a Redis-compatible client. Use this for tests or custom provider composition.

createRedisLocksProvider(options)

Creates a Beignet lifecycle provider that contributes:

  • ctx.ports.locks, the standard Beignet LocksPort
  • ctx.ports.redisLocks, an escape hatch with the raw Redis client, prefix, and checkHealth() helper

createRedisLocksProvider()

Ready-to-register provider using REDIS_LOCKS_* environment variables.

Devtools

When @beignet/devtools or another provider instrumentation sink is installed before this provider, lock acquire, renew, release, and skipped-acquire activity appears under the Locks watcher. Instrumentation includes the lock key, lease timing, acquisition status, and duration; owner tokens and result payloads are not recorded.

Failure behavior

The env-backed provider throws during startup when REDIS_LOCKS_URL is missing or Redis cannot be reached within the configured connection attempts. Runtime lock operations throw Redis errors so callers can fail or retry explicitly. Provider-owned clients get REDIS_LOCKS_SHUTDOWN_TIMEOUT_MS milliseconds (default 5000) for a graceful QUIT. A rejection or timeout forces a disconnect and rejects server.stop(), so process entrypoints can report an incomplete shutdown. withLease(...) returns a skipped result when the lease cannot be acquired; do not treat a skipped lease as a successful undefined result. Use ctx.ports.redisLocks.checkHealth() from app-owned readiness endpoints to verify that Redis can run a cheap PING or no-op script without starting any background lock work.

Local and tests

Use app-owned fake locks or an in-memory LocksPort in use-case tests when lease behavior is not the subject of the test. Use createRedisLocks(...) with a test Redis client for provider-level tests that need Lua/fencing semantics.

This package also includes an opt-in live Redis suite. It uses independent Redis connections to exercise concurrent acquisition, monotonic fencing, expired-owner rejection, bounded waiting, and timeouts. The default bun run test command remains hermetic. Set REDIS_LOCKS_TEST_URL or the shared REDIS_TEST_URL for the live suite:

REDIS_LOCKS_TEST_URL=redis://localhost:6379 bun run test:live

Deployment notes

Use a dedicated single Redis primary with persistence, predictable latency, and maxmemory-policy noeviction for work that depends on fencing tokens. The fencing counter has no TTL; an allkeys-* eviction policy can delete it and allow a later INCR to restart at 1. Beignet supports noeviction as the production policy so memory pressure fails the lock operation instead of silently reusing a fencing token. Verify the policy in the Redis deployment configuration rather than relying on application startup access to CONFIG.

The provider's two-key Lua acquisition does not currently support Redis Cluster. Primary failover with asynchronous replication can lose recent lease or fencing-counter writes, and the live suite does not simulate failover or network partitions.

Fencing tokens become a correctness boundary only when the protected durable resource atomically accepts tokens strictly greater than the last token it observed. Without that downstream check, use the lease to reduce duplicate work, not to prove that duplicates are impossible.

Correctness note

Locks coordinate work; they are not a substitute for durable correctness. Use database unique constraints, transactions, idempotency keys, and outbox claims as the source of truth for business invariants. Use leases to prevent duplicate scheduler runs, singleton workers, cache stampedes, and short critical sections.