Skip to content

Idempotent commands

The physical worldMQTTcmdIdMACHINE_DISPATCH_ATTEMPTSMACHINE_REPLY_TIMEOUT_MSMACHINE_DISPATCH_TTL_MSsnapshot

Every command carries an id and an expiry, so a retry is answered rather than run twice, and a late copy is refused.

The situation

Networks on a factory floor drop packets. The gateway must retry a dispatch that got no reply, and the machine must not start the same job twice when both copies arrive. A command that waited in a broker queue for an hour must not run when the machine wakes.

The recipe

The generic line protocol wraps every message in { v, id, at, source, body }. A command body carries its cmdId:

line/v1/site-1/line-1/bond-01/cmd
{ "v": "1", "id": "4b0e...", "at": "2026-10-23T14:02:11.000Z",
"source": { "site": "site-1", "line": "line-1", "machine": "bond-01" },
"body": { "cmdId": "c-91f2", "op": "start", "args": {} } }

The gateway’s patience is configuration:

.env
MACHINE_REPLY_TIMEOUT_MS=5000
MACHINE_DISPATCH_ATTEMPTS=3
MACHINE_DISPATCH_TTL_MS=600000

Each retry reuses the same cmdId. The machine replies accepted, and may later reply completed or failed under the same id. When no reply comes after every attempt, the gateway asks for the machine’s snapshot before deciding: a job the snapshot lists counts as accepted.

The guarantee

A repeated cmdId is answered, never run twice. A command expires at the end of the asking, so a copy that reaches the machine later is refused. A machine that restarts with a new boot id gets its accepted jobs dispatched again under the same command ids. Commands go to the broker at QoS 1, so they queue for a machine that is away. See Machines.