0.1.13: one rule for taking an event, and no handler left by a refused subscribe (hq issue 276)
A handler takes an event by returning and asks for it again by throwing; any mesh/event answer but an error is taken. Written down in the README where event handling is, with what follows for a handler: throw only when the work was not done, and be safe to run twice. Both SDKs added the handler before asking the runtime and kept it when the runtime refused, so a module that asks again until it is taken (every Go consumer does) ran its handler once per attempt for every event. A refused subscribe now removes its handler; one the Go runtime did not answer in time is kept, since the runtime may have bound it.
This commit is contained in:
@@ -54,6 +54,36 @@ they call, live in the module.
|
||||
The broker client (connect, request/reply, publish/subscribe), the event-consumer a module uses to
|
||||
react to mesh events, and the envelope/routing-key conventions. Transport primitives.
|
||||
|
||||
#### Taking an event — the one rule
|
||||
|
||||
A module's handler (`on(pattern, handler)` here, `stdio.Subscribe` in Go, the same in every language)
|
||||
**takes an event by returning, and asks for it again by throwing** (Go: returning an error). Nothing
|
||||
else counts: what a handler returns is not read.
|
||||
|
||||
On the wire, the runtime hands the bundle `mesh/event` with the envelope and acknowledges the event on
|
||||
the bus when the answer is **anything but a JSON-RPC error** — `{}` (what the SDKs send), `null`, or no
|
||||
`result` at all. An `error` answer, no answer within the runtime's event timeout (two minutes), or the
|
||||
bundle exiting leaves it unacknowledged: it is offered again after five seconds, up to the module
|
||||
consumer's maximum deliveries, after which the bus gives it up and the controller raises a condition
|
||||
(`max-deliveries`). The runtime's line for an event not taken quotes a handler's error as the
|
||||
handler's own words, so it is never mistaken for the runtime's.
|
||||
|
||||
So, for a handler:
|
||||
|
||||
- **Throw only when the work was not done** — the service refused, was unreachable, the store failed.
|
||||
Never for something that went right but answered oddly: an API that answers an empty body is a
|
||||
success, and reading it as JSON is a throw that has every event offered five times, its work done
|
||||
five times, and a false alarm raised (novox/hq issue 276).
|
||||
- **Delivery is at-least-once**, so the work must be safe to do twice, or the handler skips an event
|
||||
whose `x-event-id` (`event.id`) it already did — remembering it only once the work succeeded, so one
|
||||
that failed is done when it is offered again.
|
||||
- An event no handler matches is answered at once and taken.
|
||||
- A subscribe the runtime refused leaves no handler behind, so asking again until it is taken hands
|
||||
each event to the handler once.
|
||||
|
||||
Checked by the SDKs' stdio tests (TypeScript `test/stdio.test.ts`, Go `go/stdio_test.go`) and the
|
||||
runtime's (`node-tools/internal/launch`, mesh-tools).
|
||||
|
||||
### `state` — a module's current state on the bus
|
||||
`state(name)` reaches a state the module declared it keeps (`state` in its manifest) or reads
|
||||
(`reads`, as `<module>.<name>`): `get`, `put`, `delete`, `keys`, and `watch`, which hands the current
|
||||
|
||||
Reference in New Issue
Block a user