From 87f422453b03be3315f22f02db5a291c7e96f8fb Mon Sep 17 00:00:00 2001 From: Paul Butler Date: Wed, 18 Dec 2024 13:43:20 -0500 Subject: [PATCH] Ensure new provider works with older server versions (#363) The new client relies on a message type in order to support heartbeats and sync status. If it's talking to an older server version, the server will ignore its requests for heartbeats, and the client will assume the connection is unhealthy and attempt to reconnect in a periodic loop. To test this, run the examples along with an old version of the server, e.g. `npx y-sweet@0.6.0 serve` This fixes the library by adding a boolean field `receivedAtLeastOneSyncResponse`. As long as this is `false`, we will continue to send heartbeats and sync status requests, but we will never terminate a connection over a missed heartbeat. This is a somewhat crude approach. We should support version negotiation eventually. But it's nice and simple, and will be easy to rip out later. --- js-pkg/client/src/provider.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/js-pkg/client/src/provider.ts b/js-pkg/client/src/provider.ts index 839e7e47..a0c827aa 100644 --- a/js-pkg/client/src/provider.ts +++ b/js-pkg/client/src/provider.ts @@ -148,6 +148,17 @@ export class YSweetProvider { private retries: number = 0 + /** + * Older versions of the Y-Sweet server did not support the sync message, and would ignore it. + * This may lead to the client thinking the server is offline, when really it just doesn't + * know how to return a heartbeat. + * + * Eventually, we will build protocol version negotiation into the handshake. Until then, we + * use a simple approach: until we receive the first sync message back, we assume the server + * is an older version for the purpose of the heartbeat logic. + */ + private receivedAtLeastOneSyncResponse: boolean = false + constructor( private authEndpoint: AuthEndpoint, private docId: string, @@ -226,6 +237,13 @@ export class YSweetProvider { if (this.connectionTimeoutHandle) { return } + + if (!this.receivedAtLeastOneSyncResponse) { + // Until we receive the first sync response on the connection, we assume + // the server is an older version. + return + } + this.connectionTimeoutHandle = setTimeout(() => { if (this.websocket) { this.websocket.close() @@ -265,6 +283,8 @@ export class YSweetProvider { if (emit) { this.emit(EVENT_LOCAL_CHANGES, false) } + + this.receivedAtLeastOneSyncResponse = true } private setStatus(status: YSweetStatus) { @@ -482,6 +502,8 @@ export class YSweetProvider { this.checkSync() this.broadcastAwareness() this.resetHeartbeat() + + this.receivedAtLeastOneSyncResponse = false } private receiveMessage(event: MessageEvent) {