|
4 | 4 | encodeClientMessage, |
5 | 5 | parseServerMessage, |
6 | 6 | ServerMessage, |
| 7 | + Transition, |
7 | 8 | } from "./protocol.js"; |
8 | 9 |
|
9 | 10 | const CLOSE_NORMAL = 1000; |
@@ -199,6 +200,7 @@ export class WebSocketManager { |
199 | 200 | webSocketConstructor: typeof WebSocket, |
200 | 201 | logger: Logger, |
201 | 202 | private readonly markConnectionStateDirty: () => void, |
| 203 | + private readonly debug: boolean, |
202 | 204 | ) { |
203 | 205 | this.webSocketConstructor = webSocketConstructor; |
204 | 206 | this.socket = { state: "disconnected" }; |
@@ -312,40 +314,11 @@ export class WebSocketManager { |
312 | 314 | const messageLength = message.data.length; |
313 | 315 | const serverMessage = parseServerMessage(JSON.parse(message.data)); |
314 | 316 | this._logVerbose(`received ws message with type ${serverMessage.type}`); |
315 | | - if ( |
316 | | - serverMessage.type === "Transition" && |
317 | | - serverMessage.clientClockSkew !== undefined && |
318 | | - serverMessage.serverTs !== undefined |
319 | | - ) { |
320 | | - const transitionTransitTime = |
321 | | - monotonicMillis() - // client time now |
322 | | - // clientClockSkew = (server time + upstream latency) - client time |
323 | | - // clientClockSkew is "how many milliseconds behind (slow) is the client clock" |
324 | | - // but the latency of the Connect message inflates this, making it appear further behind |
325 | | - serverMessage.clientClockSkew - |
326 | | - serverMessage.serverTs / 1_000_000; // server time when transition was sent |
327 | | - const prettyTransitionTime = `${Math.round(transitionTransitTime)}ms`; |
328 | | - const prettyMessageMB = `${Math.round(messageLength / 10_000) / 100}MB`; |
329 | | - const bytesPerSecond = messageLength / (transitionTransitTime / 1000); |
330 | | - const prettyBytesPerSecond = `${Math.round(bytesPerSecond / 10_000) / 100}MB per second`; |
331 | | - this._logVerbose( |
332 | | - `received ${prettyMessageMB} transition in ${prettyTransitionTime} at ${prettyBytesPerSecond}`, |
333 | | - ); |
334 | | - |
335 | | - // Warnings that will show up for *all users*, so these are not very aggressive goals. |
336 | | - if (transitionTransitTime > 10_000 && messageLength > 10_000_000) { |
337 | | - this.logger.log( |
338 | | - `received query results totalling more than 10MB (${prettyMessageMB}) which took more than 10s (${prettyTransitionTime}) to arrive`, |
339 | | - ); |
340 | | - } else if (messageLength > 20_000_000) { |
341 | | - this.logger.log( |
342 | | - `received query results totalling more that 20MB (${prettyMessageMB}) which will take a long time to download on slower connections`, |
343 | | - ); |
344 | | - } else if (transitionTransitTime > 20_000) { |
345 | | - this.logger.log( |
346 | | - `received query results totalling ${prettyMessageMB} which took more than 20s to arrive (${prettyTransitionTime})`, |
347 | | - ); |
348 | | - } |
| 317 | + if (serverMessage.type === "Transition") { |
| 318 | + this.reportLargeTransition({ |
| 319 | + messageLength, |
| 320 | + transition: serverMessage, |
| 321 | + }); |
349 | 322 | } |
350 | 323 | const response = this.onMessage(serverMessage); |
351 | 324 | if (response.hasSyncedPastLastReconnect) { |
@@ -686,4 +659,58 @@ export class WebSocketManager { |
686 | 659 | const jitter = actualBackoff * (Math.random() - 0.5); |
687 | 660 | return actualBackoff + jitter; |
688 | 661 | } |
| 662 | + |
| 663 | + private reportLargeTransition({ |
| 664 | + transition, |
| 665 | + messageLength, |
| 666 | + }: { |
| 667 | + transition: Transition; |
| 668 | + messageLength: number; |
| 669 | + }) { |
| 670 | + if ( |
| 671 | + transition.clientClockSkew === undefined || |
| 672 | + transition.serverTs === undefined |
| 673 | + ) { |
| 674 | + return; |
| 675 | + } |
| 676 | + |
| 677 | + const transitionTransitTime = |
| 678 | + monotonicMillis() - // client time now |
| 679 | + // clientClockSkew = (server time + upstream latency) - client time |
| 680 | + // clientClockSkew is "how many milliseconds behind (slow) is the client clock" |
| 681 | + // but the latency of the Connect message inflates this, making it appear further behind |
| 682 | + transition.clientClockSkew - |
| 683 | + transition.serverTs / 1_000_000; // server time when transition was sent |
| 684 | + const prettyTransitionTime = `${Math.round(transitionTransitTime)}ms`; |
| 685 | + const prettyMessageMB = `${Math.round(messageLength / 10_000) / 100}MB`; |
| 686 | + const bytesPerSecond = messageLength / (transitionTransitTime / 1000); |
| 687 | + const prettyBytesPerSecond = `${Math.round(bytesPerSecond / 10_000) / 100}MB per second`; |
| 688 | + this._logVerbose( |
| 689 | + `received ${prettyMessageMB} transition in ${prettyTransitionTime} at ${prettyBytesPerSecond}`, |
| 690 | + ); |
| 691 | + |
| 692 | + // Warnings that will show up for *all users*, so these are not very aggressive goals. |
| 693 | + if (transitionTransitTime > 10_000 && messageLength > 10_000_000) { |
| 694 | + this.logger.log( |
| 695 | + `received query results totalling more than 10MB (${prettyMessageMB}) which took more than 10s (${prettyTransitionTime}) to arrive`, |
| 696 | + ); |
| 697 | + } else if (messageLength > 20_000_000) { |
| 698 | + this.logger.log( |
| 699 | + `received query results totalling more that 20MB (${prettyMessageMB}) which will take a long time to download on slower connections`, |
| 700 | + ); |
| 701 | + } else if (transitionTransitTime > 20_000) { |
| 702 | + this.logger.log( |
| 703 | + `received query results totalling ${prettyMessageMB} which took more than 20s to arrive (${prettyTransitionTime})`, |
| 704 | + ); |
| 705 | + } |
| 706 | + if (this.debug) { |
| 707 | + if (transitionTransitTime > 10_000 || messageLength > 10_000_000) { |
| 708 | + this.sendMessage({ |
| 709 | + type: "Event", |
| 710 | + eventType: "ClientReceivedTransition", |
| 711 | + event: { transitionTransitTime, messageLength }, |
| 712 | + }); |
| 713 | + } |
| 714 | + } |
| 715 | + } |
689 | 716 | } |
0 commit comments