Let a drain lapse, bound the gate's wait on the controller's clock, keep a busy wait out of apply time
mesh/delivery-group group feat/335-a-busy-container-is-replaced-when-idle checking: 1 of 2 member(s) ready
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate head b0753e2aac95 merged into trunk 72214ae989b3 — pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus…
mesh/repo-check head b0753e2aac95 merged into trunk 72214ae989b3 — pass: its merge-check.sh passed

Review of #283: a drain whose replacement was withdrawn held the agent for
ever with its health green; the gate trusted the engine's until; a machine
of the rest waiting at the bound read as silent and its apply time held the
wait; an unknown mesh-builder word exited 0; plan said nothing of a busy an
older engine is not sent; and a node with a report and no send broke the
reading of every last report (the store test's NULL).
This commit is contained in:
2026-10-14 00:07:31 +02:00
parent a69c0e50dc
commit b0753e2aac
12 changed files with 403 additions and 35 deletions
+22 -4
View File
@@ -500,13 +500,21 @@ func (e Enrolment) Heard(ctx context.Context, report Report) (news bool, err err
// And how long the machine took, from the send to this first account of it (novox/hq to-be 45
// Phase 0): what the sent-not-reported bound will be set from. Said if lost, never a failure.
//
// **Not while a container waits because it said busy** (novox/hq ADR 0335 §4): the machine is not yet
// running what it was sent, and the account that says the container replaced is the one that ends the
// apply — so a walk's rest counts the wait in its delivery time, never exempted.
// **A wait for a busy container is its own measure** (novox/hq ADR 0335): the first report that says one
// waits is the machine's answer, kept as its apply with the detail ApplyBusy; the report that says the
// container replaced ends the wait, kept as a `busy-wait` of the same send — so the apply's time holds no
// busy time, and the walk reads the machine as answered and waiting, never as silent.
outcome := doing.Outcome
if doing.Waiting() {
outcome = inventory.ApplyBusy
log.Printf("%s applied declaration %s and leaves a busy container running until it says idle: %s",
report.Node, short(report.Declared), busyWords(doing.Busy))
} else if err := e.Inventory.RecordApplyDuration(ctx, report.Node, report.Declared, doing.Outcome); err != nil {
} else if replacedAfterWaiting(doing.Busy) {
if err := e.Inventory.RecordBusyDuration(ctx, report.Node, report.Declared); err != nil {
log.Printf("how long %s waited for a busy container could not be recorded: %v", report.Node, err)
}
}
if err := e.Inventory.RecordApplyDuration(ctx, report.Node, report.Declared, outcome); err != nil {
log.Printf("how long %s took to apply could not be recorded: %v", report.Node, err)
}
@@ -530,3 +538,13 @@ func busyWords(busy []inventory.BusyWait) string {
}
return strings.Join(out, "; ")
}
// replacedAfterWaiting says a report replaced a container that had waited because it was busy.
func replacedAfterWaiting(busy []inventory.BusyWait) bool {
for _, b := range busy {
if b.State != inventory.BusyWaiting {
return true
}
}
return false
}