diff --git a/install/package.json b/install/package.json index d27a13a5ee..0a62a66e88 100644 --- a/install/package.json +++ b/install/package.json @@ -77,7 +77,7 @@ "less": "4.1.3", "lodash": "4.17.21", "logrotate-stream": "0.2.8", - "lru-cache": "7.13.1", + "lru-cache": "6.0.0", "material-design-lite": "1.3.0", "mime": "3.0.0", "mkdirp": "1.0.4", @@ -92,7 +92,7 @@ "nodebb-plugin-dbsearch": "5.1.5", "nodebb-plugin-emoji": "4.0.4", "nodebb-plugin-emoji-android": "3.0.0", - "nodebb-plugin-markdown": "10.1.0", + "nodebb-plugin-markdown": "10.0.0", "nodebb-plugin-mentions": "3.0.11", "nodebb-plugin-spam-be-gone": "1.0.0", "nodebb-rewards-essentials": "0.2.1", @@ -186,4 +186,4 @@ "url": "https://github.com/barisusakli" } ] -} +} \ No newline at end of file diff --git a/src/analytics.js b/src/analytics.js index f9e14b4caf..5d71d161a8 100644 --- a/src/analytics.js +++ b/src/analytics.js @@ -4,6 +4,7 @@ const cronJob = require('cron').CronJob; const winston = require('winston'); const nconf = require('nconf'); const crypto = require('crypto'); +const LRU = require('lru-cache'); const util = require('util'); const _ = require('lodash'); @@ -14,7 +15,6 @@ const utils = require('./utils'); const plugins = require('./plugins'); const meta = require('./meta'); const pubsub = require('./pubsub'); -const cacheCreate = require('./cacheCreate'); const Analytics = module.exports; @@ -37,9 +37,10 @@ let ipCache; const runJobs = nconf.get('runJobs'); Analytics.init = async function () { - ipCache = cacheCreate({ + ipCache = new LRU({ max: parseInt(meta.config['analytics:maxCache'], 10) || 500, - ttl: 0, + length: function () { return 1; }, + maxAge: 0, }); new cronJob('*/10 * * * * *', (async () => { diff --git a/src/cache.js b/src/cache.js index 6dcc440536..7bb27961ce 100644 --- a/src/cache.js +++ b/src/cache.js @@ -5,5 +5,5 @@ const cacheCreate = require('./cacheCreate'); module.exports = cacheCreate({ name: 'local', max: 40000, - ttl: 0, + maxAge: 0, }); diff --git a/src/cacheCreate.js b/src/cacheCreate.js index deaef9bba4..e3d7342165 100644 --- a/src/cacheCreate.js +++ b/src/cacheCreate.js @@ -4,72 +4,30 @@ module.exports = function (opts) { const LRU = require('lru-cache'); const pubsub = require('./pubsub'); - // lru-cache@7 deprecations - const winston = require('winston'); - const chalk = require('chalk'); + const cache = new LRU(opts); - // sometimes we kept passing in `length` with no corresponding `maxSize`. - // This is now enforced in v7; drop superfluous property - if (opts.hasOwnProperty('length') && !opts.hasOwnProperty('maxSize')) { - winston.warn(`[cache/init(${opts.name})] ${chalk.white.bgRed.bold('DEPRECATION')} ${chalk.yellow('length')} was passed in without a corresponding ${chalk.yellow('maxSize')}. Both are now required as of lru-cache@7.0.0.`); - delete opts.length; - } - - const deprecations = new Map([ - ['stale', 'allowStale'], - ['maxAge', 'ttl'], - ['length', 'sizeCalculation'], - ]); - deprecations.forEach((newProp, oldProp) => { - if (opts.hasOwnProperty(oldProp) && !opts.hasOwnProperty(newProp)) { - winston.warn(`[cache/init (${opts.name})] ${chalk.white.bgRed.bold('DEPRECATION')} The option ${chalk.yellow(oldProp)} has been deprecated as of lru-cache@7.0.0. Please change this to ${chalk.yellow(newProp)} instead.`); - opts[newProp] = opts[oldProp]; - delete opts[oldProp]; - } - }); - - const lruCache = new LRU(opts); - - const cache = {}; cache.name = opts.name; cache.hits = 0; cache.misses = 0; cache.enabled = opts.hasOwnProperty('enabled') ? opts.enabled : true; - const cacheSet = lruCache.set; - // backwards compatibility - const propertyMap = new Map([ - ['length', 'calculatedSize'], - ['max', 'max'], - ['maxSize', 'maxSize'], - ['itemCount', 'size'], - ]); - propertyMap.forEach((lruProp, cacheProp) => { - Object.defineProperty(cache, cacheProp, { - get: function () { - return lruCache[lruProp]; - }, - configurable: true, - enumerable: true, - }); - }); + const cacheSet = cache.set; + const cacheGet = cache.get; + const cacheDel = cache.del; + const cacheReset = cache.reset; - cache.set = function (key, value, ttl) { + cache.set = function (key, value, maxAge) { if (!cache.enabled) { return; } - const opts = {}; - if (ttl) { - opts.ttl = ttl; - } - cacheSet.apply(lruCache, [key, value, opts]); + cacheSet.apply(cache, [key, value, maxAge]); }; cache.get = function (key) { if (!cache.enabled) { return undefined; } - const data = lruCache.get(key); + const data = cacheGet.apply(cache, [key]); if (data === undefined) { cache.misses += 1; } else { @@ -83,18 +41,16 @@ module.exports = function (opts) { keys = [keys]; } pubsub.publish(`${cache.name}:cache:del`, keys); - keys.forEach(key => lruCache.delete(key)); + keys.forEach(key => cacheDel.apply(cache, [key])); }; - cache.delete = cache.del; cache.reset = function () { pubsub.publish(`${cache.name}:cache:reset`); localReset(); }; - cache.clear = cache.reset; function localReset() { - lruCache.clear(); + cacheReset.apply(cache); cache.hits = 0; cache.misses = 0; } @@ -105,7 +61,7 @@ module.exports = function (opts) { pubsub.on(`${cache.name}:cache:del`, (keys) => { if (Array.isArray(keys)) { - keys.forEach(key => lruCache.delete(key)); + keys.forEach(key => cacheDel.apply(cache, [key])); } }); @@ -131,13 +87,5 @@ module.exports = function (opts) { return unCachedKeys; }; - cache.dump = function () { - return lruCache.dump(); - }; - - cache.peek = function (key) { - return lruCache.peek(key); - }; - return cache; }; diff --git a/src/controllers/admin/cache.js b/src/controllers/admin/cache.js index 67c9864ae5..430cc28b8a 100644 --- a/src/controllers/admin/cache.js +++ b/src/controllers/admin/cache.js @@ -14,11 +14,8 @@ cacheController.get = function (req, res) { return { length: cache.length, max: cache.max, - maxSize: cache.maxSize, itemCount: cache.itemCount, - percentFull: cache.name === 'post' ? - ((cache.length / cache.maxSize) * 100).toFixed(2) : - ((cache.itemCount / cache.max) * 100).toFixed(2), + percentFull: ((cache.length / cache.max) * 100).toFixed(2), hits: utils.addCommas(String(cache.hits)), misses: utils.addCommas(String(cache.misses)), hitRatio: ((cache.hits / (cache.hits + cache.misses) || 0)).toFixed(4), diff --git a/src/database/cache.js b/src/database/cache.js index 999c860363..069b181a80 100644 --- a/src/database/cache.js +++ b/src/database/cache.js @@ -5,6 +5,7 @@ module.exports.create = function (name) { return cacheCreate({ name: `${name}-object`, max: 40000, - ttl: 0, + length: function () { return 1; }, + maxAge: 0, }); }; diff --git a/src/groups/cache.js b/src/groups/cache.js index b83e3e5928..0acea646d6 100644 --- a/src/groups/cache.js +++ b/src/groups/cache.js @@ -6,7 +6,7 @@ module.exports = function (Groups) { Groups.cache = cacheCreate({ name: 'group', max: 40000, - ttl: 0, + maxAge: 0, }); Groups.clearCache = function (uid, groupNames) { diff --git a/src/middleware/index.js b/src/middleware/index.js index 6930021aee..a31cc4430d 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -6,6 +6,7 @@ const csrf = require('csurf'); const validator = require('validator'); const nconf = require('nconf'); const toobusy = require('toobusy-js'); +const LRU = require('lru-cache'); const util = require('util'); const plugins = require('../plugins'); @@ -14,7 +15,6 @@ const user = require('../user'); const groups = require('../groups'); const analytics = require('../analytics'); const privileges = require('../privileges'); -const cacheCreate = require('../cacheCreate'); const helpers = require('./helpers'); const controllers = { @@ -22,8 +22,8 @@ const controllers = { helpers: require('../controllers/helpers'), }; -const delayCache = cacheCreate({ - ttl: 1000 * 60, +const delayCache = new LRU({ + maxAge: 1000 * 60, }); const middleware = module.exports; diff --git a/src/middleware/uploads.js b/src/middleware/uploads.js index beb36c03e0..941b9c27bc 100644 --- a/src/middleware/uploads.js +++ b/src/middleware/uploads.js @@ -1,16 +1,16 @@ 'use strict'; -const cacheCreate = require('../cacheCreate'); +const LRU = require('lru-cache'); const meta = require('../meta'); const helpers = require('./helpers'); const user = require('../user'); -const cache = cacheCreate({ - ttl: meta.config.uploadRateLimitCooldown * 1000, +const cache = new LRU({ + maxAge: meta.config.uploadRateLimitCooldown * 1000, }); exports.clearCache = function () { - cache.clear(); + cache.reset(); }; exports.ratelimit = helpers.try(async (req, res, next) => { @@ -23,6 +23,7 @@ exports.ratelimit = helpers.try(async (req, res, next) => { if (count > meta.config.uploadRateLimitThreshold) { return next(new Error(['[[error:upload-ratelimit-reached]]'])); } + cache.set(`${req.ip}:uploaded_file_count`, count); next(); }); diff --git a/src/posts/cache.js b/src/posts/cache.js index 246074697c..061c46d88d 100644 --- a/src/posts/cache.js +++ b/src/posts/cache.js @@ -5,8 +5,8 @@ const meta = require('../meta'); module.exports = cacheCreate({ name: 'post', - maxSize: meta.config.postCacheSize, - sizeCalculation: function (n) { return n.length; }, - ttl: 0, + max: meta.config.postCacheSize, + length: function (n) { return n.length; }, + maxAge: 0, enabled: global.env === 'production', }); diff --git a/src/user/blocks.js b/src/user/blocks.js index f71589911c..6655965f2a 100644 --- a/src/user/blocks.js +++ b/src/user/blocks.js @@ -9,7 +9,8 @@ module.exports = function (User) { _cache: cacheCreate({ name: 'user:blocks', max: 100, - ttl: 0, + length: function () { return 1; }, + maxAge: 0, }), }; diff --git a/src/views/admin/advanced/cache.tpl b/src/views/admin/advanced/cache.tpl index 3ac75a0d38..8de6734da0 100644 --- a/src/views/admin/advanced/cache.tpl +++ b/src/views/admin/advanced/cache.tpl @@ -12,7 +12,7 @@ - {postCache.length} / {postCache.maxSize}
+ {postCache.length} / {postCache.max}
@@ -46,7 +46,7 @@
- {objectCache.itemCount} / {objectCache.max}
+ {objectCache.length} / {objectCache.max}
[[admin/advanced/cache:percent-full, {objectCache.percentFull}]] @@ -72,7 +72,7 @@
- {groupCache.itemCount} / {groupCache.max}
+ {groupCache.length} / {groupCache.max}
@@ -98,7 +98,7 @@
- {localCache.itemCount} / {localCache.max}
+ {localCache.length} / {localCache.max}
diff --git a/test/middleware.js b/test/middleware.js index 47567717d1..cdb2797360 100644 --- a/test/middleware.js +++ b/test/middleware.js @@ -4,7 +4,7 @@ const assert = require('assert'); const nconf = require('nconf'); const request = require('request-promise-native'); const db = require('./mocks/databasemock'); - +const middleware = require('../src/middleware'); const user = require('../src/user'); const groups = require('../src/groups'); const utils = require('../src/utils'); @@ -21,7 +21,6 @@ describe('Middlewares', () => { }); it('should expose res.locals.isAdmin = false', (done) => { - const middleware = require('../src/middleware'); const resMock = { locals: {} }; middleware.exposeAdmin({}, resMock, () => { assert.strictEqual(resMock.locals.isAdmin, false); @@ -30,7 +29,6 @@ describe('Middlewares', () => { }); it('should expose res.locals.isAdmin = true', (done) => { - const middleware = require('../src/middleware'); const reqMock = { user: { uid: adminUid } }; const resMock = { locals: {} }; middleware.exposeAdmin(reqMock, resMock, () => { @@ -40,7 +38,6 @@ describe('Middlewares', () => { }); it('should expose privileges in res.locals.privileges and isSelf=true', (done) => { - const middleware = require('../src/middleware'); const reqMock = { user: { uid: adminUid }, params: { uid: adminUid } }; const resMock = { locals: {} }; middleware.exposePrivileges(reqMock, resMock, () => { @@ -54,7 +51,6 @@ describe('Middlewares', () => { }); it('should expose privileges in res.locals.privileges and isSelf=false', (done) => { - const middleware = require('../src/middleware'); const reqMock = { user: { uid: 0 }, params: { uid: adminUid } }; const resMock = { locals: {} }; middleware.exposePrivileges(reqMock, resMock, () => { @@ -68,7 +64,6 @@ describe('Middlewares', () => { }); it('should expose privilege set', (done) => { - const middleware = require('../src/middleware'); const reqMock = { user: { uid: adminUid } }; const resMock = { locals: {} }; middleware.exposePrivilegeSet(reqMock, resMock, () => { diff --git a/test/mocks/databasemock.js b/test/mocks/databasemock.js index d22e4c01b0..d9fd9ac647 100644 --- a/test/mocks/databasemock.js +++ b/test/mocks/databasemock.js @@ -184,21 +184,20 @@ async function setupMockDefaults() { const meta = require('../../src/meta'); await db.emptydb(); + require('../../src/groups').cache.reset(); + require('../../src/posts/cache').reset(); + require('../../src/cache').reset(); + require('../../src/middleware/uploads').clearCache(); + winston.info('test_database flushed'); await setupDefaultConfigs(meta); - + await giveDefaultGlobalPrivileges(); await meta.configs.init(); meta.config.postDelay = 0; meta.config.initialPostDelay = 0; meta.config.newbiePostDelay = 0; meta.config.autoDetectLang = 0; - require('../../src/groups').cache.reset(); - require('../../src/posts/cache').reset(); - require('../../src/cache').reset(); - require('../../src/middleware/uploads').clearCache(); - // privileges must be given after cache reset - await giveDefaultGlobalPrivileges(); await enableDefaultPlugins(); await meta.themes.set({ diff --git a/test/uploads.js b/test/uploads.js index 8e851d153d..2d57ea4535 100644 --- a/test/uploads.js +++ b/test/uploads.js @@ -22,6 +22,7 @@ const helpers = require('./helpers'); const file = require('../src/file'); const image = require('../src/image'); +const uploadFile = util.promisify(helpers.uploadFile); const emptyUploadsFolder = async () => { const files = await fs.readdir(`${nconf.get('upload_path')}/files`); await Promise.all(files.map(async (filename) => { @@ -516,7 +517,7 @@ describe('Upload Controllers', () => { describe('.getOrphans()', () => { before(async () => { const { jar, csrf_token } = await helpers.loginUser('regular', 'zugzug'); - await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token); + await uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token); }); it('should return files with no post associated with them', async () => { @@ -537,7 +538,7 @@ describe('Upload Controllers', () => { before(async () => { const { jar, csrf_token } = await helpers.loginUser('regular', 'zugzug'); - await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token); + await uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token); // modify all files in uploads folder to be 30 days old const files = await fs.readdir(`${nconf.get('upload_path')}/files`);