From fac68d52f69933bfaad54de1ea865adca5b29ca6 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 14 Sep 2016 21:00:41 +0300 Subject: [PATCH] cache group membership methods groups.isMember groups.isMembers groups.isMemberOfGroups clear cache for user on group.join & group.leave --- src/controllers/admin.js | 2 +- src/controllers/admin/cache.js | 36 +++++++ src/controllers/admin/postCache.js | 26 ----- src/groups/membership.js | 129 ++++++++++++++++++++++-- src/routes/admin.js | 2 +- src/views/admin/advanced/cache.tpl | 46 +++++++++ src/views/admin/advanced/post-cache.tpl | 27 ----- src/views/admin/partials/menu.tpl | 4 +- tests/user.js | 5 +- 9 files changed, 213 insertions(+), 64 deletions(-) create mode 100644 src/controllers/admin/cache.js delete mode 100644 src/controllers/admin/postCache.js create mode 100644 src/views/admin/advanced/cache.tpl delete mode 100644 src/views/admin/advanced/post-cache.tpl diff --git a/src/controllers/admin.js b/src/controllers/admin.js index c3ce96d205..7f622466cd 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -16,7 +16,7 @@ var adminController = { logs: require('./admin/logs'), errors: require('./admin/errors'), database: require('./admin/database'), - postCache: require('./admin/postCache'), + cache: require('./admin/cache'), plugins: require('./admin/plugins'), languages: require('./admin/languages'), settings: require('./admin/settings'), diff --git a/src/controllers/admin/cache.js b/src/controllers/admin/cache.js new file mode 100644 index 0000000000..de27a16775 --- /dev/null +++ b/src/controllers/admin/cache.js @@ -0,0 +1,36 @@ +'use strict'; + +var cacheController = {}; + +cacheController.get = function(req, res, next) { + var postCache = require('../../posts/cache'); + var groupCache = require('../../groups').cache; + + var avgPostSize = 0; + var percentFull = 0; + if (postCache.itemCount > 0) { + avgPostSize = parseInt((postCache.length / postCache.itemCount), 10); + percentFull = ((postCache.length / postCache.max) * 100).toFixed(2); + } + + res.render('admin/advanced/cache', { + postCache: { + length: postCache.length, + max: postCache.max, + itemCount: postCache.itemCount, + percentFull: percentFull, + avgPostSize: avgPostSize, + dump: req.query.debug ? postCache.dump() : undefined + }, + groupCache: { + length: groupCache.length, + max: groupCache.max, + itemCount: groupCache.itemCount, + percentFull: ((groupCache.length / groupCache.max) * 100).toFixed(2), + dump: req.query.debug ? groupCache.dump() : undefined + } + }); +}; + + +module.exports = cacheController; \ No newline at end of file diff --git a/src/controllers/admin/postCache.js b/src/controllers/admin/postCache.js deleted file mode 100644 index bbfd222586..0000000000 --- a/src/controllers/admin/postCache.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -var postCacheController = {}; - -postCacheController.get = function(req, res, next) { - var cache = require('../../posts/cache'); - var avgPostSize = 0; - var percentFull = 0; - if (cache.itemCount > 0) { - avgPostSize = parseInt((cache.length / cache.itemCount), 10); - percentFull = ((cache.length / cache.max) * 100).toFixed(2); - } - - res.render('admin/advanced/post-cache', { - cache: { - length: cache.length, - max: cache.max, - itemCount: cache.itemCount, - percentFull: percentFull, - avgPostSize: avgPostSize - } - }); -}; - - -module.exports = postCacheController; \ No newline at end of file diff --git a/src/groups/membership.js b/src/groups/membership.js index 3e18979f96..88993f0c9c 100644 --- a/src/groups/membership.js +++ b/src/groups/membership.js @@ -8,10 +8,21 @@ var user = require('../user'); var utils = require('../../public/src/utils'); var plugins = require('../plugins'); var notifications = require('../notifications'); -var db = require('./../database'); +var db = require('../database'); + +var pubsub = require('../pubsub'); +var LRU = require('lru-cache'); + +var cache = LRU({ + max: 200, + maxAge: 1000 * 60 * 60 +}); + module.exports = function(Groups) { + Groups.cache = cache; + Groups.join = function(groupName, uid, callback) { callback = callback || function() {}; @@ -69,6 +80,7 @@ module.exports = function(Groups) { async.parallel(tasks, next); }, function(results, next) { + clearCache(uid); setGroupTitleIfNotSet(groupName, uid, next); }, function(next) { @@ -222,6 +234,7 @@ module.exports = function(Groups) { ], next); }, function(results, next) { + clearCache(uid); Groups.getGroupFields(groupName, ['hidden', 'memberCount'], next); }, function(groupData, next) { @@ -296,26 +309,130 @@ module.exports = function(Groups) { }), callback); }; + Groups.resetCache = function() { + pubsub.publish('group:cache:reset'); + cache.reset(); + }; + + pubsub.on('group:cache:reset', function() { + cache.reset(); + }); + + function clearCache(uid) { + pubsub.publish('group:cache:del', {uid: uid}); + cache.del(uid); + } + + pubsub.on('group:cache:del', function(data) { + cache.del(data.uid); + }); + Groups.isMember = function(uid, groupName, callback) { if (!uid || parseInt(uid, 10) <= 0) { return callback(null, false); } - db.isSortedSetMember('group:' + groupName + ':members', uid, callback); + + var cachedData = cache.get(uid); + + if (cachedData && cachedData.hasOwnProperty(groupName)) { + return process.nextTick(callback, null, cachedData[groupName]); + } + + db.isSortedSetMember('group:' + groupName + ':members', uid, function(err, isMember) { + if (err) { + return callback(err); + } + + cachedData = cachedData || {}; + cachedData[groupName] = isMember; + cache.set(uid, cachedData); + callback(null, isMember); + }); }; Groups.isMembers = function(uids, groupName, callback) { - db.isSortedSetMembers('group:' + groupName + ':members', uids, callback); + if (!groupName || !uids.length) { + return callback(null, uids.map(function() {return false;})); + } + + var cachedUids = {}; + var nonCachedUids = []; + uids.forEach(function(uid) { + cachedUids[uid] = cache.get(uid); + if (!cachedUids[uid] || !cachedUids[uid].hasOwnProperty(groupName)) { + nonCachedUids.push(uid); + } + }); + + if (!nonCachedUids.length) { + var result = uids.map(function(uid) { + return cachedUids[uid] && cachedUids[uid][groupName]; + }); + return process.nextTick(callback, null, result); + } + + db.isSortedSetMembers('group:' + groupName + ':members', nonCachedUids, function(err, isMembers) { + if (err) { + return callback(err); + } + + nonCachedUids.forEach(function(uid, index) { + cachedUids[uid] = cachedUids[uid] || {}; + cachedUids[uid][groupName] = isMembers[index]; + cache.set(uid, cachedUids[uid]); + }); + + var result = uids.map(function(uid) { + return cachedUids[uid][groupName]; + }); + + callback(null, result); + }); }; Groups.isMemberOfGroups = function(uid, groups, callback) { - if (!uid || parseInt(uid, 10) <= 0) { + if (!uid || parseInt(uid, 10) <= 0 || !groups.length) { return callback(null, groups.map(function() {return false;})); } - groups = groups.map(function(groupName) { + + var cachedData = cache.get(uid); + var nonCachedGroups = []; + if (cachedData) { + groups.forEach(function(groupName) { + if (!cachedData.hasOwnProperty(groupName)) { + nonCachedGroups.push(groupName); + } + }); + } else { + nonCachedGroups = groups; + } + + // are they all cached? + if (cachedData && !nonCachedGroups.length) { + var result = groups.map(function(groupName) { + return cachedData[groupName]; + }); + return process.nextTick(callback, null, result); + } + + var nonCachedGroupsMemberSets = nonCachedGroups.map(function(groupName) { return 'group:' + groupName + ':members'; }); - db.isMemberOfSortedSets(groups, uid, callback); + db.isMemberOfSortedSets(nonCachedGroupsMemberSets, uid, function(err, isMembers) { + if (err) { + return callback(err); + } + cachedData = cachedData || {}; + nonCachedGroups.forEach(function(groupName, index) { + cachedData[groupName] = isMembers[index]; + }); + cache.set(uid, cachedData); + var result = groups.map(function(groupName) { + return cachedData[groupName]; + }); + callback(null, result); + }); }; Groups.getMemberCount = function(groupName, callback) { diff --git a/src/routes/admin.js b/src/routes/admin.js index 543b9e8489..b84e744685 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -85,7 +85,7 @@ function addRoutes(router, middleware, controllers) { router.get('/advanced/logs', middlewares, controllers.admin.logs.get); router.get('/advanced/errors', middlewares, controllers.admin.errors.get); router.get('/advanced/errors/export', middlewares, controllers.admin.errors.export); - router.get('/advanced/post-cache', middlewares, controllers.admin.postCache.get); + router.get('/advanced/cache', middlewares, controllers.admin.cache.get); router.get('/development/logger', middlewares, controllers.admin.logger.get); router.get('/development/info', middlewares, controllers.admin.info.get); diff --git a/src/views/admin/advanced/cache.tpl b/src/views/admin/advanced/cache.tpl new file mode 100644 index 0000000000..68acef6be9 --- /dev/null +++ b/src/views/admin/advanced/cache.tpl @@ -0,0 +1,46 @@ + +
+
+
+
Post Cache
+
+ +
+ {postCache.itemCount}
+ +
+ {postCache.avgPostSize}
+ +
+ {postCache.length} / {postCache.max}
+ +
+
+ {postCache.percentFull}% Full +
+
+ +
+
+ +
+
Group Cache
+
+ +
+ {groupCache.itemCount}
+ +
+ {groupCache.length} / {groupCache.max}
+ +
+
+ {groupCache.percentFull}% Full +
+
+ +
+
+
+ +
diff --git a/src/views/admin/advanced/post-cache.tpl b/src/views/admin/advanced/post-cache.tpl deleted file mode 100644 index 7f688327e4..0000000000 --- a/src/views/admin/advanced/post-cache.tpl +++ /dev/null @@ -1,27 +0,0 @@ - -
-
-
-
Post Cache
-
- -
- {cache.itemCount}
- -
- {cache.avgPostSize}
- -
- {cache.length} / {cache.max}
- -
-
- {cache.percentFull}% Full -
-
- -
-
-
- -
diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl index b971348439..712155a30b 100644 --- a/src/views/admin/partials/menu.tpl +++ b/src/views/admin/partials/menu.tpl @@ -96,7 +96,7 @@
  • Events
  • Logs
  • Errors
  • -
  • Post Cache
  • +
  • Cache
  • Logger
  • @@ -245,7 +245,7 @@
  • Events
  • Logs
  • Errors
  • -
  • Post Cache
  • +
  • Cache
  • Logger
  • diff --git a/tests/user.js b/tests/user.js index 32f43c7dbf..9ec4e49094 100644 --- a/tests/user.js +++ b/tests/user.js @@ -23,6 +23,9 @@ describe('User', function() { testCid; before(function(done) { + var groups = require('../src/groups'); + groups.resetCache(); + Categories.create({ name: 'Test Category', description: 'A test', @@ -37,7 +40,7 @@ describe('User', function() { }); }); - beforeEach(function(){ + beforeEach(function() { userData = { username: 'John Smith', fullname: 'John Smith McNamara',