fix: Remove global connection hook (#608)

Removes recent code to check connections globally

Ref: HDX-1401
This commit is contained in:
Tom Alexander 2025-02-11 17:01:03 -08:00 committed by GitHub
parent 8b5a8182d3
commit 4514f2c50f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 5 additions and 255 deletions

View file

@ -0,0 +1,5 @@
---
"@hyperdx/app": patch
---
Remove connection health hook - too noisy

View file

@ -8,8 +8,6 @@ import {
} from '@mantine/core';
import { Notifications } from '@mantine/notifications';
import { useConnectionHealth } from '@/hooks/useConnectionHealth';
const makeTheme = ({
fontFamily = '"IBM Plex Sans", monospace',
}: {
@ -151,9 +149,6 @@ export const ThemeWrapper = ({
}) => {
const theme = React.useMemo(() => makeTheme({ fontFamily }), [fontFamily]);
// Add connection health monitoring
useConnectionHealth();
return (
<MantineProvider forceColorScheme="dark" theme={theme}>
<Notifications zIndex={999999} />

View file

@ -1,119 +0,0 @@
import { notifications } from '@mantine/notifications';
import { act, renderHook } from '@testing-library/react';
import api from '@/api';
import { useConnections } from '@/connection';
import { useConnectionHealth } from '../useConnectionHealth';
// Mock dependencies
jest.mock('@mantine/notifications');
jest.mock('@/api');
jest.mock('@/connection');
describe('useConnectionHealth', () => {
const mockConnections = [
{
id: '1',
name: 'Test Connection 1',
host: 'localhost',
username: 'user1',
password: 'pass1',
},
];
beforeEach(() => {
jest.useFakeTimers();
jest.clearAllMocks();
(useConnections as jest.Mock).mockReturnValue({
data: mockConnections,
});
});
afterEach(() => {
jest.useRealTimers();
});
describe('connection monitoring', () => {
it('should start monitoring after initial delay', async () => {
const testConnectionMock = jest.fn().mockResolvedValue({ success: true });
(api.useTestConnection as jest.Mock).mockReturnValue({
mutateAsync: testConnectionMock,
});
renderHook(() => useConnectionHealth());
await act(async () => {
jest.advanceTimersByTime(5000);
});
expect(testConnectionMock).toHaveBeenCalledWith({
host: mockConnections[0].host,
username: mockConnections[0].username,
password: mockConnections[0].password,
});
});
it('should show notification when connection fails', async () => {
const testConnectionMock = jest.fn().mockResolvedValue({
success: false,
error: 'Connection failed',
});
(api.useTestConnection as jest.Mock).mockReturnValue({
mutateAsync: testConnectionMock,
});
renderHook(() => useConnectionHealth());
await act(async () => {
jest.advanceTimersByTime(5000);
});
expect(notifications.show).toHaveBeenCalledWith({
id: 'connection-error-1',
color: 'red',
message:
'Connection "Test Connection 1" is not responding: Connection failed',
autoClose: false,
});
});
it('should respect retry delay for failed connections', async () => {
const testConnectionMock = jest.fn().mockResolvedValue({
success: false,
});
(api.useTestConnection as jest.Mock).mockReturnValue({
mutateAsync: testConnectionMock,
});
renderHook(() => useConnectionHealth());
await act(async () => {
jest.advanceTimersByTime(5000);
});
await act(async () => {
jest.advanceTimersByTime(10000);
});
expect(testConnectionMock).toHaveBeenCalledTimes(1);
});
});
describe('cleanup', () => {
it('should clean up intervals on unmount', () => {
const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout');
const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
const { unmount } = renderHook(() => useConnectionHealth());
act(() => {
unmount();
});
expect(clearTimeoutSpy).toHaveBeenCalled();
expect(clearIntervalSpy).toHaveBeenCalled();
});
});
});

View file

@ -1,131 +0,0 @@
import { useCallback, useEffect, useState } from 'react';
import { notifications } from '@mantine/notifications';
import api from '@/api';
import { Connection, useConnections } from '@/connection';
const INITIAL_DELAY = 5000;
const CHECK_INTERVAL = 5 * 60 * 1000;
const RETRY_DELAY = 15 * 1000;
export function useConnectionHealth() {
const { data: connections } = useConnections();
const testConnection = api.useTestConnection();
const [failedConnections, setFailedConnections] = useState<
Map<string, number>
>(new Map());
const [isChecking, setIsChecking] = useState(false);
/* Simple function for showing notifications */
const showNotification = useCallback(
(
connectionId: string,
connectionName: string,
isError: boolean,
message?: string,
) => {
notifications.show({
id: `connection-${isError ? 'error' : 'restored'}-${connectionId}`,
color: isError ? 'red' : 'green',
message: isError
? `Connection "${connectionName}" is not responding: ${message}`
: `Connection "${connectionName}" has been restored`,
autoClose: isError ? false : 5000,
});
},
[],
);
/* Maintains a map of connection IDs to their last failed check timestamp */
const updateFailedConnections = useCallback(
(connectionId: string, shouldAdd: boolean, timestamp?: number) => {
setFailedConnections(prev => {
const next = new Map(prev);
if (shouldAdd) {
next.set(connectionId, timestamp || Date.now());
} else {
next.delete(connectionId);
}
return next;
});
},
[],
);
/* Checks if a single connection is failing and shows a notification if it is */
const checkConnection = useCallback(
async (connection: Connection) => {
const now = Date.now();
const lastCheckTime = failedConnections.get(connection.id) || 0;
if (now - lastCheckTime < RETRY_DELAY) {
return;
}
try {
const result = await testConnection.mutateAsync({
host: connection.host,
username: connection.username,
password: connection.password,
});
const wasFailedPreviously = failedConnections.has(connection.id);
if (!result.success) {
if (!wasFailedPreviously) {
updateFailedConnections(connection.id, true, now);
showNotification(
connection.id,
connection.name,
true,
result.error,
);
}
} else if (wasFailedPreviously) {
updateFailedConnections(connection.id, false);
showNotification(connection.id, connection.name, false);
}
} catch (error: any) {
if (!failedConnections.has(connection.id)) {
const body = await error.response?.json();
const errorMessage = body?.error ?? error.message;
updateFailedConnections(connection.id, true, now);
showNotification(connection.id, connection.name, true, errorMessage);
}
}
},
[
testConnection,
failedConnections,
updateFailedConnections,
showNotification,
],
);
/* Checks all connections and shows notifications if they are failing */
const checkConnections = useCallback(async () => {
if (!connections?.length || isChecking) return;
setIsChecking(true);
try {
for (const connection of connections) {
await checkConnection(connection);
// Add small delay between checks to prevent overwhelming the server
await new Promise(resolve => setTimeout(resolve, 1000));
}
} finally {
setIsChecking(false);
}
}, [connections, isChecking, checkConnection]);
/* Sets up the initial check and the interval check */
useEffect(() => {
const initialCheckTimeout = setTimeout(checkConnections, INITIAL_DELAY);
const interval = setInterval(checkConnections, CHECK_INTERVAL);
return () => {
clearTimeout(initialCheckTimeout);
clearInterval(interval);
};
}, [checkConnections]);
}