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…
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:
+21
-7
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user