Skip to content

Commit 729f507

Browse files
thomasballingerConvex, Inc.
authored andcommitted
Debug-mode clients report slow and large transitions (#40943)
GitOrigin-RevId: b932a0408c54b712c4e81e574e3ee108a784e6c7
1 parent 7c53722 commit 729f507

File tree

2 files changed

+62
-34
lines changed

2 files changed

+62
-34
lines changed

src/browser/sync/client.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ export class BaseConvexClient {
497497
webSocketConstructor,
498498
this.logger,
499499
this.markConnectionStateDirty,
500+
this.debug,
500501
);
501502
this.mark("convexClientConstructed");
502503

src/browser/sync/web_socket_manager.ts

Lines changed: 61 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
encodeClientMessage,
55
parseServerMessage,
66
ServerMessage,
7+
Transition,
78
} from "./protocol.js";
89

910
const CLOSE_NORMAL = 1000;
@@ -199,6 +200,7 @@ export class WebSocketManager {
199200
webSocketConstructor: typeof WebSocket,
200201
logger: Logger,
201202
private readonly markConnectionStateDirty: () => void,
203+
private readonly debug: boolean,
202204
) {
203205
this.webSocketConstructor = webSocketConstructor;
204206
this.socket = { state: "disconnected" };
@@ -312,40 +314,11 @@ export class WebSocketManager {
312314
const messageLength = message.data.length;
313315
const serverMessage = parseServerMessage(JSON.parse(message.data));
314316
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+
});
349322
}
350323
const response = this.onMessage(serverMessage);
351324
if (response.hasSyncedPastLastReconnect) {
@@ -686,4 +659,58 @@ export class WebSocketManager {
686659
const jitter = actualBackoff * (Math.random() - 0.5);
687660
return actualBackoff + jitter;
688661
}
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+
}
689716
}

0 commit comments

Comments
 (0)