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
isekai-main
Opliko 2 years ago committed by GitHub
parent 2568986c58
commit a9bbb586fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

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

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

@ -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(); post: require('../../posts/cache'),
} else if (data.name === 'object' && db.objectCache) { object: db.objectCache,
db.objectCache.reset(); group: require('../../groups').cache,
} else if (data.name === 'group') { local: require('../../cache'),
require('../../groups').cache.reset(); };
} else if (data.name === 'local') { caches = await plugins.hooks.fire('filter:admin.cache.get', caches);
require('../../cache').reset(); if (!caches[data.name]) {
return;
} }
caches[data.name].reset();
}; };
SocketCache.toggle = async function (socket, data) { SocketCache.toggle = async function (socket, data) {
const caches = { let 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]) { 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>
</div> <a class="btn btn-sm btn-danger clear" data-name="{@key}"><i class="fa fa-trash"></i></a>
</div>
</div>
<!-- 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> </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>
</div> </div>
{{{end}}}
<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>

Loading…
Cancel
Save