Skip to content
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

[protocol] Pre-session handshake #53

Merged
merged 13 commits into from
Feb 29, 2024
Merged

[protocol] Pre-session handshake #53

merged 13 commits into from
Feb 29, 2024

Conversation

jackyzha0
Copy link
Member

@jackyzha0 jackyzha0 commented Feb 20, 2024

Why

Having a pre-session handshake to exchange information is useful! Currently, we do this implicitly on the first message of the connection but explicit good 👍

What changed

  • Separated out client/server transport into sub-abstract classes of transport to reduce boilerplate in sending/receiving handshake sequence
  • Implement the pre-session handshake (connection isn't turned into session until the handshake is completed)

export interface ServiceContext {
state: object | unknown;
}
export interface ServiceContext {}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idk why this included state before, we have a separate type for this

@jackyzha0 jackyzha0 requested a review from lhchavez February 20, 2024 22:31
@jackyzha0 jackyzha0 marked this pull request as ready for review February 20, 2024 22:31
type: Type.Literal('CLOSE'),
});

export const PROTOCOL_VERSION = 'v1';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

@@ -36,15 +36,14 @@ export class StdioClientTransport extends Transport<StreamConnection> {
async createNewOutgoingConnection(to: TransportClientId) {
log?.info(`${this.clientId} -- establishing a new stream to ${to}`);
const conn = new StreamConnection(this.input, this.output);
conn.onData((data) => this.handleMsg(this.parseMsg(data)));
conn.onData(this.receiveWithBootSequence(conn));
const cleanup = () => {
this.onDisconnect(conn, to);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mentioned in the other PR

#52 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but why is the implementer responsible for calling onConnect and onDisconnect? Why don't they just call connect and which calls the implemented createNewOutgoingConnection and returns a Connection. Then inside connect we use the returned connection and pass it to onConnect and onDisconnect, this would probably require us to add more event listeners to the Connection interface like i mentioned in another comment.

Copy link
Contributor

@lhchavez lhchavez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left a big comment that combines semantic neatness and also addresses pid2 version drift for the first release of this.

Base automatically changed from jackyzha0/distinguish-sessions-and-connectios to main February 27, 2024 21:45
this.clientId
} -- received invalid handshake resp: ${JSON.stringify(parsed)}`,
);
return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we really return here? what happens in the scenario i outlined in the main PR? (multiplayers, one workspace is older than the other, both connecting to a newer or older pid2)

Copy link
Member Author

@jackyzha0 jackyzha0 Feb 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm I can't find the comment in the main PR you are referring to, can you reiterate? oh nvm the comment in this pr haha found it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo we shouldn't worry about mismatched river versions until after this PR lands

  • not receiving a handshake message should be treated as fatal/refuse the connection and will help us find mistakes faster
  • pid2 flag audience is small enough (3 people) that just refreshing client for now is fine
  • for ai chat, we can bake that compatability into the python river server

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh so essentially implement what i'm suggesting but only in ai chat. that seems reasonable, although i'd like us to start to build the backwards-compatibility muscle before it starts becoming load-bearing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah that makes sense, i think once this change is in place i'd be more more comfortable as this makes backwards-compatability much easier and less brittle than what we have now 👍

Comment on lines 27 to +28
abstract addDataListener(cb: (msg: Uint8Array) => void): void;
abstract removeDataListener(cb: (msg: Uint8Array) => void): void;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the disposable pattern where we return a handle to unlisten, just feels more ergonomic, no strong opinion.

Copy link
Member

@masad-frost masad-frost left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Left some comments.

One meta comment is that I don't feel like I have a good grasp on how we deal with errors, logging is fine for observability, but it doesn't allow the consumer to handle things or at least receive a signal that things are absolutely broken. Maybe downstream from that, still feels like we can be asserting more and be less forgiving with undefined states.

parsed,
)}`,
);
return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, seems like we have a ton of clean up here that we're not doing? Should we be terminating the connection after we send?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or is the idea that there's a recovery path here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm yeah idea is that client might send the proper message at some point in the future, though maybe that's bad anyways and we should just close the conn?

}

addErrorListener(cb: (err: Error) => void): void {
this.sock.on('error', cb);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

https://nodejs.org/api/net.html#event-error

Maybe we need to call sock.close here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that's only for the actual net.Server itself, sock is net.Socket so it will call both close and error

Comment on lines 563 to 565
throw new Error(
`${this.clientId} -- connection to ${to} failed after ${attempt} attempts (${err}), giving up`,
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably log this. Also serializing err here might be painful to read or get good information from.

log?.warn(
`${this.clientId} -- connection to ${to} failed (${err}), trying again in ${backoffMs}ms`,
);
setTimeout(() => this.connect(to, attempt + 1), backoffMs);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any checks we need to do before blindly calling this.connect?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope, this.connect maintains its own invariants!

@jackyzha0 jackyzha0 merged commit 076e35c into main Feb 29, 2024
3 checks passed
@jackyzha0 jackyzha0 deleted the jackyzha0/handshake branch February 29, 2024 05:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants