Use apicache instead of our broken implementation
authorChocobozzz <me@florianbigard.com>
Thu, 25 Apr 2019 09:27:13 +0000 (11:27 +0200)
committerChocobozzz <me@florianbigard.com>
Thu, 25 Apr 2019 09:27:13 +0000 (11:27 +0200)
package.json
server/lib/job-queue/job-queue.ts
server/lib/redis.ts
server/middlewares/cache.ts
yarn.lock

index 41a040c59724862ad7c430ea8b8d418ebc039989..1630706eb8f34a92d80859c70eb8da2b144c3f09 100644 (file)
@@ -91,9 +91,9 @@
     "@types/bluebird": "3.5.21"
   },
   "dependencies": {
+    "apicache": "^1.4.0",
     "application-config": "^1.0.1",
     "async": "^2.0.0",
-    "async-lock": "^1.1.2",
     "async-lru": "^1.1.1",
     "bcrypt": "3.0.5",
     "bittorrent-tracker": "^9.0.0",
     "youtube-dl": "^1.12.2"
   },
   "devDependencies": {
+    "@types/apicache": "^1.2.0",
     "@types/async": "^2.0.40",
     "@types/async-lock": "^1.1.0",
     "@types/bcrypt": "^3.0.0",
index f09eb6ff11748ccb6ec04949734774fb3d5cd83f..3c810da98e6d9b3fb4a889cd935aec8d16cabcbd 100644 (file)
@@ -71,7 +71,7 @@ class JobQueue {
     this.jobRedisPrefix = 'bull-' + WEBSERVER.HOST
     const queueOptions = {
       prefix: this.jobRedisPrefix,
-      redis: Redis.getRedisClient(),
+      redis: Redis.getRedisClientOptions(),
       settings: {
         maxStalledCount: 10 // transcoding could be long, so jobs can often be interrupted by restarts
       }
index b4044bf0f62c27c00fcaf26338080ddec5376481..f77d0b62c20ba22d3a92c01da5e097b7b35cf8c1 100644 (file)
@@ -31,7 +31,7 @@ class Redis {
     if (this.initialized === true) return
     this.initialized = true
 
-    this.client = createClient(Redis.getRedisClient())
+    this.client = createClient(Redis.getRedisClientOptions())
 
     this.client.on('error', err => {
       logger.error('Error in Redis client.', { err })
@@ -45,7 +45,7 @@ class Redis {
     this.prefix = 'redis-' + WEBSERVER.HOST + '-'
   }
 
-  static getRedisClient () {
+  static getRedisClientOptions () {
     return Object.assign({},
       (CONFIG.REDIS.AUTH && CONFIG.REDIS.AUTH != null) ? { password: CONFIG.REDIS.AUTH } : {},
       (CONFIG.REDIS.DB) ? { db: CONFIG.REDIS.DB } : {},
@@ -55,6 +55,14 @@ class Redis {
     )
   }
 
+  getClient () {
+    return this.client
+  }
+
+  getPrefix () {
+    return this.prefix
+  }
+
   /************* Forgot password *************/
 
   async setResetPasswordVerificationString (userId: number) {
index 091c82d92cdad17f6e23ff94013120aee33e1791..ef8611875b980c9768ceb137d1aaf6f6af0a6320 100644 (file)
@@ -1,73 +1,16 @@
-import * as express from 'express'
-import * as AsyncLock from 'async-lock'
-import { parseDurationToMs } from '../helpers/core-utils'
 import { Redis } from '../lib/redis'
-import { logger } from '../helpers/logger'
+import * as apicache from 'apicache'
 
-const lock = new AsyncLock({ timeout: 5000 })
+// Ensure Redis is initialized
+Redis.Instance.init()
 
-function cacheRoute (lifetimeArg: string | number) {
-  const lifetime = parseDurationToMs(lifetimeArg)
-
-  return async function (req: express.Request, res: express.Response, next: express.NextFunction) {
-    const redisKey = Redis.Instance.generateCachedRouteKey(req)
-
-    try {
-      await lock.acquire(redisKey, async (done) => {
-        const cached = await Redis.Instance.getCachedRoute(req)
-
-        // Not cached
-        if (!cached) {
-          logger.debug('No cached results for route %s.', req.originalUrl)
-
-          const sendSave = res.send.bind(res)
-          const redirectSave = res.redirect.bind(res)
-
-          res.send = (body) => {
-            if (res.statusCode >= 200 && res.statusCode < 400) {
-              const contentType = res.get('content-type')
-
-              Redis.Instance.setCachedRoute(req, body, lifetime, contentType, res.statusCode)
-                   .then(() => done())
-                   .catch(err => {
-                     logger.error('Cannot cache route.', { err })
-                     return done(err)
-                   })
-            } else {
-              done()
-            }
-
-            return sendSave(body)
-          }
-
-          res.redirect = url => {
-            done()
-
-            return redirectSave(url)
-          }
-
-          return next()
-        }
-
-        if (cached.contentType) res.set('content-type', cached.contentType)
-
-        if (cached.statusCode) {
-          const statusCode = parseInt(cached.statusCode, 10)
-          if (!isNaN(statusCode)) res.status(statusCode)
-        }
-
-        logger.debug('Use cached result for %s.', req.originalUrl)
-        res.send(cached.body).end()
-
-        return done()
-      })
-    } catch (err) {
-      logger.error('Cannot serve cached route.', { err })
-      return next()
-    }
-  }
+const options = {
+  redisClient: Redis.Instance.getClient(),
+  appendKey: () => Redis.Instance.getPrefix()
 }
 
+const cacheRoute = apicache.options(options).middleware
+
 // ---------------------------------------------------------------------------
 
 export {
index aa616f4a06bd1bd7d689927a5d78e4776ca3df57..f2cc0ee055205f4362da3fdf60dd61be43dff431 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
   dependencies:
     any-observable "^0.3.0"
 
+"@types/apicache@^1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@types/apicache/-/apicache-1.2.0.tgz#5f6e9225e66d22da97042a39ad626b3c158d650d"
+  integrity sha512-8uatdizj2GbYHtS4u+x4k2aG1thG6JBWKRidcnauXav+Bxe3bHsWS8HSwcybuLE2q39/95cwb4hkHvqmP7ja2w==
+  dependencies:
+    "@types/redis" "*"
+
 "@types/async-lock@^1.1.0":
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/@types/async-lock/-/async-lock-1.1.1.tgz#81f218213bebcc5f740efe9648272c774a2e4b4b"
   resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
   integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
 
-"@types/redis@^2.8.5":
+"@types/redis@*", "@types/redis@^2.8.5":
   version "2.8.12"
   resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.12.tgz#6405d7ece0d6cc037151b7141cef9ad3cd06f3ac"
   integrity sha512-eT5cGYr08OnF6OlAHdc2hVOBAKBpfQQNQHsWEvUwRPFiXRd+vv+hOHSSIo4xB7M5vZOZdjMT2OUlXYqo3YlIGQ==
@@ -548,6 +555,11 @@ anymatch@^2.0.0:
     micromatch "^3.1.4"
     normalize-path "^2.1.1"
 
+apicache@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/apicache/-/apicache-1.4.0.tgz#3835fbe18717caca3a44cb6272d49b52cac30d3a"
+  integrity sha512-pX/Sf9q9HNzAC5F+hPgxt8v3eQVZkXL/+8HpAnrDJXFmma80F2aHAAeWTql3BsG87lc3T6A7CFPNWMTl97L/7Q==
+
 append-field@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56"
@@ -695,11 +707,6 @@ async-limiter@~1.0.0:
   resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
   integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==
 
-async-lock@^1.1.2:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.2.0.tgz#cd6a53cb1ec3f86af25eafdeb6bc7c6e317258b8"
-  integrity sha512-81HzTQm4+qMj6PwNlnR+y9g7pDdGGzd/YBUrQnHk+BhR28ja2qv497NkQQc1KcKEqh/RShm07di2b0cIWVFrNQ==
-
 async-lru@^1.1.1:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/async-lru/-/async-lru-1.1.2.tgz#abe831f3a52123c87d44273615e203b1ef04692e"