← All writing

Technical note · 2026-09-14

One conversation, several devices: making execution ownership explicit

A focused concurrency problem: replace local loading flags with atomic lease acquisition, ownership-aware renewal and safe release across devices.

  • PostgreSQL
  • Concurrency
  • Leases
  • AI agents

Related case: AI agent application platform. The simplified SQL uses generic names and bound parameters. It illustrates the local active-session design, not a script for the business database.

A disabled button is local state

Desktop and browser clients can enter the same conversation. Their loading flags cannot prevent each other from starting work. An in-process lock also cannot coordinate independent backend processes.

The server must identify which execution owns the session and how long that ownership remains valid.

Use a bounded ownership record

A permanent running flag can survive a crashed process. A lease expires unless its owner renews it.

Store the account/session key, a fresh execution run_id and expiration time. A device ID is not a substitute for a unique execution identity.

CREATE TABLE demo_session_lease (
  account_id text NOT NULL,
  session_id text NOT NULL,
  run_id uuid NOT NULL,
  expires_at timestamptz NOT NULL,
  PRIMARY KEY (account_id, session_id)
);

Authorize the session before competing for its lease. Concurrency ownership does not replace authentication.

Acquire atomically

A separate “is it free?” query followed by insertion permits two winners. A conditional UPSERT lets a unique constraint arbitrate:

WITH lease_clock AS (
  SELECT clock_timestamp() AS observed_at
)
INSERT INTO demo_session_lease AS held (
  account_id, session_id, run_id, expires_at
)
SELECT :account_id, :session_id, :run_id,
       observed_at + make_interval(secs => :ttl_seconds)
FROM lease_clock
ON CONFLICT (account_id, session_id) DO UPDATE
SET run_id = EXCLUDED.run_id,
    expires_at = EXCLUDED.expires_at
WHERE held.expires_at <= (SELECT observed_at FROM lease_clock)
RETURNING run_id;

When the existing lease is valid, the update condition fails and no row is returned. That means acquisition failed; see PostgreSQL INSERT.

Database clock_timestamp() differs from transaction-start now(); see date/time functions. The example samples one time within the statement. Keep acquisition transactions short, bound lock/statement waits and ensure the granted lease is still valid before starting work.

Commit acquisition before long-running model or tool execution. Do not hold the transaction for the entire operation.

Renew and release by execution identity

Renewal must match the session key and run ID, and require that the lease has not expired. A late heartbeat must not revive an obsolete execution.

Release must also match the run ID:

DELETE FROM demo_session_lease
WHERE account_id = :account_id
  AND session_id = :session_id
  AND run_id = :run_id
RETURNING run_id;

If A expires and B takes over, A’s delayed cleanup cannot delete B’s record. Deleting zero rows is an expected idempotent outcome.

A lease does not undo external effects

An external command started by A may continue after A loses the lease. Unique database ownership alone does not guarantee strict exclusion for every downstream resource.

Stop subsequent work when renewal fails. Resources needing stronger protection should validate execution ownership, potentially with monotonic fencing tokens. A unique UUID distinguishes executions but is not a monotonic token. That is additional resource-side design, not an automatic property of the lease table.

Verify competing and obsolete executions

Test simultaneous acquisition using independent database sessions, unrelated session keys, valid-lease rejection, expired takeover and late renewal/release by the old owner.

Also test process loss and renewal failure. The UI should reconcile with server authority, and execution should stop claiming ownership after it is lost.