Say when the node composes again after a seat that did not answer, say what a long pass waits on, and serve the verb compose (issue 774)
On 2026-10-31 ace's route-proxy said only that the vault could not be asked, and ace said nothing for five minutes
while a pass waited after the vault restarted; the retry that already existed was invisible. A refusal for a seat or
a read that did not answer now says when the node composes again ("the node composes again at HH:MM:SS (try N)"), a
retry pass says it starts, a pass that runs on says every minute what it waits on, and the node-engine's verb
compose makes a pass now and says how it went, so a person recovers without a restart.
This commit is contained in:
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/novox/mesh-host/internal/accounts"
|
||||
"github.com/novox/mesh-host/internal/apply"
|
||||
"github.com/novox/mesh-host/internal/link"
|
||||
"github.com/novox/mesh-host/internal/selfcompose"
|
||||
"github.com/novox/mesh-host/internal/store"
|
||||
)
|
||||
|
||||
@@ -292,3 +293,36 @@ func compositionFrom(current *agentNow) func(module, digest string, diff bool) (
|
||||
return ag.Composition(ctx, module, digest, diff)
|
||||
}
|
||||
}
|
||||
|
||||
// composeReadFor bounds the verb compose's wait for its pass: a pass that waits on a seat longer says what it waits on.
|
||||
const composeReadFor = 2 * time.Minute
|
||||
|
||||
// composeFrom answers the verb compose (issue 774): a pass of the composer that runs now, and with module that module's
|
||||
// apply tried again by the node agent; refused in words while the node composes nothing of its own.
|
||||
func composeFrom(current *agentNow, composer *atomic.Pointer[selfcompose.Composer]) func(module string) (any, error) {
|
||||
return func(module string) (any, error) {
|
||||
c := composer.Load()
|
||||
if c == nil {
|
||||
return nil, errors.New("this node composes none of its own modules now (it applies the controller's " +
|
||||
"compositions, or its node agent is starting), so there is no pass to make")
|
||||
}
|
||||
ag := current.get()
|
||||
if module != "" {
|
||||
if ag == nil || !ag.Placed("")[module] {
|
||||
return nil, fmt.Errorf("%s has no want on this machine, so there is no apply of it to try again", module)
|
||||
}
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), composeReadFor)
|
||||
defer cancel()
|
||||
pass, err := c.ComposeNow(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := map[string]any{"pass": pass}
|
||||
if module != "" {
|
||||
ag.Retry(module)
|
||||
out["retried"] = module
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1142,6 +1142,7 @@ func runLink(ctx context.Context, opts options) error {
|
||||
go reconcileActs(ctx, queue.Acts)
|
||||
current := &agentNow{}
|
||||
queue.Acts.Composition = compositionFrom(current)
|
||||
queue.Acts.Compose = composeFrom(current, &composerNow)
|
||||
setRunsHere(runsFromAgent(current))
|
||||
factsOfNow := func(ctx context.Context, ag *agent.Agent) {
|
||||
sayFacts(ctx, opts, mine.Membership.Signer, queue, say, ag)
|
||||
|
||||
@@ -668,6 +668,7 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, trus
|
||||
}
|
||||
if composer != nil {
|
||||
machinesNow.Store(composer) // the machines a failing network part points at, by the placements (issue 774)
|
||||
composerNow.Store(composer) // what the verb compose asks a pass of, this run's alone
|
||||
composes.set(delivery.ComposesOwn)
|
||||
env.Composed, env.SelfComposed, env.Trusted, env.Policy = composer, true, nil, composer.Policy
|
||||
env.HoldsData = composer.HoldsData // what runs from the kept declaration says Data as its manifest does
|
||||
@@ -692,6 +693,7 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, trus
|
||||
stopRun()
|
||||
cancel()
|
||||
current.set(nil)
|
||||
composerNow.Store(nil)
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
@@ -814,6 +816,10 @@ func busyModules() []string {
|
||||
// (writeHealthReport); nil on a node that does not compose its own, whose parts then name no target.
|
||||
var machinesNow atomic.Pointer[selfcompose.Composer]
|
||||
|
||||
// composerNow is the composer of the run that composes the node's own now, nil while the node applies the controller's
|
||||
// compositions or no run is going: what the verb compose asks a pass of (issue 774).
|
||||
var composerNow atomic.Pointer[selfcompose.Composer]
|
||||
|
||||
// keptRetired says the kept declaration is read no more: every module the seat's placement puts on this node has a have of
|
||||
// its own (placedAllHave), so what runs is the agent's alone (ADR 0550 §2, design 47's L3 s9).
|
||||
var keptRetired atomic.Bool
|
||||
|
||||
Reference in New Issue
Block a user