diff options
author | Chocobozzz <me@florianbigard.com> | 2018-05-23 10:03:26 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-05-23 10:03:26 +0200 |
commit | b40f057594d51ae64e9d638d3b5877e544214b53 (patch) | |
tree | bd7918e8b5ab4f688422125a925cca9b6ff527bc /server | |
parent | e1a540b5fa14b0fafa63f99e344927b10fdbee00 (diff) | |
download | PeerTube-b40f057594d51ae64e9d638d3b5877e544214b53.tar.gz PeerTube-b40f057594d51ae64e9d638d3b5877e544214b53.tar.zst PeerTube-b40f057594d51ae64e9d638d3b5877e544214b53.zip |
Handle concurrent requests in cache middleware
Diffstat (limited to 'server')
-rw-r--r-- | server/lib/redis.ts | 24 | ||||
-rw-r--r-- | server/middlewares/cache.ts | 53 |
2 files changed, 45 insertions, 32 deletions
diff --git a/server/lib/redis.ts b/server/lib/redis.ts index 97ff3598b..5bd55109c 100644 --- a/server/lib/redis.ts +++ b/server/lib/redis.ts | |||
@@ -88,6 +88,18 @@ class Redis { | |||
88 | }) | 88 | }) |
89 | } | 89 | } |
90 | 90 | ||
91 | generateResetPasswordKey (userId: number) { | ||
92 | return 'reset-password-' + userId | ||
93 | } | ||
94 | |||
95 | buildViewKey (ip: string, videoUUID: string) { | ||
96 | return videoUUID + '-' + ip | ||
97 | } | ||
98 | |||
99 | buildCachedRouteKey (req: express.Request) { | ||
100 | return req.method + '-' + req.originalUrl | ||
101 | } | ||
102 | |||
91 | private getValue (key: string) { | 103 | private getValue (key: string) { |
92 | return new Promise<string>((res, rej) => { | 104 | return new Promise<string>((res, rej) => { |
93 | this.client.get(this.prefix + key, (err, value) => { | 105 | this.client.get(this.prefix + key, (err, value) => { |
@@ -146,18 +158,6 @@ class Redis { | |||
146 | }) | 158 | }) |
147 | } | 159 | } |
148 | 160 | ||
149 | private generateResetPasswordKey (userId: number) { | ||
150 | return 'reset-password-' + userId | ||
151 | } | ||
152 | |||
153 | private buildViewKey (ip: string, videoUUID: string) { | ||
154 | return videoUUID + '-' + ip | ||
155 | } | ||
156 | |||
157 | private buildCachedRouteKey (req: express.Request) { | ||
158 | return req.method + '-' + req.originalUrl | ||
159 | } | ||
160 | |||
161 | static get Instance () { | 161 | static get Instance () { |
162 | return this.instance || (this.instance = new this()) | 162 | return this.instance || (this.instance = new this()) |
163 | } | 163 | } |
diff --git a/server/middlewares/cache.ts b/server/middlewares/cache.ts index c589ef683..bf6659687 100644 --- a/server/middlewares/cache.ts +++ b/server/middlewares/cache.ts | |||
@@ -1,39 +1,52 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import * as AsyncLock from 'async-lock' | ||
2 | import { Redis } from '../lib/redis' | 3 | import { Redis } from '../lib/redis' |
3 | import { logger } from '../helpers/logger' | 4 | import { logger } from '../helpers/logger' |
4 | 5 | ||
6 | const lock = new AsyncLock({ timeout: 5000 }) | ||
7 | |||
5 | function cacheRoute (lifetime: number) { | 8 | function cacheRoute (lifetime: number) { |
6 | return async function (req: express.Request, res: express.Response, next: express.NextFunction) { | 9 | return async function (req: express.Request, res: express.Response, next: express.NextFunction) { |
7 | const cached = await Redis.Instance.getCachedRoute(req) | 10 | const redisKey = Redis.Instance.buildCachedRouteKey(req) |
11 | |||
12 | await lock.acquire(redisKey, async (done) => { | ||
13 | const cached = await Redis.Instance.getCachedRoute(req) | ||
8 | 14 | ||
9 | // Not cached | 15 | // Not cached |
10 | if (!cached) { | 16 | if (!cached) { |
11 | logger.debug('Not cached result for route %s.', req.originalUrl) | 17 | logger.debug('Not cached result for route %s.', req.originalUrl) |
12 | 18 | ||
13 | const sendSave = res.send.bind(res) | 19 | const sendSave = res.send.bind(res) |
14 | 20 | ||
15 | res.send = (body) => { | 21 | res.send = (body) => { |
16 | if (res.statusCode >= 200 && res.statusCode < 400) { | 22 | if (res.statusCode >= 200 && res.statusCode < 400) { |
17 | const contentType = res.getHeader('content-type').toString() | 23 | const contentType = res.getHeader('content-type').toString() |
18 | Redis.Instance.setCachedRoute(req, body, lifetime, contentType, res.statusCode) | 24 | Redis.Instance.setCachedRoute(req, body, lifetime, contentType, res.statusCode) |
19 | .catch(err => logger.error('Cannot cache route.', { err })) | 25 | .then(() => done()) |
26 | .catch(err => { | ||
27 | logger.error('Cannot cache route.', { err }) | ||
28 | return done(err) | ||
29 | }) | ||
30 | } | ||
31 | |||
32 | return sendSave(body) | ||
20 | } | 33 | } |
21 | 34 | ||
22 | return sendSave(body) | 35 | return next() |
23 | } | 36 | } |
24 | 37 | ||
25 | return next() | 38 | if (cached.contentType) res.contentType(cached.contentType) |
26 | } | ||
27 | 39 | ||
28 | if (cached.contentType) res.contentType(cached.contentType) | 40 | if (cached.statusCode) { |
41 | const statusCode = parseInt(cached.statusCode, 10) | ||
42 | if (!isNaN(statusCode)) res.status(statusCode) | ||
43 | } | ||
29 | 44 | ||
30 | if (cached.statusCode) { | 45 | logger.debug('Use cached result for %s.', req.originalUrl) |
31 | const statusCode = parseInt(cached.statusCode, 10) | 46 | res.send(cached.body).end() |
32 | if (!isNaN(statusCode)) res.status(statusCode) | ||
33 | } | ||
34 | 47 | ||
35 | logger.debug('Use cached result for %s.', req.originalUrl) | 48 | return done() |
36 | return res.send(cached.body).end() | 49 | }) |
37 | } | 50 | } |
38 | } | 51 | } |
39 | 52 | ||