The Lease Loop Is Not a Chat Completion A developer argues that distributed lease and leader-election loops should rely on deterministic fencing tokens rather than LLM chat completions, citing latency, non-determinism, and vendor-controlled safety layers as failure modes. The post provides a worked PostgreSQL example with a worker_leases table and a Python renewer that increments an epoch on a deadline and refuses writes when the lease is lost. A fencing token is not a paragraph. It is a counter that must rise on a clock, die on a deadline, and never ask a model whether the holder still deserves the lock. Free inference has no place in that loop. Teams keep stuffing chat completions into leader election anyway. A node goes quiet. Logs look messy. Someone wires a cheap model to read the noise and vote on who should keep the lease. The analogy is a lighthouse that phones a critic before each flash. The critic is witty. The reef does not care. Latency is the first crack. Lease renewal is a budget measured in hundreds of milliseconds, not in token streams. A free path can stall, queue, or return a truncated object when the provider is busy. The lock expires while the prose is still arriving. Split brain is not a literary device. It is two writers sharing one offset. Non-determinism is the second crack. The same health snapshot can yield keep, drop, or a sentence that fails JSON. Control planes need the same inputs to produce the same fence. A model that hedges is not a quorum. It is a coin with extra faces. The third crack is authority. A completion can be filtered, rate limited, or rewritten by a safety layer the caller does not own. Leader election that depends on a vendor's refusal class has already given that vendor a veto over the write path. The veto stays invisible in dashboards that only chart HTTP 200. A lease is a timed exclusive. The holder proves liveness by raising an epoch before expiry. Downstream storage must reject any write whose epoch is stale. The model of the world is small: holder id, epoch, expiry. Nothing in that tuple is improved by a temperature setting. PostgreSQL can carry a fencing token without ceremony. The schema and renewer below are a worked example, not a multi-region consensus protocol. Treat them as labeled sample code. CREATE TABLE worker leases name text PRIMARY KEY, holder text NOT NULL, epoch bigint NOT NULL, expires at timestamptz NOT NULL ; CREATE INDEX ON worker leases expires at ; python lease loop.py — keep this module free of HTTP clients and model SDKs import os import socket import time import psycopg LEASE NAME = "order-writer" TTL SECONDS = 15 RENEW EVERY = 5 HOLDER = f"{socket.gethostname }:{os.getpid }" DSN = os.environ "LEASE DATABASE URL" ACQUIRE = """ INSERT INTO worker leases name, holder, epoch, expires at VALUES %s, %s, 1, now + make interval secs = %s ON CONFLICT name DO UPDATE SET holder = EXCLUDED.holder, epoch = worker leases.epoch + 1, expires at = EXCLUDED.expires at WHERE worker leases.expires at < now RETURNING epoch; """ RENEW = """ UPDATE worker leases SET epoch = epoch + 1, expires at = now + make interval secs = %s WHERE name = %s AND holder = %s AND expires at now RETURNING epoch; """ def acquire conn : with conn.cursor as cur: cur.execute ACQUIRE, LEASE NAME, HOLDER, TTL SECONDS row = cur.fetchone conn.commit return None if row is None else int row 0 def renew conn : with conn.cursor as cur: cur.execute RENEW, TTL SECONDS, LEASE NAME, HOLDER row = cur.fetchone conn.commit return None if row is None else int row 0 def hold : with psycopg.connect DSN as conn: epoch = acquire conn if epoch is None: raise SystemExit "lease held elsewhere" while True: time.sleep RENEW EVERY epoch = renew conn if epoch is None: raise SystemExit "lost lease; refuse all writes" print f"fence={epoch}", flush=True if name == " main ": hold The control flow is the design. Acquire or renew returns an integer fence, or it returns nothing. There is no branch that asks a model to interpret the row. Writers that use the lease must send that epoch with every mutating call. A storage layer that cannot see the fence is not protected by the loop. python fenced write.py def append order conn, epoch, payload : with conn.cursor as cur: cur.execute """ INSERT INTO orders payload, fence SELECT %s, %s WHERE EXISTS SELECT 1 FROM worker leases WHERE name = %s AND holder = %s AND epoch = %s AND expires at now """, payload, epoch, LEASE NAME, HOLDER, epoch , if cur.rowcount = 1: conn.rollback raise RuntimeError "stale fence; drop the write" conn.commit A free completion that explains why the insert failed is optional theatre after the fact. It must not retry the insert, bump the epoch, or pick a new holder. Commentary can wait. Membership cannot. Opinion is cheap. Import graphs are not. The next artifact fails CI when the lease package grows an inference client. It is a proposed gate, not a claim that a production fleet already runs it. python test lease has no inference.py from pathlib import Path import ast LEASE DIR = Path "lease" FORBIDDEN IMPORTS = { "openai", "anthropic", "httpx", "aiohttp", "requests", "google", } FORBIDDEN NAME FRAGMENTS = "chat.completions", "messages.create", "generate content", def imported roots tree: ast.AST - set str : roots = set for node in ast.walk tree : if isinstance node, ast.Import : for alias in node.names: roots.add alias.name.split "." 0 elif isinstance node, ast.ImportFrom and node.module: roots.add node.module.split "." 0 return roots def test lease modules stay offline : files = list LEASE DIR.glob " .py" assert files, "lease package missing" offenders = for path in files: text = path.read text encoding="utf-8" tree = ast.parse text bad = imported roots tree & FORBIDDEN IMPORTS hits = frag for frag in FORBIDDEN NAME FRAGMENTS if frag in text if bad or hits: offenders.append f"{path}: imports={sorted bad } calls={hits}" assert not offenders, "inference reached the lease path:\n" + "\n".join offenders Run it the boring way. mkdir -p lease cp lease loop.py fenced write.py lease/ python -m pytest test lease has no inference.py -q The test is heuristic. A determined engineer can hide a completion behind a local wrapper named health.py . That is why the gate is an exit criterion, not a personality test. If the wrapper appears, the lease package is already on the wrong side of the line. Metrics exporters belong in another process, so the import ban does not become an excuse to smuggle a model through a telemetry helper. A free model is a useful intern for reading a postmortem. It is a reckless intern for holding the flashlight. The red flag is not "an LLM exists in the company." The red flag is a prompt that can change membership. If the text of a completion can extend a lease, drop a peer, or choose which replica drains a queue, the design has confused explanation with authority. Timeout arithmetic is the next flag. If the model's p95 exceeds half the TTL, the loop is already insolvent. Padding the TTL to wait for tokens just lengthens the split-brain window. Schema drift in the reply is a third. Control messages that need a JSON repair step do not belong next to a fencing token. A fourth flag is softer and easier to miss: a dashboard that treats "the model said the node looks sick" as equivalent to a missed heartbeat. Better alternatives are dull on purpose. A leases table plus make interval covers a single primary. PostgreSQL advisory locks cover even smaller footprints. etcd elections and Consul sessions cover a cluster that already speaks consensus. ZooKeeper remains a valid, if heavy, fencing service. None of these tools ask for a system prompt, and none of them improve when the invoice for tokens is zero. Use the table as a stop/go card during review, not as a personality quiz. | Observation | Stay on the local lease | Exit the design | |---|---|---| | Renewer import graph includes an LLM or generic HTTP client | Keep shipping the SQL loop | Fail closed; strip the client | | p95 of any completion exceeds half the TTL | Do not add a model to buy time | Remove the model; shorten the path | | Failure drill succeeds with inference unreachable | Model is commentary only | Keep commentary off the renewer | | Failure drill fails without inference | — | The model was holding the fence | | A human cannot state the fence rule in one sentence without mentioning a model | Rewrite the sentence until the model disappears | Stop the rollout | Exit when the renewer imports a network client that is not the database. Exit when a human cannot state the fence rule without mentioning a model. Exit when a failure drill cannot be run with the inference provider unreachable. If the drill cannot pass in that condition, inference was never optional. Scratch generation is a different job. A reviewer may want extra negative fixtures: a truncated JSON body, a 429, a refusal, a slow stream. Those fixtures can be drafted on a disposable box. Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode's free model access and free server option are a reasonable place to generate those fixtures and to run the import checker against a throwaway clone. They are not a reasonable place to host lease loop.py . The lease either talks to Postgres, or it does not talk. The example ignores clock jump, long garbage-collection pauses, and network partitions that leave the SQL session half-open. now inside PostgreSQL is better than the client's wall clock, and it is still not Raft. Teams with a multi-region write path need a real consensus group, not a Python while True . The CI checker does not prove liveness. It only forbids a class of imports. It will miss a sidecar that renews the lease over a Unix socket to an agent. Treat that sidecar as part of the lease package and apply the same rule. The checker will also nag a legitimate requests call if someone parks it in lease/ ; move the call, do not weaken the gate. Free inference is the wrong owner of adjacent decisions that look like commentary and act like membership. Draining a Kafka partition, choosing which replica is mostly caught up, or rewriting a readiness probe because logs seem unhealthy are fencing problems in costume. The same stop/go card applies. Operators who already run etcd or ZooKeeper and never call a model from the election path can skim the checker and move on. Researchers studying lock contention on synthetic traces may use a model offline, against recorded data, with no ability to write epoch . That study is not a control loop. The approach is a poor fit for product demos that exist to show an agent running the cluster. A demo can narrate a lease. It should not hold one. It is also a poor fit for anyone hoping a free completion will replace a quorum because the invoice is zero. The invoice is not the failure domain. Dual writers are. A lease loop that stays boring will look under-engineered in a screenshot. That is the point. The lighthouse does not workshop the next flash. It flashes, or it goes dark, and the chart records the gap.