Added cache invalidation functionality (#7279)

Josiah Baldwin committed Feb 1, 2026 at 02:02 UTC bfa8582fac9cb9283cb509517a77c1611ca93232
2 files changed +46 -7
meshuser.js
+2 -1
@@ -5806,6 +5806,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5806 if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come.
5807 parent.parent.DispatchEvent(targets, obj, event);
5808 }
5809 + parent.InvalidateNodeCache(newuser, node.meshid, node._id)
5810 }
5811 }
5812
@@ -5824,7 +5825,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5825 if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come.
5826 parent.parent.DispatchEvent(dispatchTargets, obj, event);
5827 }
5827 -
5828 if (command.responseid != null) { obj.send({ action: 'adddeviceuser', responseid: command.responseid, result: 'ok' }); }
5829 });
5830 }
@@ -5944,6 +5944,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5944 parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(mesh, [user._id, newuserid]), obj, event);
5945 if (command.remove === true) { msgs.push("Removed user " + newuserid.split('/')[2]); } else { msgs.push("Added user " + newuserid.split('/')[2]); }
5946 successCount++;
5947 + parent.InvalidateNodeCache(newuser, mesh)
5948 } else {
5949 msgs.push("Unknown user " + newuserid.split('/')[2]);
5950 unknownUsers.push(newuserid.split('/')[2]);
webserver.js
+44 -6
@@ -9267,15 +9267,17 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
9267 if (typeof mesh == 'string') { meshid = mesh; } else if ((typeof mesh == 'object') && (typeof mesh._id == 'string')) { meshid = mesh._id; } else return 0;
9268
9269 // Check if we have this in the cache
9270 - const cacheid = user._id + '/' + meshid + '/' + nodeid;
9271 - const cache = GetNodeRightsCache[cacheid];
9270 +
9271 + const cache = ((GetNodeRightsCache[user._id] || {})[meshid] || {})[nodeid];
9272 if (cache != null) { if (cache.t > Date.now()) { return cache.o; } else { GetNodeRightsCacheCount--; } } // Cache hit, or we need to update the cache
9273 - if (GetNodeRightsCacheCount > 2000) { GetNodeRightsCache = {}; GetNodeRightsCacheCount = 0; } // From time to time, flush the cache
9273 + if (GetNodeRightsCacheCount > 2000) { obj.FlushGetNodeRightsCache() } // From time to time, flush the cache
9274
9275 var r = obj.GetMeshRights(user, mesh);
9276 if (r == 0xFFFFFFFF) {
9277 const out = removeUserRights(r, user);
9278 - GetNodeRightsCache[cacheid] = { t: Date.now() + 10000, o: out };
9278 + GetNodeRightsCache[user._id] = GetNodeRightsCache[user._id] || {}
9279 + GetNodeRightsCache[user._id][meshid] = GetNodeRightsCache[user._id][meshid] || {}
9280 + GetNodeRightsCache[user._id][meshid][nodeid] = { t: Date.now() + 10000, o: out };
9281 GetNodeRightsCacheCount++;
9282 return out;
9283 }
@@ -9284,7 +9286,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
9286 if ((user.links != null) && (user.links[nodeid] != null)) { r |= user.links[nodeid].rights; } // TODO: Deal with reverse permissions
9287 if (r == 0xFFFFFFFF) {
9288 const out = removeUserRights(r, user);
9287 - GetNodeRightsCache[cacheid] = { t: Date.now() + 10000, o: out };
9289 + GetNodeRightsCache[user._id] = GetNodeRightsCache[user._id] || {}
9290 + GetNodeRightsCache[user._id][meshid] = GetNodeRightsCache[user._id][meshid] || {}
9291 + GetNodeRightsCache[user._id][meshid][nodeid] = { t: Date.now() + 10000, o: out };
9292 GetNodeRightsCacheCount++;
9293 return out;
9294 }
@@ -9298,11 +9302,45 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
9302 }
9303
9304 const out = removeUserRights(r, user);
9301 - GetNodeRightsCache[cacheid] = { t: Date.now() + 10000, o: out };
9305 + GetNodeRightsCache[user._id] = GetNodeRightsCache[user._id] || {}
9306 + GetNodeRightsCache[user._id][meshid] = GetNodeRightsCache[user._id][meshid] || {}
9307 + GetNodeRightsCache[user._id][meshid][nodeid] = { t: Date.now() + 10000, o: out };
9308 GetNodeRightsCacheCount++;
9309 return out;
9310 }
9311
9312 + obj.InvalidateNodeCache = function (user, mesh, nodeid) {
9313 + if (user == null) { return; }
9314 +
9315 + if (typeof user == 'string') { user = obj.users[user]; }
9316 + if (user == null) { return 0; }
9317 + var meshid;
9318 + if (typeof mesh == 'string') { meshid = mesh; } else if ((typeof mesh == 'object') && (typeof mesh._id == 'string')) { meshid = mesh._id; };
9319 +
9320 + if (mesh == null) {
9321 + for (let [key, val] of Object.entries(GetNodeRightsCache[user._id] || {})) {
9322 + GetNodeRightsCacheCount -= Object.keys(val).length
9323 + }
9324 + delete GetNodeRightsCache[user._id];
9325 + return;
9326 + }
9327 + if (nodeid == null) {
9328 + let cache_reduction = Object.keys((GetNodeRightsCache[user._id] || {})[meshid] || {}).length
9329 + delete (GetNodeRightsCache[user._id] || {})[meshid]
9330 + GetNodeRightsCacheCount -= cache_reduction;
9331 + return;
9332 + }
9333 + if (((GetNodeRightsCache[user._id] || {})[meshid] || {})[nodeid]) {
9334 + delete ((GetNodeRightsCache[user._id] || {})[meshid] || {})[nodeid]
9335 + GetNodeRightsCacheCount--;
9336 + }
9337 + }
9338 +
9339 + obj.FlushGetNodeRightsCache = function() {
9340 + GetNodeRightsCache = {};
9341 + GetNodeRightsCacheCount = 0;
9342 + }
9343 +
9344 // Returns a list of displatch targets for a given mesh
9345 // We have to target the meshid and all user groups for this mesh, plus any added targets
9346 obj.CreateMeshDispatchTargets = function (mesh, addedTargets) {