Bind a stream consumer again when the bus ends its loop, so a module is not left deaf when the controller deletes and makes again a consumer it cannot narrow (hq issue 725)
mesh/merge-gate head d3b0330ed1cb merged into trunk d0dcae53a8c0 — pass, with a note: builds mesh-tools, node-tools (+39 dependent(s)) → ace, g14, novox, s…
mesh/repo-check head d3b0330ed1cb merged into trunk d0dcae53a8c0 — pass: its merge-check.sh passed
mesh/delivery covered after its failure: a later walk that contains it was delivered: plan-1792008849869252364 (novox/mesh-tools at 5d70eb3e into main, d…

This commit is contained in:
jochen
2026-10-14 22:10:20 +02:00
parent 9921486fd5
commit d3b0330ed1
4 changed files with 188 additions and 16 deletions
+25 -4
View File
@@ -170,7 +170,7 @@ type Conn struct {
cred Credential
mu sync.Mutex
issued map[string]*Membership // module → membership; present with nil = followed, none issued
onNew []func(Membership)
onNew []*func(Membership)
subs []*nats.Subscription
Logf func(format string, args ...any)
// answering is every subject this runtime answers on, so a subscription the bus refused can be
@@ -368,7 +368,10 @@ func (c *Conn) Follow(module string) {
}
c.mu.Lock()
c.issued[module] = &m
handlers := append([]func(Membership){}, c.onNew...)
handlers := make([]func(Membership), 0, len(c.onNew))
for _, h := range c.onNew {
handlers = append(handlers, *h)
}
c.mu.Unlock()
c.Logf("[mesh-tools] %s on %s was issued a new membership; re-serving on it", module, c.node)
for _, h := range handlers {
@@ -410,10 +413,28 @@ func (c *Conn) Serving() []string {
// OnMembership is called with every new membership any followed module is issued.
func (c *Conn) OnMembership(h func(Membership)) {
c.mu.Lock()
c.onNew = append(c.onNew, h)
c.onNew = append(c.onNew, &h)
c.mu.Unlock()
}
// onMembership is OnMembership until the answered function is called, which drops the handler.
func (c *Conn) onMembership(h func(Membership)) func() {
mine := &h
c.mu.Lock()
c.onNew = append(c.onNew, mine)
c.mu.Unlock()
return func() {
c.mu.Lock()
defer c.mu.Unlock()
for i, had := range c.onNew {
if had == mine {
c.onNew = append(c.onNew[:i:i], c.onNew[i+1:]...)
return
}
}
}
}
func (c *Conn) track(s *nats.Subscription) {
c.mu.Lock()
c.subs = append(c.subs, s)
@@ -958,7 +979,7 @@ func (c *Conn) ConsumeAs(module string, deliver func(Envelope) error) (func(), e
h := newHearing(c, js, module, deliver)
// Bound by name, never created and never matched against a subject: the consumer and its
// filters are the controller's (design 29 §3), exactly as the module's own runtime bound it.
events, doing, err := h.bind(EventsStream, durable)
events, doing, err := h.bind(EventsStream, durable, nil)
if err != nil {
return nil, fmt.Errorf("%s %s's consumer %s: %w", doing, module, durable, err)
}