Hello,
I would be very grateful for the community's feedback regarding the following proposal.
Background
Increasingly more service interfaces use streaming to return asynchronous, potentially unbound results streams.
Example for this discussion
A logical example, given a hypothetical financial risk service called riskengine:
As a day trader, I want to have real-time updates to my portfolio's risk exposure so that I can react appropriately to market changes.
From my user interface, I need to invoke a method as follows:
riskengine.getPortfolioRisk(portfolioId, onRiskData, onError, onComplete);
where onRiskData, onError, and onComplete are callbacks functions with the following signatures:
function onRiskData(dataItem)
function onError(error)
function onComplete()
The above mirrors the good work on the ReactiveX and Reactive Streams.
Please note: that the above is first-class function - it has parameters, and as such is quite different to an unparameterised notification (which doesn't have a call 'context' id).
Why
I believe the only way of achieving the above at present with JsonRPC is to poll. The notification protocol is insufficient as it lacks the 'id' field.
The problems with polling are:
1. Polling introduces protocol overheads that introduce lag.
2. The polling window can result in latency and missed intermediary states that may be valuable to the client.
3. Polling inherently places greater load on the server, which in turn introduces scaling concerns (imagine 100k+ clients)
Proposal
The proposal is to introduce in a future protocol version (e.g. jsonrpc 3.0) a flow that enables a client to indicate that it wishes to receive the result of a method call as a stream.
--> {"jsonrpc": "3.0", "method": "getPortfolioRisk", "params": { portfolioId: 1} , "id": 1, "streamed": true}
The server responses can one of the following:
(a) zero or more result frames
<-- {"jsonrpc": "3.0", "result": { ... }, "id": 1}
<-- {"jsonrpc": "3.0", "result": { ... }, "id": 1}
<-- {"jsonrpc": "3.0", "result": { ... }, "id": 1}
(b) stream termination on error:
<-- {"jsonrpc": "3.0", "error": {"code": -32001, "message": "risk computation failed"}, "id": 1}
(c) or a successful stream termination:
<-- {"jsonrpc": "3.0", "completed": true, "id": 1}
Additionally: frame types (a) and (c) can be combined to reduce overheads
<-- {"jsonrpc": "3.0", "result": { ... }, "id": 1, "completed": true}
Transports
Best suited for bi-directional transports such as web-sockets.
I've implemented the above to work with SockJS which gracefully degrades from a wide range of websocket protocols, to XHR streaming (HTTP1&2), and finally, polling. The project needs further work to make it fit for public consumption. Happy to do so on your request.
Further considerations
The nature of the protocol is that it can indicate back-pressure to the transport layer.
---
Thanks for reading this far. I'd be grateful for your consideration on the above and your support for inclusion in the next version of the protocol.
kind regards,
Fuzz.