feat: add Pub operation type, HandlerKind::Sink, call.published wire event (ADR-046)
The call protocol had Subscription (server→client streaming) but lacked the directional complement: client→server streaming, where the initiator produces a stream and the responder's handler consumes it. This gap was inherited from the @alkdev/pubsub EventEnvelope prior art, which has subscribe but no wire-level publish. ADR-046 adds the Pub primitive: - OperationType::Pub (client→server streaming) - OperationType::Subscription renamed to Sub (wire: "subscription" → "sub") - SinkHandler type + HandlerKind::Sink variant - PublishStream type alias (Stream<Item = Result<Value, CallError>>) - OperationRegistry::invoke_sink() dispatch path - call.published wire event (sixth event type, additive) - OperationSpec.publish_schema (Option<Value>, validates per-chunk input) - DispatchResult::Sink + SinkDispatch (handler future + chunk channel) - Dispatcher::pump_sink (feeds call.published chunks from wire to handler) - CallConnection::publish() / publish_with_payload() client methods - from_call sink forwarding handler (make_sink_forwarding_handler) - make_sink_handler() helper Fan-out/broker (one producer, N consumers, topic matching) is deferred to the channels session — the call protocol is point-to-point; the broker is a routing concern that sits above it. The Pub primitive is the load-bearing piece the broker will compose on. - 23 new tests (366 total, up from 343) - clippy clean, fmt clean Verification: cargo test — 366 passed cargo clippy --all-targets -- -D warnings — clean cargo fmt --check — clean
This commit is contained in:
@@ -37,30 +37,36 @@ status, priority, and (when resolved) a resolution citing the ADR.
|
||||
| OQ-20 | ~~API key asymmetry~~ | dissolved | medium | PeerEntry supports multiple credential paths |
|
||||
| OQ-21 | X.509 outgoing-only case | resolved | medium | Three remote roles; PeerEntry asymmetry correct |
|
||||
|
||||
## Call Protocol — Pub/Sub (NEW)
|
||||
## Call Protocol — Pub/Sub
|
||||
|
||||
| OQ | Title | Status | Priority | Resolution |
|
||||
|----|-------|--------|----------|------------|
|
||||
| OQ-22 | Call protocol pub/sub primitive — pub to go with sub | open | high | Not yet resolved. The call protocol has `subscribe` (consumer → producer: "send me a stream") but no `pub` (producer pushes to subscribers it didn't directly receive a call.requested from). Surfaced during channels spec work — `channel/resources/subscribe` (ADR-037) needs fan-out. See §"Pub/Sub Gap" below. |
|
||||
| OQ-22 | Call protocol pub/sub primitive — pub to go with sub | partially resolved | high | ADR-046 resolves the primitive: `OperationType::Pub` + `HandlerKind::Sink` + `call.published` wire event + `invoke_sink()` dispatch path. The fan-out/broker mechanism (one producer, N consumers, topic matching) is deferred to the channels session — the call protocol is point-to-point; the broker is a routing concern that sits above it. See §"Pub/Sub Gap" below. |
|
||||
|
||||
### Pub/Sub Gap
|
||||
|
||||
The call protocol's `StreamingHandler` / `invoke_streaming()` path
|
||||
(ADR-021) is point-to-point: a `call.requested` arrives, the handler
|
||||
produces a stream of `call.responded` events back to that one caller.
|
||||
There is no mechanism for a producer to push events to subscribers that
|
||||
it didn't directly receive a `call.requested` from.
|
||||
There was no mechanism for a producer to stream data *to* a responder
|
||||
(client→server streaming), and no fan-out (one producer, N consumers).
|
||||
|
||||
The first consumer that needs this is the channels `channel/resources/
|
||||
subscribe` operation (ADR-037): the hub aggregates worker resources and
|
||||
needs to push live updates to N browser subscribers when any worker's
|
||||
resource set changes. Each browser's `subscribe` arrives on the hub's
|
||||
channel 0, but the worker resource changes arrive on separate worker
|
||||
connections. The hub needs to fan-out.
|
||||
**ADR-046** resolves the directional gap: `OperationType::Pub` is the
|
||||
client→server streaming complement to `Sub` (was `Subscription`,
|
||||
renamed for symmetry). `HandlerKind::Sink` is the consuming handler
|
||||
type. `call.published` is the wire event carrying stream chunks.
|
||||
`invoke_sink()` is the dispatch path. `CallConnection::publish()` is
|
||||
the client method. The `Subscription` variant is renamed to `Sub`
|
||||
(wire string `"sub"`).
|
||||
|
||||
**Blocked on**: An ADR specifying the pub primitive's shape — topic-based
|
||||
fan-out vs. producer-side push to existing subscriptions. This is the
|
||||
next ADR to write (alkcall ADR-046).
|
||||
**Fan-out deferred.** The broker (topic registry, `Pub`↔`Sub` matching,
|
||||
N-consumer fan-out) is deferred to the channels session. The call
|
||||
protocol is point-to-point (one initiator, one responder, a stream
|
||||
between them); a topic registry that outlives individual calls is a
|
||||
different lifecycle and a different concern. The broker's first
|
||||
consumer is the channels `channel/resources/subscribe` operation
|
||||
(ADR-037) and the hub-as-broker pattern (ADR-042). The `Pub` primitive
|
||||
is the load-bearing piece the broker composes on.
|
||||
|
||||
## Channels
|
||||
|
||||
|
||||
Reference in New Issue
Block a user