mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 08:36:12 +01:00
* feat: #8824, cache refactor ability to disable caches ability to download contents of cache refactor cache modules to remove duplicated code * fix: remove duplicate hit/miss tracking check cacheEnabled in getUncachedKeys
This commit is contained in:
committed by
GitHub
parent
bcbc085497
commit
f1f9b225b0
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"post-cache": "Post Cache",
|
"post-cache": "Post Cache",
|
||||||
"posts-in-cache": "Posts in Cache",
|
|
||||||
"average-post-size": "Average Post Size",
|
|
||||||
"length-to-max": "Length / Max",
|
|
||||||
"percent-full": "%1% Full",
|
"percent-full": "%1% Full",
|
||||||
"post-cache-size": "Post Cache Size",
|
"post-cache-size": "Post Cache Size",
|
||||||
"items-in-cache": "Items in Cache",
|
"items-in-cache": "Items in Cache",
|
||||||
"control-panel": "Control Panel",
|
"control-panel": "Control Panel",
|
||||||
"update-settings": "Update Cache Settings"
|
"update-settings": "Update Cache Settings",
|
||||||
|
"clear": "Clear",
|
||||||
|
"download": "Download",
|
||||||
|
"enabled": "Enabled"
|
||||||
}
|
}
|
||||||
@@ -23,14 +23,14 @@ get:
|
|||||||
type: number
|
type: number
|
||||||
percentFull:
|
percentFull:
|
||||||
type: number
|
type: number
|
||||||
avgPostSize:
|
|
||||||
type: number
|
|
||||||
hits:
|
hits:
|
||||||
type: string
|
type: string
|
||||||
misses:
|
misses:
|
||||||
type: string
|
type: string
|
||||||
hitRatio:
|
hitRatio:
|
||||||
type: string
|
type: string
|
||||||
|
enabled:
|
||||||
|
type: boolean
|
||||||
groupCache:
|
groupCache:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -48,6 +48,8 @@ get:
|
|||||||
type: string
|
type: string
|
||||||
hitRatio:
|
hitRatio:
|
||||||
type: string
|
type: string
|
||||||
|
enabled:
|
||||||
|
type: boolean
|
||||||
localCache:
|
localCache:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -59,14 +61,14 @@ get:
|
|||||||
type: number
|
type: number
|
||||||
percentFull:
|
percentFull:
|
||||||
type: number
|
type: number
|
||||||
dump:
|
|
||||||
type: boolean
|
|
||||||
hits:
|
hits:
|
||||||
type: string
|
type: string
|
||||||
misses:
|
misses:
|
||||||
type: string
|
type: string
|
||||||
hitRatio:
|
hitRatio:
|
||||||
type: string
|
type: string
|
||||||
|
enabled:
|
||||||
|
type: boolean
|
||||||
objectCache:
|
objectCache:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -84,6 +86,8 @@ get:
|
|||||||
type: string
|
type: string
|
||||||
hitRatio:
|
hitRatio:
|
||||||
type: string
|
type: string
|
||||||
|
enabled:
|
||||||
|
type: boolean
|
||||||
required:
|
required:
|
||||||
- postCache
|
- postCache
|
||||||
- groupCache
|
- groupCache
|
||||||
|
|||||||
@@ -15,6 +15,16 @@ define('admin/advanced/cache', function () {
|
|||||||
ajaxify.refresh();
|
ajaxify.refresh();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
$('.checkbox').on('change', function () {
|
||||||
|
var input = $(this).find('input');
|
||||||
|
var flag = input.is(':checked');
|
||||||
|
var name = $(this).attr('data-name');
|
||||||
|
socket.emit('admin.cache.toggle', { name: name, enabled: flag }, function (err) {
|
||||||
|
if (err) {
|
||||||
|
return app.alertError(err.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
return Cache;
|
return Cache;
|
||||||
});
|
});
|
||||||
|
|||||||
53
src/cache.js
53
src/cache.js
@@ -1,56 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const LRU = require('lru-cache');
|
const cacheCreate = require('./cacheCreate');
|
||||||
const pubsub = require('./pubsub');
|
|
||||||
|
|
||||||
const cache = new LRU({
|
module.exports = cacheCreate({
|
||||||
|
name: 'local',
|
||||||
max: 4000,
|
max: 4000,
|
||||||
maxAge: 0,
|
maxAge: 0,
|
||||||
});
|
});
|
||||||
cache.hits = 0;
|
|
||||||
cache.misses = 0;
|
|
||||||
|
|
||||||
const cacheGet = cache.get;
|
|
||||||
const cacheDel = cache.del;
|
|
||||||
const cacheReset = cache.reset;
|
|
||||||
|
|
||||||
cache.get = function (key) {
|
|
||||||
const data = cacheGet.apply(cache, [key]);
|
|
||||||
if (data === undefined) {
|
|
||||||
cache.misses += 1;
|
|
||||||
} else {
|
|
||||||
cache.hits += 1;
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
cache.del = function (key) {
|
|
||||||
if (!Array.isArray(key)) {
|
|
||||||
key = [key];
|
|
||||||
}
|
|
||||||
pubsub.publish('local:cache:del', key);
|
|
||||||
key.forEach(key => cacheDel.apply(cache, [key]));
|
|
||||||
};
|
|
||||||
|
|
||||||
cache.reset = function () {
|
|
||||||
pubsub.publish('local:cache:reset');
|
|
||||||
localReset();
|
|
||||||
};
|
|
||||||
|
|
||||||
function localReset() {
|
|
||||||
cacheReset.apply(cache);
|
|
||||||
cache.hits = 0;
|
|
||||||
cache.misses = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pubsub.on('local:cache:reset', function () {
|
|
||||||
localReset();
|
|
||||||
});
|
|
||||||
|
|
||||||
pubsub.on('local:cache:del', function (keys) {
|
|
||||||
if (Array.isArray(keys)) {
|
|
||||||
keys.forEach(key => cacheDel.apply(cache, [key]));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = cache;
|
|
||||||
|
|||||||
91
src/cacheCreate.js
Normal file
91
src/cacheCreate.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function (opts) {
|
||||||
|
const LRU = require('lru-cache');
|
||||||
|
const pubsub = require('./pubsub');
|
||||||
|
|
||||||
|
const cache = new LRU(opts);
|
||||||
|
|
||||||
|
cache.name = opts.name;
|
||||||
|
cache.hits = 0;
|
||||||
|
cache.misses = 0;
|
||||||
|
cache.enabled = opts.hasOwnProperty('enabled') ? opts.enabled : true;
|
||||||
|
|
||||||
|
const cacheSet = cache.set;
|
||||||
|
const cacheGet = cache.get;
|
||||||
|
const cacheDel = cache.del;
|
||||||
|
const cacheReset = cache.reset;
|
||||||
|
|
||||||
|
cache.set = function (key, value) {
|
||||||
|
if (!cache.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cacheSet.apply(cache, [key, value]);
|
||||||
|
};
|
||||||
|
|
||||||
|
cache.get = function (key) {
|
||||||
|
if (!cache.enabled) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const data = cacheGet.apply(cache, [key]);
|
||||||
|
if (data === undefined) {
|
||||||
|
cache.misses += 1;
|
||||||
|
} else {
|
||||||
|
cache.hits += 1;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
cache.del = function (keys) {
|
||||||
|
if (!Array.isArray(keys)) {
|
||||||
|
keys = [keys];
|
||||||
|
}
|
||||||
|
pubsub.publish(cache.name + ':cache:del', keys);
|
||||||
|
keys.forEach(key => cacheDel.apply(cache, [key]));
|
||||||
|
};
|
||||||
|
|
||||||
|
cache.reset = function () {
|
||||||
|
pubsub.publish(cache.name + ':cache:reset');
|
||||||
|
localReset();
|
||||||
|
};
|
||||||
|
|
||||||
|
function localReset() {
|
||||||
|
cacheReset.apply(cache);
|
||||||
|
cache.hits = 0;
|
||||||
|
cache.misses = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pubsub.on(cache.name + ':cache:reset', function () {
|
||||||
|
localReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
pubsub.on(cache.name + ':cache:del', function (keys) {
|
||||||
|
if (Array.isArray(keys)) {
|
||||||
|
keys.forEach(key => cacheDel.apply(cache, [key]));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cache.getUnCachedKeys = function (keys, cachedData) {
|
||||||
|
if (!cache.enabled) {
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
let data;
|
||||||
|
let isCached;
|
||||||
|
const unCachedKeys = keys.filter(function (key) {
|
||||||
|
data = cache.get(key);
|
||||||
|
isCached = data !== undefined;
|
||||||
|
if (isCached) {
|
||||||
|
cachedData[key] = data;
|
||||||
|
}
|
||||||
|
return !isCached;
|
||||||
|
});
|
||||||
|
|
||||||
|
var hits = keys.length - unCachedKeys.length;
|
||||||
|
var misses = keys.length - hits;
|
||||||
|
cache.hits += hits;
|
||||||
|
cache.misses += misses;
|
||||||
|
return unCachedKeys;
|
||||||
|
};
|
||||||
|
|
||||||
|
return cache;
|
||||||
|
};
|
||||||
@@ -10,10 +10,8 @@ cacheController.get = function (req, res) {
|
|||||||
const objectCache = require('../../database').objectCache;
|
const objectCache = require('../../database').objectCache;
|
||||||
const localCache = require('../../cache');
|
const localCache = require('../../cache');
|
||||||
|
|
||||||
let avgPostSize = 0;
|
|
||||||
let percentFull = 0;
|
let percentFull = 0;
|
||||||
if (postCache.itemCount > 0) {
|
if (postCache.itemCount > 0) {
|
||||||
avgPostSize = parseInt((postCache.length / postCache.itemCount), 10);
|
|
||||||
percentFull = ((postCache.length / postCache.max) * 100).toFixed(2);
|
percentFull = ((postCache.length / postCache.max) * 100).toFixed(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,10 +21,10 @@ cacheController.get = function (req, res) {
|
|||||||
max: postCache.max,
|
max: postCache.max,
|
||||||
itemCount: postCache.itemCount,
|
itemCount: postCache.itemCount,
|
||||||
percentFull: percentFull,
|
percentFull: percentFull,
|
||||||
avgPostSize: avgPostSize,
|
|
||||||
hits: utils.addCommas(String(postCache.hits)),
|
hits: utils.addCommas(String(postCache.hits)),
|
||||||
misses: utils.addCommas(String(postCache.misses)),
|
misses: utils.addCommas(String(postCache.misses)),
|
||||||
hitRatio: ((postCache.hits / (postCache.hits + postCache.misses) || 0)).toFixed(4),
|
hitRatio: ((postCache.hits / (postCache.hits + postCache.misses) || 0)).toFixed(4),
|
||||||
|
enabled: postCache.enabled,
|
||||||
},
|
},
|
||||||
groupCache: {
|
groupCache: {
|
||||||
length: groupCache.length,
|
length: groupCache.length,
|
||||||
@@ -36,16 +34,17 @@ cacheController.get = function (req, res) {
|
|||||||
hits: utils.addCommas(String(groupCache.hits)),
|
hits: utils.addCommas(String(groupCache.hits)),
|
||||||
misses: utils.addCommas(String(groupCache.misses)),
|
misses: utils.addCommas(String(groupCache.misses)),
|
||||||
hitRatio: (groupCache.hits / (groupCache.hits + groupCache.misses)).toFixed(4),
|
hitRatio: (groupCache.hits / (groupCache.hits + groupCache.misses)).toFixed(4),
|
||||||
|
enabled: groupCache.enabled,
|
||||||
},
|
},
|
||||||
localCache: {
|
localCache: {
|
||||||
length: localCache.length,
|
length: localCache.length,
|
||||||
max: localCache.max,
|
max: localCache.max,
|
||||||
itemCount: localCache.itemCount,
|
itemCount: localCache.itemCount,
|
||||||
percentFull: ((localCache.length / localCache.max) * 100).toFixed(2),
|
percentFull: ((localCache.length / localCache.max) * 100).toFixed(2),
|
||||||
dump: req.query.debug ? JSON.stringify(localCache.dump(), null, 4) : false,
|
|
||||||
hits: utils.addCommas(String(localCache.hits)),
|
hits: utils.addCommas(String(localCache.hits)),
|
||||||
misses: utils.addCommas(String(localCache.misses)),
|
misses: utils.addCommas(String(localCache.misses)),
|
||||||
hitRatio: ((localCache.hits / (localCache.hits + localCache.misses) || 0)).toFixed(4),
|
hitRatio: ((localCache.hits / (localCache.hits + localCache.misses) || 0)).toFixed(4),
|
||||||
|
enabled: localCache.enabled,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -58,8 +57,31 @@ cacheController.get = function (req, res) {
|
|||||||
hits: utils.addCommas(String(objectCache.hits)),
|
hits: utils.addCommas(String(objectCache.hits)),
|
||||||
misses: utils.addCommas(String(objectCache.misses)),
|
misses: utils.addCommas(String(objectCache.misses)),
|
||||||
hitRatio: (objectCache.hits / (objectCache.hits + objectCache.misses)).toFixed(4),
|
hitRatio: (objectCache.hits / (objectCache.hits + objectCache.misses)).toFixed(4),
|
||||||
|
enabled: objectCache.enabled,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
res.render('admin/advanced/cache', data);
|
res.render('admin/advanced/cache', data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
cacheController.dump = function (req, res, next) {
|
||||||
|
const caches = {
|
||||||
|
post: require('../../posts/cache'),
|
||||||
|
object: require('../../database').objectCache,
|
||||||
|
group: require('../../groups').cache,
|
||||||
|
local: require('../../cache'),
|
||||||
|
};
|
||||||
|
if (!caches[req.query.name]) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = JSON.stringify(caches[req.query.name].dump(), null, 4);
|
||||||
|
res.setHeader('Content-disposition', 'attachment; filename= ' + req.query.name + '-cache.json');
|
||||||
|
res.setHeader('Content-type', 'application/json');
|
||||||
|
res.write(data, function (err) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,56 +1,11 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.exports.create = function (name) {
|
module.exports.create = function (name) {
|
||||||
var LRU = require('lru-cache');
|
const cacheCreate = require('../cacheCreate');
|
||||||
var pubsub = require('../pubsub');
|
return cacheCreate({
|
||||||
|
name: name + '-object',
|
||||||
var cache = new LRU({
|
|
||||||
max: 40000,
|
max: 40000,
|
||||||
length: function () { return 1; },
|
length: function () { return 1; },
|
||||||
maxAge: 0,
|
maxAge: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
cache.misses = 0;
|
|
||||||
cache.hits = 0;
|
|
||||||
|
|
||||||
pubsub.on(name + ':hash:cache:del', function (keys) {
|
|
||||||
keys.forEach(key => cache.del(key));
|
|
||||||
});
|
|
||||||
|
|
||||||
pubsub.on(name + ':hash:cache:reset', function () {
|
|
||||||
cache.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
cache.delObjectCache = function (keys) {
|
|
||||||
if (!Array.isArray(keys)) {
|
|
||||||
keys = [keys];
|
|
||||||
}
|
|
||||||
pubsub.publish(name + ':hash:cache:del', keys);
|
|
||||||
keys.forEach(key => cache.del(key));
|
|
||||||
};
|
|
||||||
|
|
||||||
cache.resetObjectCache = function () {
|
|
||||||
pubsub.publish(name + ':hash:cache:reset');
|
|
||||||
cache.reset();
|
|
||||||
};
|
|
||||||
|
|
||||||
cache.getUnCachedKeys = function (keys, cachedData) {
|
|
||||||
let data;
|
|
||||||
let isCached;
|
|
||||||
const unCachedKeys = keys.filter(function (key) {
|
|
||||||
data = cache.get(key);
|
|
||||||
isCached = data !== undefined;
|
|
||||||
if (isCached) {
|
|
||||||
cachedData[key] = data;
|
|
||||||
}
|
|
||||||
return !isCached;
|
|
||||||
});
|
|
||||||
|
|
||||||
var hits = keys.length - unCachedKeys.length;
|
|
||||||
var misses = keys.length - hits;
|
|
||||||
cache.hits += hits;
|
|
||||||
cache.misses += misses;
|
|
||||||
return unCachedKeys;
|
|
||||||
};
|
|
||||||
return cache;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ module.exports = function (module) {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.delObjectCache(key);
|
cache.del(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.setObjectField = async function (key, field, value) {
|
module.setObjectField = async function (key, field, value) {
|
||||||
@@ -164,7 +164,7 @@ module.exports = function (module) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await module.client.collection('objects').updateOne({ _key: key }, { $unset: data });
|
await module.client.collection('objects').updateOne({ _key: key }, { $unset: data });
|
||||||
cache.delObjectCache(key);
|
cache.del(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.incrObjectField = async function (key, field) {
|
module.incrObjectField = async function (key, field) {
|
||||||
@@ -191,13 +191,13 @@ module.exports = function (module) {
|
|||||||
bulk.find({ _key: key }).upsert().update({ $inc: increment });
|
bulk.find({ _key: key }).upsert().update({ $inc: increment });
|
||||||
});
|
});
|
||||||
await bulk.execute();
|
await bulk.execute();
|
||||||
cache.delObjectCache(key);
|
cache.del(key);
|
||||||
const result = await module.getObjectsFields(key, [field]);
|
const result = await module.getObjectsFields(key, [field]);
|
||||||
return result.map(data => data && data[field]);
|
return result.map(data => data && data[field]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await module.client.collection('objects').findOneAndUpdate({ _key: key }, { $inc: increment }, { returnOriginal: false, upsert: true });
|
const result = await module.client.collection('objects').findOneAndUpdate({ _key: key }, { $inc: increment }, { returnOriginal: false, upsert: true });
|
||||||
cache.delObjectCache(key);
|
cache.del(key);
|
||||||
return result && result.value ? result.value[field] : null;
|
return result && result.value ? result.value[field] : null;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ module.exports = function (module) {
|
|||||||
|
|
||||||
module.emptydb = async function () {
|
module.emptydb = async function () {
|
||||||
await module.client.collection('objects').deleteMany({});
|
await module.client.collection('objects').deleteMany({});
|
||||||
module.objectCache.resetObjectCache();
|
module.objectCache.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exists = async function (key) {
|
module.exists = async function (key) {
|
||||||
@@ -47,7 +47,7 @@ module.exports = function (module) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await module.client.collection('objects').deleteMany({ _key: key });
|
await module.client.collection('objects').deleteMany({ _key: key });
|
||||||
module.objectCache.delObjectCache(key);
|
module.objectCache.del(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.deleteAll = async function (keys) {
|
module.deleteAll = async function (keys) {
|
||||||
@@ -55,7 +55,7 @@ module.exports = function (module) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await module.client.collection('objects').deleteMany({ _key: { $in: keys } });
|
await module.client.collection('objects').deleteMany({ _key: { $in: keys } });
|
||||||
module.objectCache.delObjectCache(keys);
|
module.objectCache.del(keys);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.get = async function (key) {
|
module.get = async function (key) {
|
||||||
@@ -96,7 +96,7 @@ module.exports = function (module) {
|
|||||||
|
|
||||||
module.rename = async function (oldKey, newKey) {
|
module.rename = async function (oldKey, newKey) {
|
||||||
await module.client.collection('objects').updateMany({ _key: oldKey }, { $set: { _key: newKey } });
|
await module.client.collection('objects').updateMany({ _key: oldKey }, { $set: { _key: newKey } });
|
||||||
module.objectCache.delObjectCache([oldKey, newKey]);
|
module.objectCache.del([oldKey, newKey]);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.type = async function (key) {
|
module.type = async function (key) {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ module.exports = function (module) {
|
|||||||
await module.client.async.hmset(key, data);
|
await module.client.async.hmset(key, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.delObjectCache(key);
|
cache.del(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.setObjectField = async function (key, field, value) {
|
module.setObjectField = async function (key, field, value) {
|
||||||
@@ -48,7 +48,7 @@ module.exports = function (module) {
|
|||||||
await module.client.async.hset(key, field, value);
|
await module.client.async.hset(key, field, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.delObjectCache(key);
|
cache.del(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.getObject = async function (key) {
|
module.getObject = async function (key) {
|
||||||
@@ -146,7 +146,7 @@ module.exports = function (module) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await module.client.async.hdel(key, field);
|
await module.client.async.hdel(key, field);
|
||||||
cache.delObjectCache(key);
|
cache.del(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.deleteObjectFields = async function (key, fields) {
|
module.deleteObjectFields = async function (key, fields) {
|
||||||
@@ -158,7 +158,7 @@ module.exports = function (module) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await module.client.async.hdel(key, fields);
|
await module.client.async.hdel(key, fields);
|
||||||
cache.delObjectCache(key);
|
cache.del(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.incrObjectField = async function (key, field) {
|
module.incrObjectField = async function (key, field) {
|
||||||
@@ -182,7 +182,7 @@ module.exports = function (module) {
|
|||||||
} else {
|
} else {
|
||||||
result = await module.client.async.hincrby(key, field, value);
|
result = await module.client.async.hincrby(key, field, value);
|
||||||
}
|
}
|
||||||
cache.delObjectCache(key);
|
cache.del(key);
|
||||||
return Array.isArray(result) ? result.map(value => parseInt(value, 10)) : parseInt(result, 10);
|
return Array.isArray(result) ? result.map(value => parseInt(value, 10)) : parseInt(result, 10);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ module.exports = function (module) {
|
|||||||
|
|
||||||
module.emptydb = async function () {
|
module.emptydb = async function () {
|
||||||
await module.flushdb();
|
await module.flushdb();
|
||||||
module.objectCache.resetObjectCache();
|
module.objectCache.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exists = async function (key) {
|
module.exists = async function (key) {
|
||||||
@@ -45,7 +45,7 @@ module.exports = function (module) {
|
|||||||
|
|
||||||
module.delete = async function (key) {
|
module.delete = async function (key) {
|
||||||
await module.client.async.del(key);
|
await module.client.async.del(key);
|
||||||
module.objectCache.delObjectCache(key);
|
module.objectCache.del(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.deleteAll = async function (keys) {
|
module.deleteAll = async function (keys) {
|
||||||
@@ -53,7 +53,7 @@ module.exports = function (module) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await module.client.async.del(keys);
|
await module.client.async.del(keys);
|
||||||
module.objectCache.delObjectCache(keys);
|
module.objectCache.del(keys);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.get = async function (key) {
|
module.get = async function (key) {
|
||||||
@@ -77,7 +77,7 @@ module.exports = function (module) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.objectCache.delObjectCache([oldKey, newKey]);
|
module.objectCache.del([oldKey, newKey]);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.type = async function (key) {
|
module.type = async function (key) {
|
||||||
|
|||||||
@@ -1,48 +1,19 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var LRU = require('lru-cache');
|
const cacheCreate = require('../cacheCreate');
|
||||||
var pubsub = require('../pubsub');
|
|
||||||
|
|
||||||
var cache = new LRU({
|
|
||||||
max: 40000,
|
|
||||||
maxAge: 0,
|
|
||||||
});
|
|
||||||
cache.hits = 0;
|
|
||||||
cache.misses = 0;
|
|
||||||
|
|
||||||
module.exports = function (Groups) {
|
module.exports = function (Groups) {
|
||||||
Groups.cache = cache;
|
Groups.cache = cacheCreate({
|
||||||
|
name: 'group',
|
||||||
pubsub.on('group:cache:reset', function () {
|
max: 40000,
|
||||||
localReset();
|
maxAge: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
pubsub.on('group:cache:del', function (data) {
|
|
||||||
if (data && data.groupNames) {
|
|
||||||
data.groupNames.forEach(function (groupName) {
|
|
||||||
cache.del(data.uid + ':' + groupName);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Groups.resetCache = function () {
|
|
||||||
pubsub.publish('group:cache:reset');
|
|
||||||
localReset();
|
|
||||||
};
|
|
||||||
|
|
||||||
function localReset() {
|
|
||||||
cache.reset();
|
|
||||||
cache.hits = 0;
|
|
||||||
cache.misses = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Groups.clearCache = function (uid, groupNames) {
|
Groups.clearCache = function (uid, groupNames) {
|
||||||
if (!Array.isArray(groupNames)) {
|
if (!Array.isArray(groupNames)) {
|
||||||
groupNames = [groupNames];
|
groupNames = [groupNames];
|
||||||
}
|
}
|
||||||
pubsub.publish('group:cache:del', { uid: uid, groupNames: groupNames });
|
const keys = groupNames.map(name => uid + ':' + name);
|
||||||
groupNames.forEach(function (groupName) {
|
Groups.cache.del(keys);
|
||||||
cache.del(uid + ':' + groupName);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ module.exports = function (Groups) {
|
|||||||
db.deleteObjectFields('groupslug:groupname', fields),
|
db.deleteObjectFields('groupslug:groupname', fields),
|
||||||
removeGroupsFromPrivilegeGroups(groupNames),
|
removeGroupsFromPrivilegeGroups(groupNames),
|
||||||
]);
|
]);
|
||||||
Groups.resetCache();
|
Groups.cache.reset();
|
||||||
plugins.fireHook('action:groups.destroy', { groups: groupsData });
|
plugins.fireHook('action:groups.destroy', { groups: groupsData });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -31,10 +31,8 @@ module.exports = function (Groups) {
|
|||||||
const cacheKey = uid + ':' + groupName;
|
const cacheKey = uid + ':' + groupName;
|
||||||
let isMember = Groups.cache.get(cacheKey);
|
let isMember = Groups.cache.get(cacheKey);
|
||||||
if (isMember !== undefined) {
|
if (isMember !== undefined) {
|
||||||
Groups.cache.hits += 1;
|
|
||||||
return isMember;
|
return isMember;
|
||||||
}
|
}
|
||||||
Groups.cache.misses += 1;
|
|
||||||
isMember = await db.isSortedSetMember('group:' + groupName + ':members', uid);
|
isMember = await db.isSortedSetMember('group:' + groupName + ':members', uid);
|
||||||
Groups.cache.set(cacheKey, isMember);
|
Groups.cache.set(cacheKey, isMember);
|
||||||
return isMember;
|
return isMember;
|
||||||
@@ -88,10 +86,7 @@ module.exports = function (Groups) {
|
|||||||
const isMember = Groups.cache.get(uid + ':' + groupName);
|
const isMember = Groups.cache.get(uid + ':' + groupName);
|
||||||
const isInCache = isMember !== undefined;
|
const isInCache = isMember !== undefined;
|
||||||
if (isInCache) {
|
if (isInCache) {
|
||||||
Groups.cache.hits += 1;
|
|
||||||
cachedData[uid + ':' + groupName] = isMember;
|
cachedData[uid + ':' + groupName] = isMember;
|
||||||
} else {
|
|
||||||
Groups.cache.misses += 1;
|
|
||||||
}
|
}
|
||||||
return !isInCache;
|
return !isInCache;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ module.exports = function (Groups) {
|
|||||||
old: oldName,
|
old: oldName,
|
||||||
new: newName,
|
new: newName,
|
||||||
});
|
});
|
||||||
Groups.resetCache();
|
Groups.cache.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
async function updateMemberGroupTitles(oldName, newName) {
|
async function updateMemberGroupTitles(oldName, newName) {
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var LRU = require('lru-cache');
|
const cacheCreate = require('../cacheCreate');
|
||||||
var meta = require('../meta');
|
const meta = require('../meta');
|
||||||
|
|
||||||
var cache = new LRU({
|
module.exports = cacheCreate({
|
||||||
|
name: 'post',
|
||||||
max: meta.config.postCacheSize,
|
max: meta.config.postCacheSize,
|
||||||
length: function (n) { return n.length; },
|
length: function (n) { return n.length; },
|
||||||
maxAge: 0,
|
maxAge: 0,
|
||||||
|
enabled: global.env === 'production',
|
||||||
});
|
});
|
||||||
cache.hits = 0;
|
|
||||||
cache.misses = 0;
|
|
||||||
|
|
||||||
module.exports = cache;
|
|
||||||
|
|||||||
@@ -57,13 +57,12 @@ module.exports = function (Posts) {
|
|||||||
const cachedContent = cache.get(pid);
|
const cachedContent = cache.get(pid);
|
||||||
if (postData.pid && cachedContent !== undefined) {
|
if (postData.pid && cachedContent !== undefined) {
|
||||||
postData.content = cachedContent;
|
postData.content = cachedContent;
|
||||||
cache.hits += 1;
|
|
||||||
return postData;
|
return postData;
|
||||||
}
|
}
|
||||||
cache.misses += 1;
|
|
||||||
const data = await plugins.fireHook('filter:parse.post', { postData: postData });
|
const data = await plugins.fireHook('filter:parse.post', { postData: postData });
|
||||||
data.postData.content = translator.escape(data.postData.content);
|
data.postData.content = translator.escape(data.postData.content);
|
||||||
if (global.env === 'production' && data.postData.pid) {
|
if (data.postData.pid) {
|
||||||
cache.set(pid, data.postData.content);
|
cache.set(pid, data.postData.content);
|
||||||
}
|
}
|
||||||
return data.postData;
|
return data.postData;
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ helpers.isUserAllowedTo = async function (privilege, uid, cid) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function isUserAllowedToCids(privilege, uid, cids) {
|
async function isUserAllowedToCids(privilege, uid, cids) {
|
||||||
|
if (!privilege) {
|
||||||
|
return cids.map(() => false);
|
||||||
|
}
|
||||||
if (parseInt(uid, 10) <= 0) {
|
if (parseInt(uid, 10) <= 0) {
|
||||||
return await isSystemGroupAllowedToCids(privilege, uid, cids);
|
return await isSystemGroupAllowedToCids(privilege, uid, cids);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ function apiRoutes(router, middleware, controllers) {
|
|||||||
router.get('/api/admin/users/csv', middleware.authenticate, helpers.tryRoute(controllers.admin.users.getCSV));
|
router.get('/api/admin/users/csv', middleware.authenticate, helpers.tryRoute(controllers.admin.users.getCSV));
|
||||||
router.get('/api/admin/groups/:groupname/csv', middleware.authenticate, helpers.tryRoute(controllers.admin.groups.getCSV));
|
router.get('/api/admin/groups/:groupname/csv', middleware.authenticate, helpers.tryRoute(controllers.admin.groups.getCSV));
|
||||||
router.get('/api/admin/analytics', middleware.authenticate, helpers.tryRoute(controllers.admin.dashboard.getAnalytics));
|
router.get('/api/admin/analytics', middleware.authenticate, helpers.tryRoute(controllers.admin.dashboard.getAnalytics));
|
||||||
|
router.get('/api/admin/advanced/cache/dump', middleware.authenticate, helpers.tryRoute(controllers.admin.cache.dump));
|
||||||
|
|
||||||
const multipart = require('connect-multiparty');
|
const multipart = require('connect-multiparty');
|
||||||
const multipartMiddleware = multipart();
|
const multipartMiddleware = multipart();
|
||||||
|
|||||||
@@ -8,3 +8,16 @@ SocketCache.clear = async function () {
|
|||||||
require('../../groups').cache.reset();
|
require('../../groups').cache.reset();
|
||||||
require('../../cache').reset();
|
require('../../cache').reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SocketCache.toggle = async function (socket, data) {
|
||||||
|
const caches = {
|
||||||
|
post: require('../../posts/cache'),
|
||||||
|
object: require('../../database').objectCache,
|
||||||
|
group: require('../../groups').cache,
|
||||||
|
local: require('../../cache'),
|
||||||
|
};
|
||||||
|
if (!caches[data.name]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
caches[data.name].enabled = data.enabled;
|
||||||
|
};
|
||||||
|
|||||||
@@ -6,7 +6,13 @@
|
|||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><i class="fa fa-calendar-o"></i> [[admin/advanced/cache:post-cache]]</div>
|
<div class="panel-heading"><i class="fa fa-calendar-o"></i> [[admin/advanced/cache:post-cache]]</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<label>[[admin/advanced/cache:length-to-max]]</label><br/>
|
<div class="checkbox" data-name="post">
|
||||||
|
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||||
|
<input class="mdl-switch__input" type="checkbox" {{{if postCache.enabled}}}checked{{{end}}}>
|
||||||
|
<span class="mdl-switch__label"><strong>[[admin/advanced/cache:enabled]]</strong></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<span>{postCache.length} / {postCache.max}</span><br/>
|
<span>{postCache.length} / {postCache.max}</span><br/>
|
||||||
|
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
@@ -21,16 +27,11 @@
|
|||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
<label>[[admin/advanced/cache:posts-in-cache]]</label><br/>
|
|
||||||
<span>{postCache.itemCount}</span><br/>
|
|
||||||
|
|
||||||
<label>[[admin/advanced/cache:average-post-size]]</label><br/>
|
|
||||||
<span>{postCache.avgPostSize}</span><br/>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="postCacheSize">[[admin/advanced/cache:post-cache-size]]</label>
|
<label for="postCacheSize">[[admin/advanced/cache:post-cache-size]]</label>
|
||||||
<input id="postCacheSize" type="text" class="form-control" value="" data-field="postCacheSize">
|
<input id="postCacheSize" type="text" class="form-control" value="" data-field="postCacheSize">
|
||||||
</div>
|
</div>
|
||||||
|
<a href="{config.relative_path}/api/admin/advanced/cache/dump?name=post" class="btn btn-default"><i class="fa fa-download"></i> [[admin/advanced/cache:download]]</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,7 +41,12 @@
|
|||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><i class="fa fa-calendar-o"></i> Object Cache</div>
|
<div class="panel-heading"><i class="fa fa-calendar-o"></i> Object Cache</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<label>[[admin/advanced/cache:length-to-max]]</label><br/>
|
<div class="checkbox" data-name="object">
|
||||||
|
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||||
|
<input class="mdl-switch__input" type="checkbox" {{{if objectCache.enabled}}}checked{{{end}}}>
|
||||||
|
<span class="mdl-switch__label"><strong>[[admin/advanced/cache:enabled]]</strong></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<span>{objectCache.length} / {objectCache.max}</span><br/>
|
<span>{objectCache.length} / {objectCache.max}</span><br/>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar" role="progressbar" aria-valuenow="{objectCache.percentFull}" aria-valuemin="0" aria-valuemax="100" style="width: {objectCache.percentFull}%;">
|
<div class="progress-bar" role="progressbar" aria-valuenow="{objectCache.percentFull}" aria-valuemin="0" aria-valuemax="100" style="width: {objectCache.percentFull}%;">
|
||||||
@@ -51,6 +57,7 @@
|
|||||||
<label>Hits:</label> <span>{objectCache.hits}</span><br/>
|
<label>Hits:</label> <span>{objectCache.hits}</span><br/>
|
||||||
<label>Misses:</label> <span>{objectCache.misses}</span><br/>
|
<label>Misses:</label> <span>{objectCache.misses}</span><br/>
|
||||||
<label>Hit Ratio:</label> <span>{objectCache.hitRatio}</span><br/>
|
<label>Hit Ratio:</label> <span>{objectCache.hitRatio}</span><br/>
|
||||||
|
<a href="{config.relative_path}/api/admin/advanced/cache/dump?name=object" class="btn btn-default"><i class="fa fa-download"></i> [[admin/advanced/cache:download]]</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -59,8 +66,12 @@
|
|||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><i class="fa fa-calendar-o"></i> Group Cache</div>
|
<div class="panel-heading"><i class="fa fa-calendar-o"></i> Group Cache</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
<div class="checkbox" data-name="group">
|
||||||
<label>[[admin/advanced/cache:length-to-max]]</label><br/>
|
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||||
|
<input class="mdl-switch__input" type="checkbox" {{{if groupCache.enabled}}}checked{{{end}}}>
|
||||||
|
<span class="mdl-switch__label"><strong>[[admin/advanced/cache:enabled]]</strong></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<span>{groupCache.length} / {groupCache.max}</span><br/>
|
<span>{groupCache.length} / {groupCache.max}</span><br/>
|
||||||
|
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
@@ -72,6 +83,7 @@
|
|||||||
<label>Hits:</label> <span>{groupCache.hits}</span><br/>
|
<label>Hits:</label> <span>{groupCache.hits}</span><br/>
|
||||||
<label>Misses:</label> <span>{groupCache.misses}</span><br/>
|
<label>Misses:</label> <span>{groupCache.misses}</span><br/>
|
||||||
<label>Hit Ratio:</label> <span>{groupCache.hitRatio}</span><br/>
|
<label>Hit Ratio:</label> <span>{groupCache.hitRatio}</span><br/>
|
||||||
|
<a href="{config.relative_path}/api/admin/advanced/cache/dump?name=group" class="btn btn-default"><i class="fa fa-download"></i> [[admin/advanced/cache:download]]</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -79,8 +91,12 @@
|
|||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><i class="fa fa-calendar-o"></i> Local Cache</div>
|
<div class="panel-heading"><i class="fa fa-calendar-o"></i> Local Cache</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
<div class="checkbox" data-name="local">
|
||||||
<label>[[admin/advanced/cache:length-to-max]]</label><br/>
|
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||||
|
<input class="mdl-switch__input" type="checkbox" {{{if localCache.enabled}}}checked{{{end}}}>
|
||||||
|
<span class="mdl-switch__label"><strong>[[admin/advanced/cache:enabled]]</strong></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<span>{localCache.length} / {localCache.max}</span><br/>
|
<span>{localCache.length} / {localCache.max}</span><br/>
|
||||||
|
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
@@ -92,11 +108,7 @@
|
|||||||
<label>Hits:</label> <span>{localCache.hits}</span><br/>
|
<label>Hits:</label> <span>{localCache.hits}</span><br/>
|
||||||
<label>Misses:</label> <span>{localCache.misses}</span><br/>
|
<label>Misses:</label> <span>{localCache.misses}</span><br/>
|
||||||
<label>Hit Ratio:</label> <span>{localCache.hitRatio}</span><br/>
|
<label>Hit Ratio:</label> <span>{localCache.hitRatio}</span><br/>
|
||||||
|
<a href="{config.relative_path}/api/admin/advanced/cache/dump?name=local" class="btn btn-default"><i class="fa fa-download"></i> [[admin/advanced/cache:download]]</a>
|
||||||
<!-- IF localCache.dump -->
|
|
||||||
<pre>{localCache.dump}</pre>
|
|
||||||
<!-- ENDIF localCache.dump -->
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ describe('meta', function () {
|
|||||||
var herpUid;
|
var herpUid;
|
||||||
|
|
||||||
before(function (done) {
|
before(function (done) {
|
||||||
Groups.resetCache();
|
Groups.cache.reset();
|
||||||
// Create 3 users: 1 admin, 2 regular
|
// Create 3 users: 1 admin, 2 regular
|
||||||
async.series([
|
async.series([
|
||||||
async.apply(User.create, { username: 'foo', password: 'barbar' }), // admin
|
async.apply(User.create, { username: 'foo', password: 'barbar' }), // admin
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ async function setupMockDefaults() {
|
|||||||
const meta = require('../../src/meta');
|
const meta = require('../../src/meta');
|
||||||
await db.emptydb();
|
await db.emptydb();
|
||||||
|
|
||||||
require('../../src/groups').resetCache();
|
require('../../src/groups').cache.reset();
|
||||||
require('../../src/posts/cache').reset();
|
require('../../src/posts/cache').reset();
|
||||||
require('../../src/cache').reset();
|
require('../../src/cache').reset();
|
||||||
winston.info('test_database flushed');
|
winston.info('test_database flushed');
|
||||||
|
|||||||
Reference in New Issue
Block a user