From ccbb5c41c60ab46f42ac3bf1da449ede1cf508d5 Mon Sep 17 00:00:00 2001 From: "courtmanr@gmail.com" Date: Wed, 4 Jun 2025 20:42:18 +0100 Subject: [PATCH] fix: make individual node resource fetching non-blocking The smart cluster handling prevented endpoint duplication but individual node resource fetching was still sequential and blocking. This adds: - Promise.allSettled for parallel node resource fetching - 8-second timeouts on individual resource requests - Proper error handling for failed resources This should completely eliminate dashboard freezes when nodes go offline. Addresses issue #104 follow-up --- server/dataFetcher.js | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/server/dataFetcher.js b/server/dataFetcher.js index 49433d1ed..122c70acf 100644 --- a/server/dataFetcher.js +++ b/server/dataFetcher.js @@ -129,7 +129,10 @@ async function initializePLimit() { // Helper function to fetch data and handle common errors/warnings async function fetchNodeResource(apiClient, endpointId, nodeName, resourcePath, resourceName, expectArray = false, transformFn = null) { try { - const response = await apiClient.get(`/nodes/${nodeName}/${resourcePath}`); + // Add a short timeout for individual resource calls to fail fast + const response = await apiClient.get(`/nodes/${nodeName}/${resourcePath}`, { + timeout: 8000 // 8 second timeout per resource to prevent long blocks + }); const data = response.data?.data; if (data) { @@ -149,24 +152,25 @@ async function fetchNodeResource(apiClient, endpointId, nodeName, resourcePath, } async function fetchDataForNode(apiClient, endpointId, nodeName) { - const nodeStatus = await fetchNodeResource(apiClient, endpointId, nodeName, 'status', 'Node status'); - const storage = await fetchNodeResource(apiClient, endpointId, nodeName, 'storage', 'Node storage', true); - - const vms = await fetchNodeResource( - apiClient, endpointId, nodeName, 'qemu', 'VMs (qemu)', true, - (data) => data.map(vm => ({ ...vm, node: nodeName, endpointId: endpointId, type: 'qemu' })) - ); - - const containers = await fetchNodeResource( - apiClient, endpointId, nodeName, 'lxc', 'Containers (lxc)', true, - (data) => data.map(ct => ({ ...ct, node: nodeName, endpointId: endpointId, type: 'lxc' })) - ); + // Make all node resource fetches parallel to prevent blocking when one node is down + const [nodeStatus, storage, vms, containers] = await Promise.allSettled([ + fetchNodeResource(apiClient, endpointId, nodeName, 'status', 'Node status'), + fetchNodeResource(apiClient, endpointId, nodeName, 'storage', 'Node storage', true), + fetchNodeResource( + apiClient, endpointId, nodeName, 'qemu', 'VMs (qemu)', true, + (data) => data.map(vm => ({ ...vm, node: nodeName, endpointId: endpointId, type: 'qemu' })) + ), + fetchNodeResource( + apiClient, endpointId, nodeName, 'lxc', 'Containers (lxc)', true, + (data) => data.map(ct => ({ ...ct, node: nodeName, endpointId: endpointId, type: 'lxc' })) + ) + ]); return { - vms: vms || [], - containers: containers || [], - nodeStatus: nodeStatus || {}, - storage: storage || [], + vms: (vms.status === 'fulfilled' ? vms.value : []) || [], + containers: (containers.status === 'fulfilled' ? containers.value : []) || [], + nodeStatus: (nodeStatus.status === 'fulfilled' ? nodeStatus.value : {}) || {}, + storage: (storage.status === 'fulfilled' ? storage.value : []) || [], }; }