Merge pull request 'Design 47: a placement change is answered by the nodes it places before it merges (ADR 0559, issue 1126)' (#485) from feat/1126-l2b into main
This commit was merged in pull request #485.
This commit is contained in:
+7
-1
@@ -237,7 +237,7 @@ every other domain has something to run on. **Recorded by** the controller and t
|
|||||||
**Purpose.** To decide, send and apply what each node runs, and to say why.
|
**Purpose.** To decide, send and apply what each node runs, and to say why.
|
||||||
**Recorded by** the controller's `inventory` database; from ADR 0549, by the catalogue's placement files, which
|
**Recorded by** the controller's `inventory` database; from ADR 0549, by the catalogue's placement files, which
|
||||||
the indexer reads onto the bus. **Upstream of** Provisioning, Connectivity, Data
|
the indexer reads onto the bus. **Upstream of** Provisioning, Connectivity, Data
|
||||||
and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 0252, 0261, 0411, 0539, 0549.
|
and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 0252, 0261, 0411, 0539, 0549, 0559.
|
||||||
**Uses:** node, machine, module, seat, manifest (Module), config value (Config).
|
**Uses:** node, machine, module, seat, manifest (Module), config value (Config).
|
||||||
|
|
||||||
- **assignment** — a module put on a node. Its config values on that node (module-assignment config,
|
- **assignment** — a module put on a node. Its config values on that node (module-assignment config,
|
||||||
@@ -247,6 +247,12 @@ and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 02
|
|||||||
and the indexer reads it at each trunk merge into its state `placement`, one key per node, which every reader
|
and the indexer reads it at each trunk merge into its state `placement`, one key per node, which every reader
|
||||||
of placement watches (ADR 0549 §1 to §3). Until ADR 0547's third milestone the controller's assignments are
|
of placement watches (ADR 0549 §1 to §3). Until ADR 0547's third milestone the controller's assignments are
|
||||||
kept equal to it.
|
kept equal to it.
|
||||||
|
- **placement candidate** — a placement file of one node as an open catalogue pull request has it at its head,
|
||||||
|
kept by the indexer in its state `placement-candidates` under `<group>.<node>` while the pull request is open.
|
||||||
|
Every node composes it in **shadow** (composed and compared, never applied) and answers in its own have whether
|
||||||
|
what it would run is the same; the composition check passes only when every placed node has answered the same
|
||||||
|
or differs only where the change intends (ADR 0559). Not the indexer's candidates of a group, which are
|
||||||
|
fingerprints (Delivery).
|
||||||
- **pending assignment** — an assignment of a module whose build is still running, kept, and made by the
|
- **pending assignment** — an assignment of a module whose build is still running, kept, and made by the
|
||||||
controller when that build registers the module, or ended with why when it does not
|
controller when that build registers the module, or ended with why when it does not
|
||||||
(ADR 0261).
|
(ADR 0261).
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
layer: to-be
|
layer: to-be
|
||||||
status: in-progress
|
status: in-progress
|
||||||
code: [mesh-controller, mesh-catalog modules/mesh-delivery-gate, mesh-delivery, mesh-host, mesh-sdk, mesh-vault, hq 00-META/checks]
|
code: [mesh-controller, mesh-catalog modules/mesh-delivery-gate, mesh-delivery, mesh-host, mesh-sdk, mesh-vault, hq 00-META/checks]
|
||||||
updated: 2026-10-26
|
updated: 2026-10-27
|
||||||
decisions:
|
decisions:
|
||||||
|
- "ADR 0559"
|
||||||
- "ADR 0555"
|
- "ADR 0555"
|
||||||
- "ADR 0551"
|
- "ADR 0551"
|
||||||
- "ADR 0550"
|
- "ADR 0550"
|
||||||
@@ -129,7 +130,11 @@ source revision is derived from the keys: every **want** equals its module's **f
|
|||||||
group and the modules of the trunk. At each trunk merge of the catalogue it also reads the catalogue's
|
group and the modules of the trunk. At each trunk merge of the catalogue it also reads the catalogue's
|
||||||
**placement files**, one per node, into its state `placement`: one key `placement.<node>` with that node's
|
**placement files**, one per node, into its state `placement`: one key `placement.<node>` with that node's
|
||||||
modules, their pins, host ports and bindings, and the source revision. It adds the nodes to `modules.<module>`
|
modules, their pins, host ports and bindings, and the source revision. It adds the nodes to `modules.<module>`
|
||||||
and says `placement-changed` only for a node whose key differs (ADR 0549 §1, §2).
|
and says `placement-changed` only for a node whose key differs (ADR 0549 §1, §2). While a catalogue pull request
|
||||||
|
that changes a placement file is open, the forge bridge carries its placement files at the head and the indexer
|
||||||
|
keeps, at each head, each node's file that differs from the trunk's as a **placement candidate** in a state of its
|
||||||
|
own, `placement-candidates`, one key `<group>.<node>`, deleting the group's other keys, and all of them when the
|
||||||
|
pull request merges or closes; nothing that composes what runs reads it (ADR 0559 §2, §3).
|
||||||
- **The builder** claims a build by creating its key, once per fingerprint, and orders it as an ask on the stream of the artifact's
|
- **The builder** claims a build by creating its key, once per fingerprint, and orders it as an ask on the stream of the artifact's
|
||||||
language; it makes no artifact's bytes since R4e and chooses no node, because the build modules pull the asks (ADR 0498 §1, ADR 0525 §1, §4). From R4g it
|
language; it makes no artifact's bytes since R4e and chooses no node, because the build modules pull the asks (ADR 0498 §1, ADR 0525 §1, §4). From R4g it
|
||||||
delivers its own module, the build modules and the controller from its keys, and nothing falls back to build-agent, which is removed
|
delivers its own module, the build modules and the controller from its keys, and nothing falls back to build-agent, which is removed
|
||||||
@@ -165,7 +170,15 @@ source revision is derived from the keys: every **want** equals its module's **f
|
|||||||
It also runs the **composition check**: whether the machines a change touches still compose with it, read
|
It also runs the **composition check**: whether the machines a change touches still compose with it, read
|
||||||
from the keys the indexer and the builder hold and from the machines, settings and grants it reads, with
|
from the keys the indexer and the builder hold and from the machines, settings and grants it reads, with
|
||||||
mesh-sdk's composition, the one every node agent composes with (ADR 0446, ADR 0547 §2). Its module was made in
|
mesh-sdk's composition, the one every node agent composes with (ADR 0446, ADR 0547 §2). Its module was made in
|
||||||
R4g with release 5; from release 6 it writes no difference (ADR 0524 §4, ADR 0528, ADR 0539 §8).
|
R4g with release 5; from release 6 it writes no difference (ADR 0524 §4, ADR 0528, ADR 0539 §8). For a
|
||||||
|
change that carries placement candidates it composes the change with them laid over the trunk's placement,
|
||||||
|
counts every node whose placement changes among the machines it touches (ADR 0446 §3, as ADR 0559 §5 changes it), and names each refusal; then it reads each
|
||||||
|
placed node's answer to the candidate and fails on a module that differs where the change does not intend it (on
|
||||||
|
a node the trunk does not place yet, as at the seed, no difference is intended), on
|
||||||
|
a refusal, and on a node that has not answered within 10 minutes, named, so a laptop asleep is never passed;
|
||||||
|
that node's later answer at the same head asks the check again on its event, as for any part of the mesh not
|
||||||
|
heard (ADR 0548 §1, ADR 0559 §1, §5). Its own composition stands in for every sealed value, so only a node's answer can say that
|
||||||
|
what the node would run is the same.
|
||||||
- **The node agent**, the node-engine's own process, follows its node's wants, **composes each wanted module for
|
- **The node agent**, the node-engine's own process, follows its node's wants, **composes each wanted module for
|
||||||
its own node** with mesh-sdk's composition from the manifest by digest, the module's resolved values from
|
its own node** with mesh-sdk's composition from the manifest by digest, the module's resolved values from
|
||||||
mesh-config, its secrets and bus credential asked of the vault sealed to the node's key, its licences and what
|
mesh-config, its secrets and bus credential asked of the vault sealed to the node's key, its licences and what
|
||||||
@@ -691,7 +704,7 @@ backup holder backs up nothing, and the have says so.
|
|||||||
controller's assignments name on a node, and the grants part composes both, holding a seat held once for the mesh
|
controller's assignments name on a node, and the grants part composes both, holding a seat held once for the mesh
|
||||||
where the placement says.
|
where the placement says.
|
||||||
- **The placement file.** One file per node in the catalogue, changed only by a pull request and checked before
|
- **The placement file.** One file per node in the catalogue, changed only by a pull request and checked before
|
||||||
it merges (ADR 0549 §1, ADR 0555). It holds what a person decides of the node and nothing a machine observes:
|
it merges by the nodes it places (ADR 0549 §1, ADR 0555, ADR 0559). It holds what a person decides of the node and nothing a machine observes:
|
||||||
- the node's decided facts: its endpoint and site, whether it is the private network's hub, its address on the
|
- the node's decided facts: its endpoint and site, whether it is the private network's hub, its address on the
|
||||||
private network (a decision written once at enrolment; nothing allocates one at run time), the domain its
|
private network (a decision written once at enrolment; nothing allocates one at run time), the domain its
|
||||||
routed names are composed under, its operator and agent logins by name with their homes, whether it is
|
routed names are composed under, its operator and agent logins by name with their homes, whether it is
|
||||||
@@ -707,6 +720,21 @@ backup holder backs up nothing, and the have says so.
|
|||||||
The catalogue's check refuses a placement naming a module it does not hold or a node not enrolled, two modules
|
The catalogue's check refuses a placement naming a module it does not hold or a node not enrolled, two modules
|
||||||
of one node on one host port, a pin, binding or holding naming a provider not placed where it says, a seat held
|
of one node on one host port, a pin, binding or holding naming a provider not placed where it says, a seat held
|
||||||
once for the mesh held twice, two nodes with one address and more than one hub, each named in one answer.
|
once for the mesh held twice, two nodes with one address and more than one hub, each named in one answer.
|
||||||
|
- **A node answers a placement change before it merges.** The node agent watches every placement candidate. For
|
||||||
|
each open group it composes its own node in shadow, from the trunk's placement with the group's candidates laid
|
||||||
|
over it and its own inputs, the sealed values and vault boxes it already holds among them, and applies nothing.
|
||||||
|
It compares the result with what it runs, module by module by composition digest, the bus's user-list resource
|
||||||
|
left out, and writes the answer into its own have key `<node>.node` under `candidates`, by group: the head
|
||||||
|
answered, the modules the same, each module that differs with both digests, each module added or removed, and
|
||||||
|
each refusal. A module the candidate adds is answered as added, naming the values not yet issued for it, since
|
||||||
|
its credential and boxes are made only once it is placed; a refusal is a value missing for a module the node
|
||||||
|
runs. It writes only when the answer changes, removes it when the candidates go, and answers
|
||||||
|
whether or not the node is placed yet, so before the seed it answers against what it runs from the controller's
|
||||||
|
compositions. The candidate's key is the node's event, the answer is a key the node alone writes, and the
|
||||||
|
composition check hears that key's change; nothing polls. `indexer_placement`, served by the status view, also
|
||||||
|
shows each open group's candidates and every node's answer, and the indexer's `indexer_placement_reread` reads
|
||||||
|
placement and the candidates again now, writing only what differs. The contract (the state, its key, the answer,
|
||||||
|
the composition with a given placement and the comparison) is mesh-sdk's (ADR 0559 §4, §6, §7, §8).
|
||||||
- **The node record.** What the machine observes of itself: its tunnel's public key, its capabilities, the
|
- **The node record.** What the machine observes of itself: its tunnel's public key, its capabilities, the
|
||||||
public halves of its sealing and serving keys, the certificate issued for its internal name, its outward links
|
public halves of its sealing and serving keys, the certificate issued for its internal name, its outward links
|
||||||
and networks, and its node-engine's contract. Written at start and when an observed fact changes, only when it
|
and networks, and its node-engine's contract. Written at start and when an observed fact changes, only when it
|
||||||
@@ -738,8 +766,8 @@ backup holder backs up nothing, and the have says so.
|
|||||||
mesh-config's and the assigner's reading of placement, and the indexer writing it. Nothing changes on a node by
|
mesh-config's and the assigner's reading of placement, and the indexer writing it. Nothing changes on a node by
|
||||||
that delivery alone, since no placement file exists yet: the node records appear, and the grants part's list gives
|
that delivery alone, since no placement file exists yet: the node records appear, and the grants part's list gives
|
||||||
the nodes their reads. Then the seed: one catalogue pull request writes each node's placement file from the
|
the nodes their reads. Then the seed: one catalogue pull request writes each node's placement file from the
|
||||||
controller's export, and its composition check composes every node from it with no difference to what the node
|
controller's export, and its composition check passes only when every node it places has composed it in shadow
|
||||||
runs; only then is it merged. The indexer writes `placement`, each node begins composing its own, and the assigner
|
and answered that what it would run is the same (ADR 0559 §1); only then is it merged. The indexer writes `placement`, each node begins composing its own, and the assigner
|
||||||
writes its wants as the build alone.
|
writes its wants as the build alone.
|
||||||
- **Composed again on events, never on a timer.** A changed input at the same fingerprint (a setting or given
|
- **Composed again on events, never on a timer.** A changed input at the same fingerprint (a setting or given
|
||||||
value, a secret rotated or moved, a credential issued, a provider's contribution, a peer added to the hub) is
|
value, a secret rotated or moved, a credential issued, a provider's contribution, a peer added to the hub) is
|
||||||
@@ -861,6 +889,7 @@ assignments bucket is kept equal to it until ADR 0547's third milestone and is a
|
|||||||
| want | the fingerprint a node should run of a module, and the digest of the one composition it allows; the node's own want names its node composition | the assigner | `mesh-delivery-assigner_wants` |
|
| want | the fingerprint a node should run of a module, and the digest of the one composition it allows; the node's own want names its node composition | the assigner | `mesh-delivery-assigner_wants` |
|
||||||
| have | what the node runs of a module: the fingerprint, the composition's digest, the phase and the health | the node agent | `node-engine_have` |
|
| have | what the node runs of a module: the fingerprint, the composition's digest, the phase and the health | the node agent | `node-engine_have` |
|
||||||
| placement | each node's modules with their pins, host ports and bindings, from the catalogue's placement file of that node at each trunk merge of the catalogue | the indexer | the indexer's state `placement` |
|
| placement | each node's modules with their pins, host ports and bindings, from the catalogue's placement file of that node at each trunk merge of the catalogue | the indexer | the indexer's state `placement` |
|
||||||
|
| placement candidate | while a catalogue pull request that changes placement is open: one node's placement file at its head, beside the chain and never composed into what runs (ADR 0559 §3) | the indexer | the indexer's state `placement-candidates` |
|
||||||
| assignment | until ADR 0547's third milestone: the module and node, pinned or not, ended or not, written by `assign` and `unassign` after a merged placement change, so that it equals the catalogue (ADR 0549 §5) | the controller | `mesh-controller_assignments` |
|
| assignment | until ADR 0547's third milestone: the module and node, pinned or not, ended or not, written by `assign` and `unassign` after a merged placement change, so that it equals the catalogue (ADR 0549 §5) | the controller | `mesh-controller_assignments` |
|
||||||
| rollout | read-only from release 6: each plan of releases 1 to 5, its tiers, the builds it carried and the assignments it changed | nobody from release 6 | `mesh-controller_rollouts` |
|
| rollout | read-only from release 6: each plan of releases 1 to 5, its tiers, the builds it carried and the assignments it changed | nobody from release 6 | `mesh-controller_rollouts` |
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user