Skip to content

Instance Affinity

Every target is assigned to exactly one Thorax execution server at a time. That assignment is its instance affinity, and it decides which server dispatches work to the target.

In listen (push) mode, Thorax dials the agent (Thorax -> Tarsus :9342) and holds that connection; the owning instance is the one that connected. Pinning the target to one instance means:

  • Only one server dispatches to a target, so a command is never delivered twice
  • The open connection is reused rather than renegotiated per deployment
  • Deployment logs and step state stream back over a single path

The assignment is recorded on the target itself as assigned_thorax_id, together with the time it was made.

Targets are assigned when they register. Selection is tenant-aware: a target is only ever assigned to a Thorax instance permitted to serve its tenant, so multi-tenant isolation holds across the execution tier as well as the API.

Affinity is not permanent. Two services work together to move targets off a dead server:

  1. Instance cleanup checks every 30 seconds and marks a Thorax instance offline once it has missed heartbeats for 60 seconds.
  2. Thorax reassignment then finds every target still pointing at an offline instance, selects a new one for each — again tenant-aware — updates assigned_thorax_id, and publishes the change so connected agents learn about it.

Agent targets use a longer liveness window than Thorax instances (360 seconds), deliberately set above the maximum agent heartbeat interval so that a slow agent is not mistaken for a dead one.

  • Targets move between servers on their own; no manual step is required
  • Work already queued for the old instance is not lost — see Competing Consumers for how it is reclaimed
  • Reassignment is recorded against the target