mirror of
https://github.com/hyperdxio/hyperdx
synced 2026-04-21 13:37:15 +00:00
fix: Remove global connection hook (#608)
Removes recent code to check connections globally Ref: HDX-1401
This commit is contained in:
parent
8b5a8182d3
commit
4514f2c50f
4 changed files with 5 additions and 255 deletions
5
.changeset/silver-mails-try.md
Normal file
5
.changeset/silver-mails-try.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@hyperdx/app": patch
|
||||
---
|
||||
|
||||
Remove connection health hook - too noisy
|
||||
|
|
@ -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} />
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -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]);
|
||||
}
|
||||
Loading…
Reference in a new issue