Say a running module ok once looked at when nothing of it is judged, so mesh-host's canary can be healthy and its other nodes get the new engine (issue 1945)
mesh/delivery held for a person: merged without a passing check: only a person decides what follows
mesh/check pass 958ccbde3ed4 on f06058390c0f | unit 297 run, 1038 kept, 5 skipped | integration 300 run, 1039 kept, 4 skipped | over bound: unit (slow…

This commit is contained in:
2026-10-25 11:08:10 +01:00
parent 43d1c2456f
commit 958ccbde3e
2 changed files with 44 additions and 7 deletions
+21 -7
View File
@@ -36,7 +36,10 @@ type haveState struct {
mu sync.Mutex
modules map[string]ModuleFact
health map[string]delivery.Health
node *delivery.HaveNode
// looked says a look's health was taken: a running module it judges no resource of then has nothing that could be
// down, and is ok (issue 1945).
looked bool
node *delivery.HaveNode
// nodeComposed is the digest of the node's own composition it runs (ADR 0539 §9), kept beside the node's facts.
nodeComposed string
// written is the build of each module this node-engine wrote a have key for and that the declaration it applied
@@ -110,7 +113,7 @@ func (q *Session) noteApply(r Report) bool {
s.mu.Lock()
defer s.mu.Unlock()
if r.Health != nil {
s.health = moduleHealths(r.Health)
s.health, s.looked = moduleHealths(r.Health), true
}
n := nodeFacts(r)
s.node = &n
@@ -152,7 +155,7 @@ func (q *Session) noteApply(r Report) bool {
}
s.modules = next
if r.Health != nil {
s.health = moduleHealths(r.Health)
s.health, s.looked = moduleHealths(r.Health), true
}
n := nodeFacts(r)
s.node = &n
@@ -166,20 +169,20 @@ func (q *Session) noteHealth(h Health) bool {
defer s.mu.Unlock()
next := moduleHealths(&h)
changed := false
for module := range s.modules {
for module, f := range s.modules {
want, ok := next[module]
if !ok {
want = delivery.HealthUnknown
want = nothingJudged(f, true)
}
have, known := s.health[module]
if !known {
have = delivery.HealthUnknown
have = nothingJudged(f, s.looked)
}
if have != want {
changed = true
}
}
s.health = next
s.health, s.looked = next, true
return changed
}
@@ -232,6 +235,8 @@ func (q *Session) writeHaves(ctx context.Context, bus Bus) {
}
if got, known := s.health[module]; known {
h.Health = got
} else {
h.Health = nothingJudged(f, s.looked)
}
key, err := delivery.HaveKey(node, module)
if err != nil {
@@ -452,3 +457,12 @@ func (q *Session) ReadHave(ctx context.Context, module string) (delivery.Have, b
}
return h, true
}
// nothingJudged is the health of a module a look judged no resource of: ok when it was looked at and the module applied
// and runs (nothing of it could be down), unknown otherwise.
func nothingJudged(f ModuleFact, looked bool) delivery.Health {
if looked && !f.Failed && (f.Phase == "" || f.Phase == string(delivery.PhaseRunning)) {
return delivery.HealthOK
}
return delivery.HealthUnknown
}
+23
View File
@@ -498,3 +498,26 @@ func TestAnAppliedModulesHaveNamesTheFingerprintAndTheDigestAndFollowsItsHealth(
t.Fatalf("the node key: %+v %v", n, err)
}
}
// A module applied and running that has nothing long-running to judge is ok once the engine has looked: there is nothing
// of it that could be down. Unknown kept it from ever being healthy, so the assigner's canary rule held every other node
// of mesh-host back for good (issue 1945, live on 2026-10-25: ace's have of mesh-host said unknown, and novox and shanks
// never got the new node-engine). A failed one stays failed, and before any look it is unknown.
func TestAModuleWithNothingToJudgeIsOkOnceLookedAt(t *testing.T) {
l := newHaveLink()
q, _ := aHaveQueue(l)
q.SayFacts(context.Background(), Report{Modules: []ModuleFact{{Module: "mesh-host", Build: "b"}, {Module: "web", Build: "b"},
{Module: "broken", Build: "b", Failed: true, Detail: "x"}}})
if h := l.have(t, "n1.mesh-host").Health; h != delivery.HealthUnknown {
t.Fatalf("before any look: %s", h)
}
if !q.SayHealth(context.Background(), *healthOf(map[string]string{"web": StateHealthy})) {
t.Fatal("not said")
}
if h := l.have(t, "n1.mesh-host").Health; h != delivery.HealthOK {
t.Fatalf("a running module with nothing to judge after a look: %s", h)
}
if h := l.have(t, "n1.broken").Health; h == delivery.HealthOK {
t.Fatalf("a failed module was said ok")
}
}