Backend · Consul
A complete Consul client, and moonconsul/sd: the contract as
an ordinary Consul service with a ttl check. Other Consul clients see a normal
registration; the contract sees a lease.
What it is
Consul's own fields carry the endpoint — address, port, tags, metadata — so an operator
looking at the catalogue sees what they expect, and a client written in any language finds the
service. The contract's id travels in metadata under moonsd_id,
because Consul's service id has to be unique per agent and the contract only requires it to be
unique per service.
The lease
A service registration with a ttl check named after it. The token in the lease is
{service}:{id}, which is enough to find the check again.
agent().check_pass(check_id). A check that goes critical
takes the instance out of resolve at once; Consul reaps the
registration a minute later, which is only how long the corpse stays on the agent.
The service is deregistered with its check, and a registration that has already been reaped is answered as success rather than as an error.
The watcher
A health query takes wait_index and wait_time:
the agent holds the request until the answer changes or the wait runs out, and returns the
index that produced it. The watcher feeds that index into the next query, so a change arrives
as soon as the agent knows it, and a quiet service costs one idle request every
wait — thirty seconds, which is long enough not to be a poll and
short enough that closing the watcher is noticed promptly.
The client behind it
The client covers the API a deployment actually uses: agent, catalog, health, kv, sessions, events, ACLs, status, coordinates, prepared queries, config entries, Connect and its CA, plus locks and semaphores built on sessions. Blocking queries are a first-class primitive, with a watch loop that retries on backoff when an agent goes away.