mirror of
https://github.com/ValueCell-ai/ClawX.git
synced 2026-08-14 00:48:10 +00:00
chore: lower gateway heartbeat restart threshold to 4 consecutive misses (#1237)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user