fix: apply empty agent updates immediately when existing agents are stale
The anti-flapping logic previously required 3 consecutive empty updates before clearing stale agents from the UI. Now if all existing hosts/dockerHosts have lastSeen > 60 seconds ago, empty updates are applied immediately. This fixes the issue where removed agents stayed visible for too long.
This commit is contained in:
parent
b02a1556b7
commit
68456763f9
1 changed files with 38 additions and 16 deletions
|
|
@ -334,17 +334,17 @@ export function createWebSocketStore(url: string) {
|
||||||
try {
|
try {
|
||||||
const message: WSMessage = data;
|
const message: WSMessage = data;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
message.type === WEBSOCKET.MESSAGE_TYPES.INITIAL_STATE ||
|
message.type === WEBSOCKET.MESSAGE_TYPES.INITIAL_STATE ||
|
||||||
message.type === WEBSOCKET.MESSAGE_TYPES.RAW_DATA
|
message.type === WEBSOCKET.MESSAGE_TYPES.RAW_DATA
|
||||||
) {
|
) {
|
||||||
// Update state properties individually, but batch the whole payload to
|
// Update state properties individually, but batch the whole payload to
|
||||||
// reduce reactive recomputations and UI thrash on large updates.
|
// reduce reactive recomputations and UI thrash on large updates.
|
||||||
if (message.data) batch(() => {
|
if (message.data) batch(() => {
|
||||||
// Mark that we've received usable data (initial payload or raw update)
|
// Mark that we've received usable data (initial payload or raw update)
|
||||||
if (!initialDataReceived()) {
|
if (!initialDataReceived()) {
|
||||||
setInitialDataReceived(true);
|
setInitialDataReceived(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only update if we have actual data, don't overwrite with empty arrays
|
// Only update if we have actual data, don't overwrite with empty arrays
|
||||||
if (message.data.nodes !== undefined) {
|
if (message.data.nodes !== undefined) {
|
||||||
|
|
@ -443,14 +443,25 @@ export function createWebSocketStore(url: string) {
|
||||||
if (incomingHosts.length === 0) {
|
if (incomingHosts.length === 0) {
|
||||||
consecutiveEmptyDockerUpdates += 1;
|
consecutiveEmptyDockerUpdates += 1;
|
||||||
|
|
||||||
|
// Check if all existing docker hosts are stale (>60s since lastSeen)
|
||||||
|
// If so, they're probably really gone - apply the empty update immediately
|
||||||
|
const now = Date.now();
|
||||||
|
const staleThresholdMs = 60_000; // 60 seconds
|
||||||
|
const existingDockerHosts = state.dockerHosts || [];
|
||||||
|
const allStale = existingDockerHosts.length === 0 || existingDockerHosts.every(
|
||||||
|
(h) => !h.lastSeen || (now - h.lastSeen) > staleThresholdMs
|
||||||
|
);
|
||||||
|
|
||||||
shouldApplyDockerHosts =
|
shouldApplyDockerHosts =
|
||||||
!hasReceivedNonEmptyDockerHosts ||
|
!hasReceivedNonEmptyDockerHosts ||
|
||||||
|
allStale ||
|
||||||
consecutiveEmptyDockerUpdates >= 3 ||
|
consecutiveEmptyDockerUpdates >= 3 ||
|
||||||
message.type === WEBSOCKET.MESSAGE_TYPES.INITIAL_STATE;
|
message.type === WEBSOCKET.MESSAGE_TYPES.INITIAL_STATE;
|
||||||
|
|
||||||
if (shouldApplyDockerHosts) {
|
if (shouldApplyDockerHosts) {
|
||||||
logger.debug('[WebSocket] Updating dockerHosts', {
|
logger.debug('[WebSocket] Updating dockerHosts', {
|
||||||
count: incomingHosts.length,
|
count: incomingHosts.length,
|
||||||
|
reason: allStale ? 'allStale' : 'threshold',
|
||||||
});
|
});
|
||||||
processedDockerHosts = mergeDockerHostRevocations(incomingHosts);
|
processedDockerHosts = mergeDockerHostRevocations(incomingHosts);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -486,14 +497,25 @@ export function createWebSocketStore(url: string) {
|
||||||
if (incomingHosts.length === 0) {
|
if (incomingHosts.length === 0) {
|
||||||
consecutiveEmptyHostUpdates += 1;
|
consecutiveEmptyHostUpdates += 1;
|
||||||
|
|
||||||
|
// Check if all existing hosts are stale (>60s since lastSeen)
|
||||||
|
// If so, they're probably really gone - apply the empty update immediately
|
||||||
|
const now = Date.now();
|
||||||
|
const staleThresholdMs = 60_000; // 60 seconds
|
||||||
|
const existingHosts = state.hosts || [];
|
||||||
|
const allHostsStale = existingHosts.length === 0 || existingHosts.every(
|
||||||
|
(h) => !h.lastSeen || (now - h.lastSeen) > staleThresholdMs
|
||||||
|
);
|
||||||
|
|
||||||
shouldApplyHosts =
|
shouldApplyHosts =
|
||||||
!hasReceivedNonEmptyHosts ||
|
!hasReceivedNonEmptyHosts ||
|
||||||
|
allHostsStale ||
|
||||||
consecutiveEmptyHostUpdates >= 3 ||
|
consecutiveEmptyHostUpdates >= 3 ||
|
||||||
message.type === WEBSOCKET.MESSAGE_TYPES.INITIAL_STATE;
|
message.type === WEBSOCKET.MESSAGE_TYPES.INITIAL_STATE;
|
||||||
|
|
||||||
if (shouldApplyHosts) {
|
if (shouldApplyHosts) {
|
||||||
logger.debug('[WebSocket] Updating hosts', {
|
logger.debug('[WebSocket] Updating hosts', {
|
||||||
count: incomingHosts.length,
|
count: incomingHosts.length,
|
||||||
|
reason: allHostsStale ? 'allStale' : 'threshold',
|
||||||
});
|
});
|
||||||
processedHosts = mergeHostRevocations(incomingHosts);
|
processedHosts = mergeHostRevocations(incomingHosts);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -646,11 +668,11 @@ export function createWebSocketStore(url: string) {
|
||||||
|
|
||||||
// Updated recentlyResolved
|
// Updated recentlyResolved
|
||||||
}
|
}
|
||||||
setState('lastUpdate', message.data.lastUpdate || new Date().toISOString());
|
setState('lastUpdate', message.data.lastUpdate || new Date().toISOString());
|
||||||
});
|
});
|
||||||
logger.debug('message', {
|
logger.debug('message', {
|
||||||
type: message.type,
|
type: message.type,
|
||||||
hasData: !!message.data,
|
hasData: !!message.data,
|
||||||
nodeCount: message.data?.nodes?.length || 0,
|
nodeCount: message.data?.nodes?.length || 0,
|
||||||
vmCount: message.data?.vms?.length || 0,
|
vmCount: message.data?.vms?.length || 0,
|
||||||
containerCount: message.data?.containers?.length || 0,
|
containerCount: message.data?.containers?.length || 0,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue