A put without a hold ends once the node runs its fingerprint healthy, and assigner_puts says a put older than the newest build
mesh/delivery ready: it is merged on the Approve
mesh/check pass b71358e2cc14 on fc21f2d1bd56 | unit 708 run, 794 kept | integration 717 run, 851 kept | composition passed in 148s on 4 machines
mesh/delivery ready: it is merged on the Approve
mesh/check pass b71358e2cc14 on fc21f2d1bd56 | unit 708 run, 794 kept | integration 717 run, 851 kept | composition passed in 148s on 4 machines
A put with hold false waited for the node's composition digest to equal its own and never ended when the node composes its own, pinning the module's want (issue 1966, F21). Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -458,6 +458,13 @@ func putOfTheBus(t *testing.T) {
|
||||
if got := step(); got.Step != puts.Released {
|
||||
t.Fatalf("released, the put stands at %s", got.Step)
|
||||
}
|
||||
// A put that stands at an older version than the newest says so (issue 1966): it pins the node's want.
|
||||
okey, _ := puts.Key("g14", "mesh-catalog")
|
||||
older, _ := puts.Encode(puts.Put{FP: meshcatalogclient.SourcePrefix2 + strings.Repeat("b", 64), Why: "old"}.Said(now, puts.Wanted, "wanted"))
|
||||
putsKV.values[okey] = older
|
||||
if l, _ := s.doPuts(map[string]any{"module": "mesh-catalog"}); l.(map[string]any)["puts"].([]putAnswer)[0].Older != fp {
|
||||
t.Errorf("a put older than the newest build says %+v", l)
|
||||
}
|
||||
listed, _ := s.doPuts(map[string]any{"module": "nats"})
|
||||
if l := listed.(map[string]any)["puts"].([]putAnswer); len(l) != 1 || l[0].Step != puts.Released || len(l[0].Steps) < 6 {
|
||||
t.Errorf("puts lists %+v", l)
|
||||
|
||||
@@ -130,6 +130,9 @@ type putAnswer struct {
|
||||
Module string `json:"module"`
|
||||
puts.Put
|
||||
Follow string `json:"follow,omitempty"`
|
||||
// Older says a put that has not ended stands at an older version than the newest build, and which that is: it pins the
|
||||
// node's want until it ends or is released.
|
||||
Older string `json:"older-than-newest,omitempty"`
|
||||
}
|
||||
|
||||
// doPut is the tool assigner_put.
|
||||
@@ -249,6 +252,8 @@ func (s *putTools) doPuts(args map[string]any) (any, error) {
|
||||
}
|
||||
sort.Strings(keys)
|
||||
out := []putAnswer{}
|
||||
newest := map[string]string{} // the indexer's newest fingerprint by module, asked once and only for a put that stands
|
||||
asked := false
|
||||
for _, key := range keys {
|
||||
node, module, err := puts.ParseKey(key)
|
||||
if err != nil || (arg(args, "node") != "" && node != arg(args, "node")) || (arg(args, "module") != "" && module != arg(args, "module")) {
|
||||
@@ -262,7 +267,23 @@ func (s *putTools) doPuts(args map[string]any) (any, error) {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
out = append(out, putAnswer{Node: node, Module: module, Put: p})
|
||||
a := putAnswer{Node: node, Module: module, Put: p}
|
||||
if !puts.Ended(p.Step) {
|
||||
if !asked {
|
||||
asked = true
|
||||
if mods, err := indexerView(s.ask, indexerModules); err == nil {
|
||||
for name, raw := range mods {
|
||||
if m, err := meshcatalogclient.DecodeModule(raw); err == nil {
|
||||
newest[name] = m.Fp2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if n := newest[module]; n != "" && n != p.FP {
|
||||
a.Older = n
|
||||
}
|
||||
}
|
||||
out = append(out, a)
|
||||
}
|
||||
return map[string]any{"puts": out}, nil
|
||||
}
|
||||
|
||||
@@ -157,7 +157,15 @@ func StepPut(w PutWorld) PutNext {
|
||||
// applied is a wanted put once the node's have says how it went: holding or done when it runs the composition healthy.
|
||||
func applied(w PutWorld, p puts.Put) puts.Put {
|
||||
h := w.Have
|
||||
if h == nil || h.Fp != p.FP || h.Composed != p.Composed {
|
||||
if h == nil || h.Fp != p.FP {
|
||||
return p
|
||||
}
|
||||
// A put without a hold ends on the fingerprint alone: a node that composes its own digests no composition of the put's, and
|
||||
// a put that waits for that one pins the module's want for good (issue 1966). A held put keeps its composition.
|
||||
if !p.Hold && h.Phase == nodeengineclient.PhaseRunning && h.Health == nodeengineclient.HealthOK {
|
||||
return p.Said(w.Now, puts.Done, fmt.Sprintf("%s runs %s at %s healthy; the rules write its want again", w.Node, w.Module, short(p.FP)))
|
||||
}
|
||||
if h.Composed != p.Composed {
|
||||
return p
|
||||
}
|
||||
switch {
|
||||
|
||||
@@ -933,6 +933,21 @@ func stepsOfAPut(t *testing.T) {
|
||||
w.Have = &nodeengine.HaveByNodeModuleValue{Fp: fNew, Composed: d.Digest, Phase: nodeengineclient.PhaseRunning, Health: nodeengineclient.HealthOK}
|
||||
return w
|
||||
}, puts.Done, "the rules write its want again", false, false, false},
|
||||
{"wanted without hold, the node composes its own: done at the fingerprint", func() PutWorld {
|
||||
w := bus()
|
||||
w.Put.Hold = false
|
||||
w.Put = w.Put.Said(now, puts.Wanted, "wanted")
|
||||
w.Put.Composed = d.Digest
|
||||
w.Have = &nodeengine.HaveByNodeModuleValue{Fp: fNew, Composed: dig(fOld), Phase: nodeengineclient.PhaseRunning, Health: nodeengineclient.HealthOK}
|
||||
return w
|
||||
}, puts.Done, "the rules write its want again", false, false, false},
|
||||
{"wanted with hold, the node composes its own: stays wanted", func() PutWorld {
|
||||
w := bus()
|
||||
w.Put = w.Put.Said(now, puts.Wanted, "wanted")
|
||||
w.Put.Composed = d.Digest
|
||||
w.Have = &nodeengine.HaveByNodeModuleValue{Fp: fNew, Composed: dig(fOld), Phase: nodeengineclient.PhaseRunning, Health: nodeengineclient.HealthOK}
|
||||
return w
|
||||
}, puts.Wanted, "wanted", false, false, false},
|
||||
{"released: nothing more", func() PutWorld { w := bus(); w.Put = w.Put.Said(now, puts.Released, "released"); return w }, puts.Released, "released", false, false, false},
|
||||
}
|
||||
for _, c := range cases {
|
||||
|
||||
Reference in New Issue
Block a user