From 6bacbd964df5c3bed1aeb4ca5ce3382e4c2bcced Mon Sep 17 00:00:00 2001 From: Haze <709547807@qq.com> Date: Fri, 24 Apr 2026 17:04:10 +0800 Subject: [PATCH] perf(gateway): shorten gateway.ready fallback timeout from 30s to 5s The fallback exists as a safety net for the server-side gateway.ready event. In practice OpenClaw's plugin bootstrap can push the real event well past 30s (observed: handshake completes, then 30s tick by, then the fallback fires with no event having arrived). That long tail kept the stale gating code blocking UI state for the full 30s. Step 1 moved sessions.list off the gatewayReady gate, so this value now only matters as a belt-and-braces signal for any future consumer. 5s is long enough to preserve "event wins when it actually fires on a healthy boot" while avoiding a multi-second stall whenever the server is slow. Updated gateway-ready-fallback.test.ts to advance timers around the new boundary. --- electron/gateway/manager.ts | 7 ++++++- tests/unit/gateway-ready-fallback.test.ts | 12 ++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/electron/gateway/manager.ts b/electron/gateway/manager.ts index df6e40c..3a3b581 100644 --- a/electron/gateway/manager.ts +++ b/electron/gateway/manager.ts @@ -155,7 +155,12 @@ export class GatewayManager extends EventEmitter { private static readonly HEARTBEAT_TIMEOUT_MS_WIN = 25_000; private static readonly HEARTBEAT_MAX_MISSES_WIN = 5; public static readonly RESTART_COOLDOWN_MS = 5_000; - private static readonly GATEWAY_READY_FALLBACK_MS = 30_000; + // Fallback for the server-side gateway.ready event: if the event doesn't + // arrive within this window after the WS handshake completes, we assume the + // gateway is effectively ready so downstream consumers don't block forever. + // Kept short (5s) because handshake completion already implies a working + // RPC channel — this is only a safety net, not the primary signal. + private static readonly GATEWAY_READY_FALLBACK_MS = 5_000; private lastRestartAt = 0; /** Set by scheduleReconnect() before calling start() to signal auto-reconnect. */ private isAutoReconnectStart = false; diff --git a/tests/unit/gateway-ready-fallback.test.ts b/tests/unit/gateway-ready-fallback.test.ts index 8cd9596..62cfd1d 100644 --- a/tests/unit/gateway-ready-fallback.test.ts +++ b/tests/unit/gateway-ready-fallback.test.ts @@ -90,10 +90,10 @@ describe('GatewayManager gatewayReady fallback', () => { (manager as unknown as { scheduleGatewayReadyFallback: () => void }).scheduleGatewayReadyFallback(); // Before timeout, no gatewayReady update - vi.advanceTimersByTime(29_000); + vi.advanceTimersByTime(4_000); expect(statusUpdates.find((u) => u.gatewayReady === true)).toBeUndefined(); - // After 30s fallback timeout + // After fallback timeout (5s) vi.advanceTimersByTime(2_000); const readyUpdate = statusUpdates.find((u) => u.gatewayReady === true); expect(readyUpdate).toBeDefined(); @@ -115,13 +115,13 @@ describe('GatewayManager gatewayReady fallback', () => { // Schedule fallback (manager as unknown as { scheduleGatewayReadyFallback: () => void }).scheduleGatewayReadyFallback(); - // gateway:ready event arrives at 5s - vi.advanceTimersByTime(5_000); + // gateway:ready event arrives before fallback (at 1s, well under 5s) + vi.advanceTimersByTime(1_000); manager.emit('gateway:ready', {}); expect(statusUpdates.filter((u) => u.gatewayReady === true)).toHaveLength(1); - // After 30s, no duplicate gatewayReady=true - vi.advanceTimersByTime(30_000); + // Well past the fallback window, no duplicate gatewayReady=true + vi.advanceTimersByTime(10_000); expect(statusUpdates.filter((u) => u.gatewayReady === true)).toHaveLength(1); }); });