mesh/delivery-group group feat/335-a-busy-container-is-replaced-when-idle rejected: its order contradicts itself: novox/mesh-catalog@190608193764, novox/mesh-c…
mesh/merge-gate out of date: judged against trunk 7a41e120635b, the trunk is now 8431ac96562e; judged again on a recheck or when it is merged
mesh/repo-check out of date: judged against trunk 7a41e120635b, the trunk is now 8431ac96562e; judged again on a recheck or when it is merged
mesh/delivery checking
A send that replaced the build agent killed the check it ran, and the queue started it again elsewhere (issue 594). A container may now declare busy; the node-engine asks it before replacing it, applies everything else, says the wait, and replaces it on the first idle pass or at the bound, kept across restarts (novox/hq ADR 0335).
88 lines
2.9 KiB
Go
88 lines
2.9 KiB
Go
package declaration
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Busy is how a long-running container says it holds work that would be lost if it were stopped now
|
|
// (novox/hq ADR 0335 §1): a command run inside it, by its shell, as an exec health check is, that exits 0
|
|
// while it is busy and non-zero when it is idle. Being asked is also the container's notice that it is
|
|
// about to be replaced: from the first ask it takes no new work, so its answer ends once the work in hand
|
|
// ends.
|
|
//
|
|
// **Not part of what the container is.** Busy is not in the container's spec: a changed command changes
|
|
// when the container is replaced, never whether — a container is not recreated for its busy command alone.
|
|
type Busy struct {
|
|
Command string `json:"command"`
|
|
// Timeout is how long one ask may take; empty is BusyTimeoutDefault.
|
|
Timeout string `json:"timeout,omitempty"`
|
|
}
|
|
|
|
// The bounds of an ask (novox/hq ADR 0335).
|
|
const (
|
|
BusyTimeoutDefault = 10 * time.Second
|
|
// BusyTimeoutCeiling is the longest one ask may take: an ask runs inside an apply, and an apply waits
|
|
// for nothing longer than a look.
|
|
BusyTimeoutCeiling = time.Minute
|
|
// BusyBound is how long a container that says busy is left running after the first apply that would
|
|
// have replaced it (ADR 0335 §3). The node-engine's, not the module's: no module holds its own
|
|
// replacement for ever.
|
|
BusyBound = 15 * time.Minute
|
|
)
|
|
|
|
// UnmarshalJSON reads a busy strictly, as everything a declaration carries is read.
|
|
func (b *Busy) UnmarshalJSON(raw []byte) error {
|
|
type plain Busy
|
|
var p plain
|
|
dec := json.NewDecoder(bytes.NewReader(raw))
|
|
dec.DisallowUnknownFields()
|
|
if err := dec.Decode(&p); err != nil {
|
|
return fmt.Errorf("busy: %w", err)
|
|
}
|
|
*b = Busy(p)
|
|
return nil
|
|
}
|
|
|
|
// Within is how long one ask may take. Validated on arrival, so an unreadable timeout is the default.
|
|
func (b *Busy) Within() time.Duration {
|
|
if d, err := time.ParseDuration(b.Timeout); err == nil && d > 0 {
|
|
return d
|
|
}
|
|
return BusyTimeoutDefault
|
|
}
|
|
|
|
// problems holds a container's busy to its shape: a one-line command, a timeout within its bounds, and only
|
|
// on a container that stays up.
|
|
func (b *Busy) problems(where string, longRunning bool) []string {
|
|
if b == nil {
|
|
return nil
|
|
}
|
|
var problems []string
|
|
say := func(format string, args ...any) {
|
|
problems = append(problems, where+": "+fmt.Sprintf(format, args...))
|
|
}
|
|
if !longRunning {
|
|
say("busy is asked of a container that stays up; a step or a scheduled run is never replaced mid-run")
|
|
}
|
|
if strings.TrimSpace(b.Command) == "" {
|
|
say("busy needs a command")
|
|
}
|
|
if strings.ContainsAny(b.Command, "\n\r") {
|
|
say("busy's command is one line")
|
|
}
|
|
if b.Timeout != "" {
|
|
d, err := time.ParseDuration(b.Timeout)
|
|
switch {
|
|
case err != nil:
|
|
say("busy's timeout is a duration, not %q", b.Timeout)
|
|
case d <= 0 || d > BusyTimeoutCeiling:
|
|
say("an ask of busy takes more than nothing and at most %s, not %s", BusyTimeoutCeiling, d)
|
|
}
|
|
}
|
|
return problems
|
|
}
|