mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-02-08 10:55:44 -05:00
improve page load performance of large amount urls
1) fix loop to async 2) query n+1 problem
This commit is contained in:
parent
bab771df05
commit
84f261c81b
@ -71,23 +71,15 @@ class Monitor extends BeanModel {
|
||||
|
||||
/**
|
||||
* Return an object that ready to parse to JSON
|
||||
* @param {object} preloadData Include precalculate data in
|
||||
* @param {boolean} includeSensitiveData Include sensitive data in
|
||||
* JSON
|
||||
* @returns {Promise<object>} Object ready to parse
|
||||
*/
|
||||
async toJSON(includeSensitiveData = true) {
|
||||
async toJSON(preloadData = {}, includeSensitiveData = true) {
|
||||
|
||||
let notificationIDList = {};
|
||||
|
||||
let list = await R.find("monitor_notification", " monitor_id = ? ", [
|
||||
this.id,
|
||||
]);
|
||||
|
||||
for (let bean of list) {
|
||||
notificationIDList[bean.notification_id] = true;
|
||||
}
|
||||
|
||||
const tags = await this.getTags();
|
||||
const tags = preloadData.tags[this.id] || [];
|
||||
const notificationIDList = preloadData.notifications[this.id] || {};
|
||||
|
||||
let screenshot = null;
|
||||
|
||||
@ -105,15 +97,15 @@ class Monitor extends BeanModel {
|
||||
path,
|
||||
pathName,
|
||||
parent: this.parent,
|
||||
childrenIDs: await Monitor.getAllChildrenIDs(this.id),
|
||||
childrenIDs: preloadData.childrenIDs[this.id] || [],
|
||||
url: this.url,
|
||||
method: this.method,
|
||||
hostname: this.hostname,
|
||||
port: this.port,
|
||||
maxretries: this.maxretries,
|
||||
weight: this.weight,
|
||||
active: await this.isActive(),
|
||||
forceInactive: !await Monitor.isParentActive(this.id),
|
||||
active: preloadData.activeStatus[this.id],
|
||||
forceInactive: preloadData.forceInactive[this.id],
|
||||
type: this.type,
|
||||
timeout: this.timeout,
|
||||
interval: this.interval,
|
||||
@ -134,8 +126,8 @@ class Monitor extends BeanModel {
|
||||
docker_host: this.docker_host,
|
||||
proxyId: this.proxy_id,
|
||||
notificationIDList,
|
||||
tags: tags,
|
||||
maintenance: await Monitor.isUnderMaintenance(this.id),
|
||||
tags,
|
||||
maintenance: preloadData.maintenanceStatus[this.id],
|
||||
mqttTopic: this.mqttTopic,
|
||||
mqttSuccessMessage: this.mqttSuccessMessage,
|
||||
mqttCheckType: this.mqttCheckType,
|
||||
@ -199,16 +191,6 @@ class Monitor extends BeanModel {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the monitor is active based on itself and its parents
|
||||
* @returns {Promise<boolean>} Is the monitor active?
|
||||
*/
|
||||
async isActive() {
|
||||
const parentActive = await Monitor.isParentActive(this.id);
|
||||
|
||||
return (this.active === 1) && parentActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all tags applied to this monitor
|
||||
* @returns {Promise<LooseObject<any>[]>} List of tags on the
|
||||
@ -1178,6 +1160,18 @@ class Monitor extends BeanModel {
|
||||
return checkCertificateResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the monitor is active based on itself and its parents
|
||||
* @param {number} monitorID ID of monitor to send
|
||||
* @param {boolean} active is active
|
||||
* @returns {Promise<boolean>} Is the monitor active?
|
||||
*/
|
||||
static async isActive(monitorID, active) {
|
||||
const parentActive = await Monitor.isParentActive(monitorID);
|
||||
|
||||
return (active === 1) && parentActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send statistics to clients
|
||||
* @param {Server} io Socket server instance
|
||||
@ -1314,7 +1308,10 @@ class Monitor extends BeanModel {
|
||||
for (let notification of notificationList) {
|
||||
try {
|
||||
const heartbeatJSON = bean.toJSON();
|
||||
|
||||
const monitorData = [{ id: monitor.id,
|
||||
active: monitor.active
|
||||
}];
|
||||
const preloadData = await Monitor.preparePreloadData(monitorData);
|
||||
// Prevent if the msg is undefined, notifications such as Discord cannot send out.
|
||||
if (!heartbeatJSON["msg"]) {
|
||||
heartbeatJSON["msg"] = "N/A";
|
||||
@ -1325,7 +1322,7 @@ class Monitor extends BeanModel {
|
||||
heartbeatJSON["timezoneOffset"] = UptimeKumaServer.getInstance().getTimezoneOffset();
|
||||
heartbeatJSON["localDateTime"] = dayjs.utc(heartbeatJSON["time"]).tz(heartbeatJSON["timezone"]).format(SQL_DATETIME_FORMAT);
|
||||
|
||||
await Notification.send(JSON.parse(notification.config), msg, await monitor.toJSON(false), heartbeatJSON);
|
||||
await Notification.send(JSON.parse(notification.config), msg, await monitor.toJSON(preloadData, false), heartbeatJSON);
|
||||
} catch (e) {
|
||||
log.error("monitor", "Cannot send notification to " + notification.name);
|
||||
log.error("monitor", e);
|
||||
@ -1487,6 +1484,81 @@ class Monitor extends BeanModel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets monitor notification of multiple monitor
|
||||
* @param {Array} monitorIDs IDs of monitor to get
|
||||
* @returns {Promise<LooseObject<any>>} object
|
||||
*/
|
||||
static async getMonitorNotification(monitorIDs) {
|
||||
return await R.getAll(`
|
||||
SELECT monitor_notification.monitor_id, monitor_notification.notification_id
|
||||
FROM monitor_notification
|
||||
WHERE monitor_notification.monitor_id IN (?)
|
||||
`, [
|
||||
monitorIDs,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets monitor tags of multiple monitor
|
||||
* @param {Array} monitorIDs IDs of monitor to get
|
||||
* @returns {Promise<LooseObject<any>>} object
|
||||
*/
|
||||
static async getMonitorTag(monitorIDs) {
|
||||
return await R.getAll(`
|
||||
SELECT monitor_tag.monitor_id, tag.name, tag.color
|
||||
FROM monitor_tag
|
||||
JOIN tag ON monitor_tag.tag_id = tag.id
|
||||
WHERE monitor_tag.monitor_id IN (?)
|
||||
`, [
|
||||
monitorIDs,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare preloaded data for efficient access
|
||||
* @param {Array} monitorData IDs & active field of monitor to get
|
||||
* @returns {Promise<LooseObject<any>>} object
|
||||
*/
|
||||
static async preparePreloadData(monitorData) {
|
||||
const monitorIDs = monitorData.map(monitor => monitor.id);
|
||||
const notifications = await Monitor.getMonitorNotification(monitorIDs);
|
||||
const tags = await Monitor.getMonitorTag(monitorIDs);
|
||||
const maintenanceStatuses = await Promise.all(
|
||||
monitorData.map(monitor => Monitor.isUnderMaintenance(monitor.id))
|
||||
);
|
||||
const childrenIDs = await Promise.all(
|
||||
monitorData.map(monitor => Monitor.getAllChildrenIDs(monitor.id))
|
||||
);
|
||||
const activeStatuses = await Promise.all(
|
||||
monitorData.map(monitor => Monitor.isActive(monitor.id, monitor.active))
|
||||
);
|
||||
const forceInactiveStatuses = await Promise.all(
|
||||
monitorData.map(monitor => Monitor.isParentActive(monitor.id))
|
||||
);
|
||||
|
||||
// Organize preloaded data for efficient access
|
||||
return {
|
||||
notifications: notifications.reduce((acc, row) => {
|
||||
acc[row.monitor_id] = acc[row.monitor_id] || {};
|
||||
acc[row.monitor_id][row.notification_id] = true;
|
||||
return acc;
|
||||
}, {}),
|
||||
tags: tags.reduce((acc, row) => {
|
||||
acc[row.monitor_id] = acc[row.monitor_id] || [];
|
||||
acc[row.monitor_id].push({ name: row.name,
|
||||
color: row.color
|
||||
});
|
||||
return acc;
|
||||
}, {}),
|
||||
maintenanceStatus: Object.fromEntries(monitorData.map((m, index) => [ m.id, maintenanceStatuses[index] ])),
|
||||
childrenIDs: Object.fromEntries(monitorData.map((m, index) => [ m.id, childrenIDs[index] ])),
|
||||
activeStatus: Object.fromEntries(monitorData.map((m, index) => [ m.id, activeStatuses[index] ])),
|
||||
forceInactive: Object.fromEntries(monitorData.map((m, index) => [ m.id, !forceInactiveStatuses[index] ])),
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Parent of the monitor
|
||||
* @param {number} monitorID ID of monitor to get
|
||||
|
@ -890,14 +890,17 @@ let needSetup = false;
|
||||
|
||||
log.info("monitor", `Get Monitor: ${monitorID} User ID: ${socket.userID}`);
|
||||
|
||||
let bean = await R.findOne("monitor", " id = ? AND user_id = ? ", [
|
||||
let monitor = await R.findOne("monitor", " id = ? AND user_id = ? ", [
|
||||
monitorID,
|
||||
socket.userID,
|
||||
]);
|
||||
|
||||
const monitorData = [{ id: monitor.id,
|
||||
active: monitor.active
|
||||
}];
|
||||
const preloadData = await Monitor.preparePreloadData(monitorData);
|
||||
callback({
|
||||
ok: true,
|
||||
monitor: await bean.toJSON(),
|
||||
monitor: await monitor.toJSON(preloadData),
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
@ -1644,13 +1647,20 @@ async function afterLogin(socket, user) {
|
||||
|
||||
await StatusPage.sendStatusPageList(io, socket);
|
||||
|
||||
// Create an array to store the combined promises for both sendHeartbeatList and sendStats
|
||||
const monitorPromises = [];
|
||||
for (let monitorID in monitorList) {
|
||||
await sendHeartbeatList(socket, monitorID);
|
||||
// Combine both sendHeartbeatList and sendStats for each monitor into a single Promise
|
||||
monitorPromises.push(
|
||||
Promise.all([
|
||||
sendHeartbeatList(socket, monitorID),
|
||||
Monitor.sendStats(io, monitorID, user.id)
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
for (let monitorID in monitorList) {
|
||||
await Monitor.sendStats(io, monitorID, user.id);
|
||||
}
|
||||
// Await all combined promises
|
||||
await Promise.all(monitorPromises);
|
||||
|
||||
// Set server timezone from client browser if not set
|
||||
// It should be run once only
|
||||
|
@ -219,9 +219,27 @@ class UptimeKumaServer {
|
||||
userID,
|
||||
]);
|
||||
|
||||
for (let monitor of monitorList) {
|
||||
result[monitor.id] = await monitor.toJSON();
|
||||
}
|
||||
// Collect monitor IDs
|
||||
// Create monitorData with id, active
|
||||
const monitorData = monitorList.map(monitor => ({
|
||||
id: monitor.id,
|
||||
active: monitor.active,
|
||||
}));
|
||||
const preloadData = await Monitor.preparePreloadData(monitorData);
|
||||
|
||||
// Create an array of promises to convert each monitor to JSON in parallel
|
||||
const monitorPromises = monitorList.map(monitor => monitor.toJSON(preloadData).then(json => {
|
||||
return { id: monitor.id,
|
||||
json
|
||||
};
|
||||
}));
|
||||
// Wait for all promises to resolve
|
||||
const monitors = await Promise.all(monitorPromises);
|
||||
|
||||
// Populate the result object with monitor IDs as keys, JSON objects as values
|
||||
monitors.forEach(monitor => {
|
||||
result[monitor.id] = monitor.json;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -520,3 +538,4 @@ const { DnsMonitorType } = require("./monitor-types/dns");
|
||||
const { MqttMonitorType } = require("./monitor-types/mqtt");
|
||||
const { SNMPMonitorType } = require("./monitor-types/snmp");
|
||||
const { MongodbMonitorType } = require("./monitor-types/mongodb");
|
||||
const Monitor = require("./model/monitor");
|
||||
|
Loading…
x
Reference in New Issue
Block a user