H-18: running carries healthy and is said again when it changes

Builds: design 47 G §520
This commit is contained in:
2026-11-12 04:54:19 +01:00
parent 7d06ef7997
commit 94f6c3dba4
2 changed files with 19 additions and 9 deletions
+14 -8
View File
@@ -919,26 +919,32 @@ func nothingJudged(f ModuleFact, looked bool) string {
return nodeengineclient.HealthUnknown
}
// runningToSay is the event running for a module whose have was just written running and healthy at a fingerprint whose
// manifest is known, when it has not been said for that fingerprint and manifest (design 47, *What each step says*): the
// module, the fingerprint and the digest of the manifest of that build. A module not yet healthy, or one whose manifest
// the node does not know, says nothing, and is said at the write that knows. Under s.mu.
// runningToSay is the event running for a module whose have was just written running with a health of ok or failed at a
// fingerprint whose manifest is known, when it has not been said for that fingerprint, manifest and health (design 47 G
// §520): `healthy` says the build's health, and it is said again each time that changes and, the said set being empty, for
// every module the have holds at the node-engine's start. A module whose health is waits or unknown, or whose manifest the
// node does not know, says nothing, and is said at the write that knows. Under s.mu.
func (s *haveState) runningToSay(module string, h nodeengine.HaveByNodeModuleValue, manifest func(module, fp string) string) (nodeengine.Running, bool) {
if h.Phase != nodeengineclient.PhaseRunning || h.Health != nodeengineclient.HealthOK || h.Fp == "" || manifest == nil {
if h.Phase != nodeengineclient.PhaseRunning || h.Fp == "" || manifest == nil {
return nodeengine.Running{}, false
}
if h.Health != nodeengineclient.HealthOK && h.Health != nodeengineclient.HealthFailed {
return nodeengine.Running{}, false
}
digest := manifest(module, h.Fp)
if digest == "" {
return nodeengine.Running{}, false
}
if s.runningSaid[module] == h.Fp+"\x00"+digest {
healthy := h.Health == nodeengineclient.HealthOK
said := fmt.Sprintf("%s\x00%s\x00%t", h.Fp, digest, healthy)
if s.runningSaid[module] == said {
return nodeengine.Running{}, false
}
if s.runningSaid == nil {
s.runningSaid = map[string]string{}
}
s.runningSaid[module] = h.Fp + "\x00" + digest
return nodeengine.Running{V: sdkkeys.Version, Module: module, Fingerprint: h.Fp, Manifest: digest}, true
s.runningSaid[module] = said
return nodeengine.Running{V: sdkkeys.Version, Module: module, Fingerprint: h.Fp, Manifest: digest, Healthy: &healthy}, true
}
// waitToSay is the event wait-held for a have written with a wait the last one did not say, or wait-ended for one written with
+5 -1
View File
@@ -722,7 +722,7 @@ func TestAnAppliedModulesHaveNamesTheFingerprintAndTheDigestAndFollowsItsHealth(
q.SayHealth(context.Background(), *healthOf(map[string]string{"web": StateHealthy}))
q.SayHealth(context.Background(), *healthOf(map[string]string{"web": StateHealthy}))
if got := l.saidOn("mesh.seat.node-engine.event.running.n1.web "); len(got) != 1 ||
!strings.Contains(got[0], `"fingerprint":"`+fp+`"`) || !strings.Contains(got[0], `"manifest":"sha256:`+strings.Repeat("c", 64)+`"`) {
!strings.Contains(got[0], `"healthy":true`) || !strings.Contains(got[0], `"fingerprint":"`+fp+`"`) || !strings.Contains(got[0], `"manifest":"sha256:`+strings.Repeat("c", 64)+`"`) {
t.Fatalf("running said %v, want one for the healthy build", got)
}
// A have with a wait says wait-held once on wait-held.<node>.<module>, and wait-ended when the wait is gone (design 47 G §541, A-113).
@@ -750,6 +750,10 @@ func TestAnAppliedModulesHaveNamesTheFingerprintAndTheDigestAndFollowsItsHealth(
if got := l.have(t, "n1.web"); got.Health != nodeengineclient.HealthFailed || got.Fp != fp || got.Composed != dig {
t.Fatalf("a failing check after the start window: %+v", got)
}
// §520: running is said again when healthy changes, now false, and the stream's newest says it.
if got := l.saidOn("mesh.seat.node-engine.event.running.n1.web "); len(got) != 2 || !strings.Contains(got[1], `"healthy":false`) {
t.Fatalf("running said %v after the check failed, want a second with healthy false", got)
}
// The bus's user list written alone rides on the module's have, and an apply after it keeps it (ADR 0547 §8).
w := nodeengine.UserListWritten{Source: "n1.web." + fp, Revision: 4, Wanted: dig, Digest: dig, Users: 3,