mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 16:46:12 +01:00
feat: allow plugins to add to admin cache list (#10833)
* feat: allow plugins to add to admin cache list resolves #10820 plugins will have to use `filter:admin.cache.get` hook and just add their own cache to the object there. * feat: add dump to ttlCache and expose properties * feat: also expose properties under their current names * feat: display TTL if set
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"post-cache": "Post Cache",
|
"post-cache": "Post Cache",
|
||||||
|
"group-cache": "Group Cache",
|
||||||
|
"local-cache": "Local Cache",
|
||||||
|
"object-cache": "Object Cache",
|
||||||
"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"
|
||||||
|
|||||||
5
src/cache/lru.js
vendored
5
src/cache/lru.js
vendored
@@ -37,12 +37,15 @@ module.exports = function (opts) {
|
|||||||
cache.enabled = opts.hasOwnProperty('enabled') ? opts.enabled : true;
|
cache.enabled = opts.hasOwnProperty('enabled') ? opts.enabled : true;
|
||||||
const cacheSet = lruCache.set;
|
const cacheSet = lruCache.set;
|
||||||
|
|
||||||
// backwards compatibility
|
// expose properties while keeping backwards compatibility
|
||||||
const propertyMap = new Map([
|
const propertyMap = new Map([
|
||||||
['length', 'calculatedSize'],
|
['length', 'calculatedSize'],
|
||||||
|
['calculatedSize', 'calculatedSize'],
|
||||||
['max', 'max'],
|
['max', 'max'],
|
||||||
['maxSize', 'maxSize'],
|
['maxSize', 'maxSize'],
|
||||||
['itemCount', 'size'],
|
['itemCount', 'size'],
|
||||||
|
['size', 'size'],
|
||||||
|
['ttl', 'ttl'],
|
||||||
]);
|
]);
|
||||||
propertyMap.forEach((lruProp, cacheProp) => {
|
propertyMap.forEach((lruProp, cacheProp) => {
|
||||||
Object.defineProperty(cache, cacheProp, {
|
Object.defineProperty(cache, cacheProp, {
|
||||||
|
|||||||
25
src/cache/ttl.js
vendored
25
src/cache/ttl.js
vendored
@@ -13,6 +13,23 @@ module.exports = function (opts) {
|
|||||||
cache.enabled = opts.hasOwnProperty('enabled') ? opts.enabled : true;
|
cache.enabled = opts.hasOwnProperty('enabled') ? opts.enabled : true;
|
||||||
const cacheSet = ttlCache.set;
|
const cacheSet = ttlCache.set;
|
||||||
|
|
||||||
|
// expose properties
|
||||||
|
const propertyMap = new Map([
|
||||||
|
['max', 'max'],
|
||||||
|
['itemCount', 'size'],
|
||||||
|
['size', 'size'],
|
||||||
|
['ttl', 'ttl'],
|
||||||
|
]);
|
||||||
|
propertyMap.forEach((ttlProp, cacheProp) => {
|
||||||
|
Object.defineProperty(cache, cacheProp, {
|
||||||
|
get: function () {
|
||||||
|
return ttlCache[ttlProp];
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
cache.set = function (key, value, ttl) {
|
cache.set = function (key, value, ttl) {
|
||||||
if (!cache.enabled) {
|
if (!cache.enabled) {
|
||||||
return;
|
return;
|
||||||
@@ -90,5 +107,13 @@ module.exports = function (opts) {
|
|||||||
return unCachedKeys;
|
return unCachedKeys;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
cache.dump = function () {
|
||||||
|
return Array.from(ttlCache.entries());
|
||||||
|
};
|
||||||
|
|
||||||
|
cache.peek = function (key) {
|
||||||
|
return ttlCache.get(key, { updateAgeOnGet: false });
|
||||||
|
};
|
||||||
|
|
||||||
return cache;
|
return cache;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,8 +3,9 @@
|
|||||||
const cacheController = module.exports;
|
const cacheController = module.exports;
|
||||||
|
|
||||||
const utils = require('../../utils');
|
const utils = require('../../utils');
|
||||||
|
const plugins = require('../../plugins');
|
||||||
|
|
||||||
cacheController.get = function (req, res) {
|
cacheController.get = async function (req, res) {
|
||||||
const postCache = require('../../posts/cache');
|
const postCache = require('../../posts/cache');
|
||||||
const groupCache = require('../../groups').cache;
|
const groupCache = require('../../groups').cache;
|
||||||
const { objectCache } = require('../../database');
|
const { objectCache } = require('../../database');
|
||||||
@@ -23,29 +24,33 @@ cacheController.get = function (req, res) {
|
|||||||
misses: utils.addCommas(String(cache.misses)),
|
misses: utils.addCommas(String(cache.misses)),
|
||||||
hitRatio: ((cache.hits / (cache.hits + cache.misses) || 0)).toFixed(4),
|
hitRatio: ((cache.hits / (cache.hits + cache.misses) || 0)).toFixed(4),
|
||||||
enabled: cache.enabled,
|
enabled: cache.enabled,
|
||||||
|
ttl: cache.ttl,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
let caches = {
|
||||||
const data = {
|
post: postCache,
|
||||||
postCache: getInfo(postCache),
|
group: groupCache,
|
||||||
groupCache: getInfo(groupCache),
|
local: localCache,
|
||||||
localCache: getInfo(localCache),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (objectCache) {
|
if (objectCache) {
|
||||||
data.objectCache = getInfo(objectCache);
|
caches.object = objectCache;
|
||||||
|
}
|
||||||
|
caches = await plugins.hooks.fire('filter:admin.cache.get', caches);
|
||||||
|
for (const [key, value] of Object.entries(caches)) {
|
||||||
|
caches[key] = getInfo(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.render('admin/advanced/cache', data);
|
res.render('admin/advanced/cache', { caches });
|
||||||
};
|
};
|
||||||
|
|
||||||
cacheController.dump = function (req, res, next) {
|
cacheController.dump = async function (req, res, next) {
|
||||||
const caches = {
|
let caches = {
|
||||||
post: require('../../posts/cache'),
|
post: require('../../posts/cache'),
|
||||||
object: require('../../database').objectCache,
|
object: require('../../database').objectCache,
|
||||||
group: require('../../groups').cache,
|
group: require('../../groups').cache,
|
||||||
local: require('../../cache'),
|
local: require('../../cache'),
|
||||||
};
|
};
|
||||||
|
caches = await plugins.hooks.fire('filter:admin.cache.get', caches);
|
||||||
if (!caches[req.query.name]) {
|
if (!caches[req.query.name]) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,26 +3,30 @@
|
|||||||
const SocketCache = module.exports;
|
const SocketCache = module.exports;
|
||||||
|
|
||||||
const db = require('../../database');
|
const db = require('../../database');
|
||||||
|
const plugins = require('../../plugins');
|
||||||
|
|
||||||
SocketCache.clear = async function (socket, data) {
|
SocketCache.clear = async function (socket, data) {
|
||||||
if (data.name === 'post') {
|
let caches = {
|
||||||
require('../../posts/cache').reset();
|
|
||||||
} else if (data.name === 'object' && db.objectCache) {
|
|
||||||
db.objectCache.reset();
|
|
||||||
} else if (data.name === 'group') {
|
|
||||||
require('../../groups').cache.reset();
|
|
||||||
} else if (data.name === 'local') {
|
|
||||||
require('../../cache').reset();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
SocketCache.toggle = async function (socket, data) {
|
|
||||||
const caches = {
|
|
||||||
post: require('../../posts/cache'),
|
post: require('../../posts/cache'),
|
||||||
object: db.objectCache,
|
object: db.objectCache,
|
||||||
group: require('../../groups').cache,
|
group: require('../../groups').cache,
|
||||||
local: require('../../cache'),
|
local: require('../../cache'),
|
||||||
};
|
};
|
||||||
|
caches = await plugins.hooks.fire('filter:admin.cache.get', caches);
|
||||||
|
if (!caches[data.name]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
caches[data.name].reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
SocketCache.toggle = async function (socket, data) {
|
||||||
|
let caches = {
|
||||||
|
post: require('../../posts/cache'),
|
||||||
|
object: db.objectCache,
|
||||||
|
group: require('../../groups').cache,
|
||||||
|
local: require('../../cache'),
|
||||||
|
};
|
||||||
|
caches = await plugins.hooks.fire('filter:admin.cache.get', caches);
|
||||||
if (!caches[data.name]) {
|
if (!caches[data.name]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,118 +2,43 @@
|
|||||||
<div class="row post-cache">
|
<div class="row post-cache">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
{{{each caches}}}
|
||||||
<div class="col-lg-3">
|
<div class="col-lg-3">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">[[admin/advanced/cache:post-cache]]</div>
|
<div class="panel-heading">[[admin/advanced/cache:{@key}-cache]]</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="checkbox" data-name="post">
|
<div class="checkbox" data-name="{@key}">
|
||||||
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||||
<input class="mdl-switch__input" type="checkbox" {{{if postCache.enabled}}}checked{{{end}}}>
|
<input class="mdl-switch__input" type="checkbox" {{{if caches.enabled}}}checked{{{end}}}>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span>{postCache.length} / {postCache.maxSize}</span><br/>
|
<span>{{{if ../length}}}{../length}{{{else}}}{../itemCount}{{{end}}} / {{{if ../max}}}{../max}{{{else}}}{../maxSize}{{{end}}}</span><br/>
|
||||||
|
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar" role="progressbar" aria-valuenow="{postCache.percentFull}" aria-valuemin="0" aria-valuemax="100" style="width: {postCache.percentFull}%;">
|
<div class="progress-bar" role="progressbar" aria-valuenow="{../percentFull}" aria-valuemin="0" aria-valuemax="100" style="width: {../percentFull}%;">
|
||||||
[[admin/advanced/cache:percent-full, {postCache.percentFull}]]
|
[[admin/advanced/cache:percent-full, {../percentFull}]]
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label>Hits:</label> <span>{postCache.hits}</span><br/>
|
<label>Hits:</label> <span>{../hits}</span><br/>
|
||||||
<label>Misses:</label> <span>{postCache.misses}</span><br/>
|
<label>Misses:</label> <span>{../misses}</span><br/>
|
||||||
<label>Hit Ratio:</label> <span>{postCache.hitRatio}</span><br/>
|
<label>Hit Ratio:</label> <span>{../hitRatio}</span><br/>
|
||||||
|
{{{if ../ttl}}}<label>TTL:</label> <span>{../ttl}</span></br>{{{end}}}
|
||||||
|
{{{if (@key == "post")}}}
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
<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-sm btn-default"><i class="fa fa-download"></i></a>
|
{{{end}}}
|
||||||
<a class="btn btn-sm btn-danger clear" data-name="post"><i class="fa fa-trash"></i></a>
|
<a href="{config.relative_path}/api/admin/advanced/cache/dump?name={@key}" class="btn btn-sm btn-default"><i class="fa fa-download"></i></a>
|
||||||
|
<a class="btn btn-sm btn-danger clear" data-name="{@key}"><i class="fa fa-trash"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{{end}}}
|
||||||
|
|
||||||
<!-- IF objectCache -->
|
|
||||||
<div class="col-lg-3">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">Object Cache</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<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}}}>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<span>{objectCache.itemCount} / {objectCache.max}</span><br/>
|
|
||||||
<div class="progress">
|
|
||||||
<div class="progress-bar" role="progressbar" aria-valuenow="{objectCache.percentFull}" aria-valuemin="0" aria-valuemax="100" style="width: {objectCache.percentFull}%;">
|
|
||||||
[[admin/advanced/cache:percent-full, {objectCache.percentFull}]]
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label>Hits:</label> <span>{objectCache.hits}</span><br/>
|
|
||||||
<label>Misses:</label> <span>{objectCache.misses}</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-sm btn-default"><i class="fa fa-download"></i></a>
|
|
||||||
<a class="btn btn-sm btn-danger clear" data-name="object"><i class="fa fa-trash"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- ENDIF objectCache -->
|
|
||||||
|
|
||||||
<div class="col-lg-3">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">Group Cache</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="checkbox" data-name="group">
|
|
||||||
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
|
||||||
<input class="mdl-switch__input" type="checkbox" {{{if groupCache.enabled}}}checked{{{end}}}>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<span>{groupCache.itemCount} / {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}%;">
|
|
||||||
[[admin/advanced/cache:percent-full, {groupCache.percentFull}]]
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label>Hits:</label> <span>{groupCache.hits}</span><br/>
|
|
||||||
<label>Misses:</label> <span>{groupCache.misses}</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-sm btn-default"><i class="fa fa-download"></i></a>
|
|
||||||
<a class="btn btn-sm btn-danger clear" data-name="group"><i class="fa fa-trash"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-lg-3">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">Local Cache</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="checkbox" data-name="local">
|
|
||||||
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
|
||||||
<input class="mdl-switch__input" type="checkbox" {{{if localCache.enabled}}}checked{{{end}}}>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<span>{localCache.itemCount} / {localCache.max}</span><br/>
|
|
||||||
|
|
||||||
<div class="progress">
|
|
||||||
<div class="progress-bar" role="progressbar" aria-valuenow="{localCache.percentFull}" aria-valuemin="0" aria-valuemax="100" style="width: {localCache.percentFull}%;">
|
|
||||||
[[admin/advanced/cache:percent-full, {localCache.percentFull}]]
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label>Hits:</label> <span>{localCache.hits}</span><br/>
|
|
||||||
<label>Misses:</label> <span>{localCache.misses}</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-sm btn-default"><i class="fa fa-download"></i></a>
|
|
||||||
<a class="btn btn-sm btn-danger clear" data-name="local"><i class="fa fa-trash"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user