cache group membership methods

groups.isMember
groups.isMembers
groups.isMemberOfGroups
clear cache for user on group.join & group.leave
v1.18.x
barisusakli
parent 599ac80f3d
commit fac68d52f6

@ -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'),

@ -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;

@ -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;

@ -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) {

@ -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);

@ -0,0 +1,46 @@
<div class="post-cache">
<div class="col-lg-9">
<div class="panel panel-default">
<div class="panel-heading"><i class="fa fa-calendar-o"></i> Post Cache</div>
<div class="panel-body">
<label>Posts in Cache</label><br/>
<span>{postCache.itemCount}</span><br/>
<label>Average Post Size</label><br/>
<span>{postCache.avgPostSize}</span><br/>
<label>Length / Max</label><br/>
<span>{postCache.length} / {postCache.max}</span><br/>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="{postCache.percentFull}" aria-valuemin="0" aria-valuemax="100" style="width: {postCache.percentFull}%;">
{postCache.percentFull}% Full
</div>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><i class="fa fa-calendar-o"></i> Group Cache</div>
<div class="panel-body">
<label>Users in Cache</label><br/>
<span>{groupCache.itemCount}</span><br/>
<label>Length / Max</label><br/>
<span>{groupCache.length} / {groupCache.max}</span><br/>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="{groupCache.percentFull}" aria-valuemin="0" aria-valuemax="100" style="width: {groupCache.percentFull}%;">
{groupCache.percentFull}% Full
</div>
</div>
</div>
</div>
</div>
</div>

@ -1,27 +0,0 @@
<div class="post-cache">
<div class="col-lg-9">
<div class="panel panel-default">
<div class="panel-heading"><i class="fa fa-calendar-o"></i> Post Cache</div>
<div class="panel-body" data-next="{next}">
<label>Posts in Cache</label><br/>
<span>{cache.itemCount}</span><br/>
<label>Average Post Size</label><br/>
<span>{cache.avgPostSize}</span><br/>
<label>Length / Max</label><br/>
<span>{cache.length} / {cache.max}</span><br/>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="{cache.percentFull}" aria-valuemin="0" aria-valuemax="100" style="width: {cache.percentFull}%;">
{cache.percentFull}% Full
</div>
</div>
</div>
</div>
</div>
</div>

@ -96,7 +96,7 @@
<li><a href="{relative_path}/admin/advanced/events">Events</a></li>
<li><a href="{relative_path}/admin/advanced/logs">Logs</a></li>
<li><a href="{relative_path}/admin/advanced/errors">Errors</a></li>
<li><a href="{relative_path}/admin/advanced/post-cache">Post Cache</a></li>
<li><a href="{relative_path}/admin/advanced/cache">Cache</a></li>
<!-- IF env -->
<li><a href="{relative_path}/admin/development/logger">Logger</a></li>
<!-- ENDIF env -->
@ -245,7 +245,7 @@
<li><a href="{relative_path}/admin/advanced/events">Events</a></li>
<li><a href="{relative_path}/admin/advanced/logs">Logs</a></li>
<li><a href="{relative_path}/admin/advanced/errors">Errors</a></li>
<li><a href="{relative_path}/admin/advanced/post-cache">Post Cache</a></li>
<li><a href="{relative_path}/admin/advanced/cache">Cache</a></li>
<!-- IF env -->
<li><a href="{relative_path}/admin/development/logger">Logger</a></li>
<!-- ENDIF env -->

@ -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',

Loading…
Cancel
Save