From b5eb47e69056532b069e38d681ef3941428811dd Mon Sep 17 00:00:00 2001 From: shats Date: Wed, 18 Feb 2026 11:55:36 +0700 Subject: [PATCH] feat(NetworkMapDashboard): implement runWithOnePerKey function to ensure single task execution per node during speed tests --- frontend/src/NetworkMapDashboard.jsx | 43 +++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/frontend/src/NetworkMapDashboard.jsx b/frontend/src/NetworkMapDashboard.jsx index b5d9f53..9f71d64 100644 --- a/frontend/src/NetworkMapDashboard.jsx +++ b/frontend/src/NetworkMapDashboard.jsx @@ -25,6 +25,46 @@ async function runWithLimit(tasks, limit = 4) { return results; } +/** + * Выполняет задачи так, что на каждый ключ (ноду) в один момент времени + * выполняется не более одной задачи. Разные ноды — параллельно. + * getKey(i) возвращает ключ для задачи с индексом i. + */ +async function runWithOnePerKey(tasks, getKey) { + const n = tasks.length; + const keyToIndices = new Map(); + for (let i = 0; i < n; i++) { + const k = String(getKey(i)); + if (!keyToIndices.has(k)) keyToIndices.set(k, []); + keyToIndices.get(k).push(i); + } + const results = []; + for (let i = 0; i < n; i++) results[i] = null; + + const keys = Array.from(keyToIndices.keys()); + let round = 0; + while (true) { + const batch = []; + for (const k of keys) { + const indices = keyToIndices.get(k); + if (round < indices.length) batch.push(indices[round]); + } + if (batch.length === 0) break; + await Promise.all( + batch.map(async (i) => { + try { + const value = await tasks[i](); + results[i] = { value }; + } catch (err) { + results[i] = { error: err }; + } + }) + ); + round++; + } + return results; +} + export default function NetworkMapDashboard() { const [servers, setServers] = useState([]); const [connections, setConnections] = useState([]); @@ -222,7 +262,8 @@ export default function NetworkMapDashboard() { setSpeedMap((prev) => ({ ...prev, [key]: null })); } }); - await runWithLimit(tasks, 2); + // На каждой ноде (speedTestServerId) в один момент — только один тест, чтобы не делить канал + await runWithOnePerKey(tasks, (i) => withSpeed[i].speedTestServerId); if (speedAbortRef.current) return; setSpeedLoading(false); }, [connections]);