Added database counters.

Ylian Saint-Hilaire committed Mar 1, 2021 at 19:32 UTC c9035b80c80022520ff6e428bb1695a9ac0248a9
3 files changed +65 -10
db.js
+51 -7
@@ -38,9 +38,26 @@ module.exports.CreateDB = function (parent, func) {
38 obj.dbRecordsDecryptKey = null;
39 obj.changeStream = false;
40 obj.pluginsActive = ((parent.config) && (parent.config.settings) && (parent.config.settings.plugins != null) && (parent.config.settings.plugins != false) && ((typeof parent.config.settings.plugins != 'object') || (parent.config.settings.plugins.enabled != false)));
41 + obj.dbCounters = {
42 + fileSet: 0,
43 + fileRemove: 0,
44 + powerSet: 0,
45 + eventsSet: 0
46 + }
47
48 // MongoDB bulk operations state
49 if (parent.config.settings.mongodbbulkoperations) {
50 + // Added counters
51 + obj.dbCounters.fileSetPending = 0;
52 + obj.dbCounters.fileSetBulk = 0;
53 + obj.dbCounters.fileRemovePending = 0;
54 + obj.dbCounters.fileRemoveBulk = 0;
55 + obj.dbCounters.powerSetPending = 0;
56 + obj.dbCounters.powerSetBulk = 0;
57 + obj.dbCounters.eventsSetPending = 0;
58 + obj.dbCounters.eventsSetBulk = 0;
59 +
60 + /// Added bulk accumulators
61 obj.filePendingGet = null;
62 obj.filePendingGets = null;
63 obj.filePendingRemove = null;
@@ -514,12 +531,15 @@ module.exports.CreateDB = function (parent, func) {
531
532 // Setup the changeStream on the MongoDB main collection if possible
533 if (parent.args.mongodbchangestream == true) {
534 + obj.dbCounters.changeStream = { change: 0, update: 0, insert: 0, delete: 0 };
535 if (typeof obj.file.watch != 'function') {
536 console.log('WARNING: watch() is not a function, MongoDB ChangeStream not supported.');
537 } else {
538 obj.fileChangeStream = obj.file.watch([{ $match: { $or: [{ 'fullDocument.type': { $in: ['node', 'mesh', 'user', 'ugrp'] } }, { 'operationType': 'delete' }] } }], { fullDocument: 'updateLookup' });
539 obj.fileChangeStream.on('change', function (change) {
540 + obj.dbCounters.changeStream.change++;
541 if ((change.operationType == 'update') || (change.operationType == 'replace')) {
542 + obj.dbCounters.changeStream.update++;
543 switch (change.fullDocument.type) {
544 case 'node': { dbNodeChange(change, false); break; } // A node has changed
545 case 'mesh': { dbMeshChange(change, false); break; } // A device group has changed
@@ -527,6 +547,7 @@ module.exports.CreateDB = function (parent, func) {
547 case 'ugrp': { dbUGrpChange(change, false); break; } // A user account has changed
548 }
549 } else if (change.operationType == 'insert') {
550 + obj.dbCounters.changeStream.insert++;
551 switch (change.fullDocument.type) {
552 case 'node': { dbNodeChange(change, true); break; } // A node has added
553 case 'mesh': { dbMeshChange(change, true); break; } // A device group has created
@@ -534,6 +555,7 @@ module.exports.CreateDB = function (parent, func) {
555 case 'ugrp': { dbUGrpChange(change, true); break; } // A user account has created
556 }
557 } else if (change.operationType == 'delete') {
558 + obj.dbCounters.changeStream.delete++;
559 if ((change.documentKey == null) || (change.documentKey._id == null)) return;
560 var splitId = change.documentKey._id.split('/');
561 switch (splitId[0]) {
@@ -898,6 +920,7 @@ module.exports.CreateDB = function (parent, func) {
920 if ((obj.databaseType == 4) || (obj.databaseType == 5)) {
921 // Database actions on the main collection (MariaDB or MySQL)
922 obj.Set = function (value, func) {
923 + obj.dbCounters.fileSet++;
924 var extra = null, extraex = null;
925 value = common.escapeLinksFieldNameEx(value);
926 if (value.meshid) { extra = value.meshid; } else if (value.email) { extra = 'email/' + value.email; } else if (value.nodeid) { extra = value.nodeid; }
@@ -947,6 +970,7 @@ module.exports.CreateDB = function (parent, func) {
970 // Database actions on the events collection
971 obj.GetAllEvents = function (func) { sqlDbQuery('SELECT doc FROM meshcentral.events', null, func); };
972 obj.StoreEvent = function (event, func) {
973 + obj.dbCounters.eventsSet++;
974 var batchQuery = [['INSERT INTO meshcentral.events VALUE (?, ?, ?, ?, ?, ?, ?)', [null, event.time, ((typeof event.domain == 'string') ? event.domain : null), event.action, event.nodeid ? event.nodeid : null, event.userid ? event.userid : null, JSON.stringify(event)]]];
975 for (var i in event.ids) { if (event.ids[i] != '*') { batchQuery.push(['INSERT INTO meshcentral.eventids VALUE (LAST_INSERT_ID(), ?)', [event.ids[i]]]); } }
976 sqlDbBatchExec(batchQuery, function (err, docs) { if (func != null) { func(err, docs); } });
@@ -991,7 +1015,7 @@ module.exports.CreateDB = function (parent, func) {
1015
1016 // Database actions on the power collection
1017 obj.getAllPower = function (func) { sqlDbQuery('SELECT doc FROM meshcentral.power', null, func); };
994 - obj.storePowerEvent = function (event, multiServer, func) { if (multiServer != null) { event.server = multiServer.serverid; } sqlDbQuery('INSERT INTO meshcentral.power VALUE (?, ?, ?, ?)', [null, event.time, event.nodeid ? event.nodeid : null, JSON.stringify(event)], func); };
1018 + obj.storePowerEvent = function (event, multiServer, func) { obj.dbCounters.powerSet++; if (multiServer != null) { event.server = multiServer.serverid; } sqlDbQuery('INSERT INTO meshcentral.power VALUE (?, ?, ?, ?)', [null, event.time, event.nodeid ? event.nodeid : null, JSON.stringify(event)], func); };
1019 obj.getPowerTimeline = function (nodeid, func) { sqlDbQuery('SELECT doc FROM meshcentral.power WHERE ((nodeid = ?) OR (nodeid = "*")) ORDER BY time DESC', [nodeid], func); };
1020 obj.removeAllPowerEvents = function () { sqlDbQuery('DELETE FROM meshcentral.power', null, function (err, docs) { }); };
1021 obj.removeAllPowerEventsForNode = function (nodeid) { sqlDbQuery('DELETE FROM meshcentral.power WHERE nodeid = ?', [nodeid], function (err, docs) { }); };
@@ -1055,11 +1079,13 @@ module.exports.CreateDB = function (parent, func) {
1079 obj.Set = function (data, func) { // Fast Set operation using bulkWrite(), this is much faster then using replaceOne()
1080 if (obj.filePendingSet == false) {
1081 // Perform the operation now
1082 + obj.dbCounters.fileSet++;
1083 obj.filePendingSet = true; obj.filePendingSets = null;
1084 if (func != null) { obj.filePendingCbs = [func]; }
1085 obj.file.bulkWrite([{ replaceOne: { filter: { _id: data._id }, replacement: performTypedRecordEncrypt(common.escapeLinksFieldNameEx(data)), upsert: true } }], fileBulkWriteCompleted);
1086 } else {
1087 // Add this operation to the pending list
1088 + obj.dbCounters.fileSetPending++;
1089 if (obj.filePendingSets == null) { obj.filePendingSets = {} }
1090 obj.filePendingSets[data._id] = data;
1091 if (func != null) { if (obj.filePendingCb == null) { obj.filePendingCb = [func]; } else { obj.filePendingCb.push(func); } }
@@ -1093,7 +1119,11 @@ module.exports.CreateDB = function (parent, func) {
1119 }
1120 };
1121 } else {
1096 - obj.Set = function (data, func) { data = common.escapeLinksFieldNameEx(data); obj.file.replaceOne({ _id: data._id }, performTypedRecordEncrypt(data), { upsert: true }, func); };
1122 + obj.Set = function (data, func) {
1123 + obj.dbCounters.fileSet++;
1124 + data = common.escapeLinksFieldNameEx(data);
1125 + obj.file.replaceOne({ _id: data._id }, performTypedRecordEncrypt(data), { upsert: true }, func);
1126 + };
1127 obj.Get = function (id, func) {
1128 if (arguments.length > 2) {
1129 var parms = [func];
@@ -1145,18 +1175,20 @@ module.exports.CreateDB = function (parent, func) {
1175 if (parent.config.settings.mongodbbulkoperations) {
1176 obj.Remove = function (id, func) { // Fast remove operation using a bulk find() to reduce round trips to the database.
1177 if (obj.filePendingRemoves == null) {
1148 - // No pending gets, perform the operation now.
1178 + // No pending removes, perform the operation now.
1179 + obj.dbCounters.fileRemove++;
1180 obj.filePendingRemoves = {};
1181 obj.filePendingRemoves[id] = [func];
1182 obj.file.deleteOne({ _id: id }, fileBulkRemoveCompleted);
1183 } else {
1184 // Add remove to pending list.
1185 + obj.dbCounters.fileRemovePending++;
1186 if (obj.filePendingRemove == null) { obj.filePendingRemove = {}; }
1187 if (obj.filePendingRemove[id] == null) { obj.filePendingRemove[id] = [func]; } else { obj.filePendingRemove[id].push(func); }
1188 }
1189 };
1190 } else {
1159 - obj.Remove = function (id, func) { obj.file.deleteOne({ _id: id }, func); };
1191 + obj.Remove = function (id, func) { obj.dbCounters.fileRemove++; obj.file.deleteOne({ _id: id }, func); };
1192 }
1193
1194 obj.RemoveAll = function (func) { obj.file.deleteMany({}, { multi: true }, func); };
@@ -1189,18 +1221,20 @@ module.exports.CreateDB = function (parent, func) {
1221 obj.StoreEvent = function (event, func) { // Fast MongoDB event store using bulkWrite()
1222 if (obj.eventsFilePendingSet == false) {
1223 // Perform the operation now
1224 + obj.dbCounters.eventsSet++;
1225 obj.eventsFilePendingSet = true; obj.eventsFilePendingSets = null;
1226 if (func != null) { obj.eventsFilePendingCbs = [func]; }
1227 obj.eventsfile.bulkWrite([{ insertOne: { document: event } }], eventsFileBulkWriteCompleted);
1228 } else {
1229 // Add this operation to the pending list
1230 + obj.dbCounters.eventsSetPending++;
1231 if (obj.eventsFilePendingSets == null) { obj.eventsFilePendingSets = [] }
1232 obj.eventsFilePendingSets.push(event);
1233 if (func != null) { if (obj.eventsFilePendingCb == null) { obj.eventsFilePendingCb = [func]; } else { obj.eventsFilePendingCb.push(func); } }
1234 }
1235 };
1236 } else {
1203 - obj.StoreEvent = function (event, func) { obj.eventsfile.insertOne(event, func); };
1237 + obj.StoreEvent = function (event, func) { obj.dbCounters.eventsSet++; obj.eventsfile.insertOne(event, func); };
1238 }
1239
1240 obj.GetEvents = function (ids, domain, func) { obj.eventsfile.find({ domain: domain, ids: { $in: ids } }).project({ type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).toArray(func); };
@@ -1230,18 +1264,20 @@ module.exports.CreateDB = function (parent, func) {
1264 if (multiServer != null) { event.server = multiServer.serverid; }
1265 if (obj.powerFilePendingSet == false) {
1266 // Perform the operation now
1267 + obj.dbCounters.powerSet++;
1268 obj.powerFilePendingSet = true; obj.powerFilePendingSets = null;
1269 if (func != null) { obj.powerFilePendingCbs = [func]; }
1270 obj.powerfile.bulkWrite([{ insertOne: { document: event } }], powerFileBulkWriteCompleted);
1271 } else {
1272 // Add this operation to the pending list
1273 + obj.dbCounters.powerSetPending++;
1274 if (obj.powerFilePendingSets == null) { obj.powerFilePendingSets = [] }
1275 obj.powerFilePendingSets.push(event);
1276 if (func != null) { if (obj.powerFilePendingCb == null) { obj.powerFilePendingCb = [func]; } else { obj.powerFilePendingCb.push(func); } }
1277 }
1278 };
1279 } else {
1244 - obj.storePowerEvent = function (event, multiServer, func) { if (multiServer != null) { event.server = multiServer.serverid; } obj.powerfile.insertOne(event, func); };
1280 + obj.storePowerEvent = function (event, multiServer, func) { obj.dbCounters.powerSet++; if (multiServer != null) { event.server = multiServer.serverid; } obj.powerfile.insertOne(event, func); };
1281 }
1282
1283 obj.getPowerTimeline = function (nodeid, func) { obj.powerfile.find({ nodeid: { $in: ['*', nodeid] } }).project({ _id: 0, nodeid: 0, s: 0 }).sort({ time: 1 }).toArray(func); };
@@ -1304,7 +1340,11 @@ module.exports.CreateDB = function (parent, func) {
1340
1341 } else {
1342 // Database actions on the main collection (NeDB and MongoJS)
1307 - obj.Set = function (data, func) { data = common.escapeLinksFieldNameEx(data); var xdata = performTypedRecordEncrypt(data); obj.file.update({ _id: xdata._id }, xdata, { upsert: true }, func); };
1343 + obj.Set = function (data, func) {
1344 + obj.dbCounters.fileSet++;
1345 + data = common.escapeLinksFieldNameEx(data);
1346 + var xdata = performTypedRecordEncrypt(data); obj.file.update({ _id: xdata._id }, xdata, { upsert: true }, func);
1347 + };
1348 obj.Get = function (id, func) {
1349 if (arguments.length > 2) {
1350 var parms = [func];
@@ -1578,6 +1618,7 @@ module.exports.CreateDB = function (parent, func) {
1618 obj.filePendingRemoves = obj.filePendingRemove;
1619 obj.filePendingRemove = null;
1620 if (obj.filePendingRemoves != null) {
1621 + obj.dbCounters.fileRemoveBulk++;
1622 var findlist = [], count = 0;
1623 for (var i in obj.filePendingRemoves) { findlist.push(i); count++; }
1624 obj.file.deleteMany({ _id: { $in: findlist } }, { multi: true }, fileBulkRemoveCompleted);
@@ -1593,6 +1634,7 @@ module.exports.CreateDB = function (parent, func) {
1634 }
1635 if (obj.filePendingSets != null) {
1636 // Perform pending operations
1637 + obj.dbCounters.fileSetBulk++;
1638 var ops = [];
1639 obj.filePendingCbs = obj.filePendingCb;
1640 obj.filePendingCb = null;
@@ -1611,6 +1653,7 @@ module.exports.CreateDB = function (parent, func) {
1653 if (obj.eventsFilePendingCbs != null) { for (var i in obj.eventsFilePendingCbs) { obj.eventsFilePendingCbs[i](); } obj.eventsFilePendingCbs = null; }
1654 if (obj.eventsFilePendingSets != null) {
1655 // Perform pending operations
1656 + obj.dbCounters.eventsSetBulk++;
1657 var ops = [];
1658 for (var i in obj.eventsFilePendingSets) { ops.push({ document: obj.eventsFilePendingSets[i] }); }
1659 obj.eventsFilePendingCbs = obj.eventsFilePendingCb;
@@ -1629,6 +1672,7 @@ module.exports.CreateDB = function (parent, func) {
1672 if (obj.powerFilePendingCbs != null) { for (var i in obj.powerFilePendingCbs) { obj.powerFilePendingCbs[i](); } obj.powerFilePendingCbs = null; }
1673 if (obj.powerFilePendingSets != null) {
1674 // Perform pending operations
1675 + obj.dbCounters.powerSetBulk++;
1676 var ops = [];
1677 for (var i in obj.powerFilePendingSets) { ops.push({ document: obj.powerFilePendingSets[i] }); }
1678 obj.powerFilePendingCbs = obj.powerFilePendingCb;
meshuser.js
+6 -2
@@ -887,7 +887,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
887
888 switch (cmd) {
889 case 'help': {
890 - var fin = '', f = '', availcommands = 'help,maintenance,info,versions,resetserver,usersessions,closeusersessions,tasklimiter,setmaxtasks,cores,migrationagents,agentstats,agentissues,webstats,mpsstats,swarmstats,acceleratorsstats,updatecheck,serverupdate,nodeconfig,heapdump,relays,autobackup,backupconfig,dupagents,dispatchtable,badlogins,showpaths,le,lecheck,leevents,dbstats,sms,amtacm,certhashes,watchdog,amtmanager';
890 + var fin = '', f = '', availcommands = 'help,maintenance,info,versions,resetserver,usersessions,closeusersessions,tasklimiter,setmaxtasks,cores,migrationagents,agentstats,agentissues,webstats,mpsstats,swarmstats,acceleratorsstats,updatecheck,serverupdate,nodeconfig,heapdump,relays,autobackup,backupconfig,dupagents,dispatchtable,badlogins,showpaths,le,lecheck,leevents,dbstats,dbcounters,sms,amtacm,certhashes,watchdog,amtmanager';
891 if (parent.parent.config.settings.heapdump === true) { availcommands += ',heapdump'; }
892 availcommands = availcommands.split(',').sort();
893 while (availcommands.length > 0) { if (f.length > 80) { fin += (f + ',\r\n'); f = ''; } f += (((f != '') ? ', ' : ' ') + availcommands.shift()); }
@@ -1135,7 +1135,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
1135 var r2 = '';
1136 for (var i in stats) { r2 += (i + ': ' + stats[i] + '\r\n'); }
1137 try { ws.send(JSON.stringify({ action: 'serverconsole', value: r2, tag: command.tag })); } catch (ex) { }
1138 - })
1138 + });
1139 + break;
1140 + }
1141 + case 'dbcounters': {
1142 + try { ws.send(JSON.stringify({ action: 'serverconsole', value: JSON.stringify(parent.parent.db.dbCounters, null, 2), tag: command.tag })); } catch (ex) { }
1143 break;
1144 }
1145 case 'serverupdate': {
views/default.handlebars
+8 -1
@@ -6483,7 +6483,14 @@
6483 }
6484
6485 function deskClipboardOutFunction() {
6486 - if ((navigator.clipboard != null) && (navigator.clipboard.readText != null)) { navigator.clipboard.readText().then(function(text) { meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: text }); }).catch(function(err) { }); }
6486 + if ((navigator.clipboard != null) && (navigator.clipboard.readText != null)) {
6487 + try {
6488 + navigator.clipboard.readText().then(function(text) {
6489 + meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: text });
6490 + }).catch(function(err) { console.log(err); });
6491 + } catch (ex) { console.log(ex); }
6492 + }
6493 + return true;
6494 }
6495
6496 function deskRefreshFunction() {