Merge pull request 'Node shadow: what runs is the composition at the have's digest, not the stale kept copy (issue 1126, group fix/1126-shadow-bodies)' (#157) from fix/1126-shadow-bodies into main
This commit was merged in pull request #157.
This commit is contained in:
@@ -3,7 +3,7 @@ module github.com/novox/mesh-host
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261028000902-4e0c57771e8c
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261028003907-362a3f1378aa
|
||||
github.com/nats-io/nats-server/v2 v2.11.17
|
||||
github.com/nats-io/nats.go v1.54.0
|
||||
golang.org/x/crypto v0.57.0
|
||||
|
||||
@@ -28,6 +28,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027232145-7359d187db8d h1:DhR7DRKa
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027232145-7359d187db8d/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261028000902-4e0c57771e8c h1:4lS82cVTtxls17bRn3551+m2xlZO+0WEtv0a7iME8Ec=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261028000902-4e0c57771e8c/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261028003907-362a3f1378aa h1:DFYX7pIluzeeEyM+YXGeB6GvEnOYyRhz/1Ke/LFa8+8=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261028003907-362a3f1378aa/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
||||
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
|
||||
@@ -751,6 +751,28 @@ func TestTheNodeComposesItsWantsFromTheBus(t *testing.T) {
|
||||
t.Fatal("the shadow, once ready, answered nothing")
|
||||
}
|
||||
|
||||
// Issue 1126, probe #532 on shanks: what runs is the composition the have names, never the copy of the declaration
|
||||
// kept before the switch (no fp, no digest): a module composed at the digest its have says runs is the same.
|
||||
{
|
||||
probe := delivery.CandidateAnswer{Head: "h", Revision: 68, Verdict: delivery.CandidateDiffers, Same: []string{"adwaita"},
|
||||
Differs: []delivery.ModuleDiffers{
|
||||
{Module: "xterm", Composed: "sha256:3c0a31df64283c0678b1cdbeff8ac030609cab65cc7e47faaf714da743d9340e", Why: "the fingerprint differs"},
|
||||
{Module: "zsh", Composed: "sha256:6093f31056d39eaf604410b7289ec6b5fe602b19c8547283562fcfc078c4844f", Resource: "zsh.bundle-tools", Why: "the resource differs"},
|
||||
{Module: "brother", Composed: "sha256:0702b13991288da72d230460624e949d3c1d7d833eac2ec2134073c82e5a61cf",
|
||||
Running: "sha256:c11c14af2738e3fcdb04da4004bbe84728bdefaf8c986d76601ffbe502435871", Resource: "brother.needs-broker-device", Why: "the resource differs"}}}
|
||||
got := asHavesSay(probe, map[string]string{
|
||||
"xterm": "sha256:3c0a31df64283c0678b1cdbeff8ac030609cab65cc7e47faaf714da743d9340e",
|
||||
"zsh": "sha256:6093f31056d39eaf604410b7289ec6b5fe602b19c8547283562fcfc078c4844f",
|
||||
"brother": "sha256:c11c14af2738e3fcdb04da4004bbe84728bdefaf8c986d76601ffbe502435871"})
|
||||
if fmt.Sprint(got.Same) != "[adwaita xterm zsh]" || len(got.Differs) != 1 || got.Differs[0].Module != "brother" || got.Verdict != delivery.CandidateDiffers {
|
||||
t.Errorf("shanks' probe answered %+v", got)
|
||||
}
|
||||
if one := asHavesSay(delivery.CandidateAnswer{Head: "h", Verdict: delivery.CandidateDiffers, Differs: probe.Differs[:1]},
|
||||
map[string]string{"xterm": probe.Differs[0].Composed}); one.Verdict != delivery.CandidateSame {
|
||||
t.Errorf("a module the same as its have says is answered %+v", one)
|
||||
}
|
||||
}
|
||||
|
||||
// The way back, row by row (review of the shadow, round 1): the controller's compositions at n1's wants compared
|
||||
// with what runs; one differing is answered so; one the controller holds none of refuses, without returning.
|
||||
var runsList []delivery.Composed
|
||||
|
||||
@@ -36,6 +36,9 @@ type Shadow struct {
|
||||
// wants are the node's wants by module (agent.NodeModule for its own): what a head deleting its placement returns
|
||||
// it to, the controller's compositions at them.
|
||||
wants map[string]delivery.Want
|
||||
// ran is, by module, the digest the module's have says runs: what runs, where the agent's list holds only the copy
|
||||
// from the declaration kept before the switch (issue 1126, probe #532 on shanks).
|
||||
ran map[string]string
|
||||
}
|
||||
|
||||
// candidateKey is one candidate key as last read: its value and revision.
|
||||
@@ -47,7 +50,7 @@ type candidateKey struct {
|
||||
// NewShadow is the shadow composer of one node; answer writes the node's answers into its own have key.
|
||||
func NewShadow(env Env, answer func(ctx context.Context, answers map[string]delivery.CandidateAnswer)) *Shadow {
|
||||
return &Shadow{c: New(env), answer: answer, candidates: map[string]map[string]candidateKey{},
|
||||
answers: map[string]delivery.CandidateAnswer{}, wants: map[string]delivery.Want{}}
|
||||
answers: map[string]delivery.CandidateAnswer{}, wants: map[string]delivery.Want{}, ran: map[string]string{}}
|
||||
}
|
||||
|
||||
// Keep runs the shadow until ctx ends, beginning again after backoff(try) whenever it stops: an input it cannot watch
|
||||
@@ -90,6 +93,7 @@ func (s *Shadow) Run(ctx context.Context) error {
|
||||
s.mu.Lock()
|
||||
s.candidates = map[string]map[string]candidateKey{}
|
||||
s.wants = map[string]delivery.Want{}
|
||||
s.ran = map[string]string{}
|
||||
s.c = New(s.c.env)
|
||||
s.mu.Unlock()
|
||||
c := s.c
|
||||
@@ -158,6 +162,7 @@ func (s *Shadow) Run(ctx context.Context) error {
|
||||
dirty = s.takeRecord(t.u) || dirty
|
||||
case t.in == 5:
|
||||
c.takeModuleHave(t.u)
|
||||
s.takeRan(t.u)
|
||||
dirty = !isNodeKey(t.u.Key) || dirty
|
||||
case t.in == 6:
|
||||
dirty = s.takeWant(t.u) || dirty
|
||||
@@ -398,7 +403,51 @@ func (s *Shadow) answerGroup(ctx context.Context, group string) (delivery.Candid
|
||||
c.mu.Unlock()
|
||||
a := frombus.Answer(ctx, reader{c: c}, c.env.Mesh, frombus.Scope{Node: node, KeptAt: kept, Placements: given, InOwn: inOwn,
|
||||
SameValue: c.sameValue}, fps, running, head, rev)
|
||||
return a, true
|
||||
s.mu.Lock()
|
||||
ran := make(map[string]string, len(s.ran))
|
||||
for m, d := range s.ran {
|
||||
ran[m] = d
|
||||
}
|
||||
s.mu.Unlock()
|
||||
return asHavesSay(a, ran), true
|
||||
}
|
||||
|
||||
// takeRan keeps the digest one of this node's module haves says runs.
|
||||
func (s *Shadow) takeRan(u agent.Update) {
|
||||
node, module, isNode, err := delivery.ParseHaveKey(u.Key)
|
||||
if err != nil || isNode || node != s.c.env.Node {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
h, err := delivery.DecodeHave(u.Value)
|
||||
if u.Deleted || err != nil || h.Composed == "" || h.Phase == delivery.PhaseRemoved {
|
||||
delete(s.ran, module)
|
||||
return
|
||||
}
|
||||
s.ran[module] = h.Composed
|
||||
}
|
||||
|
||||
// asHavesSay is a with each module that differs only from the copy the agent keeps of the declaration before the switch
|
||||
// counted the same where the composed digest is the one its have says runs: what runs is the composition at that
|
||||
// digest, never the kept copy (issue 1126, probe #532: shanks' xterm, zsh, zsh-autosuggestions and
|
||||
// zsh-syntax-highlighting).
|
||||
func asHavesSay(a delivery.CandidateAnswer, ran map[string]string) delivery.CandidateAnswer {
|
||||
if a.Refused != "" || len(a.Differs) == 0 {
|
||||
return a
|
||||
}
|
||||
kept := a.Differs[:0:0]
|
||||
for _, d := range a.Differs {
|
||||
if d.Composed != "" && ran[d.Module] == d.Composed {
|
||||
a.Same = append(a.Same, d.Module)
|
||||
continue
|
||||
}
|
||||
kept = append(kept, d)
|
||||
}
|
||||
a.Differs = kept
|
||||
sort.Strings(a.Same)
|
||||
a.Verdict = delivery.VerdictOf(a)
|
||||
return a
|
||||
}
|
||||
|
||||
// shadowBuilds is the build each module placed is composed at in shadow: the one it runs, and for one the candidate
|
||||
|
||||
Reference in New Issue
Block a user