Skip to content

Commit 503e882

Browse files
Implement cache notifier (#6)
1 parent bf99b23 commit 503e882

File tree

4 files changed

+44
-37
lines changed

4 files changed

+44
-37
lines changed

index.js

+28-20
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22

33
const fp = require('fastify-plugin')
44
const Keyv = require('keyv')
5-
const BPromise = require('bluebird')
65
const crypto = require('crypto')
6+
const {EventEmitter} = require('events')
77

88
const CACHEABLE_METHODS = ['GET']
9-
const INTERVAL = 200
109
const X_RESPONSE_CACHE = 'x-response-cache'
1110
const X_RESPONSE_CACHE_HIT = 'hit'
1211
const X_RESPONSE_CACHE_MISS = 'miss'
@@ -27,36 +26,41 @@ function buildCacheKey(req, {headers}) {
2726
return key
2827
}
2928

30-
async function waitForCacheFulfilled(cache, key, timeout) {
31-
let cachedString = await cache.get(key)
32-
let waitedFor = 0
33-
34-
while (!cachedString) {
35-
await BPromise.delay(INTERVAL)
36-
cachedString = await cache.get(key)
37-
38-
waitedFor += INTERVAL
39-
if (!cachedString && waitedFor > timeout) {
40-
return
41-
}
42-
}
43-
44-
return cachedString
45-
}
46-
4729
function createOnRequestHandler({ttl, additionalCondition: {headers}}) {
4830
return async function handler(req, res) {
4931
if (!isCacheableRequest(req)) {
5032
return
5133
}
5234

5335
const cache = this.responseCache
36+
const cacheNotifier = this.responseCacheNotifier
5437
const key = buildCacheKey(req, {headers})
5538
const requestKey = `${key}__requested`
5639
const isRequestExisted = await cache.get(requestKey)
5740

41+
async function waitForCacheFulfilled(key) {
42+
return new Promise((resolve) => {
43+
cache.get(key).then((cachedString) => {
44+
if (cachedString) {
45+
resolve(cachedString)
46+
}
47+
})
48+
49+
const handler = async () => {
50+
const cachedString = await cache.get(key)
51+
52+
resolve(cachedString)
53+
}
54+
55+
cacheNotifier.once(key, handler)
56+
57+
setTimeout(() => cacheNotifier.removeListener(key, handler), ttl)
58+
setTimeout(() => resolve(), ttl)
59+
})
60+
}
61+
5862
if (isRequestExisted) {
59-
const cachedString = await waitForCacheFulfilled(cache, key, ttl)
63+
const cachedString = await waitForCacheFulfilled(key)
6064

6165
if (cachedString) {
6266
const cached = JSON.parse(cachedString)
@@ -80,6 +84,7 @@ function createOnSendHandler({ttl, additionalCondition: {headers}}) {
8084
}
8185

8286
const cache = this.responseCache
87+
const cacheNotifier = this.responseCacheNotifier
8388
const key = buildCacheKey(req, {headers})
8489

8590
await cache.set(
@@ -90,6 +95,7 @@ function createOnSendHandler({ttl, additionalCondition: {headers}}) {
9095
}),
9196
ttl,
9297
)
98+
cacheNotifier.emit(key)
9399
}
94100
}
95101

@@ -101,8 +107,10 @@ const responseCachingPlugin = (
101107
const headers = additionalCondition.headers || []
102108
const opts = {ttl, additionalCondition: {headers}}
103109
const responseCache = new Keyv()
110+
const responseCacheNotifier = new EventEmitter()
104111

105112
instance.decorate('responseCache', responseCache)
113+
instance.decorate('responseCacheNotifier', responseCacheNotifier)
106114
instance.addHook('onRequest', createOnRequestHandler(opts))
107115
instance.addHook('onSend', createOnSendHandler(opts))
108116

index.test.js

+13-10
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ const test = require('tap').test
44

55
const axios = require('axios')
66
const fastify = require('fastify')
7-
const BPromise = require('bluebird')
87

98
const plugin = require('./index.js')
109

10+
function delay(ms) {
11+
return new Promise((resolve) => setTimeout(resolve, ms))
12+
}
13+
1114
test('should decorate cache to fastify instance', (t) => {
1215
t.plan(3)
1316
const instance = fastify()
@@ -30,7 +33,7 @@ test('should cache the cacheable request', (t) => {
3033
instance.server.unref()
3134
const portNum = instance.server.address().port
3235
const address = `http://127.0.0.1:${portNum}/cache`
33-
const [response1, response2] = await BPromise.all([
36+
const [response1, response2] = await Promise.all([
3437
axios.get(address),
3538
axios.get(address),
3639
])
@@ -55,7 +58,7 @@ test('should not cache the uncacheable request', (t) => {
5558
instance.server.unref()
5659
const portNum = instance.server.address().port
5760
const address = `http://127.0.0.1:${portNum}/no-cache`
58-
const [response1, response2] = await BPromise.all([
61+
const [response1, response2] = await Promise.all([
5962
axios.post(address, {}),
6063
axios.post(address, {}),
6164
])
@@ -80,11 +83,11 @@ test('should apply ttl config', (t) => {
8083
instance.server.unref()
8184
const portNum = instance.server.address().port
8285
const address = `http://127.0.0.1:${portNum}/ttl`
83-
const [response1, response2] = await BPromise.all([
86+
const [response1, response2] = await Promise.all([
8487
axios.get(address),
8588
axios.get(address),
8689
])
87-
await BPromise.delay(3000)
90+
await delay(3000)
8891
const response3 = await axios.get(address)
8992
t.is(response1.status, 200)
9093
t.is(response2.status, 200)
@@ -114,7 +117,7 @@ test('should apply additionalCondition config', (t) => {
114117
instance.server.unref()
115118
const portNum = instance.server.address().port
116119
const address = `http://127.0.0.1:${portNum}/headers`
117-
const [response1, response2, response3, response4] = await BPromise.all([
120+
const [response1, response2, response3, response4] = await Promise.all([
118121
axios.get(address, {
119122
headers: {'x-should-applied': 'yes'},
120123
}),
@@ -146,15 +149,15 @@ test('should waiting for cache if multiple same request come in', (t) => {
146149
const instance = fastify()
147150
instance.register(plugin, {ttl: 5000})
148151
instance.get('/waiting', async (req, res) => {
149-
await BPromise.delay(3000)
152+
await delay(3000)
150153
res.send({hello: 'world'})
151154
})
152155
instance.listen(0, async (err) => {
153156
if (err) t.threw(err)
154157
instance.server.unref()
155158
const portNum = instance.server.address().port
156159
const address = `http://127.0.0.1:${portNum}/waiting`
157-
const [response1, response2] = await BPromise.all([
160+
const [response1, response2] = await Promise.all([
158161
axios.get(address),
159162
axios.get(address),
160163
])
@@ -172,15 +175,15 @@ test('should not waiting for cache due to timeout', (t) => {
172175
const instance = fastify()
173176
instance.register(plugin)
174177
instance.get('/abort', async (req, res) => {
175-
await BPromise.delay(2000)
178+
await delay(2000)
176179
res.send({hello: 'world'})
177180
})
178181
instance.listen(0, async (err) => {
179182
if (err) t.threw(err)
180183
instance.server.unref()
181184
const portNum = instance.server.address().port
182185
const address = `http://127.0.0.1:${portNum}/abort`
183-
const [response1, response2] = await BPromise.all([
186+
const [response1, response2] = await Promise.all([
184187
axios.get(address),
185188
axios.get(address),
186189
])

package-lock.json

-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
}
1010
},
1111
"lint-staged": {
12-
"**/*.js": ["eslint"]
12+
"**/*.js": [
13+
"eslint"
14+
]
1315
},
1416
"scripts": {
1517
"test": "tap --cov *.test.js",
@@ -33,7 +35,6 @@
3335
},
3436
"homepage": "https://github.com/codeaholicguy/fastify-response-caching#readme",
3537
"dependencies": {
36-
"bluebird": "^3.7.2",
3738
"fastify-plugin": "^2.3.4",
3839
"keyv": "^4.0.2"
3940
},

0 commit comments

Comments
 (0)