chore: lower gateway heartbeat restart threshold to 4 consecutive misses (#1237)

This commit is contained in:
paisley
2026-08-12 14:14:54 +08:00
committed by GitHub
parent d5168842de
commit df53de94df
4 changed files with 22 additions and 22 deletions
+1 -1
View File
@@ -196,7 +196,7 @@ export class GatewayManager extends EventEmitter {
private reconnectSuccessTotal = 0;
private static readonly HEARTBEAT_INTERVAL_MS = 60_000;
private static readonly HEARTBEAT_TIMEOUT_MS = 30_000;
private static readonly HEARTBEAT_MAX_MISSES = 10;
private static readonly HEARTBEAT_MAX_MISSES = 4;
public static readonly RESTART_COOLDOWN_MS = 5_000;
private static readonly GATEWAY_READY_FALLBACK_PROBE_DELAYS_MS = [1_500, 3_000, 5_000, 8_000, 12_000, 30_000] as const;
private lastRestartAt = 0;
+1 -1
View File
@@ -13,7 +13,7 @@ type BuildGatewayHealthSummaryOptions = {
};
const CHANNEL_STATUS_FAILURE_WINDOW_MS = 2 * 60_000;
const HEARTBEAT_MISS_THRESHOLD = 10;
const HEARTBEAT_MISS_THRESHOLD = 4;
export function buildGatewayHealthSummary(
options: BuildGatewayHealthSummaryOptions,
@@ -215,7 +215,7 @@ describe('GatewayManager diagnostics', () => {
expect(snapshot.core.rpcRouter).toBe('blocked');
});
it('marks windows gateway health unresponsive and restarts after ten heartbeat misses', async () => {
it('marks windows gateway health unresponsive and restarts after four heartbeat misses', async () => {
Object.defineProperty(process, 'platform', { value: 'win32' });
const { GatewayManager } = await import('@electron/gateway/manager');
@@ -239,10 +239,10 @@ describe('GatewayManager diagnostics', () => {
const restartSpy = vi.spyOn(manager, 'restart').mockResolvedValue();
(manager as unknown as { startPing: () => void }).startPing();
vi.advanceTimersByTime(660_000);
vi.advanceTimersByTime(300_000);
expect(restartSpy).toHaveBeenCalledTimes(1);
expect(manager.getDiagnostics().consecutiveHeartbeatMisses).toBe(10);
expect(manager.getDiagnostics().consecutiveHeartbeatMisses).toBe(4);
const health = buildGatewayHealthSummary({
status: { state: 'running', port: 18789 },
+17 -17
View File
@@ -28,7 +28,7 @@ describe('GatewayManager heartbeat recovery', () => {
Object.defineProperty(process, 'platform', { value: originalPlatform });
});
it('restarts only after ten consecutive heartbeat misses', async () => {
it('restarts only after four consecutive heartbeat misses', async () => {
const { GatewayManager } = await import('@electron/gateway/manager');
const manager = new GatewayManager();
@@ -49,13 +49,13 @@ describe('GatewayManager heartbeat recovery', () => {
const restartSpy = vi.spyOn(manager, 'restart').mockResolvedValue();
(manager as unknown as { startPing: () => void }).startPing();
vi.advanceTimersByTime(659_999);
vi.advanceTimersByTime(299_999);
expect(ws.ping).toHaveBeenCalledTimes(10);
expect(ws.ping).toHaveBeenCalledTimes(4);
expect(
(manager as unknown as { connectionMonitor: { getConsecutiveMisses: () => number } })
.connectionMonitor.getConsecutiveMisses(),
).toBe(9);
).toBe(3);
expect(restartSpy).not.toHaveBeenCalled();
expect(manager.getDiagnostics().consecutiveHeartbeatMisses).toBe(0);
@@ -63,7 +63,7 @@ describe('GatewayManager heartbeat recovery', () => {
expect(ws.terminate).not.toHaveBeenCalled();
expect(restartSpy).toHaveBeenCalledTimes(1);
expect(manager.getDiagnostics().consecutiveHeartbeatMisses).toBe(10);
expect(manager.getDiagnostics().consecutiveHeartbeatMisses).toBe(4);
expect(manager.getDiagnostics().lastHeartbeatTimeoutAt).toBe(Date.now());
vi.advanceTimersByTime(180_000);
@@ -72,7 +72,7 @@ describe('GatewayManager heartbeat recovery', () => {
(manager as unknown as { connectionMonitor: { clear: () => void } }).connectionMonitor.clear();
});
it('does not restart after ten misses when auto-reconnect is disabled', async () => {
it('does not restart after four misses when auto-reconnect is disabled', async () => {
const { GatewayManager } = await import('@electron/gateway/manager');
const manager = new GatewayManager();
@@ -92,16 +92,16 @@ describe('GatewayManager heartbeat recovery', () => {
const restartSpy = vi.spyOn(manager, 'restart').mockResolvedValue();
(manager as unknown as { startPing: () => void }).startPing();
vi.advanceTimersByTime(660_000);
vi.advanceTimersByTime(300_000);
expect(manager.getDiagnostics().consecutiveHeartbeatMisses).toBe(10);
expect(manager.getDiagnostics().consecutiveHeartbeatMisses).toBe(4);
expect(ws.terminate).not.toHaveBeenCalled();
expect(restartSpy).not.toHaveBeenCalled();
(manager as unknown as { connectionMonitor: { clear: () => void } }).connectionMonitor.clear();
});
it('requires ten new consecutive misses after responsiveness recovers', async () => {
it('requires four new consecutive misses after responsiveness recovers', async () => {
const { GatewayManager } = await import('@electron/gateway/manager');
const manager = new GatewayManager();
@@ -121,29 +121,29 @@ describe('GatewayManager heartbeat recovery', () => {
const restartSpy = vi.spyOn(manager, 'restart').mockResolvedValue();
(manager as unknown as { startPing: () => void }).startPing();
vi.advanceTimersByTime(600_000);
vi.advanceTimersByTime(240_000);
expect(
(manager as unknown as { connectionMonitor: { getConsecutiveMisses: () => number } })
.connectionMonitor.getConsecutiveMisses(),
).toBe(9);
).toBe(3);
(manager as unknown as { handleMessage: (message: unknown) => void }).handleMessage('alive');
expect(manager.getDiagnostics().consecutiveHeartbeatMisses).toBe(0);
expect(manager.getDiagnostics().lastAliveAt).toBe(Date.now());
vi.advanceTimersByTime(600_000);
vi.advanceTimersByTime(240_000);
expect(restartSpy).not.toHaveBeenCalled();
vi.advanceTimersByTime(60_000);
expect(ws.terminate).not.toHaveBeenCalled();
expect(restartSpy).toHaveBeenCalledTimes(1);
expect(manager.getDiagnostics().consecutiveHeartbeatMisses).toBe(10);
expect(manager.getDiagnostics().consecutiveHeartbeatMisses).toBe(4);
(manager as unknown as { connectionMonitor: { clear: () => void } }).connectionMonitor.clear();
});
it('restarts after ten consecutive heartbeat misses on windows', async () => {
it('restarts after four consecutive heartbeat misses on windows', async () => {
Object.defineProperty(process, 'platform', { value: 'win32' });
const { GatewayManager } = await import('@electron/gateway/manager');
@@ -165,12 +165,12 @@ describe('GatewayManager heartbeat recovery', () => {
const restartSpy = vi.spyOn(manager, 'restart').mockResolvedValue();
(manager as unknown as { startPing: () => void }).startPing();
vi.advanceTimersByTime(660_000);
vi.advanceTimersByTime(300_000);
expect(ws.ping).toHaveBeenCalledTimes(10);
expect(ws.ping).toHaveBeenCalledTimes(4);
expect(ws.terminate).not.toHaveBeenCalled();
expect(restartSpy).toHaveBeenCalledTimes(1);
expect(manager.getDiagnostics().consecutiveHeartbeatMisses).toBe(10);
expect(manager.getDiagnostics().consecutiveHeartbeatMisses).toBe(4);
(manager as unknown as { connectionMonitor: { clear: () => void } }).connectionMonitor.clear();
});