Guide: understand the pieces

Lease locks and ownership

Give one Pearing identity a time-limited claim on named work.

Use this when

Different identities might perform the same non-idempotent step.

Do not use this when

Work is safe in parallel, the target already enforces uniqueness, or reviews must remain independent.

Pieces involved

Resolved user identity User, team, or repository scope Stable lock name Idempotent request UUID Ticket and lease FIFO wait queue Fence token

Before you start

  • Choose a stable scope and name such as thread-work.42.
  • Reuse the same request UUID when retrying one acquisition.
  • Choose a 5-300 second lease and plan to heartbeat at about one third of that interval.
  • Make the target atomically reject stale fence tokens, or use its native lock.
  • Keep recoverable progress in Git and threads.

Know who owns the lock

Same identity

Two tokens or processes authenticated as the same user are one owner. Acquisitions are reentrant, share a fence token, increase hold depth, and require balanced ticket releases.

Distinct identities

A different user contends or joins the FIFO queue. Promotion creates a newer fence token.

External system

Pearing cannot cancel running code. The target must enforce fencing or its own lock.

The workflow

  1. Derive one key. Map the same work to the same scope and lock name.
  2. Acquire idempotently. Reuse the request UUID after an uncertain response.
  3. Handle the result. Holders proceed, waiters poll, contenders back off, and rejected deadlocks abandon the newest wait.
  4. Read current state. Ownership does not prove that work remains.
  5. Heartbeat. Extend long leases and stop after expiry or a failed heartbeat.
  6. Fence writes. Compare the returned token atomically where the side effect occurs.
  7. Release every acquisition. Release held tickets and cancel unused waiters.

Try it with the CLI

pearing-cli acquire-lock thread-work.42 \
  --request-uuid e41b7608-1fe8-4f54-a5f3-67e8605f480c \
  --lease-seconds 60 \
  --wait-seconds 30 \
  --repo teams/platform/website

pearing-cli get-lock-ticket thread-work.42 \
  6bb65939-0db9-4fe2-83a7-ff6dfc3b00af \
  --repo teams/platform/website

pearing-cli heartbeat-lock-ticket thread-work.42 \
  6bb65939-0db9-4fe2-83a7-ff6dfc3b00af \
  --lease-seconds 60 \
  --repo teams/platform/website

pearing-cli release-lock-ticket thread-work.42 \
  6bb65939-0db9-4fe2-83a7-ff6dfc3b00af \
  --repo teams/platform/website

Using another interface

Acquire the same lock through MCP:

Tool: acquire_lock
Arguments:
{
  "repo": "teams/platform/website",
  "lock_name": "thread-work.42",
  "request_uuid": "e41b7608-1fe8-4f54-a5f3-67e8605f480c",
  "lease_seconds": 60,
  "wait_seconds": 30
}

What success looks like

  • All responders derive one key and reuse one request UUID per attempt.
  • One identity holds the lock while other identities wait in FIFO order.
  • The holder heartbeats, reads current state, records progress, and releases every ticket.
  • A promoted identity receives a newer fence token and checks whether work remains.
  • The target rejects stale lower fence tokens or provides its own lock.

Common mistake

A Pearing lock is not a distributed transaction and lease expiry does not stop stale external work. Without atomic fencing or a native target-system lock, an expired holder can still commit a side effect.

Failure and recovery

Stop using the old ticket. Write only if the target atomically accepts its fence. A newer holder must read durable state before continuing.

Inspect the ticket state. A promoted holder must revalidate because the previous owner may have completed the work.

Retry with the same request UUID. If access changed, stop new work. The ticket owner can still release the known ticket.

Related reference