Emit a warning when memory usage exceeds 7GB (#7613)

This commit is contained in:
Jacob MacDonald
2025-09-17 14:32:46 -07:00
committed by GitHub
parent 0cae7caaab
commit 13a65ad94f
7 changed files with 156 additions and 0 deletions
@@ -0,0 +1,71 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { renderHook } from '@testing-library/react';
import { vi } from 'vitest';
import {
useMemoryMonitor,
MEMORY_CHECK_INTERVAL,
MEMORY_WARNING_THRESHOLD,
} from './useMemoryMonitor.js';
import process from 'node:process';
import { MessageType } from '../types.js';
describe('useMemoryMonitor', () => {
const memoryUsageSpy = vi.spyOn(process, 'memoryUsage');
const addItem = vi.fn();
beforeEach(() => {
vi.useFakeTimers();
vi.clearAllMocks();
});
afterEach(() => {
vi.useRealTimers();
});
it('should not warn when memory usage is below threshold', () => {
memoryUsageSpy.mockReturnValue({
rss: MEMORY_WARNING_THRESHOLD / 2,
} as NodeJS.MemoryUsage);
renderHook(() => useMemoryMonitor({ addItem }));
vi.advanceTimersByTime(10000);
expect(addItem).not.toHaveBeenCalled();
});
it('should warn when memory usage is above threshold', () => {
memoryUsageSpy.mockReturnValue({
rss: MEMORY_WARNING_THRESHOLD * 1.5,
} as NodeJS.MemoryUsage);
renderHook(() => useMemoryMonitor({ addItem }));
vi.advanceTimersByTime(MEMORY_CHECK_INTERVAL);
expect(addItem).toHaveBeenCalledTimes(1);
expect(addItem).toHaveBeenCalledWith(
{
type: MessageType.WARNING,
text: 'High memory usage detected: 10.50 GB. If you experience a crash, please file a bug report by running `/bug`',
},
expect.any(Number),
);
});
it('should only warn once', () => {
memoryUsageSpy.mockReturnValue({
rss: MEMORY_WARNING_THRESHOLD * 1.5,
} as NodeJS.MemoryUsage);
const { rerender } = renderHook(() => useMemoryMonitor({ addItem }));
vi.advanceTimersByTime(MEMORY_CHECK_INTERVAL);
expect(addItem).toHaveBeenCalledTimes(1);
// Rerender and advance timers, should not warn again
memoryUsageSpy.mockReturnValue({
rss: MEMORY_WARNING_THRESHOLD * 1.5,
} as NodeJS.MemoryUsage);
rerender();
vi.advanceTimersByTime(MEMORY_CHECK_INTERVAL);
expect(addItem).toHaveBeenCalledTimes(1);
});
});
@@ -0,0 +1,41 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { useEffect } from 'react';
import process from 'node:process';
import { type HistoryItemWithoutId, MessageType } from '../types.js';
export const MEMORY_WARNING_THRESHOLD = 7 * 1024 * 1024 * 1024; // 7GB in bytes
export const MEMORY_CHECK_INTERVAL = 60 * 1000; // one minute
interface MemoryMonitorOptions {
addItem: (item: HistoryItemWithoutId, timestamp: number) => void;
}
export const useMemoryMonitor = ({ addItem }: MemoryMonitorOptions) => {
useEffect(() => {
const intervalId = setInterval(() => {
const usage = process.memoryUsage().rss;
if (usage > MEMORY_WARNING_THRESHOLD) {
addItem(
{
type: MessageType.WARNING,
text:
`High memory usage detected: ${(
usage /
(1024 * 1024 * 1024)
).toFixed(2)} GB. ` +
'If you experience a crash, please file a bug report by running `/bug`',
},
Date.now(),
);
clearInterval(intervalId);
}
}, MEMORY_CHECK_INTERVAL);
return () => clearInterval(intervalId);
}, [addItem]);
};