From b7f7a9b3cc6f34bbf2abc12baaa172fcb05f1654 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 19:14:56 +0000 Subject: [PATCH 1/6] Initial plan From 3a6ea2c3d5372cf2c2e280397b5c32ddb2e416fa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 19:16:26 +0000 Subject: [PATCH 2/6] fix: select least-loaded eligible node for new containers Agent-Logs-Url: https://github.com/mieweb/opensource-server/sessions/5e42ef55-c7b0-40df-b430-cf3866fe9528 Co-authored-by: runleveldev <44057501+runleveldev@users.noreply.github.com> --- create-a-container/routers/containers.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/create-a-container/routers/containers.js b/create-a-container/routers/containers.js index 9ace32f5..67f0bf2f 100644 --- a/create-a-container/routers/containers.js +++ b/create-a-container/routers/containers.js @@ -351,7 +351,24 @@ router.post('/', async (req, res) => { nodeWhere.nvidiaAvailable = true; } - const node = await Node.findOne({ where: nodeWhere }); + const [node] = await Node.findAll({ + where: nodeWhere, + attributes: [ + 'id', + [Sequelize.fn('COUNT', Sequelize.col('containers.id')), 'containerCount'] + ], + include: [{ + association: 'containers', + attributes: [], + required: false + }], + group: ['Node.id'], + order: [ + [Sequelize.literal('containerCount'), 'ASC'], + ['id', 'ASC'] + ], + limit: 1 + }); if (!node && wantsNvidia) { throw new Error('NVIDIA requested but no NVIDIA-capable nodes are available in this site'); } From 6ff825f24fd14745aa2bd706dce476ea42754a7e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 19:17:24 +0000 Subject: [PATCH 3/6] refactor: avoid literal in least-loaded node ordering Agent-Logs-Url: https://github.com/mieweb/opensource-server/sessions/5e42ef55-c7b0-40df-b430-cf3866fe9528 Co-authored-by: runleveldev <44057501+runleveldev@users.noreply.github.com> --- create-a-container/routers/containers.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/create-a-container/routers/containers.js b/create-a-container/routers/containers.js index 67f0bf2f..7797b7c2 100644 --- a/create-a-container/routers/containers.js +++ b/create-a-container/routers/containers.js @@ -351,7 +351,7 @@ router.post('/', async (req, res) => { nodeWhere.nvidiaAvailable = true; } - const [node] = await Node.findAll({ + const eligibleNodes = await Node.findAll({ where: nodeWhere, attributes: [ 'id', @@ -364,11 +364,12 @@ router.post('/', async (req, res) => { }], group: ['Node.id'], order: [ - [Sequelize.literal('containerCount'), 'ASC'], + [Sequelize.fn('COUNT', Sequelize.col('containers.id')), 'ASC'], ['id', 'ASC'] ], limit: 1 }); + const node = eligibleNodes[0] || null; if (!node && wantsNvidia) { throw new Error('NVIDIA requested but no NVIDIA-capable nodes are available in this site'); } From 8945cd922e42bbcb69eb069e7e223682125136b7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 19:18:18 +0000 Subject: [PATCH 4/6] chore: tidy least-loaded node query naming Agent-Logs-Url: https://github.com/mieweb/opensource-server/sessions/5e42ef55-c7b0-40df-b430-cf3866fe9528 Co-authored-by: runleveldev <44057501+runleveldev@users.noreply.github.com> --- create-a-container/routers/containers.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/create-a-container/routers/containers.js b/create-a-container/routers/containers.js index 7797b7c2..52859826 100644 --- a/create-a-container/routers/containers.js +++ b/create-a-container/routers/containers.js @@ -351,12 +351,9 @@ router.post('/', async (req, res) => { nodeWhere.nvidiaAvailable = true; } - const eligibleNodes = await Node.findAll({ + const leastLoadedNodes = await Node.findAll({ where: nodeWhere, - attributes: [ - 'id', - [Sequelize.fn('COUNT', Sequelize.col('containers.id')), 'containerCount'] - ], + attributes: ['id'], include: [{ association: 'containers', attributes: [], @@ -369,7 +366,7 @@ router.post('/', async (req, res) => { ], limit: 1 }); - const node = eligibleNodes[0] || null; + const node = leastLoadedNodes[0] || null; if (!node && wantsNvidia) { throw new Error('NVIDIA requested but no NVIDIA-capable nodes are available in this site'); } From e822a5c02ee75ff757f2348391c2e5baf187c5f7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 19:19:15 +0000 Subject: [PATCH 5/6] refactor: use findOne for least-loaded node selection Agent-Logs-Url: https://github.com/mieweb/opensource-server/sessions/5e42ef55-c7b0-40df-b430-cf3866fe9528 Co-authored-by: runleveldev <44057501+runleveldev@users.noreply.github.com> --- create-a-container/routers/containers.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/create-a-container/routers/containers.js b/create-a-container/routers/containers.js index 52859826..afaf6b79 100644 --- a/create-a-container/routers/containers.js +++ b/create-a-container/routers/containers.js @@ -351,7 +351,7 @@ router.post('/', async (req, res) => { nodeWhere.nvidiaAvailable = true; } - const leastLoadedNodes = await Node.findAll({ + const node = await Node.findOne({ where: nodeWhere, attributes: ['id'], include: [{ @@ -363,10 +363,8 @@ router.post('/', async (req, res) => { order: [ [Sequelize.fn('COUNT', Sequelize.col('containers.id')), 'ASC'], ['id', 'ASC'] - ], - limit: 1 + ] }); - const node = leastLoadedNodes[0] || null; if (!node && wantsNvidia) { throw new Error('NVIDIA requested but no NVIDIA-capable nodes are available in this site'); } From 1dd342809cc2eec331d4ae23bd2a74f814e3db2f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 19:20:16 +0000 Subject: [PATCH 6/6] fix: choose least-loaded node using per-node container counts Agent-Logs-Url: https://github.com/mieweb/opensource-server/sessions/5e42ef55-c7b0-40df-b430-cf3866fe9528 Co-authored-by: runleveldev <44057501+runleveldev@users.noreply.github.com> --- create-a-container/routers/containers.js | 36 ++++++++++++++++-------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/create-a-container/routers/containers.js b/create-a-container/routers/containers.js index afaf6b79..87af5dc7 100644 --- a/create-a-container/routers/containers.js +++ b/create-a-container/routers/containers.js @@ -351,20 +351,32 @@ router.post('/', async (req, res) => { nodeWhere.nvidiaAvailable = true; } - const node = await Node.findOne({ + const eligibleNodes = await Node.findAll({ where: nodeWhere, - attributes: ['id'], - include: [{ - association: 'containers', - attributes: [], - required: false - }], - group: ['Node.id'], - order: [ - [Sequelize.fn('COUNT', Sequelize.col('containers.id')), 'ASC'], - ['id', 'ASC'] - ] + attributes: ['id'] }); + const eligibleNodeIds = eligibleNodes.map(({ id }) => id); + const nodeContainerCounts = eligibleNodeIds.length === 0 ? [] : await Container.findAll({ + where: { nodeId: eligibleNodeIds }, + attributes: [ + 'nodeId', + [Sequelize.fn('COUNT', Sequelize.col('id')), 'containerCount'] + ], + group: ['nodeId'], + raw: true + }); + const countByNodeId = new Map( + nodeContainerCounts.map(({ nodeId, containerCount }) => [Number(nodeId), Number(containerCount)]) + ); + const node = eligibleNodes.reduce((leastLoadedNode, candidateNode) => { + const candidateCount = countByNodeId.get(candidateNode.id) || 0; + if (!leastLoadedNode) return candidateNode; + + const leastLoadedCount = countByNodeId.get(leastLoadedNode.id) || 0; + if (candidateCount < leastLoadedCount) return candidateNode; + if (candidateCount === leastLoadedCount && candidateNode.id < leastLoadedNode.id) return candidateNode; + return leastLoadedNode; + }, null); if (!node && wantsNvidia) { throw new Error('NVIDIA requested but no NVIDIA-capable nodes are available in this site'); }