Refuse an event heard during a reload instead of panicking, and judge a bundle by its entrypoint alone (issue 1945, review)
An event a module's consumer heard after the module was dropped for a reload read a deleted entry and panicked node-tools, taking every tool on the machine down. The handler now refuses an event of a consumer that is no longer the module's, and a stopped hearing hands nothing on and offers the event again at once. A bundle that writes beside itself (a cache, a socket) loaded itself again after every quiet spell, because the signature took in every file in its directory. A delivery puts every file in place anew, so the entrypoint alone tells a new build. The watch also looks once as it begins, so a bundle that came between its load and the watch is not missed. Tests: 1945/AC23 and AC24 in internal/runtime/reload_test.go, and a stopped hearing in internal/bus/hearing_stopped_test.go.
This commit is contained in:
@@ -802,23 +802,7 @@ func (b *moduleBus) Subscribe(deliver func(json.RawMessage) error) error {
|
||||
return nil
|
||||
}
|
||||
module := b.module
|
||||
stop, err := c.conn.ConsumeAs(module, func(env bus.Envelope) error {
|
||||
raw, err := json.Marshal(env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.mu.Lock()
|
||||
targets := append([]*func(json.RawMessage) error(nil), c.of[module].delivers...)
|
||||
c.mu.Unlock()
|
||||
for _, d := range targets {
|
||||
if err := (*d)(raw); err != nil {
|
||||
c.logf("[mesh-tools] %s did not take %s%s: %s; offered again in %s", module, env.Key,
|
||||
eventRef(env), whyNotTaken(err), bus.NakDelay)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
stop, err := c.conn.ConsumeAs(module, func(env bus.Envelope) error { return c.handOut(module, m, env) })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -827,6 +811,35 @@ func (b *moduleBus) Subscribe(deliver func(json.RawMessage) error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// errModuleReloading refuses an event heard by a module's consumer that was stopped for a reload.
|
||||
var errModuleReloading = errors.New("the module's bundle is being loaded again; offered again")
|
||||
|
||||
// handOut hands one event of a module's consumer m to every child of the module that subscribed; an
|
||||
// event of a consumer that is no longer the module's is refused.
|
||||
func (c *consumers) handOut(module string, m *moduleEvents, env bus.Envelope) error {
|
||||
raw, err := json.Marshal(env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.mu.Lock()
|
||||
if c.of[module] != m {
|
||||
// Heard by a consumer stopped while the module is loaded again (issue 1945): refused, so the bus
|
||||
// offers it again, to the bundle loaded in its place once it subscribes.
|
||||
c.mu.Unlock()
|
||||
return errModuleReloading
|
||||
}
|
||||
targets := append([]*func(json.RawMessage) error(nil), m.delivers...)
|
||||
c.mu.Unlock()
|
||||
for _, d := range targets {
|
||||
if err := (*d)(raw); err != nil {
|
||||
c.logf("[mesh-tools] %s did not take %s%s: %s; offered again in %s", module, env.Key,
|
||||
eventRef(env), whyNotTaken(err), bus.NakDelay)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// whyNotTaken says why a bundle did not take an event, so the line names who failed: the module's
|
||||
// handler, in its own words — an error it answered — or the runtime not reaching it. A bundle's
|
||||
// handler error read bare ("Unexpected end of JSON input") looked like the runtime's (issue 276).
|
||||
|
||||
Reference in New Issue
Block a user