review: a unit whose file the mesh wrote is stopped when undeclared, and what was found survives a failed first apply (hq ADR 0118)

Records written before Found existed left the adoption guard and the converge filter loaded on
undeclare, then deleted their unit files from under them; a unit whose own file the mesh created
is now the mesh's, whatever its record says. Found is kept apart the moment it is read, so a
first apply that enabled and then failed is not read back as the machine's; boot is found the
first time the mesh sets it; a service once stateless, or moved to another unit, is found afresh
(the old unit given back). The unit is read after the reload that loads a file written in the
same apply, and removal reports what it actually did.
This commit is contained in:
jochen
2026-09-27 00:41:02 +02:00
parent 08c0f40ff0
commit 23a4436499
6 changed files with 718 additions and 56 deletions
+50
View File
@@ -154,6 +154,18 @@ type State struct {
// other mode can be refused before it is applied (novox/hq issue 104).
Mode string `json:"mode,omitempty"`
// FoundFirst is, by resource id, what a service's unit was found as by an apply of it that did
// not finish — kept apart from Resources, because a record follows the fact and this apply's
// fact never came (novox/hq ADR 0118).
//
// **The capture is the one reading that cannot be taken again.** A first apply that enabled a
// unit and then failed to start it leaves no record; without this, the next apply would find
// the unit enabled, take that for what the machine had, and undeclaring would leave enabled a
// unit the mesh enabled. So what is found is written the moment it is read, whatever the apply
// of the resource then does, and a later apply reads it here before it reads the machine.
// Dropped once a record carrying it is written, and when its resource is no longer declared.
FoundFirst map[string]PendingFound `json:"found_first,omitempty"`
// Genesis is the bundle this host consumed raising the foundation, if it has. Once recorded,
// the bundle carried in the binary is not applied again: what genesis applied was rewritten
// for this machine, and the mesh has said more since (novox/hq issue 104).
@@ -457,9 +469,47 @@ func originOf(r Applied) string {
// FoundUnit is a service's unit as the host first found it.
type FoundUnit struct {
// Unit is which unit this was read from. What was found about one unit says nothing about
// another, so a service whose declaration moves to a different unit is read again for that one
// (novox/hq ADR 0118). Empty on what was kept before this was: the record's Target then says.
Unit string `json:"unit,omitempty"`
// State is "running" or "stopped".
State string `json:"state"`
// Boot is "enabled" or "disabled" — or empty when the declaration never set it, and the host
// never touched it.
Boot string `json:"boot,omitempty"`
}
// PendingFound is a unit as found by an apply of its service that has not yet been recorded, and
// who asked for that apply — so only a declaration from the same origin can say it is gone.
type PendingFound struct {
FoundUnit
Origin string `json:"origin,omitempty"`
}
// KeepFound writes down what an unfinished apply found a service's unit as.
func (s *State) KeepFound(id, origin string, f FoundUnit) {
if s.FoundFirst == nil {
s.FoundFirst = map[string]PendingFound{}
}
s.FoundFirst[id] = PendingFound{FoundUnit: f, Origin: origin}
}
// DropFound forgets what was found for one resource: its record now carries it, or it is gone.
func (s *State) DropFound(id string) {
delete(s.FoundFirst, id)
if len(s.FoundFirst) == 0 {
s.FoundFirst = nil
}
}
// DropFoundUndeclared forgets what was found for every resource of this origin the declaration no
// longer names. Only this origin's, for the reason Orphans gives: a mesh declaration's silence says
// nothing about what the bundle applies, nor the other way round.
func (s *State) DropFoundUndeclared(declared map[string]bool, origin string) {
for id, p := range s.FoundFirst {
if !declared[id] && originOf(Applied{Origin: p.Origin}) == origin {
s.DropFound(id)
}
}
}