mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 11:05:54 +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
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							bcbc085497
						
					
				
				
					commit
					f1f9b225b0
				
			| @@ -1,11 +1,11 @@ | ||||
| { | ||||
| 	"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", | ||||
| 	"post-cache-size": "Post Cache Size", | ||||
| 	"items-in-cache": "Items in Cache", | ||||
| 	"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 | ||||
|                       percentFull: | ||||
|                         type: number | ||||
|                       avgPostSize: | ||||
|                         type: number | ||||
|                       hits: | ||||
|                         type: string | ||||
|                       misses: | ||||
|                         type: string | ||||
|                       hitRatio: | ||||
|                         type: string | ||||
|                       enabled: | ||||
|                         type: boolean | ||||
|                   groupCache: | ||||
|                     type: object | ||||
|                     properties: | ||||
| @@ -48,6 +48,8 @@ get: | ||||
|                         type: string | ||||
|                       hitRatio: | ||||
|                         type: string | ||||
|                       enabled: | ||||
|                         type: boolean | ||||
|                   localCache: | ||||
|                     type: object | ||||
|                     properties: | ||||
| @@ -59,14 +61,14 @@ get: | ||||
|                         type: number | ||||
|                       percentFull: | ||||
|                         type: number | ||||
|                       dump: | ||||
|                         type: boolean | ||||
|                       hits: | ||||
|                         type: string | ||||
|                       misses: | ||||
|                         type: string | ||||
|                       hitRatio: | ||||
|                         type: string | ||||
|                       enabled: | ||||
|                         type: boolean | ||||
|                   objectCache: | ||||
|                     type: object | ||||
|                     properties: | ||||
| @@ -84,6 +86,8 @@ get: | ||||
|                         type: string | ||||
|                       hitRatio: | ||||
|                         type: string | ||||
|                       enabled: | ||||
|                         type: boolean | ||||
|                 required: | ||||
|                   - postCache | ||||
|                   - groupCache | ||||
|   | ||||
| @@ -15,6 +15,16 @@ define('admin/advanced/cache', function () { | ||||
| 				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; | ||||
| }); | ||||
|   | ||||
							
								
								
									
										53
									
								
								src/cache.js
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								src/cache.js
									
									
									
									
									
								
							| @@ -1,56 +1,9 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| const LRU = require('lru-cache'); | ||||
| const pubsub = require('./pubsub'); | ||||
| const cacheCreate = require('./cacheCreate'); | ||||
|  | ||||
| const cache = new LRU({ | ||||
| module.exports = cacheCreate({ | ||||
| 	name: 'local', | ||||
| 	max: 4000, | ||||
| 	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 localCache = require('../../cache'); | ||||
|  | ||||
| 	let avgPostSize = 0; | ||||
| 	let percentFull = 0; | ||||
| 	if (postCache.itemCount > 0) { | ||||
| 		avgPostSize = parseInt((postCache.length / postCache.itemCount), 10); | ||||
| 		percentFull = ((postCache.length / postCache.max) * 100).toFixed(2); | ||||
| 	} | ||||
|  | ||||
| @@ -23,10 +21,10 @@ cacheController.get = function (req, res) { | ||||
| 			max: postCache.max, | ||||
| 			itemCount: postCache.itemCount, | ||||
| 			percentFull: percentFull, | ||||
| 			avgPostSize: avgPostSize, | ||||
| 			hits: utils.addCommas(String(postCache.hits)), | ||||
| 			misses: utils.addCommas(String(postCache.misses)), | ||||
| 			hitRatio: ((postCache.hits / (postCache.hits + postCache.misses) || 0)).toFixed(4), | ||||
| 			enabled: postCache.enabled, | ||||
| 		}, | ||||
| 		groupCache: { | ||||
| 			length: groupCache.length, | ||||
| @@ -36,16 +34,17 @@ cacheController.get = function (req, res) { | ||||
| 			hits: utils.addCommas(String(groupCache.hits)), | ||||
| 			misses: utils.addCommas(String(groupCache.misses)), | ||||
| 			hitRatio: (groupCache.hits / (groupCache.hits + groupCache.misses)).toFixed(4), | ||||
| 			enabled: groupCache.enabled, | ||||
| 		}, | ||||
| 		localCache: { | ||||
| 			length: localCache.length, | ||||
| 			max: localCache.max, | ||||
| 			itemCount: localCache.itemCount, | ||||
| 			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)), | ||||
| 			misses: utils.addCommas(String(localCache.misses)), | ||||
| 			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)), | ||||
| 			misses: utils.addCommas(String(objectCache.misses)), | ||||
| 			hitRatio: (objectCache.hits / (objectCache.hits + objectCache.misses)).toFixed(4), | ||||
| 			enabled: objectCache.enabled, | ||||
| 		}; | ||||
| 	} | ||||
|  | ||||
| 	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'; | ||||
|  | ||||
| module.exports.create = function (name) { | ||||
| 	var LRU = require('lru-cache'); | ||||
| 	var pubsub = require('../pubsub'); | ||||
|  | ||||
| 	var cache = new LRU({ | ||||
| 	const cacheCreate = require('../cacheCreate'); | ||||
| 	return cacheCreate({ | ||||
| 		name: name + '-object', | ||||
| 		max: 40000, | ||||
| 		length: function () { return 1; }, | ||||
| 		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; | ||||
| 		} | ||||
|  | ||||
| 		cache.delObjectCache(key); | ||||
| 		cache.del(key); | ||||
| 	}; | ||||
|  | ||||
| 	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 }); | ||||
| 		cache.delObjectCache(key); | ||||
| 		cache.del(key); | ||||
| 	}; | ||||
|  | ||||
| 	module.incrObjectField = async function (key, field) { | ||||
| @@ -191,13 +191,13 @@ module.exports = function (module) { | ||||
| 				bulk.find({ _key: key }).upsert().update({ $inc: increment }); | ||||
| 			}); | ||||
| 			await bulk.execute(); | ||||
| 			cache.delObjectCache(key); | ||||
| 			cache.del(key); | ||||
| 			const result = await module.getObjectsFields(key, [field]); | ||||
| 			return result.map(data => data && data[field]); | ||||
| 		} | ||||
|  | ||||
| 		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; | ||||
| 	}; | ||||
| }; | ||||
|   | ||||
| @@ -8,7 +8,7 @@ module.exports = function (module) { | ||||
|  | ||||
| 	module.emptydb = async function () { | ||||
| 		await module.client.collection('objects').deleteMany({}); | ||||
| 		module.objectCache.resetObjectCache(); | ||||
| 		module.objectCache.reset(); | ||||
| 	}; | ||||
|  | ||||
| 	module.exists = async function (key) { | ||||
| @@ -47,7 +47,7 @@ module.exports = function (module) { | ||||
| 			return; | ||||
| 		} | ||||
| 		await module.client.collection('objects').deleteMany({ _key: key }); | ||||
| 		module.objectCache.delObjectCache(key); | ||||
| 		module.objectCache.del(key); | ||||
| 	}; | ||||
|  | ||||
| 	module.deleteAll = async function (keys) { | ||||
| @@ -55,7 +55,7 @@ module.exports = function (module) { | ||||
| 			return; | ||||
| 		} | ||||
| 		await module.client.collection('objects').deleteMany({ _key: { $in: keys } }); | ||||
| 		module.objectCache.delObjectCache(keys); | ||||
| 		module.objectCache.del(keys); | ||||
| 	}; | ||||
|  | ||||
| 	module.get = async function (key) { | ||||
| @@ -96,7 +96,7 @@ module.exports = function (module) { | ||||
|  | ||||
| 	module.rename = async function (oldKey, 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) { | ||||
|   | ||||
| @@ -33,7 +33,7 @@ module.exports = function (module) { | ||||
| 			await module.client.async.hmset(key, data); | ||||
| 		} | ||||
|  | ||||
| 		cache.delObjectCache(key); | ||||
| 		cache.del(key); | ||||
| 	}; | ||||
|  | ||||
| 	module.setObjectField = async function (key, field, value) { | ||||
| @@ -48,7 +48,7 @@ module.exports = function (module) { | ||||
| 			await module.client.async.hset(key, field, value); | ||||
| 		} | ||||
|  | ||||
| 		cache.delObjectCache(key); | ||||
| 		cache.del(key); | ||||
| 	}; | ||||
|  | ||||
| 	module.getObject = async function (key) { | ||||
| @@ -146,7 +146,7 @@ module.exports = function (module) { | ||||
| 			return; | ||||
| 		} | ||||
| 		await module.client.async.hdel(key, field); | ||||
| 		cache.delObjectCache(key); | ||||
| 		cache.del(key); | ||||
| 	}; | ||||
|  | ||||
| 	module.deleteObjectFields = async function (key, fields) { | ||||
| @@ -158,7 +158,7 @@ module.exports = function (module) { | ||||
| 			return; | ||||
| 		} | ||||
| 		await module.client.async.hdel(key, fields); | ||||
| 		cache.delObjectCache(key); | ||||
| 		cache.del(key); | ||||
| 	}; | ||||
|  | ||||
| 	module.incrObjectField = async function (key, field) { | ||||
| @@ -182,7 +182,7 @@ module.exports = function (module) { | ||||
| 		} else { | ||||
| 			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); | ||||
| 	}; | ||||
| }; | ||||
|   | ||||
| @@ -9,7 +9,7 @@ module.exports = function (module) { | ||||
|  | ||||
| 	module.emptydb = async function () { | ||||
| 		await module.flushdb(); | ||||
| 		module.objectCache.resetObjectCache(); | ||||
| 		module.objectCache.reset(); | ||||
| 	}; | ||||
|  | ||||
| 	module.exists = async function (key) { | ||||
| @@ -45,7 +45,7 @@ module.exports = function (module) { | ||||
|  | ||||
| 	module.delete = async function (key) { | ||||
| 		await module.client.async.del(key); | ||||
| 		module.objectCache.delObjectCache(key); | ||||
| 		module.objectCache.del(key); | ||||
| 	}; | ||||
|  | ||||
| 	module.deleteAll = async function (keys) { | ||||
| @@ -53,7 +53,7 @@ module.exports = function (module) { | ||||
| 			return; | ||||
| 		} | ||||
| 		await module.client.async.del(keys); | ||||
| 		module.objectCache.delObjectCache(keys); | ||||
| 		module.objectCache.del(keys); | ||||
| 	}; | ||||
|  | ||||
| 	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) { | ||||
|   | ||||
| @@ -1,48 +1,19 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var LRU = require('lru-cache'); | ||||
| var pubsub = require('../pubsub'); | ||||
|  | ||||
| var cache = new LRU({ | ||||
| 	max: 40000, | ||||
| 	maxAge: 0, | ||||
| }); | ||||
| cache.hits = 0; | ||||
| cache.misses = 0; | ||||
| const cacheCreate = require('../cacheCreate'); | ||||
|  | ||||
| module.exports = function (Groups) { | ||||
| 	Groups.cache = cache; | ||||
|  | ||||
| 	pubsub.on('group:cache:reset', function () { | ||||
| 		localReset(); | ||||
| 	Groups.cache = cacheCreate({ | ||||
| 		name: 'group', | ||||
| 		max: 40000, | ||||
| 		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) { | ||||
| 		if (!Array.isArray(groupNames)) { | ||||
| 			groupNames = [groupNames]; | ||||
| 		} | ||||
| 		pubsub.publish('group:cache:del', { uid: uid, groupNames: groupNames }); | ||||
| 		groupNames.forEach(function (groupName) { | ||||
| 			cache.del(uid + ':' + groupName); | ||||
| 		}); | ||||
| 		const keys = groupNames.map(name => uid + ':' + name); | ||||
| 		Groups.cache.del(keys); | ||||
| 	}; | ||||
| }; | ||||
|   | ||||
| @@ -40,7 +40,7 @@ module.exports = function (Groups) { | ||||
| 			db.deleteObjectFields('groupslug:groupname', fields), | ||||
| 			removeGroupsFromPrivilegeGroups(groupNames), | ||||
| 		]); | ||||
| 		Groups.resetCache(); | ||||
| 		Groups.cache.reset(); | ||||
| 		plugins.fireHook('action:groups.destroy', { groups: groupsData }); | ||||
| 	}; | ||||
|  | ||||
|   | ||||
| @@ -31,10 +31,8 @@ module.exports = function (Groups) { | ||||
| 		const cacheKey = uid + ':' + groupName; | ||||
| 		let isMember = Groups.cache.get(cacheKey); | ||||
| 		if (isMember !== undefined) { | ||||
| 			Groups.cache.hits += 1; | ||||
| 			return isMember; | ||||
| 		} | ||||
| 		Groups.cache.misses += 1; | ||||
| 		isMember = await db.isSortedSetMember('group:' + groupName + ':members', uid); | ||||
| 		Groups.cache.set(cacheKey, isMember); | ||||
| 		return isMember; | ||||
| @@ -88,10 +86,7 @@ module.exports = function (Groups) { | ||||
| 		const isMember = Groups.cache.get(uid + ':' + groupName); | ||||
| 		const isInCache = isMember !== undefined; | ||||
| 		if (isInCache) { | ||||
| 			Groups.cache.hits += 1; | ||||
| 			cachedData[uid + ':' + groupName] = isMember; | ||||
| 		} else { | ||||
| 			Groups.cache.misses += 1; | ||||
| 		} | ||||
| 		return !isInCache; | ||||
| 	} | ||||
|   | ||||
| @@ -197,7 +197,7 @@ module.exports = function (Groups) { | ||||
| 			old: oldName, | ||||
| 			new: newName, | ||||
| 		}); | ||||
| 		Groups.resetCache(); | ||||
| 		Groups.cache.reset(); | ||||
| 	}; | ||||
|  | ||||
| 	async function updateMemberGroupTitles(oldName, newName) { | ||||
|   | ||||
| @@ -1,14 +1,12 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var LRU = require('lru-cache'); | ||||
| var meta = require('../meta'); | ||||
| const cacheCreate = require('../cacheCreate'); | ||||
| const meta = require('../meta'); | ||||
|  | ||||
| var cache = new LRU({ | ||||
| module.exports = cacheCreate({ | ||||
| 	name: 'post', | ||||
| 	max: meta.config.postCacheSize, | ||||
| 	length: function (n) { return n.length; }, | ||||
| 	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); | ||||
| 		if (postData.pid && cachedContent !== undefined) { | ||||
| 			postData.content = cachedContent; | ||||
| 			cache.hits += 1; | ||||
| 			return postData; | ||||
| 		} | ||||
| 		cache.misses += 1; | ||||
|  | ||||
| 		const data = await plugins.fireHook('filter:parse.post', { postData: postData }); | ||||
| 		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); | ||||
| 		} | ||||
| 		return data.postData; | ||||
|   | ||||
| @@ -41,6 +41,9 @@ helpers.isUserAllowedTo = async function (privilege, uid, cid) { | ||||
| }; | ||||
|  | ||||
| async function isUserAllowedToCids(privilege, uid, cids) { | ||||
| 	if (!privilege) { | ||||
| 		return cids.map(() => false); | ||||
| 	} | ||||
| 	if (parseInt(uid, 10) <= 0) { | ||||
| 		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/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/advanced/cache/dump', middleware.authenticate, helpers.tryRoute(controllers.admin.cache.dump)); | ||||
|  | ||||
| 	const multipart = require('connect-multiparty'); | ||||
| 	const multipartMiddleware = multipart(); | ||||
|   | ||||
| @@ -8,3 +8,16 @@ SocketCache.clear = async function () { | ||||
| 	require('../../groups').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-heading"><i class="fa fa-calendar-o"></i> [[admin/advanced/cache:post-cache]]</div> | ||||
| 					<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/> | ||||
|  | ||||
| 						<div class="progress"> | ||||
| @@ -21,16 +27,11 @@ | ||||
|  | ||||
| 						<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"> | ||||
| 							<label for="postCacheSize">[[admin/advanced/cache:post-cache-size]]</label> | ||||
| 							<input id="postCacheSize" type="text" class="form-control" value="" data-field="postCacheSize"> | ||||
| 						</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> | ||||
| @@ -40,7 +41,12 @@ | ||||
| 				<div class="panel panel-default"> | ||||
| 					<div class="panel-heading"><i class="fa fa-calendar-o"></i> Object Cache</div> | ||||
| 					<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/> | ||||
| 						<div class="progress"> | ||||
| 							<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>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-default"><i class="fa fa-download"></i> [[admin/advanced/cache:download]]</a> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| @@ -59,8 +66,12 @@ | ||||
| 				<div class="panel panel-default"> | ||||
| 					<div class="panel-heading"><i class="fa fa-calendar-o"></i> Group Cache</div> | ||||
| 					<div class="panel-body"> | ||||
|  | ||||
| 						<label>[[admin/advanced/cache:length-to-max]]</label><br/> | ||||
| 						<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}}}> | ||||
| 								<span class="mdl-switch__label"><strong>[[admin/advanced/cache:enabled]]</strong></span> | ||||
| 							</label> | ||||
| 						</div> | ||||
| 						<span>{groupCache.length} / {groupCache.max}</span><br/> | ||||
|  | ||||
| 						<div class="progress"> | ||||
| @@ -72,6 +83,7 @@ | ||||
| 						<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-default"><i class="fa fa-download"></i> [[admin/advanced/cache:download]]</a> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| @@ -79,8 +91,12 @@ | ||||
| 				<div class="panel panel-default"> | ||||
| 					<div class="panel-heading"><i class="fa fa-calendar-o"></i> Local Cache</div> | ||||
| 					<div class="panel-body"> | ||||
|  | ||||
| 						<label>[[admin/advanced/cache:length-to-max]]</label><br/> | ||||
| 						<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}}}> | ||||
| 								<span class="mdl-switch__label"><strong>[[admin/advanced/cache:enabled]]</strong></span> | ||||
| 							</label> | ||||
| 						</div> | ||||
| 						<span>{localCache.length} / {localCache.max}</span><br/> | ||||
|  | ||||
| 						<div class="progress"> | ||||
| @@ -92,11 +108,7 @@ | ||||
| 						<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/> | ||||
|  | ||||
| 						<!-- IF localCache.dump --> | ||||
| 						<pre>{localCache.dump}</pre> | ||||
| 						<!-- ENDIF localCache.dump --> | ||||
|  | ||||
| 						<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> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
|   | ||||
| @@ -16,7 +16,7 @@ describe('meta', function () { | ||||
| 	var herpUid; | ||||
|  | ||||
| 	before(function (done) { | ||||
| 		Groups.resetCache(); | ||||
| 		Groups.cache.reset(); | ||||
| 		// Create 3 users: 1 admin, 2 regular | ||||
| 		async.series([ | ||||
| 			async.apply(User.create, { username: 'foo', password: 'barbar' }),	// admin | ||||
|   | ||||
| @@ -168,7 +168,7 @@ async function setupMockDefaults() { | ||||
| 	const meta = require('../../src/meta'); | ||||
| 	await db.emptydb(); | ||||
|  | ||||
| 	require('../../src/groups').resetCache(); | ||||
| 	require('../../src/groups').cache.reset(); | ||||
| 	require('../../src/posts/cache').reset(); | ||||
| 	require('../../src/cache').reset(); | ||||
| 	winston.info('test_database flushed'); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user