-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Base branch for protocol v2 #127
base: main
Are you sure you want to change the base?
Conversation
cc48498
to
23a05c8
Compare
@@ -64,27 +64,43 @@ Note that this protocol specification does NOT detail the language-level specifi | |||
1. `subscription`: the client sends 1 message, the server responds with m messages. | |||
|
|||
A server (also called a router) is made up of multiple 'services'. Each 'service' has multiple 'procedures'. | |||
A procedure declares its type (`rpc | stream | upload | subscription`), an input message type (`Input`), an output message type (`Output`), an error type (`Error`), and the associated handler. | |||
A procedure declares its type (`rpc | stream | upload | subscription`), an initial message (`Init`), an output message type (`Output`), an error type (`Error`), and the associated handler. `upload` and `stream` may define an input message type (`Input`), which means they accept further messages from the client. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's still weird to me that 'additional' messages are called Input
feels like
Init
should beInput
Input
should beStream
orBody
(i just cooked up Stream
off the top of my dome but that actually seems like the reasonable thing here)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the direction that input gives. Body can mean output too. Maybe we should use request/response terminology.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Init could also mean output too, I agree though maybe request/terminology would help here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docs
|
||
Note that any procedure that has a client-to-server procedure stream (i.e. `stream` and `upload`) can optionally define a single initialization message to be sent to the server before the client starts sending the actual `Input` messages. | ||
- `rpc`: `Init -> Result<Output, Error>` | ||
- `upload`: `Init, AsyncIter<Input> -> Result<Output, Error>` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe replace AsyncIter + Pushable with readable/writable streams and provide TS definitions for those here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah could use an update here re: AsyncIter + Pushable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docs
|
||
Writers send messages that are recieved by the other side's reader. ONLY writers can end streams. Readers can send a signal to the writer that they are no longer interested in the stream, but the writer can choose to ignore this signal and continue sending messages. | ||
|
||
Streams can be in a half-closed state. This happens when one party sends a close signal indicating that it will no longer send |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe to clarify, we should say that any given stream is made up of a write side and read side
A | B
1 <--|--- 3
2 --|---> 4
1. A-side read
2. A-side write
3. B-side write
4. B-side read
half-closes can only be sent on 2
and 3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docs
d310cd0
to
4311f54
Compare
973954f
to
fd4afee
Compare
* Update PROTOCOL.md * Update PROTOCOL.md Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * Update PROTOCOL.md * half-close semantics * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * fmt * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * Update PROTOCOL.md * Include reader-writer semantics --------- Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>
* ReadStream implementation * remove tee for now * Make the stream itself an iterable * Implement return for iterator * comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
talked more IRL, going to think a bit more about the stream interface and ergonomics broadly
i think directionally very correct but could use a lot of simplification
input: Type.Object({ n: Type.Number() }), | ||
output: Type.Object({ result: Type.Number() }), | ||
requestData: Type.Object({ n: Type.Number() }), | ||
responseData: Type.Object({ result: Type.Number() }), | ||
errors: Type.Never(), | ||
// note that a handler is unique per user RPC | ||
async handler(ctx, { n }) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably needs an update too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same with await client.example.add.rpc
below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docs
|
||
Note that any procedure that has a client-to-server procedure stream (i.e. `stream` and `upload`) can optionally define a single initialization message to be sent to the server before the client starts sending the actual `Input` messages. | ||
- `rpc`: `Init -> Result<Output, Error>` | ||
- `upload`: `Init, AsyncIter<Input> -> Result<Output, Error>` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah could use an update here re: AsyncIter + Pushable
- It is an explicit heartbeat, so the `AckBit` MUST be the only bit set. | ||
- The payload MUST be `{ type: 'ACK' }`. | ||
- Because this is a control message that is not associated with a specific stream, you MUST NOT set `serviceName` or `procedureName` and `streamId` can be something arbitrary (e.g. `heartbeat`). | ||
|
||
There are 2 error payloads that are defined in the protocol sent from server to client, these codes are reserved: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are 2 error payloads that are defined in the protocol sent from server to client, these codes are reserved: | |
There are 3 error payloads that are defined in the protocol sent from server to client, these codes are reserved: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docs
|
||
`ProtocolError`s, just like service-level errors, are wrapped with a `Result`, which is further wrapped with `TransportMessage` and MUST have a `StreamAbortBit` flag. Please note that these are separate from user-defined errors, which should be treated just like any output message. | ||
|
||
There are 4 `Control` payloads: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
control payloads technically aren't normative i think, i just send these in the TS implementation so its easier to debug without needing to decode the controlFlags but controlFlags are enough
is the suggestion here that we change it to be normative?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docs
|
||
interface ControlHandshakeRequest { | ||
type: 'HANDSHAKE_REQ'; | ||
protocolVersion: 'v2.0'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lfg
maybe we should have docs on how to handle v1 receiving a v2 request and vice versa
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docs
good review, i'll be sending some PRs and we can sync up afterwards |
Got some comments on streams being complicated, so this is a take on simplified API. - Less methods and states that people need to understand - Merge interfaces into a single object (i.e. a `stream` call would have both read and write capabilities on the same object)
## Why Follow up on #249 to actually use the new interfaces. Removed extra refactor done in #249 that merges the interfaces as that proved to be a challenging API (un-yak-shave 🙅 🐃) ## What changed - Removed close requests (mostly cherry-picked from #248) - Otherwise a simple swapping out of the interfaces ## Versioning - [ ] Breaking protocol change - [ ] Breaking ts/js API change <!-- Kind reminder to add tests and updated documentation if needed -->
Just a simple follow up to #250
All the changes are documented in
Protocol.md
but here's a summary:Pushable
andAsyncIterator
APIs to aReadStream
andWriteStream
init
and some haveinput
While the changes are not strictly backwards compatible, hence the major protocol bump, the system can still operate across versions to some extent.
See PRs linked below for more information on the above
TODOs
Define protocol and update doc [protocol v2] RFC PROTOCOL V2 #111
Design stream abstractions [protocol v2] RFC: custom streams #118
Implement stream abstractions
All streams have init, some have input.
Use stream abstractions & implement protocol closing semantics
Input
iterator on the server useResult
so we can signal stream closes, client disconnects, and aborts [protocolv2] Make ReadStream always emit Result values #172Add Abort mechanism
INVALID_REQUEST
to schema Add INVALID_REQUEST to RiverUncaughtSchema #107INVALID_REQUEST
errors with an abort bit [protocolv2] Handle invalid requests and internal server errors #203INTERNAL_RIVER_ERROR
with an abort bit [protocolv2] Handle invalid requests and internal server errors #203UNCAUGHT_ERROR
[protocolv2] Treat uncaught handler errors as abort #201Try to find uncovered areas to test
undefined
value forinit
,input
, &output
.Update docs
Changelog