Say a unit's restart count is its whole life, not since its state began
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds mesh-controller → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed

The count comes from systemd's NRestarts or the runtime's restart count; beside
"since" it read as thousands of restarts tonight (novox/hq issue 458).
This commit is contained in:
2026-10-12 10:42:19 +02:00
parent 3bd984c41e
commit 0d08d5c730
2 changed files with 41 additions and 5 deletions
+13 -5
View File
@@ -497,9 +497,10 @@ func moduleUnhealthyObservation(module, node string, rs []inventory.ResourceHeal
continue
}
words = append(words, fmt.Sprintf("its %s %s %s", r.Kind, r.Resource, reasonWords(r)))
said = append(said, fmt.Sprintf("%s (%s %s): %s, %d look(s) in a row, %d restart(s) counted, since %s",
r.Resource, r.Kind, r.Target, orNotSaid(r.Reason), r.Streak, r.Restarts,
r.Since.UTC().Format("2006-01-02 15:04:05 MST")))
// The restart count is the unit's or container's whole life, never the time since (novox/hq issue 458).
said = append(said, fmt.Sprintf("%s (%s %s): %s, since %s, %d look(s) in a row, %s",
r.Resource, r.Kind, r.Target, orNotSaid(r.Reason), r.Since.UTC().Format("2006-01-02 15:04:05 MST"),
r.Streak, restartsWords(r.Restarts)))
}
return conditions.Observation{Scope: conditions.ScopeModule, ID: module + "." + node, Token: "unhealthy",
Kind: kindModuleUnhealthy, Machine: node, Severity: conditions.Warning, Source: sourceHealth,
@@ -513,6 +514,13 @@ func moduleUnhealthyObservation(module, node string, rs []inventory.ResourceHeal
Resolved: fmt.Sprintf("%s works again on %s", module, node)}
}
// restartsWords is a restart count as what it is: the engine reads it from systemd's NRestarts or the
// runtime's restart count, which run over the unit's or container's whole life, so it is never said beside a
// "since" as if it counted from there (novox/hq issue 458).
func restartsWords(n int) string {
return fmt.Sprintf("%d restart(s) over its whole life", n)
}
// reasonWords is why a resource is unhealthy, as a person reads it.
func reasonWords(r inventory.ResourceHealth) string {
// An account waiting for a new login (ADR 0252) is said in the mesh's words, not the engine's.
@@ -521,7 +529,7 @@ func reasonWords(r inventory.ResourceHealth) string {
}
switch r.Reason {
case "restarting":
return fmt.Sprintf("keeps restarting (%d restart(s) counted)", r.Restarts)
return fmt.Sprintf("keeps restarting (%s)", restartsWords(r.Restarts))
case "down":
return "is not running"
case "":
@@ -725,7 +733,7 @@ func healthLines(h inventory.NodeHealth, had bool, now time.Time) []string {
line += " — " + r.Reason
}
if r.Restarts > 0 {
line += fmt.Sprintf(", %d restart(s) counted", r.Restarts)
line += ", " + restartsWords(r.Restarts) + ", not since then"
}
if r.State == link.StateUnhealthy && h.Streaks[r.Module] < moduleUnhealthyAfter {
line += " (unconfirmed: said once)"