Beignet API reference
    Preparing search index...

    Module @beignet/provider-locks-redis

    @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.

    bun add @beignet/provider-locks-redis @beignet/core ioredis
    
    // 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.

    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.

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

    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

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

    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.

    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.

    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
    

    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.

    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.

    CreateRedisLocksOptions
    CreateRedisLocksProviderOptions
    RedisLocksClient
    RedisLocksConfig
    RedisLocksEscapeHatch
    RedisLocksHealth
    RedisLocksProviderPorts
    RedisLocksProvider
    createRedisLocks
    createRedisLocksProvider