History

Skip healthy env reconnects after browser resume (#2677)

Julius Marminge committed 6 months ago

Commit 90eea041 parent f92e1e13 files · +59 −2Browse files

Unified · Split

3 files
  1. apps/web/src/environments/runtime/service.threadSubscriptions.test.ts +53 −1
  2. apps/web/src/environments/runtime/service.ts +4 −1
  3. apps/web/src/rpc/wsRpcClient.ts +2 −0
apps/web/src/environments/runtime/service.threadSubscriptions.test.ts+53−1
@@ -176,6 +176,7 @@ describe("retainThreadDetailSubscription", () => { · 175 unmodified lines
176176 },
177177 })),
178178 },
179+ isHeartbeatFresh: vi.fn(() => true),
179180 orchestration: {
180181 subscribeThread: mockSubscribeThread,
181182 },
@@ -382,7 +383,7 @@ describe("retainThreadDetailSubscription", () => { · 200 unmodified lines
382383 await resetEnvironmentServiceForTests();
383384 });
384385
385− it("reconnects environment streams when the browser resumes from the background", async () => {
386+ it("keeps healthy environment streams connected when the browser resumes from the background", async () => {
386387 let visibilityState: DocumentVisibilityState = "visible";
387388 const documentTarget = new EventTarget();
388389 const windowTarget = new EventTarget();
@@ -408,6 +409,57 @@ describe("retainThreadDetailSubscription", () => { · 19 unmodified lines
408409 documentTarget.dispatchEvent(new Event("visibilitychange"));
409410 expect(mockConnectionReconnects[0]).not.toHaveBeenCalled();
410411
412+ visibilityState = "visible";
413+ documentTarget.dispatchEvent(new Event("visibilitychange"));
414+ expect(mockConnectionReconnects[0]).not.toHaveBeenCalled();
415+
416+ stop();
417+ await resetEnvironmentServiceForTests();
418+ });
419+
420+ it("reconnects stale environment streams when the browser resumes from the background", async () => {
421+ let visibilityState: DocumentVisibilityState = "visible";
422+ const documentTarget = new EventTarget();
423+ const windowTarget = new EventTarget();
424+ vi.stubGlobal("document", {
425+ addEventListener: documentTarget.addEventListener.bind(documentTarget),
426+ removeEventListener: documentTarget.removeEventListener.bind(documentTarget),
427+ get visibilityState() {
428+ return visibilityState;
429+ },
430+ });
431+ vi.stubGlobal("window", {
432+ addEventListener: windowTarget.addEventListener.bind(windowTarget),
433+ removeEventListener: windowTarget.removeEventListener.bind(windowTarget),
434+ });
435+ mockCreateWsRpcClient.mockReturnValue({
436+ server: {
437+ getConfig: vi.fn(async () => ({
438+ environment: {
439+ environmentId: EnvironmentId.make("env-remote"),
440+ label: "Remote env",
441+ platform: { os: "darwin", arch: "arm64" },
442+ serverVersion: "0.0.0-test",
443+ capabilities: { repositoryIdentity: true },
444+ },
445+ })),
446+ },
447+ isHeartbeatFresh: vi.fn(() => false),
448+ orchestration: {
449+ subscribeThread: mockSubscribeThread,
450+ },
451+ });
452+
453+ const { resetEnvironmentServiceForTests, startEnvironmentConnectionService } =
454+ await import("./service");
455+
456+ const stop = startEnvironmentConnectionService(new QueryClient());
457+ expect(mockConnectionReconnects).toHaveLength(1);
458+
459+ visibilityState = "hidden";
460+ documentTarget.dispatchEvent(new Event("visibilitychange"));
461+ expect(mockConnectionReconnects[0]).not.toHaveBeenCalled();
462+
411463 visibilityState = "visible";
412464 documentTarget.dispatchEvent(new Event("visibilitychange"));
413465 expect(mockConnectionReconnects[0]).toHaveBeenCalledTimes(1);
apps/web/src/environments/runtime/service.ts+4−1
@@ -1482,9 +1482,12 @@ function reconnectEnvironmentConnectionsAfterBrowserResume(reason: string): void · 1481 unmodified lines
14821482 if (now - lastBrowserResumeReconnectAt < BROWSER_RESUME_RECONNECT_COOLDOWN_MS) {
14831483 return;
14841484 }
1485− lastBrowserResumeReconnectAt = now;
14861485
14871486 for (const connection of environmentConnections.values()) {
1487+ if (connection.client.isHeartbeatFresh()) {
1488+ continue;
1489+ }
1490+ lastBrowserResumeReconnectAt = now;
14881491 void connection.reconnect().catch((error) => {
14891492 console.warn("Environment reconnect after browser resume failed", {
14901493 environmentId: connection.environmentId,
apps/web/src/rpc/wsRpcClient.ts+2−0
@@ -56,6 +56,7 @@ interface GitRunStackedActionOptions { · 55 unmodified lines
5656 export interface WsRpcClient {
5757 readonly dispose: () => Promise<void>;
5858 readonly reconnect: () => Promise<void>;
59+ readonly isHeartbeatFresh: () => boolean;
5960 readonly terminal: {
6061 readonly open: RpcUnaryMethod<typeof WS_METHODS.terminalOpen>;
6162 readonly write: RpcUnaryMethod<typeof WS_METHODS.terminalWrite>;
@@ -158,6 +159,7 @@ export function createWsRpcClient(transport: WsTransport): WsRpcClient { · 96 unmodified lines
158159 resetWsReconnectBackoff();
159160 await transport.reconnect();
160161 },
162+ isHeartbeatFresh: () => transport.isHeartbeatFresh(),
161163 terminal: {
162164 open: (input) => transport.request((client) => client[WS_METHODS.terminalOpen](input)),
163165 write: (input) => transport.request((client) => client[WS_METHODS.terminalWrite](input)),

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t