TUTORIALS 10 min read

Lease-Based Task Ownership for AI Agents: Stop Duplicate Work Before It Starts

Long-running agents need recoverable ownership, not permanent locks. Use leases, fencing tokens, heartbeats, and reconciliation to prevent duplicate work.

By EgoistAI ·
Lease-Based Task Ownership for AI Agents: Stop Duplicate Work Before It Starts

Two AI workers claiming the same job is not a cute race condition. It is how you send two invoices, publish two posts, or let separate agents rewrite the same file. Permanent locks avoid overlap until the owner crashes; then they turn your queue into a graveyard.

A lease gives one worker temporary ownership that can be recovered after failure. The trick is making expired owners harmless, because a slow agent may wake up after another worker has taken over.

What Should a Lease Record Contain?

Store a task ID, owner ID, expiry time, monotonically increasing fencing token, and the version of the task being processed. Claim the task with one atomic database operation: only an unowned or expired row may transition to your owner ID.

UPDATE tasks
SET owner_id = :worker,
    lease_until = now() + interval '60 seconds',
    fence = fence + 1
WHERE id = :id
  AND (lease_until IS NULL OR lease_until < now())
RETURNING fence;

If the update returns nothing, another worker owns the task. Do not ask the model to negotiate. Back off, pick another job, or subscribe to a completion event.

Why Is Expiry Alone Not Enough?

Suppose worker A pauses during a slow model call. Its lease expires, worker B claims the job, and then A resumes. Both now believe they can write. A fencing token solves this by making every claim newer than the last.

Downstream mutation APIs must reject stale tokens. If worker A holds fence 41 and worker B holds 42, a database write, object-store update, or tool gateway should accept 42 and reject 41. Without enforcement at the side effect, the token is decorative.

Use heartbeats to renew healthy work, but cap the total runtime. An agent that loops forever should not retain authority forever. Separate a renewable execution lease from the immutable business command and its idempotency key.

How Do You Handle Long Model Calls?

Do not hold a database transaction open while waiting for an LLM. Claim the lease, commit, then call the model. Renew in a lightweight background loop and stop work immediately when renewal fails.

For tools with irreversible effects, verify lease ownership and fence immediately before execution. Record the effect in an idempotency ledger. Lease ownership prevents concurrent planners; idempotency prevents duplicate business effects across retries.

FailureLease responseExtra control
Worker crashExpire and reclaimIdempotency key
Network partitionReject stale fenceReconciliation
Slow model responseStop after failed renewalRuntime budget
Duplicate queue deliveryOne atomic claim winsUnique task identity

How Should You Test It?

Freeze a worker after claim, let the lease expire, and start a replacement. Resume the old worker and confirm every mutation rejects its fence. Kill workers before and after tool calls. Skew clocks in tests or, better, rely on database time so worker clocks do not decide ownership.

Track claim conflicts, renewal failures, expired active leases, stale-fence rejections, and reconciliation volume. If you never see rejections in chaos tests, the protection is probably not wired to the real side effect.

Leases make ownership recoverable. Fencing makes recovery safe. Idempotency makes effects repeatable. You need all three before autonomous workers can share a production queue without stepping on each other.

Share this article

> Want more like this?

Get the best AI insights delivered weekly.

By subscribing, you agree to our Privacy Policy. You can unsubscribe at any time.

> Related Articles

Tags

AI agentsdistributed systemsleasesqueuesreliability

> Stay in the loop

Weekly AI tools & insights.