dca9b87190
In 3.0, the packet loss metric used to trigger connection status alerts was changed to the one generated by the `startMonitoringNetwork` method used by the connection status modal. Since packet loss thresholds were not adjusted (0.5, 0.1, 0.2), a single lost packet causes the status alert to be permanently stuck on "critical". This is explained by how different those metrics are: - **Before (2.7):** A 5-probe wide calculation of inbound packet loss fraction based on `packetsLost` and `packetsReceived` metrics. - **Now (3.0):** An absolute counter of inbound lost packets. This commit restores the previous packet loss metric used to trigger connection status alerts, reverting to the original collection method via `/utils/stats.js`. This resolves the issue, but further work is needed in subsequent PRs: - Unify the collection done in `/utils/stats.js` with the `startMonitoringNetwork` method. - Incorporate the remote-inbound `fractionsLost` metric to account for packet loss on both legs of the network (in/out). - Update the packet loss metric displayed in the connection status modal to show a more meaningful value (e.g., packet loss percentage over a specific probe interval). An absolute counter of lost packets isn't useful for end users. - Update the alert log to use the fraction or percentage above
105 lines
3.1 KiB
JavaScript
Executable File
105 lines
3.1 KiB
JavaScript
Executable File
import { useEffect, useRef } from 'react';
|
|
import { useMutation } from '@apollo/client';
|
|
import { UPDATE_CONNECTION_ALIVE_AT } from './mutations';
|
|
import {
|
|
getStatus,
|
|
handleAudioStatsEvent,
|
|
startMonitoringNetwork,
|
|
} from '/imports/ui/components/connection-status/service';
|
|
import connectionStatus from '../../core/graphql/singletons/connectionStatus';
|
|
|
|
import getBaseUrl from '/imports/ui/core/utils/getBaseUrl';
|
|
import useCurrentUser from '../../core/hooks/useCurrentUser';
|
|
|
|
const ConnectionStatus = () => {
|
|
const STATS_INTERVAL = window.meetingClientSettings.public.stats.interval;
|
|
const networkRttInMs = useRef(0); // Ref to store the last rtt
|
|
const timeoutRef = useRef(null);
|
|
|
|
const [updateConnectionAliveAtM] = useMutation(UPDATE_CONNECTION_ALIVE_AT);
|
|
|
|
const {
|
|
data,
|
|
} = useCurrentUser((u) => ({
|
|
userId: u.userId,
|
|
avatar: u.avatar,
|
|
isModerator: u.isModerator,
|
|
color: u.color,
|
|
currentlyInMeeting: u.currentlyInMeeting,
|
|
}));
|
|
|
|
const handleUpdateConnectionAliveAt = () => {
|
|
const startTime = performance.now();
|
|
fetch(
|
|
`${getBaseUrl()}/ping`,
|
|
{ signal: AbortSignal.timeout(STATS_INTERVAL) },
|
|
)
|
|
.then((res) => {
|
|
if (res.ok && res.status === 200) {
|
|
const rttLevels = window.meetingClientSettings.public.stats.rtt;
|
|
const endTime = performance.now();
|
|
const networkRtt = Math.round(endTime - startTime);
|
|
networkRttInMs.current = networkRtt;
|
|
updateConnectionAliveAtM({
|
|
variables: {
|
|
networkRttInMs: networkRtt,
|
|
},
|
|
});
|
|
const rttStatus = getStatus(rttLevels, networkRtt);
|
|
connectionStatus.setRttValue(networkRtt);
|
|
connectionStatus.setRttStatus(rttStatus);
|
|
connectionStatus.setLastRttRequestSuccess(true);
|
|
|
|
if (Object.keys(rttLevels).includes(rttStatus)) {
|
|
connectionStatus.addUserNetworkHistory(
|
|
data,
|
|
rttStatus,
|
|
Date.now(),
|
|
);
|
|
}
|
|
}
|
|
})
|
|
.catch(() => {
|
|
connectionStatus.setLastRttRequestSuccess(false);
|
|
// gets the worst status
|
|
connectionStatus.setRttStatus('critical');
|
|
})
|
|
.finally(() => {
|
|
if (timeoutRef.current) {
|
|
clearTimeout(timeoutRef.current);
|
|
}
|
|
|
|
timeoutRef.current = setTimeout(() => {
|
|
handleUpdateConnectionAliveAt();
|
|
}, STATS_INTERVAL);
|
|
});
|
|
};
|
|
|
|
useEffect(() => {
|
|
// Delay first connectionAlive to avoid high RTT misestimation
|
|
// due to initial subscription and mutation traffic at client render
|
|
timeoutRef.current = setTimeout(() => {
|
|
handleUpdateConnectionAliveAt();
|
|
}, STATS_INTERVAL / 2);
|
|
|
|
const STATS_ENABLED = window.meetingClientSettings.public.stats.enabled;
|
|
|
|
if (STATS_ENABLED) {
|
|
window.addEventListener('audiostats', handleAudioStatsEvent);
|
|
startMonitoringNetwork();
|
|
}
|
|
|
|
return () => {
|
|
window.removeEventListener('audiostats', handleAudioStatsEvent);
|
|
|
|
if (timeoutRef.current) {
|
|
clearTimeout(timeoutRef.current);
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
return null;
|
|
};
|
|
|
|
export default ConnectionStatus;
|