Node-engine: the have, the node's facts, the record, the health report and the data items are the seat's contract types, written through the node-engine client; the interim bridge is gone (Have group, ADR 0634 §1, §4; issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
+64
-58
@@ -17,6 +17,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient"
|
||||
"git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
|
||||
"github.com/novox/mesh-host/internal/compositions"
|
||||
"slices"
|
||||
"sort"
|
||||
@@ -61,18 +63,18 @@ type Have struct {
|
||||
// waits for a person's yes (ADR 0547 §3).
|
||||
Judgement string
|
||||
Waits string
|
||||
// BuildAlone says, in phase waiting-yes, the move changes the build alone (novox/hq ADR 0601 §1, delivery.Have.BuildAlone).
|
||||
// BuildAlone says, in phase waiting-yes, the move changes the build alone (novox/hq ADR 0601 §1, nodeengine.HaveByNodeModuleValue.BuildAlone).
|
||||
BuildAlone bool
|
||||
// Kept says, in words, what the node keeps of the module as it runs because its manifest does not yet say it (ADR 0601
|
||||
// §4, delivery.Have.Kept): the watchman raises a condition from it.
|
||||
// §4, nodeengine.HaveByNodeModuleValue.Kept): the watchman raises a condition from it.
|
||||
Kept []string
|
||||
// Sealed is, on a node composing its own, each value the composition it names carries sealed to the node's key, by
|
||||
// slot (delivery.Have.SealedAt): kept in the have so the composer reuses them after a restart (ADR 0547 §3), each at
|
||||
// slot (nodeengine.HaveByNodeModuleValue.SealedAt): kept in the have so the composer reuses them after a restart (ADR 0547 §3), each at
|
||||
// its own slot alone (issue 1126, C1).
|
||||
Sealed compositions.Seals
|
||||
// PullWait is, on a failed have, the pull from a store its apply failed on and waits on the holder of (issue 1966
|
||||
// ENG-9, delivery.Have.PullWait): read through the have, as node_haves shows it.
|
||||
PullWait *delivery.PullWait
|
||||
// ENG-9, nodeengine.HaveByNodeModuleValue.PullWait): read through the have, as node_haves shows it.
|
||||
PullWait *nodeengine.PullWait
|
||||
}
|
||||
|
||||
// Haves is the node's have bucket as the agent uses it.
|
||||
@@ -220,7 +222,7 @@ type Agent struct {
|
||||
mu sync.Mutex
|
||||
wants map[string]wantNow // by module
|
||||
comps map[string]compNow // by composed key
|
||||
running map[string]delivery.Have
|
||||
running map[string]nodeengine.HaveByNodeModuleValue
|
||||
// applied is the composition each module was last applied from: a revision a want named from the key's history is
|
||||
// not the key's latest value, and what it holds is still held out of the others' applies.
|
||||
applied map[string]compNow
|
||||
@@ -303,7 +305,7 @@ func New(env Env) *Agent {
|
||||
if env.Say == nil {
|
||||
env.Say = func(string) {}
|
||||
}
|
||||
return &Agent{env: env, wants: map[string]wantNow{}, comps: map[string]compNow{}, running: map[string]delivery.Have{}, applied: map[string]compNow{}, had: map[string]compNow{}, keptData: map[string]bool{},
|
||||
return &Agent{env: env, wants: map[string]wantNow{}, comps: map[string]compNow{}, running: map[string]nodeengine.HaveByNodeModuleValue{}, applied: map[string]compNow{}, had: map[string]compNow{}, keptData: map[string]bool{},
|
||||
attempt: map[string]attempt{}, checked: map[string]bool{}, prior: map[string]bool{}, said: map[string]string{}, notComposed: map[string]notComposedMark{},
|
||||
pulls: map[string]*pullWait{}, settledAt: map[string]time.Time{}, settledSeq: map[string]int{},
|
||||
retry: make(chan []string, 8), reconcile: make(chan []string, 64), failedAgain: make(chan string, 8), tries: map[string]int{}, again: make(chan againAsk, 64),
|
||||
@@ -495,7 +497,7 @@ const NotComposedDetail = "the node cannot compose it, and keeps running what it
|
||||
|
||||
// composerMark says h is a mark NotComposed wrote: failed, its detail opening with NotComposedDetail.
|
||||
func composerMark(h Have) bool {
|
||||
return h.Phase == string(delivery.PhaseFailed) && strings.HasPrefix(h.Detail, NotComposedDetail)
|
||||
return h.Phase == string(nodeengineclient.PhaseFailed) && strings.HasPrefix(h.Detail, NotComposedDetail)
|
||||
}
|
||||
|
||||
// HavesReadErr is what a Haves may answer besides Read: a have absent (false, nil) told from one that could not be read
|
||||
@@ -590,7 +592,7 @@ func (a *Agent) NotComposedEach(ctx context.Context, why string, held map[string
|
||||
a.mu.Unlock()
|
||||
back := mark.prior
|
||||
if ran {
|
||||
back = Have{Module: module, FP: r.FP, Composed: r.Composed, Phase: string(delivery.PhaseRunning)}
|
||||
back = Have{Module: module, FP: r.Fp, Composed: r.Composed, Phase: string(nodeengineclient.PhaseRunning)}
|
||||
if r.Phase != "" {
|
||||
back.Phase = string(r.Phase)
|
||||
}
|
||||
@@ -603,9 +605,9 @@ func (a *Agent) NotComposedEach(ctx context.Context, why string, held map[string
|
||||
}
|
||||
if !marked {
|
||||
if ran {
|
||||
mark.prior = Have{Module: module, FP: r.FP, Composed: r.Composed, Phase: string(r.Phase)}
|
||||
mark.prior = Have{Module: module, FP: r.Fp, Composed: r.Composed, Phase: string(r.Phase)}
|
||||
if mark.prior.Phase == "" {
|
||||
mark.prior.Phase = string(delivery.PhaseRunning)
|
||||
mark.prior.Phase = string(nodeengineclient.PhaseRunning)
|
||||
}
|
||||
} else if h, found, unread := a.readHave(ctx, module); found {
|
||||
mark.prior = h // what its last have said runs: this agent has run nothing of it since its start
|
||||
@@ -624,7 +626,7 @@ func (a *Agent) NotComposedEach(ctx context.Context, why string, held map[string
|
||||
// A have that already says another cause (an apply that failed, a backup held, an apply under way) keeps it: a
|
||||
// composer's mark over it would hide that cause, and baseline would take the mark for what runs (review of the
|
||||
// switch, round 1).
|
||||
if p := mark.prior.Phase; !marked && p != "" && p != string(delivery.PhaseRunning) && !composerMark(mark.prior) {
|
||||
if p := mark.prior.Phase; !marked && p != "" && p != string(nodeengineclient.PhaseRunning) && !composerMark(mark.prior) {
|
||||
a.mu.Lock()
|
||||
a.sayOnce("unmarked-cause:"+module, fmt.Sprintf("%s is not marked: its have says %s (%s), and the node cannot compose it: %s",
|
||||
module, p, mark.prior.Detail, why))
|
||||
@@ -639,7 +641,7 @@ func (a *Agent) NotComposedEach(ctx context.Context, why string, held map[string
|
||||
a.notComposed[module] = mark
|
||||
a.mu.Unlock()
|
||||
a.writeHave(ctx, Have{Module: module, FP: mark.prior.FP, Composed: mark.prior.Composed, Sealed: mark.prior.Sealed,
|
||||
Phase: string(delivery.PhaseFailed), Detail: NotComposedDetail + why})
|
||||
Phase: string(nodeengineclient.PhaseFailed), Detail: NotComposedDetail + why})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -696,7 +698,7 @@ func (a *Agent) KeptNow(ctx context.Context, kept map[string][]string) {
|
||||
a.mu.Unlock()
|
||||
slices.Sort(changed)
|
||||
for _, m := range changed {
|
||||
if h, ok := a.env.Haves.Read(ctx, m); ok && m != NodeModule && h.Phase != string(delivery.PhaseWaitingYes) {
|
||||
if h, ok := a.env.Haves.Read(ctx, m); ok && m != NodeModule && h.Phase != string(nodeengineclient.PhaseWaitingYes) {
|
||||
h.Module = m
|
||||
a.writeHave(ctx, h)
|
||||
}
|
||||
@@ -733,10 +735,10 @@ func (a *Agent) Running() []compositions.Composed {
|
||||
var out []compositions.Composed
|
||||
for _, m := range mods {
|
||||
h := a.running[m]
|
||||
if h.Phase == delivery.PhaseRemoved {
|
||||
if h.Phase == nodeengineclient.PhaseRemoved {
|
||||
continue
|
||||
}
|
||||
if c, ok := a.compositionFor(m, h.FP); ok && c.Err == nil && c.Dig == h.Composed {
|
||||
if c, ok := a.compositionFor(m, h.Fp); ok && c.Err == nil && c.Dig == h.Composed {
|
||||
out = append(out, c.C)
|
||||
} else if c, ok := a.applied[m]; ok && c.Dig == h.Composed {
|
||||
out = append(out, c.C)
|
||||
@@ -890,10 +892,10 @@ func (a *Agent) others(except string) []compositions.Composed {
|
||||
sort.Strings(mods)
|
||||
for _, m := range mods {
|
||||
h := a.running[m]
|
||||
if h.Phase == delivery.PhaseRemoved {
|
||||
if h.Phase == nodeengineclient.PhaseRemoved {
|
||||
continue
|
||||
}
|
||||
if c, ok := a.compositionFor(m, h.FP); ok && c.Err == nil && c.Dig == h.Composed {
|
||||
if c, ok := a.compositionFor(m, h.Fp); ok && c.Err == nil && c.Dig == h.Composed {
|
||||
out = append(out, c.C)
|
||||
} else if c, ok := a.applied[m]; ok && c.Dig == h.Composed {
|
||||
out = append(out, c.C)
|
||||
@@ -1091,8 +1093,8 @@ func (a *Agent) keepApplied(module string, comp compNow) {
|
||||
// backup, or a composer's mark over what still runs. A have backing up, applying, checking, failed or waiting names a
|
||||
// composition not applied, or not yet (issue 924, review of 031e106).
|
||||
func haveRuns(h Have) bool {
|
||||
switch delivery.Phase(h.Phase) {
|
||||
case "", delivery.PhaseRunning, delivery.PhaseHeld:
|
||||
switch string(h.Phase) {
|
||||
case "", nodeengineclient.PhaseRunning, nodeengineclient.PhaseHeld:
|
||||
return true
|
||||
}
|
||||
return composerMark(h)
|
||||
@@ -1171,7 +1173,7 @@ func (a *Agent) heldOut(except string) []compositions.Composed {
|
||||
if err != nil || isNode || c.Err != nil || m == except || held[m] {
|
||||
continue
|
||||
}
|
||||
if h, ok := a.running[m]; ok && h.Phase != delivery.PhaseRemoved {
|
||||
if h, ok := a.running[m]; ok && h.Phase != nodeengineclient.PhaseRemoved {
|
||||
continue
|
||||
}
|
||||
if w := a.wants[m]; w.Present && w.Want.Removed {
|
||||
@@ -1281,7 +1283,7 @@ func (a *Agent) RetryFailed(module string) error {
|
||||
return fmt.Errorf("%s has no want on this machine, so there is no apply of it to try again", module)
|
||||
case tried && att.Held:
|
||||
return fmt.Errorf("%s is held for a failed backup: the operator's answer releases it", module)
|
||||
case !tried && ran && r.FP == w.Want.FP && r.Phase != delivery.PhaseRemoved:
|
||||
case !tried && ran && r.Fp == w.Want.FP && r.Phase != nodeengineclient.PhaseRemoved:
|
||||
return fmt.Errorf("%s runs what its want names, nothing to retry", module)
|
||||
case !tried:
|
||||
return fmt.Errorf("%s has no failed apply to try again: it waits for its composition, or settles on its own", module)
|
||||
@@ -1314,7 +1316,7 @@ func (a *Agent) HoldsNode() bool {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
h, ok := a.running[NodeModule]
|
||||
return ok && h.Phase != delivery.PhaseRemoved
|
||||
return ok && h.Phase != nodeengineclient.PhaseRemoved
|
||||
}
|
||||
|
||||
func (a *Agent) settleOne(ctx context.Context, module string) {
|
||||
@@ -1370,7 +1372,7 @@ func (a *Agent) settleOne(ctx context.Context, module string) {
|
||||
}
|
||||
a.mu.Lock()
|
||||
r, runs := a.running[module]
|
||||
runsWant := runs && r.FP == w.Want.FP && r.Composed == w.Want.Composed && r.Phase != delivery.PhaseRemoved
|
||||
runsWant := runs && r.Fp == w.Want.FP && r.Composed == w.Want.Composed && r.Phase != nodeengineclient.PhaseRemoved
|
||||
att, tried := a.attempt[module]
|
||||
same := tried && att.FP == w.Want.FP && att.Composed == w.Want.Composed
|
||||
if same && att.Held {
|
||||
@@ -1445,7 +1447,7 @@ func (a *Agent) baseline(ctx context.Context, module string, w wantNow, comp com
|
||||
a.mu.Lock()
|
||||
a.prior[module] = true
|
||||
a.mu.Unlock()
|
||||
if h.Phase == string(delivery.PhaseHeld) && h.Detail == BackupInterrupted && w.Want.Backup != delivery.BackupSkip {
|
||||
if h.Phase == string(nodeengineclient.PhaseHeld) && h.Detail == BackupInterrupted && w.Want.Backup != delivery.BackupSkip {
|
||||
// Held only because this node agent stopped while the backup ran (postgres on novox, 2026-10-31): no
|
||||
// failure and nothing for the operator to answer. It is backed up again and applied (a.prior makes the
|
||||
// apply back it up first); the want still asking a backup is all the answer needed.
|
||||
@@ -1454,7 +1456,7 @@ func (a *Agent) baseline(ctx context.Context, module string, w wantNow, comp com
|
||||
a.mu.Unlock()
|
||||
return false
|
||||
}
|
||||
if h.Phase == string(delivery.PhaseHeld) && (w.Want.Backup == delivery.BackupAgain || w.Want.Backup == delivery.BackupSkip) {
|
||||
if h.Phase == string(nodeengineclient.PhaseHeld) && (w.Want.Backup == delivery.BackupAgain || w.Want.Backup == delivery.BackupSkip) {
|
||||
// Held for a failed backup, and the want already carries the operator's answer, written before this engine
|
||||
// started (nats on novox, 2026-11-04: "Back up again" answered, then the engine restarted and took the
|
||||
// answer for the hold's own revision, so it was never acted on; issue 1585). The answer is acted on now:
|
||||
@@ -1467,7 +1469,7 @@ func (a *Agent) baseline(ctx context.Context, module string, w wantNow, comp com
|
||||
a.mu.Unlock()
|
||||
return false
|
||||
}
|
||||
if h.Phase == string(delivery.PhaseHeld) {
|
||||
if h.Phase == string(nodeengineclient.PhaseHeld) {
|
||||
// Held for a failed backup, and the operator's answer is still to come: the hold is kept, nothing is
|
||||
// backed up again or applied, and the next revision of the want is the answer (ADR 0568 §4). Said, so
|
||||
// why it holds is read from the journal and not guessed (issue 1585: this branch was silent).
|
||||
@@ -1478,7 +1480,7 @@ func (a *Agent) baseline(ctx context.Context, module string, w wantNow, comp com
|
||||
a.attempt[module] = attempt{FP: w.Want.FP, Composed: w.Want.Composed, WantRev: w.Rev, CompRev: comp.Rev, Held: true,
|
||||
AnsweredRev: w.Rev}
|
||||
if h.Composed != "" {
|
||||
a.running[module] = delivery.Have{FP: h.FP, Composed: h.Composed, Phase: delivery.PhaseRunning}
|
||||
a.running[module] = nodeengine.HaveByNodeModuleValue{Fp: h.FP, Composed: h.Composed, Phase: nodeengineclient.PhaseRunning}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
a.runsChanged(module + " was taken as held")
|
||||
@@ -1495,18 +1497,18 @@ func (a *Agent) baseline(ctx context.Context, module string, w wantNow, comp com
|
||||
h.Detail = ""
|
||||
}
|
||||
if h.FP == w.Want.FP && h.Composed == w.Want.Composed &&
|
||||
(h.Phase == "" || h.Phase == string(delivery.PhaseRunning) || marked) {
|
||||
(h.Phase == "" || h.Phase == string(nodeengineclient.PhaseRunning) || marked) {
|
||||
if a.inactiveAtStart(ctx, module, comp) {
|
||||
return false
|
||||
}
|
||||
a.mu.Lock()
|
||||
a.running[module] = delivery.Have{FP: h.FP, Composed: h.Composed, Phase: delivery.PhaseRunning}
|
||||
a.running[module] = nodeengine.HaveByNodeModuleValue{Fp: h.FP, Composed: h.Composed, Phase: nodeengineclient.PhaseRunning}
|
||||
a.mu.Unlock()
|
||||
a.runsChanged(module + " was taken from its have")
|
||||
// Said again, so the session holds the module and its key carries the health this engine judges,
|
||||
// not the one an older engine left there (xorg's unknown on g14 and shanks, 2026-10-25).
|
||||
a.writeHave(ctx, Have{Module: module, FP: h.FP, Composed: h.Composed,
|
||||
Phase: string(delivery.PhaseRunning), Detail: h.Detail, Sealed: h.Sealed})
|
||||
Phase: string(nodeengineclient.PhaseRunning), Detail: h.Detail, Sealed: h.Sealed})
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -1529,10 +1531,10 @@ func (a *Agent) baseline(ctx context.Context, module string, w wantNow, comp com
|
||||
return false
|
||||
}
|
||||
a.mu.Lock()
|
||||
a.running[module] = delivery.Have{FP: w.Want.FP, Composed: w.Want.Composed, Phase: delivery.PhaseRunning}
|
||||
a.running[module] = nodeengine.HaveByNodeModuleValue{Fp: w.Want.FP, Composed: w.Want.Composed, Phase: nodeengineclient.PhaseRunning}
|
||||
a.mu.Unlock()
|
||||
a.runsChanged(module + " was taken from the kept declaration")
|
||||
a.writeHave(ctx, Have{Module: module, FP: w.Want.FP, Composed: w.Want.Composed, Phase: string(delivery.PhaseRunning)})
|
||||
a.writeHave(ctx, Have{Module: module, FP: w.Want.FP, Composed: w.Want.Composed, Phase: string(nodeengineclient.PhaseRunning)})
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1567,10 +1569,10 @@ func (a *Agent) apply(ctx context.Context, module string, w wantNow, comp compNo
|
||||
a.mu.Unlock()
|
||||
if !runs {
|
||||
if h, found, _ := a.readHave(ctx, module); found {
|
||||
prev = delivery.Have{FP: h.FP, Composed: h.Composed}
|
||||
prev = nodeengine.HaveByNodeModuleValue{Fp: h.FP, Composed: h.Composed}
|
||||
}
|
||||
}
|
||||
a.writeHave(ctx, Have{Module: module, FP: fp, Composed: dig, Phase: string(delivery.PhaseBackingUp)})
|
||||
a.writeHave(ctx, Have{Module: module, FP: fp, Composed: dig, Phase: string(nodeengineclient.PhaseBackingUp)})
|
||||
if got := a.env.Backup(ctx, module); !got.Done {
|
||||
words := got.Words
|
||||
if got.Interrupted || ctx.Err() != nil {
|
||||
@@ -1585,12 +1587,12 @@ func (a *Agent) apply(ctx context.Context, module string, w wantNow, comp compNo
|
||||
a.attempt[module] = attempt{FP: fp, Composed: dig, WantRev: w.Rev, CompRev: comp.Rev, Held: true, AnsweredRev: w.Rev}
|
||||
a.mu.Unlock()
|
||||
// It keeps running what it has: the have says what it holds and why it waits.
|
||||
a.writeHave(ctx, Have{Module: module, FP: prev.FP, Composed: prev.Composed, Phase: string(delivery.PhaseHeld),
|
||||
a.writeHave(ctx, Have{Module: module, FP: prev.Fp, Composed: prev.Composed, Phase: string(nodeengineclient.PhaseHeld),
|
||||
Detail: words})
|
||||
return
|
||||
}
|
||||
}
|
||||
a.writeHave(ctx, Have{Module: module, FP: fp, Composed: dig, Phase: string(delivery.PhaseApplying)})
|
||||
a.writeHave(ctx, Have{Module: module, FP: fp, Composed: dig, Phase: string(nodeengineclient.PhaseApplying)})
|
||||
res := a.env.Apply(ctx, comp.C, others)
|
||||
a.mu.Lock()
|
||||
if !res.Failed || res.Transient {
|
||||
@@ -1607,7 +1609,7 @@ func (a *Agent) apply(ctx context.Context, module string, w wantNow, comp compNo
|
||||
gen := a.gen
|
||||
a.attempt[module] = attempt{FP: fp, Composed: dig, WantRev: w.Rev, CompRev: comp.Rev, Transient: true, Gen: gen}
|
||||
a.mu.Unlock()
|
||||
phase, when := delivery.PhaseApplying, "when the bus answers again"
|
||||
phase, when := nodeengineclient.PhaseApplying, "when the bus answers again"
|
||||
if a.env.Backoff != nil {
|
||||
wait := a.env.Backoff(try)
|
||||
when = "in " + wait.String()
|
||||
@@ -1622,7 +1624,7 @@ func (a *Agent) apply(ctx context.Context, module string, w wantNow, comp compNo
|
||||
})
|
||||
}
|
||||
if try > transientFor {
|
||||
phase = delivery.PhaseFailed // still retried, and said as failed: no longer a moment
|
||||
phase = nodeengineclient.PhaseFailed // still retried, and said as failed: no longer a moment
|
||||
}
|
||||
a.env.Say(fmt.Sprintf("applying %s failed because the bus did not answer (try %d), retried %s: %s", what, try, when, res.Detail))
|
||||
a.writeHave(ctx, Have{Module: module, FP: fp, Composed: dig, Phase: string(phase),
|
||||
@@ -1640,7 +1642,7 @@ func (a *Agent) apply(ctx context.Context, module string, w wantNow, comp compNo
|
||||
wait := a.pullWaitOf(module)
|
||||
a.mu.Unlock()
|
||||
a.env.Say(fmt.Sprintf("applying %s failed: %s", what, res.Detail))
|
||||
a.writeHave(ctx, Have{Module: module, FP: fp, Composed: dig, Phase: string(delivery.PhaseFailed), Detail: detail,
|
||||
a.writeHave(ctx, Have{Module: module, FP: fp, Composed: dig, Phase: string(nodeengineclient.PhaseFailed), Detail: detail,
|
||||
PullWait: wait})
|
||||
if again != "" {
|
||||
a.askPullAgain(module, again)
|
||||
@@ -1650,14 +1652,14 @@ func (a *Agent) apply(ctx context.Context, module string, w wantNow, comp compNo
|
||||
delete(a.pulls, module)
|
||||
delete(a.attempt, module)
|
||||
delete(a.notComposed, module) // the apply says what it runs now
|
||||
a.running[module] = delivery.Have{FP: fp, Composed: dig, Phase: delivery.PhaseRunning}
|
||||
a.running[module] = nodeengine.HaveByNodeModuleValue{Fp: fp, Composed: dig, Phase: nodeengineclient.PhaseRunning}
|
||||
a.applied[module] = comp
|
||||
a.mu.Unlock()
|
||||
a.keepApplied(module, comp)
|
||||
if a.env.Settled != nil {
|
||||
a.env.Settled(module)
|
||||
}
|
||||
a.writeHave(ctx, Have{Module: module, FP: fp, Composed: dig, Phase: string(delivery.PhaseRunning), Detail: res.Detail})
|
||||
a.writeHave(ctx, Have{Module: module, FP: fp, Composed: dig, Phase: string(nodeengineclient.PhaseRunning), Detail: res.Detail})
|
||||
}
|
||||
|
||||
func (a *Agent) remove(ctx context.Context, module string, w wantNow) {
|
||||
@@ -1668,7 +1670,7 @@ func (a *Agent) remove(ctx context.Context, module string, w wantNow) {
|
||||
a.mu.Unlock()
|
||||
return
|
||||
}
|
||||
if ran && r.Phase == delivery.PhaseRemoved {
|
||||
if ran && r.Phase == nodeengineclient.PhaseRemoved {
|
||||
a.mu.Unlock()
|
||||
return
|
||||
}
|
||||
@@ -1685,7 +1687,7 @@ func (a *Agent) remove(ctx context.Context, module string, w wantNow) {
|
||||
delete(a.attempt, module)
|
||||
delete(a.pulls, module)
|
||||
delete(a.had, module)
|
||||
a.running[module] = delivery.Have{Phase: delivery.PhaseRemoved}
|
||||
a.running[module] = nodeengine.HaveByNodeModuleValue{Phase: nodeengineclient.PhaseRemoved}
|
||||
a.mu.Unlock()
|
||||
if a.env.Applied != nil {
|
||||
if err := a.env.Applied.Drop(module); err != nil {
|
||||
@@ -1766,17 +1768,17 @@ func (a *Agent) waitsForYes(ctx context.Context, module string, w wantNow, comp
|
||||
a.readKeptData(ctx, module) // the manifest may read now though it did not at the start
|
||||
a.mu.Lock()
|
||||
r, ran := a.running[module]
|
||||
if ran && r.Composed == comp.Dig && r.Phase != delivery.PhaseRemoved {
|
||||
if ran && r.Composed == comp.Dig && r.Phase != nodeengineclient.PhaseRemoved {
|
||||
a.mu.Unlock()
|
||||
// Composed back to what runs: a have still waiting for a yes for a move since gone says running again, or it waits
|
||||
// for a yes nobody needs (issue 774, 2026-10-31: four providers stayed waiting-yes once their consumers composed).
|
||||
if h, ok := a.env.Haves.Read(ctx, module); ok && h.Phase == string(delivery.PhaseWaitingYes) {
|
||||
if h, ok := a.env.Haves.Read(ctx, module); ok && h.Phase == string(nodeengineclient.PhaseWaitingYes) {
|
||||
// Its sealed values only when they are of what runs; else writeHave reads them (sealedOf).
|
||||
var sealed compositions.Seals
|
||||
if h.Composed == r.Composed {
|
||||
sealed = h.Sealed
|
||||
}
|
||||
a.writeHave(ctx, Have{Module: module, FP: r.FP, Composed: r.Composed, Phase: string(delivery.PhaseRunning), Sealed: sealed})
|
||||
a.writeHave(ctx, Have{Module: module, FP: r.Fp, Composed: r.Composed, Phase: string(nodeengineclient.PhaseRunning), Sealed: sealed})
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1799,12 +1801,12 @@ func (a *Agent) waitsForYes(ctx context.Context, module string, w wantNow, comp
|
||||
case known:
|
||||
j = judge.JudgeMove(&from, comp.C)
|
||||
case prior:
|
||||
j = judge.Judged{Judgement: delivery.JudgedCannot, Words: "what " + module + " runs on this node cannot be read"}
|
||||
j = judge.Judged{Judgement: nodeengineclient.JudgedCannot, Words: "what " + module + " runs on this node cannot be read"}
|
||||
default:
|
||||
j = judge.JudgeMove(nil, comp.C)
|
||||
}
|
||||
switch j.Judgement {
|
||||
case delivery.JudgedRecreates, delivery.JudgedMovesData, delivery.JudgedCannot:
|
||||
case nodeengineclient.JudgedRecreates, nodeengineclient.JudgedMovesData, nodeengineclient.JudgedCannot:
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@@ -1812,7 +1814,7 @@ func (a *Agent) waitsForYes(ctx context.Context, module string, w wantNow, comp
|
||||
a.sayOnce("yes:"+module+":"+comp.Dig, fmt.Sprintf("%s at %s waits for a person's yes: %s", module, w.Want.FP, j.Words))
|
||||
prev := a.running[module]
|
||||
a.mu.Unlock()
|
||||
a.writeHave(ctx, Have{Module: module, FP: prev.FP, Composed: prev.Composed, Phase: string(delivery.PhaseWaitingYes),
|
||||
a.writeHave(ctx, Have{Module: module, FP: prev.Fp, Composed: prev.Composed, Phase: string(nodeengineclient.PhaseWaitingYes),
|
||||
Detail: j.Words, Judgement: string(j.Judgement), Waits: comp.Dig, BuildAlone: a.buildAlone(ctx, module, prev, w.Want.FP)})
|
||||
return true
|
||||
}
|
||||
@@ -1820,17 +1822,17 @@ func (a *Agent) waitsForYes(ctx context.Context, module string, w wantNow, comp
|
||||
// buildAlone says a move of module from what runs to the build want changes the build alone (ADR 0601 §1): another build than
|
||||
// the one it runs, and the build it runs composed with today's settings and secrets is the composition it runs. Anything it
|
||||
// cannot tell is no: the move is then asked of a person.
|
||||
func (a *Agent) buildAlone(ctx context.Context, module string, prev delivery.Have, want string) bool {
|
||||
if prev.FP == "" && prev.Composed == "" {
|
||||
func (a *Agent) buildAlone(ctx context.Context, module string, prev nodeengine.HaveByNodeModuleValue, want string) bool {
|
||||
if prev.Fp == "" && prev.Composed == "" {
|
||||
// Nothing of it run since this agent's start: its last have says what runs, as the have written keeps it.
|
||||
if h, ok := a.env.Haves.Read(ctx, module); ok {
|
||||
prev.FP, prev.Composed = h.FP, h.Composed
|
||||
prev.Fp, prev.Composed = h.FP, h.Composed
|
||||
}
|
||||
}
|
||||
if a.env.ComposedToday == nil || prev.FP == "" || prev.Composed == "" || prev.FP == want {
|
||||
if a.env.ComposedToday == nil || prev.Fp == "" || prev.Composed == "" || prev.Fp == want {
|
||||
return false
|
||||
}
|
||||
dig, ok := a.env.ComposedToday(ctx, module, prev.FP)
|
||||
dig, ok := a.env.ComposedToday(ctx, module, prev.Fp)
|
||||
return ok && dig == prev.Composed
|
||||
}
|
||||
|
||||
@@ -2001,11 +2003,15 @@ func (a *Agent) Settled(holder string) {
|
||||
|
||||
// pullWaitOf is the pull a failed apply of module waits on, as its have carries it (ENG-9): the image, the seat and its
|
||||
// holder, when the pull began, and the last retry with what triggered it; nil when it waits on no holder. a.mu is held.
|
||||
func (a *Agent) pullWaitOf(module string) *delivery.PullWait {
|
||||
func (a *Agent) pullWaitOf(module string) *nodeengine.PullWait {
|
||||
pw := a.pulls[module]
|
||||
if pw == nil {
|
||||
return nil
|
||||
}
|
||||
return &delivery.PullWait{Image: pw.Image, Seat: pw.Seat, Holder: pw.Holder, Began: pw.Started, Retried: pw.last,
|
||||
RetriedAt: pw.lastAt}
|
||||
out := &nodeengine.PullWait{Image: pw.Image, Seat: pw.Seat, Holder: pw.Holder, Began: pw.Started, Retried: pw.last}
|
||||
if !pw.lastAt.IsZero() {
|
||||
at := pw.lastAt
|
||||
out.RetriedAt = &at
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user