The runtime serves a bundle its module's state (novox/hq ADR 0201)
mesh/state.get, put, delete, keys and watch on the stdio channel, from the buckets the membership issues. A watch hands the current values without deletions, then every change, and is answered once the current values are delivered. Refused with the reason: state not issued, a reader's write, a value with a credential-named field — the bus alone would answer a refused write with a timeout.
This commit is contained in:
@@ -50,10 +50,16 @@ type Registration struct {
|
||||
// publishes, asks and subscribes on the module's behalf. Subscribe is asked each time the module's
|
||||
// code subscribes; the runtime binds the module's consumer once and calls deliver for every event,
|
||||
// acknowledging it on the bus when deliver returns nil.
|
||||
//
|
||||
// State and Watch reach the module's state (novox/hq ADR 0201): State answers `get`, `put`, `delete`
|
||||
// and `keys`; Watch hands deliver the current values and then every change, and returns once the
|
||||
// current values are delivered — the watch is the child's, and stops when the child does.
|
||||
type Bus interface {
|
||||
Publish(params json.RawMessage) error
|
||||
Ask(params json.RawMessage) (json.RawMessage, error)
|
||||
Subscribe(deliver func(envelope json.RawMessage) error) error
|
||||
State(verb string, params json.RawMessage) (json.RawMessage, error)
|
||||
Watch(params json.RawMessage, deliver func(change json.RawMessage) error) (stop func(), err error)
|
||||
}
|
||||
|
||||
// EventTimeout bounds how long a bundle has to handle one event before it is offered again.
|
||||
@@ -104,6 +110,9 @@ type child struct {
|
||||
next int64
|
||||
dead chan struct{}
|
||||
why error
|
||||
// watches are the state watches this child asked for, stopped when it exits (ADR 0201): a child
|
||||
// that starts again watches again, as its code runs again.
|
||||
watches []func()
|
||||
}
|
||||
|
||||
func (c *child) write(m any) error {
|
||||
@@ -216,6 +225,30 @@ func Start(module, entry string, env []string, mesh Bus, logf func(string, ...an
|
||||
subscriber = c
|
||||
mu.Unlock()
|
||||
err = mesh.Subscribe(deliver)
|
||||
case "mesh/state.get", "mesh/state.put", "mesh/state.delete", "mesh/state.keys":
|
||||
var answered json.RawMessage
|
||||
if answered, err = mesh.State(strings.TrimPrefix(m.Method, "mesh/state."), m.Params); err == nil {
|
||||
result = answered
|
||||
}
|
||||
case "mesh/state.watch":
|
||||
// Each change is asked of this child, in order; the watch is answered once the
|
||||
// current values have been handed over, so a bundle that awaits it has the whole
|
||||
// of the state before it goes on (ADR 0201).
|
||||
var stop func()
|
||||
stop, err = mesh.Watch(m.Params, func(change json.RawMessage) error {
|
||||
_, err := c.ask(module, "mesh/state", map[string]any{"change": change}, EventTimeout)
|
||||
return err
|
||||
})
|
||||
if err == nil {
|
||||
c.mu.Lock()
|
||||
if c.why != nil {
|
||||
c.mu.Unlock()
|
||||
stop()
|
||||
} else {
|
||||
c.watches = append(c.watches, stop)
|
||||
c.mu.Unlock()
|
||||
}
|
||||
}
|
||||
default:
|
||||
if len(m.ID) > 0 {
|
||||
_ = c.write(map[string]any{"jsonrpc": "2.0", "id": m.ID,
|
||||
@@ -265,7 +298,12 @@ func Start(module, entry string, env []string, mesh Bus, logf func(string, ...an
|
||||
saidMu.Unlock()
|
||||
c.mu.Lock()
|
||||
c.why = errors.New(why)
|
||||
watches := c.watches
|
||||
c.watches = nil
|
||||
c.mu.Unlock()
|
||||
for _, stop := range watches {
|
||||
stop()
|
||||
}
|
||||
close(c.dead)
|
||||
mu.Lock()
|
||||
// Only a child that was serving is brought back; one that died in its own handshake was
|
||||
|
||||
Reference in New Issue
Block a user