diff --git a/internal/link/have.go b/internal/link/have.go index ccbf71b9..202cac27 100644 --- a/internal/link/have.go +++ b/internal/link/have.go @@ -444,7 +444,7 @@ func (q *Session) writeHaves(ctx context.Context, bus Bus) { said = append(said, func() { q.sayRunning(ev) }) } } - if !q.writeRemoved(write, bucket, node) { + if !q.writeRemoved(write, bucket, node, &said) { return } if !q.writeUserListsAlone(write, bucket, node) { @@ -521,7 +521,7 @@ func (q *Session) haveSucceeded() { // writeRemoved writes, once, each module the applied declaration no longer holds and this node-engine wrote a have key // for, with the phase removed over the revision it reads (issue 1369). The key is never deleted, and a key that is not // in the bucket is not created. False when the pass should stop. The caller holds the lock of the haves. -func (q *Session) writeRemoved(ctx context.Context, b HaveBucket, node string) bool { +func (q *Session) writeRemoved(ctx context.Context, b HaveBucket, node string, said *[]func()) bool { s := &q.haves gone := make([]string, 0, len(s.removed)) for module := range s.removed { @@ -567,10 +567,19 @@ func (q *Session) writeRemoved(ctx context.Context, b HaveBucket, node string) b return false } delete(s.removed, module) + // The module no longer runs here: running says stopped, with no fingerprint, on its own subject (design 47 G §365, §404). + delete(s.runningSaid, module) + *said = append(*said, func() { q.sayRunning(stoppedRunning(node, module)) }) } return true } +// stoppedRunning is the event running of a module the node-engine's have no longer holds: stopped, with no fingerprint +// and no manifest (design 47 G §365, §404). +func stoppedRunning(node, module string) nodeengine.Running { + return nodeengine.Running{V: sdkkeys.Version, Node: node, Module: module, Stopped: true} +} + // writeUserListsAlone writes the user list of each module this node-engine holds no fact of (no apply here said its have) // into that module's key as the bucket holds it, keeping every other field the key says (novox/hq ADR 0547 §8). The have // of a module is one value with parts from more than one source: the apply's (fingerprint, digest, build, phase, health) diff --git a/internal/link/have_test.go b/internal/link/have_test.go index a575afee..caaa39fc 100644 --- a/internal/link/have_test.go +++ b/internal/link/have_test.go @@ -552,6 +552,10 @@ func TestAModuleLeavingTheDeclarationIsWrittenRemoved(t *testing.T) { if l.kv["n1.b"].rev != revB+1 { t.Errorf("b was written at revision %d, want one over %d", l.kv["n1.b"].rev, revB) } + // Design 47 G §365: a module the have no longer holds says running stopped, with no fingerprint, once, on its own subject. + if got := l.saidOn("mesh.seat.node-engine.event.running.n1.b "); len(got) != 1 || !strings.Contains(got[0], `"stopped":true`) || strings.Contains(got[0], `"fingerprint"`) { + t.Errorf("running said %v for b, want one stopped with no fingerprint", got) + } if l.kv["n1.a"].rev != revA || l.have(t, "n1.a").Phase != nodeengineclient.PhaseRunning { t.Errorf("a changed: %+v at %d", l.have(t, "n1.a"), l.kv["n1.a"].rev) }