Several api nodes¶
Status: built in M7-17. Rules: 16-operations.md 4.4 and 4.5, 02-architecture.md sections 11.6 and 16 (level 2), 13-push-notifications.md 12 point 4. The Compose example is the overlay of install-compose.md 5.3.
1. When to run more than one api node¶
One process in role all is the default. Add api nodes when one node reaches
perf.ws_connections_per_node or its CPU limit, or when an update must not drop connections
(level 2 of the scaling path). Separate api and worker (level 1) come first; several workers
need nothing besides more worker containers (level 3).
2. What every process needs¶
cluster.mode = multion every api node and on the worker. Withsingle, nodes do not see each other's typing, live box and presence, and a device or agent connected to two nodes is not closed.- One database and one key store of the signing keys (
secrets.signing_key_dir, 16 5.2): a shared volume, never a copy per node. A key rotated on one node is used by all nodes withinsigning_key.cache_ttl; with a store per node, tokens of one node fail on the others. - The same file storage (
file.*) and the same secrets as for one node.
3. The edge¶
- REST and the gateway (
/api/v1/gateway) need no sticky sessions. A client that reconnects on another node identifies again and catches up from its cursor. - Terminal streams need the hash rule of 16 4.4: both stream paths of a session go to the same
node.
infra/compose/edge/nginx.confis the example. - Rate limits count per node. With
nnodes, divide theratelimit.*values byn, or let the edge keep a client on one node.
4. What crosses the nodes¶
| What | How | What you see when it fails |
|---|---|---|
| Typing, live box, agent presence | NOTIFY ephemeral with IDs; the receiving node reads the rest from the database |
ephemeral listener lost in the log; live indicators pause until the connection is back |
| A second connection of the same device | NOTIFY ephemeral; the older connection closes with 4017 |
both connections stay open until the next reconnect |
| A second connection of the same agent consumer | the cursor row (holder_session_id) and NOTIFY ephemeral; the older connection closes with 4015 |
the older connection closes at its next save instead of at once; no event is delivered twice |
| Desktop hints and the delay of mobile pushes | gateway_presence, written by every api node every cluster.presence_flush_interval, read by the worker; the hint goes to the node of the connection through NOTIFY ephemeral |
gateway presence not written and bothy.gateway.presence.write_failures; after cluster.presence_ttl new hints count the desktop as offline; a delayed push keeps its release time |
| Command autocomplete | stays on the node of the request | the request ends with an empty result when the agent has no connection on that node |
5. Updates and failures¶
- Replace one api node after the other. A stopped node deletes its rows of
gateway_presence; its connections reconnect to the other nodes. - A crashed node leaves its rows; the worker ignores them after
cluster.presence_ttl, and the next flush of any node deletes them. - After a crash of the database the table
gateway_presenceis empty (UNLOGGED); every node writes its rows again within onecluster.presence_flush_interval. - Migrations follow expand and contract (16 section 10), because old and new nodes run side by side during the update.
6. Not proven¶
The integration tests run two api nodes and one worker in one test process behind an in-process edge. Behaviour behind a real load balancer and with more than two api nodes is not tested.