Skip to content

Allow seeding Lock UUID for safe cross-process release #16

Description

@francofantini

Feature request

Please consider allowing Lock to be constructed with an existing UUID/token so a lock acquired in one process can be released safely from another process.

Use case

In serverless/workflow environments, the process that acquires the lock is not always the same process that knows when the protected work has finished. For example:

  1. A cron route acquires a lock before starting an async workflow/job.
  2. The route returns immediately after start() succeeds.
  3. A later workflow step, worker, or finalizer needs to release the same lock.

@upstash/lock already releases owner-safely by checking the UUID value in Redis before deleting the key. The missing piece is a public way to seed that UUID into a new Lock instance so the finalizer can call release() without having called acquire() in that same process.

Proposed API

const lock = new Lock({
  id: "cron:sync-tn-stock",
  redis,
  lease: 10 * 60 * 1000,
});

const uuid = crypto.randomUUID();
const acquired = await lock.acquire({ uuid });
if (!acquired) return;

// Persist/pass `uuid` to the async worker or workflow input/output.

Then later, in another process:

const lock = new Lock({
  id: "cron:sync-tn-stock",
  redis,
  UUID: uuid,
});

await lock.release();

Patch shape

This is the minimal change we are currently carrying locally with patch-package:

type LockCreateConfig = {
  redis: Redis;
  id: string;
  lease?: number;
  retry?: RetryConfig;
+ UUID?: string;
};

this.lock = {
  redis: config.redis,
  id: config.id,
  lease: config.lease ?? this.DEFAULT_LEASE_MS,
- UUID: null,
+ UUID: config.UUID ?? null,
  retry: { ... }
};

Why this helps

This keeps the existing UUID-matched Lua release semantics intact while supporting async/serverless orchestration patterns without relying only on TTL expiry. It would also avoid downstream projects needing to patch node_modules for this small extension.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions