From 703e3d0faef1459dfd77233f84b6cff5e0bbba46 Mon Sep 17 00:00:00 2001 From: Peace Date: Sun, 13 Oct 2024 00:25:45 +0200 Subject: [PATCH 01/15] fix: pause child monitors if parent is paused --- server/server.js | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/server/server.js b/server/server.js index db58ae829..0d5285d62 100644 --- a/server/server.js +++ b/server/server.js @@ -1758,6 +1758,41 @@ async function startMonitor(userID, monitorID) { server.monitorList[monitor.id] = monitor; await monitor.start(io); + + const children = await Monitor.getChildren(monitorID); + for (let child of children) { + await startMonitorAsChild(userID, child.id); + } +} + +/** + * Start the specified monitor as child + * @param {number} userID ID of user who owns monitor + * @param {number} monitorID ID of monitor to start + * @returns {Promise} + */ +async function startMonitorAsChild(userID, monitorID) { + + let monitor = await R.findOne("monitor", " id = ? ", [ + monitorID, + ]); + if (monitor.active !== 1) { + return; + } + + log.info("manage", `Resume Monitor: ${monitorID} User ID: ${userID}`); + + if (monitor.id in server.monitorList) { + await server.monitorList[monitor.id].stop(); + } + + server.monitorList[monitor.id] = monitor; + await monitor.start(io); + + const children = await Monitor.getChildren(monitorID); + for (let child of children) { + await startMonitorAsChild(userID, child.id); + } } /** @@ -1790,6 +1825,37 @@ async function pauseMonitor(userID, monitorID) { await server.monitorList[monitorID].stop(); server.monitorList[monitorID].active = 0; } + + const children = await Monitor.getChildren(monitorID); + for (let child of children) { + await pauseMonitorAsChild(userID, child.id); + } +} + +/** + * Pause a given monitor as child + * @param {number} userID ID of user who owns monitor + * @param {number} monitorID ID of monitor to start + * @returns {Promise} + */ +async function pauseMonitorAsChild(userID, monitorID) { + let monitor = await R.findOne("monitor", " id = ? ", [ + monitorID, + ]); + if (monitor.active !== 1) { + return; + } + + log.info("manage", `Pause Monitor: ${monitorID} User ID: ${userID}`); + + if (monitorID in server.monitorList) { + await server.monitorList[monitorID].stop(); + } + + const children = await Monitor.getChildren(monitorID); + for (let child of children) { + await pauseMonitorAsChild(userID, child.id); + } } /** @@ -1798,6 +1864,7 @@ async function pauseMonitor(userID, monitorID) { */ async function startMonitors() { let list = await R.find("monitor", " active = 1 "); + list = list.filter((monitor) => Monitor.isActive(monitor.id, monitor.active)); for (let monitor of list) { server.monitorList[monitor.id] = monitor; From b4fabbb00f68a7bedc2876b9c8e867ad83ed29ed Mon Sep 17 00:00:00 2001 From: Peace Date: Sun, 13 Oct 2024 01:14:24 +0200 Subject: [PATCH 02/15] fix: update children in ui on pause --- server/server.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/server/server.js b/server/server.js index 0d5285d62..2d2194f19 100644 --- a/server/server.js +++ b/server/server.js @@ -987,6 +987,11 @@ let needSetup = false; await startMonitor(socket.userID, monitorID); await server.sendUpdateMonitorIntoList(socket, monitorID); + const childrenIDs = await Monitor.getAllChildrenIDs(monitorID); + for (let childID of childrenIDs) { + await server.sendUpdateMonitorIntoList(socket, childID); + } + callback({ ok: true, msg: "successResumed", @@ -1007,6 +1012,11 @@ let needSetup = false; await pauseMonitor(socket.userID, monitorID); await server.sendUpdateMonitorIntoList(socket, monitorID); + const childrenIDs = await Monitor.getAllChildrenIDs(monitorID); + for (let childID of childrenIDs) { + await server.sendUpdateMonitorIntoList(socket, childID); + } + callback({ ok: true, msg: "successPaused", From d1677300a433500c7e389297dbd3c6cdd3db9617 Mon Sep 17 00:00:00 2001 From: Peace Date: Sun, 13 Oct 2024 01:16:16 +0200 Subject: [PATCH 03/15] style: fix formatting --- server/server.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/server/server.js b/server/server.js index 2d2194f19..9d6c801f9 100644 --- a/server/server.js +++ b/server/server.js @@ -987,10 +987,10 @@ let needSetup = false; await startMonitor(socket.userID, monitorID); await server.sendUpdateMonitorIntoList(socket, monitorID); - const childrenIDs = await Monitor.getAllChildrenIDs(monitorID); - for (let childID of childrenIDs) { - await server.sendUpdateMonitorIntoList(socket, childID); - } + const childrenIDs = await Monitor.getAllChildrenIDs(monitorID); + for (let childID of childrenIDs) { + await server.sendUpdateMonitorIntoList(socket, childID); + } callback({ ok: true, @@ -1012,10 +1012,10 @@ let needSetup = false; await pauseMonitor(socket.userID, monitorID); await server.sendUpdateMonitorIntoList(socket, monitorID); - const childrenIDs = await Monitor.getAllChildrenIDs(monitorID); - for (let childID of childrenIDs) { - await server.sendUpdateMonitorIntoList(socket, childID); - } + const childrenIDs = await Monitor.getAllChildrenIDs(monitorID); + for (let childID of childrenIDs) { + await server.sendUpdateMonitorIntoList(socket, childID); + } callback({ ok: true, From 9eda25d0b62ff19efd927b8b73fca6ea4957d04e Mon Sep 17 00:00:00 2001 From: Peace Date: Sun, 13 Oct 2024 23:29:11 +0200 Subject: [PATCH 04/15] revert: fix: pause child monitors if parent is paused --- server/server.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/server/server.js b/server/server.js index 9d6c801f9..0d5285d62 100644 --- a/server/server.js +++ b/server/server.js @@ -987,11 +987,6 @@ let needSetup = false; await startMonitor(socket.userID, monitorID); await server.sendUpdateMonitorIntoList(socket, monitorID); - const childrenIDs = await Monitor.getAllChildrenIDs(monitorID); - for (let childID of childrenIDs) { - await server.sendUpdateMonitorIntoList(socket, childID); - } - callback({ ok: true, msg: "successResumed", @@ -1012,11 +1007,6 @@ let needSetup = false; await pauseMonitor(socket.userID, monitorID); await server.sendUpdateMonitorIntoList(socket, monitorID); - const childrenIDs = await Monitor.getAllChildrenIDs(monitorID); - for (let childID of childrenIDs) { - await server.sendUpdateMonitorIntoList(socket, childID); - } - callback({ ok: true, msg: "successPaused", From b02b21299beaade5aa922198b3122bf3b7cd57be Mon Sep 17 00:00:00 2001 From: Peace Date: Mon, 14 Oct 2024 00:01:55 +0200 Subject: [PATCH 05/15] feat: set group to pending if all childs are paused. --- server/model/monitor.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 5b7e5871a..aa3339582 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -380,7 +380,11 @@ class Monitor extends BeanModel { } else if (this.type === "group") { const children = await Monitor.getChildren(this.id); - if (children.length > 0) { + if (children.length > 0 && children.filter(child => child.active).length === 0) { + // Set status pending if all children are paused + bean.status = PENDING; + bean.msg = "All Children are paused."; + } else if (children.length > 0) { bean.status = UP; bean.msg = "All children up and running"; for (const child of children) { From d336d09d787372b1ef428512999e977e5d12a713 Mon Sep 17 00:00:00 2001 From: Peace Date: Mon, 14 Oct 2024 23:04:07 +0200 Subject: [PATCH 06/15] fix: display children as paused when pausing parent --- server/server.js | 12 ++++++++---- server/uptime-kuma-server.js | 22 +++++++++++++--------- src/mixins/socket.js | 2 +- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/server/server.js b/server/server.js index 0d5285d62..178b58b71 100644 --- a/server/server.js +++ b/server/server.js @@ -727,7 +727,7 @@ let needSetup = false; await updateMonitorNotification(bean.id, notificationIDList); - await server.sendUpdateMonitorIntoList(socket, bean.id); + await server.sendUpdateMonitorsIntoList(socket, bean.id); if (monitor.active !== false) { await startMonitor(socket.userID, bean.id); @@ -884,7 +884,7 @@ let needSetup = false; await restartMonitor(socket.userID, bean.id); } - await server.sendUpdateMonitorIntoList(socket, bean.id); + await server.sendUpdateMonitorsIntoList(socket, bean.id); callback({ ok: true, @@ -985,7 +985,9 @@ let needSetup = false; try { checkLogin(socket); await startMonitor(socket.userID, monitorID); - await server.sendUpdateMonitorIntoList(socket, monitorID); + + const childrenIDs = await Monitor.getAllChildrenIDs(monitorID); + await server.sendUpdateMonitorsIntoList(socket, [ monitorID, ...childrenIDs ]); callback({ ok: true, @@ -1005,7 +1007,9 @@ let needSetup = false; try { checkLogin(socket); await pauseMonitor(socket.userID, monitorID); - await server.sendUpdateMonitorIntoList(socket, monitorID); + + const childrenIDs = await Monitor.getAllChildrenIDs(monitorID); + await server.sendUpdateMonitorsIntoList(socket, [ monitorID, ...childrenIDs ]); callback({ ok: true, diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js index 76bf42565..5c1b281e1 100644 --- a/server/uptime-kuma-server.js +++ b/server/uptime-kuma-server.js @@ -208,12 +208,16 @@ class UptimeKumaServer { /** * Update Monitor into list * @param {Socket} socket Socket to send list on - * @param {number} monitorID update or deleted monitor id + * @param {number | number[]} monitorIDs update or deleted monitor ids * @returns {Promise} */ - async sendUpdateMonitorIntoList(socket, monitorID) { - let list = await this.getMonitorJSONList(socket.userID, monitorID); - this.io.to(socket.userID).emit("updateMonitorIntoList", list); + async sendUpdateMonitorsIntoList(socket, monitorIDs) { + if (!Array.isArray(monitorIDs)) { + monitorIDs = [ monitorIDs ]; + } + + let list = await this.getMonitorJSONList(socket.userID, monitorIDs); + this.io.to(socket.userID).emit("updateMonitorsIntoList", list); } /** @@ -229,19 +233,19 @@ class UptimeKumaServer { /** * Get a list of monitors for the given user. * @param {string} userID - The ID of the user to get monitors for. - * @param {number} monitorID - The ID of monitor for. + * @param {number[]} monitorIDs - The IDs of monitors for. * @returns {Promise} A promise that resolves to an object with monitor IDs as keys and monitor objects as values. * * Generated by Trelent */ - async getMonitorJSONList(userID, monitorID = null) { + async getMonitorJSONList(userID, monitorIDs = null) { let query = " user_id = ? "; let queryParams = [ userID ]; - if (monitorID) { - query += "AND id = ? "; - queryParams.push(monitorID); + if (monitorIDs) { + query += `AND id IN (${monitorIDs.map((_) => "?").join(",")}) `; + queryParams.push(...monitorIDs); } let monitorList = await R.find("monitor", query + "ORDER BY weight DESC, name", queryParams); diff --git a/src/mixins/socket.js b/src/mixins/socket.js index 3272e042c..31b7d630d 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -145,7 +145,7 @@ export default { this.monitorList = data; }); - socket.on("updateMonitorIntoList", (data) => { + socket.on("updateMonitorsIntoList", (data) => { this.assignMonitorUrlParser(data); Object.entries(data).forEach(([ monitorID, updatedMonitor ]) => { this.monitorList[monitorID] = updatedMonitor; From f622898ff16f31a3aa58c1c532d551dc90ce2403 Mon Sep 17 00:00:00 2001 From: Peace Date: Wed, 16 Oct 2024 22:54:15 +0200 Subject: [PATCH 07/15] perf: less sql statements and concurrent start/stop --- server/model/monitor.js | 29 ++++++------- server/server.js | 94 +++++++++-------------------------------- 2 files changed, 33 insertions(+), 90 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index aa3339582..741284f1e 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1661,25 +1661,24 @@ class Monitor extends BeanModel { } /** - * Gets recursive all child ids + * Gets recursive all children ids * @param {number} monitorID ID of the monitor to get - * @returns {Promise} IDs of all children + * @returns {Promise} IDs of all children */ static async getAllChildrenIDs(monitorID) { - const childs = await Monitor.getChildren(monitorID); + const children = await R.getAll(` + WITH RECURSIVE MonitorHierarchy(id) AS ( + SELECT id FROM monitor WHERE id = ? + UNION ALL + SELECT m.id FROM monitor m INNER JOIN MonitorHierarchy mh ON m.parent = mh.id + ) + SELECT id FROM MonitorHierarchy WHERE id != ?; + `, [ + monitorID, + monitorID + ]); - if (childs === null) { - return []; - } - - let childrenIDs = []; - - for (const child of childs) { - childrenIDs.push(child.id); - childrenIDs = childrenIDs.concat(await Monitor.getAllChildrenIDs(child.id)); - } - - return childrenIDs; + return children.map((child) => child.id); } /** diff --git a/server/server.js b/server/server.js index 178b58b71..dc28a5100 100644 --- a/server/server.js +++ b/server/server.js @@ -1752,51 +1752,20 @@ async function startMonitor(userID, monitorID) { userID, ]); - let monitor = await R.findOne("monitor", " id = ? ", [ - monitorID, - ]); + const childrenIDs = await Monitor.getAllChildrenIDs(monitorID); + console.log(childrenIDs); + const monitorIDs = [ monitorID, ...childrenIDs ]; - if (monitor.id in server.monitorList) { - await server.monitorList[monitor.id].stop(); - } + let monitors = await R.find("monitor", ` id IN (${monitorIDs.map((_) => "?").join(",")})`, monitorIDs); - server.monitorList[monitor.id] = monitor; - await monitor.start(io); + await Promise.all(monitors.map(async (monitor) => { + if (monitor.id in server.monitorList) { + await server.monitorList[monitor.id].stop(); + } - const children = await Monitor.getChildren(monitorID); - for (let child of children) { - await startMonitorAsChild(userID, child.id); - } -} - -/** - * Start the specified monitor as child - * @param {number} userID ID of user who owns monitor - * @param {number} monitorID ID of monitor to start - * @returns {Promise} - */ -async function startMonitorAsChild(userID, monitorID) { - - let monitor = await R.findOne("monitor", " id = ? ", [ - monitorID, - ]); - if (monitor.active !== 1) { - return; - } - - log.info("manage", `Resume Monitor: ${monitorID} User ID: ${userID}`); - - if (monitor.id in server.monitorList) { - await server.monitorList[monitor.id].stop(); - } - - server.monitorList[monitor.id] = monitor; - await monitor.start(io); - - const children = await Monitor.getChildren(monitorID); - for (let child of children) { - await startMonitorAsChild(userID, child.id); - } + server.monitorList[monitor.id] = monitor; + await monitor.start(io); + })); } /** @@ -1825,41 +1794,16 @@ async function pauseMonitor(userID, monitorID) { userID, ]); - if (monitorID in server.monitorList) { - await server.monitorList[monitorID].stop(); - server.monitorList[monitorID].active = 0; - } + const childrenIDs = await Monitor.getAllChildrenIDs(monitorID); + const monitorIDs = [ monitorID, ...childrenIDs ]; - const children = await Monitor.getChildren(monitorID); - for (let child of children) { - await pauseMonitorAsChild(userID, child.id); - } -} + await Promise.all(monitorIDs.map(async (currentMonitorID) => { + if (currentMonitorID in server.monitorList) { + await server.monitorList[currentMonitorID].stop(); + } + })); -/** - * Pause a given monitor as child - * @param {number} userID ID of user who owns monitor - * @param {number} monitorID ID of monitor to start - * @returns {Promise} - */ -async function pauseMonitorAsChild(userID, monitorID) { - let monitor = await R.findOne("monitor", " id = ? ", [ - monitorID, - ]); - if (monitor.active !== 1) { - return; - } - - log.info("manage", `Pause Monitor: ${monitorID} User ID: ${userID}`); - - if (monitorID in server.monitorList) { - await server.monitorList[monitorID].stop(); - } - - const children = await Monitor.getChildren(monitorID); - for (let child of children) { - await pauseMonitorAsChild(userID, child.id); - } + server.monitorList[monitorID].active = 0; } /** From 22dcba17c8ca313e2388a95fcc3cf74bdd19a0c4 Mon Sep 17 00:00:00 2001 From: Peace Date: Wed, 16 Oct 2024 22:56:14 +0200 Subject: [PATCH 08/15] fix: remove test logging --- server/server.js | 1 - 1 file changed, 1 deletion(-) diff --git a/server/server.js b/server/server.js index dc28a5100..782c47f39 100644 --- a/server/server.js +++ b/server/server.js @@ -1753,7 +1753,6 @@ async function startMonitor(userID, monitorID) { ]); const childrenIDs = await Monitor.getAllChildrenIDs(monitorID); - console.log(childrenIDs); const monitorIDs = [ monitorID, ...childrenIDs ]; let monitors = await R.find("monitor", ` id IN (${monitorIDs.map((_) => "?").join(",")})`, monitorIDs); From 71c7ee69c70b4aec4e72fde84b102fda321f0ee4 Mon Sep 17 00:00:00 2001 From: Peace Date: Thu, 17 Oct 2024 00:04:45 +0200 Subject: [PATCH 09/15] feat: db statement to get all active monitors --- server/model/monitor.js | 18 ++++++++++++++++++ server/server.js | 3 +-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 741284f1e..7f9185be2 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1608,6 +1608,24 @@ class Monitor extends BeanModel { }; } + /** + * Gets all active monitors + * @returns {Promise} Active Monitors + */ + static async getAllActiveMonitors() { + return R.convertToBeans("monitor", await R.getAll(` + WITH RECURSIVE MonitorHierarchy AS ( + SELECT * FROM monitor + WHERE parent IS NULL AND active = 1 + UNION ALL + SELECT m.* FROM monitor m + INNER JOIN MonitorHierarchy mh ON m.parent = mh.id + WHERE m.active = 1 + ) + SELECT * FROM MonitorHierarchy; + `)); + } + /** * Gets Parent of the monitor * @param {number} monitorID ID of monitor to get diff --git a/server/server.js b/server/server.js index 782c47f39..8dba73a1e 100644 --- a/server/server.js +++ b/server/server.js @@ -1810,8 +1810,7 @@ async function pauseMonitor(userID, monitorID) { * @returns {Promise} */ async function startMonitors() { - let list = await R.find("monitor", " active = 1 "); - list = list.filter((monitor) => Monitor.isActive(monitor.id, monitor.active)); + let list = await Monitor.getAllActiveMonitors(); for (let monitor of list) { server.monitorList[monitor.id] = monitor; From c079971a7b13eedec18b0885930700c4469827e0 Mon Sep 17 00:00:00 2001 From: Peace Date: Thu, 17 Oct 2024 00:05:29 +0200 Subject: [PATCH 10/15] fix: cast getParent and getChildren to Beans --- server/model/monitor.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 7f9185be2..e5580a2e8 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1629,10 +1629,10 @@ class Monitor extends BeanModel { /** * Gets Parent of the monitor * @param {number} monitorID ID of monitor to get - * @returns {Promise>} Parent + * @returns {Promise} Parent */ static async getParent(monitorID) { - return await R.getRow(` + const result = await R.getRow(` SELECT parent.* FROM monitor parent LEFT JOIN monitor child ON child.parent = parent.id @@ -1640,20 +1640,25 @@ class Monitor extends BeanModel { `, [ monitorID, ]); + + if (!result) { + return null; + } + return R.convertToBean("monitor", result); } /** * Gets all Children of the monitor * @param {number} monitorID ID of monitor to get - * @returns {Promise>} Children + * @returns {Promise} Children */ static async getChildren(monitorID) { - return await R.getAll(` + return R.convertToBeans("monitor", await R.getAll(` SELECT * FROM monitor WHERE parent = ? `, [ monitorID, - ]); + ])); } /** From 5dc66e1495c1becabe364eb96fee72c7ef645fee Mon Sep 17 00:00:00 2001 From: Peace Date: Thu, 17 Oct 2024 00:05:55 +0200 Subject: [PATCH 11/15] refactor: make getAllChildrenIDs more compact --- server/model/monitor.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index e5580a2e8..b51056643 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1689,7 +1689,7 @@ class Monitor extends BeanModel { * @returns {Promise} IDs of all children */ static async getAllChildrenIDs(monitorID) { - const children = await R.getAll(` + return await R.getCol(` WITH RECURSIVE MonitorHierarchy(id) AS ( SELECT id FROM monitor WHERE id = ? UNION ALL @@ -1700,8 +1700,6 @@ class Monitor extends BeanModel { monitorID, monitorID ]); - - return children.map((child) => child.id); } /** From 16c04f6ac22cd3bd940c57bb7cb8d01984e257a2 Mon Sep 17 00:00:00 2001 From: Peace Date: Thu, 17 Oct 2024 00:06:40 +0200 Subject: [PATCH 12/15] perf: less recursion for isParentActive using db query --- server/model/monitor.js | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index b51056643..593f7436b 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1719,14 +1719,31 @@ class Monitor extends BeanModel { * @returns {Promise} Is the parent monitor active? */ static async isParentActive(monitorID) { - const parent = await Monitor.getParent(monitorID); + const result = await R.getRow(` + WITH RECURSIVE MonitorHierarchy AS ( + SELECT parent FROM monitor + WHERE id = ? + UNION ALL + SELECT m.parent FROM monitor m + JOIN MonitorHierarchy mh ON m.id = mh.parent + ) + SELECT + CASE + WHEN (SELECT parent FROM monitor WHERE id = ?) IS NULL THEN 1 + ELSE + CASE + WHEN COUNT(m.id) = SUM(m.active) THEN 1 + ELSE 0 + END + END AS all_active + FROM MonitorHierarchy mh + LEFT JOIN monitor m ON mh.parent = m.id; + `, [ + monitorID, + monitorID + ]); - if (parent === null) { - return true; - } - - const parentActive = await Monitor.isParentActive(parent.id); - return parent.active && parentActive; + return result.all_active === 1; } /** From c9e5dff162f1faab4daa09287b18658992fc76ab Mon Sep 17 00:00:00 2001 From: Peace Date: Thu, 17 Oct 2024 00:07:07 +0200 Subject: [PATCH 13/15] perf: less recursion for isUnderMaintenance using db query --- server/model/monitor.js | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 593f7436b..f38259fb2 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1469,10 +1469,12 @@ class Monitor extends BeanModel { * @returns {Promise} Is the monitor under maintenance */ static async isUnderMaintenance(monitorID) { + const ancestorIDs = await Monitor.getAllAncestorIDs(monitorID); + const allIDs = [ monitorID, ...ancestorIDs ]; const maintenanceIDList = await R.getCol(` SELECT maintenance_id FROM monitor_maintenance - WHERE monitor_id = ? - `, [ monitorID ]); + WHERE monitor_id IN (${allIDs.map((_) => "?").join(",")}) + `, allIDs); for (const maintenanceID of maintenanceIDList) { const maintenance = await UptimeKumaServer.getInstance().getMaintenance(maintenanceID); @@ -1481,11 +1483,6 @@ class Monitor extends BeanModel { } } - const parent = await Monitor.getParent(monitorID); - if (parent != null) { - return await Monitor.isUnderMaintenance(parent.id); - } - return false; } @@ -1683,6 +1680,27 @@ class Monitor extends BeanModel { return path; } + /** + * Gets recursive all ancestor ids + * @param {number} monitorID ID of the monitor to get + * @returns {Promise} IDs of all ancestors + */ + static async getAllAncestorIDs(monitorID) { + return await R.getCol(` + WITH RECURSIVE Ancestors AS ( + SELECT parent FROM monitor + WHERE id = ? + UNION ALL + SELECT m.parent FROM monitor m + JOIN Ancestors a ON m.id = a.parent + ) + SELECT parent AS ancestor_id FROM Ancestors + WHERE parent IS NOT NULL; + `, [ + monitorID, + ]); + } + /** * Gets recursive all children ids * @param {number} monitorID ID of the monitor to get From ec2cebc5dfa2bda5c6b207c7a5d0f51af1dbecad Mon Sep 17 00:00:00 2001 From: Peace Date: Thu, 17 Oct 2024 17:16:08 +0200 Subject: [PATCH 14/15] perf: option to only get ids of active children --- server/model/monitor.js | 22 +++++++++++++++++++++- server/server.js | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 893379cbd..0149e11fb 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1701,9 +1701,29 @@ class Monitor extends BeanModel { /** * Gets recursive all children ids * @param {number} monitorID ID of the monitor to get + * @param {boolean} onlyActive Return only monitors which are active (including all ancestors) * @returns {Promise} IDs of all children */ - static async getAllChildrenIDs(monitorID) { + static async getAllChildrenIDs(monitorID, onlyActive = false) { + if (onlyActive) { + // Gets all children monitor ids recursive but only if they and all their ancestors are active + return await R.getCol(` + WITH RECURSIVE MonitorHierarchy(id, active_chain) AS ( + SELECT id, active FROM monitor + WHERE id = ? + UNION ALL + SELECT m.id, m.active * mh.active_chain FROM monitor m + INNER JOIN MonitorHierarchy mh ON m.parent = mh.id + WHERE mh.active_chain = 1 + ) + SELECT id FROM MonitorHierarchy + WHERE id != ? AND active_chain = 1; + `, [ + monitorID, + monitorID + ]); + } + // Gets all children monitor ids recursive return await R.getCol(` WITH RECURSIVE MonitorHierarchy(id) AS ( SELECT id FROM monitor WHERE id = ? diff --git a/server/server.js b/server/server.js index 8dba73a1e..ef0fb49af 100644 --- a/server/server.js +++ b/server/server.js @@ -1752,7 +1752,7 @@ async function startMonitor(userID, monitorID) { userID, ]); - const childrenIDs = await Monitor.getAllChildrenIDs(monitorID); + const childrenIDs = await Monitor.getAllChildrenIDs(monitorID, true); const monitorIDs = [ monitorID, ...childrenIDs ]; let monitors = await R.find("monitor", ` id IN (${monitorIDs.map((_) => "?").join(",")})`, monitorIDs); From ac8d1d6346730cd4461b40e238db850c6266b678 Mon Sep 17 00:00:00 2001 From: Peace Date: Thu, 17 Oct 2024 17:16:29 +0200 Subject: [PATCH 15/15] docs: add comments for queries --- server/model/monitor.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/model/monitor.js b/server/model/monitor.js index 0149e11fb..c653590fe 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1607,6 +1607,7 @@ class Monitor extends BeanModel { * @returns {Promise} Active Monitors */ static async getAllActiveMonitors() { + // Gets all monitors but only if they and all their ancestors are active return R.convertToBeans("monitor", await R.getAll(` WITH RECURSIVE MonitorHierarchy AS ( SELECT * FROM monitor @@ -1683,6 +1684,7 @@ class Monitor extends BeanModel { * @returns {Promise} IDs of all ancestors */ static async getAllAncestorIDs(monitorID) { + // Gets all ancestor monitor ids recursive return await R.getCol(` WITH RECURSIVE Ancestors AS ( SELECT parent FROM monitor @@ -1754,6 +1756,7 @@ class Monitor extends BeanModel { * @returns {Promise} Is the parent monitor active? */ static async isParentActive(monitorID) { + // Checks recursive if the parent and all its ancestors are active const result = await R.getRow(` WITH RECURSIVE MonitorHierarchy AS ( SELECT parent FROM monitor