From d5fa55f2ba1a5608b09f7a20daf1a3dbe19d960d Mon Sep 17 00:00:00 2001 From: Mikhail Cheshkov Date: Wed, 15 Jan 2025 14:35:28 +0200 Subject: [PATCH] fix(client-ws-transport): Flush send queue in close() to avoid race Before this there were a race between `setTimeout` in `sendMessage` and `close`. Even for a good citizen, that calls `unsubscribe()` and awaits result there were no synchronization between unsubscribe adding message to `messageQueue` and restarting timer, and actually sending this message to WebSocket. When `close()` is called close frame is sent immediately to WebSocket. If at that moment messageQueue is not empty, and timer is armed it can wake up, and discover socket in CLOSING state: send side closed, recv side open. For now - just flush everything queued, and send close frame after, so it would look like close was queued after. More complete solution is to add synchronization between unsubscribe call and sending message, or even receiving ack for it. --- packages/cubejs-client-ws-transport/src/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/cubejs-client-ws-transport/src/index.ts b/packages/cubejs-client-ws-transport/src/index.ts index 947b47271f70e..2c28be16233ee 100644 --- a/packages/cubejs-client-ws-transport/src/index.ts +++ b/packages/cubejs-client-ws-transport/src/index.ts @@ -91,6 +91,9 @@ class WebSocketTransport implements ITransport { public async close(): Promise { if (this.ws) { + // Flush send queue before sending close frame + this.ws.sendQueue(); + this.ws.close(); } }