exframe-cache-manager 2.1.2 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +4 -2
- package/docker-compose.yml +0 -1
- package/index.js +37 -12
- package/package.json +6 -5
- package/test/cacheManager.test.js +49 -15
package/Dockerfile
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
FROM node:
|
|
1
|
+
FROM node:24-alpine
|
|
2
2
|
|
|
3
3
|
LABEL maintainer=Exzeo
|
|
4
4
|
|
|
@@ -10,8 +10,10 @@ COPY ./package.json /app/package.json
|
|
|
10
10
|
COPY . /app
|
|
11
11
|
|
|
12
12
|
WORKDIR /app
|
|
13
|
-
RUN npm install
|
|
13
|
+
RUN npm install --ignore-scripts
|
|
14
14
|
RUN npm run peers
|
|
15
15
|
|
|
16
|
+
USER node
|
|
17
|
+
|
|
16
18
|
# Override the command, to run the test instead of the application
|
|
17
19
|
CMD ["npm", "run", "unit-test"]
|
package/docker-compose.yml
CHANGED
package/index.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
import util from 'node:util';
|
|
4
|
+
import { setTimeout as waitTimeout } from 'node:timers/promises';
|
|
5
|
+
import CacheManager from 'cache-manager';
|
|
6
|
+
import RedisStore from 'cache-manager-redis-store';
|
|
7
|
+
import { lazyInstrument } from 'exframe-metrics';
|
|
8
|
+
import health from 'exframe-health';
|
|
9
|
+
import service from 'exframe-service';
|
|
10
|
+
import { generateShortId } from 'exframe-utilities';
|
|
11
|
+
|
|
12
|
+
const DEFAULT_TTL = 600;
|
|
11
13
|
|
|
12
14
|
const db = 0;
|
|
13
15
|
|
|
@@ -24,7 +26,7 @@ function cachemanager(options) {
|
|
|
24
26
|
|
|
25
27
|
const redisCache = CacheManager.caching({
|
|
26
28
|
store: RedisStore,
|
|
27
|
-
ttl:
|
|
29
|
+
ttl: DEFAULT_TTL,
|
|
28
30
|
db,
|
|
29
31
|
...options
|
|
30
32
|
});
|
|
@@ -98,6 +100,30 @@ function cachemanager(options) {
|
|
|
98
100
|
return value;
|
|
99
101
|
},
|
|
100
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Set the item if it does not exist yet. If the item already exists, do not set and return null.
|
|
105
|
+
* @template T
|
|
106
|
+
* @param {String} key
|
|
107
|
+
* @param {T} value
|
|
108
|
+
* @param {Number} ttl
|
|
109
|
+
* @returns {Promise<T|null>}
|
|
110
|
+
*/
|
|
111
|
+
async setItemIfNotExists(key, value, ttl) {
|
|
112
|
+
const ttlToSet = ttl ?? (process.env.REDIS_CACHE_TTL
|
|
113
|
+
? Number(process.env.REDIS_CACHE_TTL)
|
|
114
|
+
: DEFAULT_TTL);
|
|
115
|
+
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
redisClient.set(key, value, 'NX', 'EX', ttlToSet, (error, result) => {
|
|
118
|
+
if (error) {
|
|
119
|
+
return reject(error);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
resolve(result === 'OK' ? value : null);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
},
|
|
126
|
+
|
|
101
127
|
/**
|
|
102
128
|
*
|
|
103
129
|
* @param {string} key
|
|
@@ -226,9 +252,8 @@ function cachemanager(options) {
|
|
|
226
252
|
return instance;
|
|
227
253
|
}
|
|
228
254
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
};
|
|
255
|
+
// eslint-disable-next-line import/prefer-default-export
|
|
256
|
+
export { cachemanager as create };
|
|
232
257
|
|
|
233
258
|
/**
|
|
234
259
|
* @typedef {RedisCacheManagerOptions & { store?: any, ttl?: number, db?: number }} Options
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "exframe-cache-manager",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Managing the cache",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"config": {
|
|
7
8
|
"reporter": "mocha-exzeo-reporter"
|
|
8
9
|
},
|
|
@@ -24,14 +25,14 @@
|
|
|
24
25
|
"exframe-service": "1.x"
|
|
25
26
|
},
|
|
26
27
|
"dependencies": {
|
|
27
|
-
"@types/cache-manager": "^
|
|
28
|
+
"@types/cache-manager": "^4.0.6",
|
|
28
29
|
"cache-manager": "^3.6.0",
|
|
29
30
|
"cache-manager-redis-store": "^2.0.0",
|
|
30
|
-
"exframe-metrics": "^1.
|
|
31
|
+
"exframe-metrics": "^1.5.0",
|
|
31
32
|
"exframe-utilities": "^1.1.0"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
|
-
"chai": "^
|
|
35
|
+
"chai": "^6",
|
|
35
36
|
"eslint": "*",
|
|
36
37
|
"eslint-config-exzeo": "*",
|
|
37
38
|
"eslint-plugin-import": "*",
|
|
@@ -46,5 +47,5 @@
|
|
|
46
47
|
"url": "https://bitbucket.org/exzeo-usa/exframe",
|
|
47
48
|
"directory": "packages/exframe-cache-manager"
|
|
48
49
|
},
|
|
49
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "25463e1a2459e770277640472111e372cacf4aa5"
|
|
50
51
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
import { expect } from 'chai';
|
|
4
|
+
import { createSandbox, match, stub as _stub } from 'sinon';
|
|
5
|
+
import service from 'exframe-service';
|
|
6
|
+
import health from 'exframe-health';
|
|
7
|
+
import { setTimeout as waitTimeout } from 'node:timers/promises';
|
|
8
|
+
|
|
9
|
+
import { create } from '../index.js';
|
|
9
10
|
|
|
10
11
|
const { prometheusClient } = service;
|
|
11
12
|
const userId = 'auth0|1234567890';
|
|
@@ -25,7 +26,7 @@ context('Test User Profile Middleware', () => {
|
|
|
25
26
|
let sinonInstance;
|
|
26
27
|
|
|
27
28
|
beforeEach(() => {
|
|
28
|
-
sinonInstance =
|
|
29
|
+
sinonInstance = createSandbox();
|
|
29
30
|
});
|
|
30
31
|
|
|
31
32
|
afterEach(() => {
|
|
@@ -35,25 +36,25 @@ context('Test User Profile Middleware', () => {
|
|
|
35
36
|
it('adds a health check with the instance ID in the name, check function, and liveness timeout of 60 seconds', () => {
|
|
36
37
|
const healthAddSpy = sinonInstance.spy(health, 'add');
|
|
37
38
|
|
|
38
|
-
const instance =
|
|
39
|
+
const instance = create(options);
|
|
39
40
|
expect(healthAddSpy.calledOnce).to.equal(true);
|
|
40
|
-
expect(healthAddSpy.calledWithMatch(`redis-${instance.id}`,
|
|
41
|
+
expect(healthAddSpy.calledWithMatch(`redis-${instance.id}`, match.func, { promotionTimeout: 60000 })).to.be.true;
|
|
41
42
|
});
|
|
42
43
|
|
|
43
44
|
it('registers a service resource with the instance ID in the name, onSignal function, and order of "last"', () => {
|
|
44
45
|
const registerSpy = sinonInstance.spy(service, 'registerResource');
|
|
45
46
|
|
|
46
|
-
const instance =
|
|
47
|
+
const instance = create(options);
|
|
47
48
|
expect(registerSpy.calledOnce).to.equal(true);
|
|
48
|
-
expect(registerSpy.calledWithMatch(`exframe-cache-manager-${instance.id}`,
|
|
49
|
-
onSignal:
|
|
49
|
+
expect(registerSpy.calledWithMatch(`exframe-cache-manager-${instance.id}`, match({
|
|
50
|
+
onSignal: match.func,
|
|
50
51
|
order: 'last'
|
|
51
52
|
}))).to.be.true;
|
|
52
53
|
});
|
|
53
54
|
});
|
|
54
55
|
|
|
55
56
|
describe('instance', () => {
|
|
56
|
-
const app =
|
|
57
|
+
const app = create(options);
|
|
57
58
|
|
|
58
59
|
beforeEach(() => {
|
|
59
60
|
prometheusClient.register.clear();
|
|
@@ -69,6 +70,39 @@ context('Test User Profile Middleware', () => {
|
|
|
69
70
|
await app.flushCache({ log: { info: (info) => { console.log(info); } } }, cacheKey);
|
|
70
71
|
});
|
|
71
72
|
|
|
73
|
+
it('validates the setItemIfNotExists function on the basis of key and value', async () => {
|
|
74
|
+
const data = await app.setItemIfNotExists(cacheKey, 'value1');
|
|
75
|
+
expect(data).to.be.ok.and.to.eql('value1');
|
|
76
|
+
|
|
77
|
+
const metrics = prometheusClient.register.getMetricsAsArray().filter(({ name }) => name.includes('setItemIfNotExists'));
|
|
78
|
+
expect(metrics).to.be.an('array').and.to.have.lengthOf(1);
|
|
79
|
+
|
|
80
|
+
await app.flushCache({ log: { info: (info) => { console.log(info); } } }, cacheKey);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('validates the setItemIfNotExists function on the basis of key, value, and ttl', async () => {
|
|
84
|
+
const data = await app.setItemIfNotExists(cacheKey, 'value1', 1000);
|
|
85
|
+
expect(data).to.be.ok.and.to.eql('value1');
|
|
86
|
+
|
|
87
|
+
const metrics = prometheusClient.register.getMetricsAsArray().filter(({ name }) => name.includes('setItemIfNotExists'));
|
|
88
|
+
expect(metrics).to.be.an('array').and.to.have.lengthOf(1);
|
|
89
|
+
|
|
90
|
+
await app.flushCache({ log: { info: (info) => { console.log(info); } } }, cacheKey);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('validates the setItemIfNotExists function returns null if key is already set', async () => {
|
|
94
|
+
const data1 = await app.setItemIfNotExists(cacheKey, 'value1', 1000);
|
|
95
|
+
expect(data1).to.be.ok.and.to.eql('value1');
|
|
96
|
+
|
|
97
|
+
const data2 = await app.setItemIfNotExists(cacheKey, 'value2');
|
|
98
|
+
expect(data2).to.eql(null);
|
|
99
|
+
|
|
100
|
+
const metrics = prometheusClient.register.getMetricsAsArray().filter(({ name }) => name.includes('setItemIfNotExists'));
|
|
101
|
+
expect(metrics).to.be.an('array').and.to.have.lengthOf(1);
|
|
102
|
+
|
|
103
|
+
await app.flushCache({ log: { info: (info) => { console.log(info); } } }, cacheKey);
|
|
104
|
+
});
|
|
105
|
+
|
|
72
106
|
it('validate the getItem function on the basis of key', async () => {
|
|
73
107
|
await app.setItem(cacheKey, 'someValue');
|
|
74
108
|
const value = await app.getItem(cacheKey);
|
|
@@ -94,7 +128,7 @@ context('Test User Profile Middleware', () => {
|
|
|
94
128
|
await waitTimeout(5000);
|
|
95
129
|
return 'finished';
|
|
96
130
|
};
|
|
97
|
-
const stub =
|
|
131
|
+
const stub = _stub(app.redisCache, 'get').callsFake(fake);
|
|
98
132
|
|
|
99
133
|
await app.setItem(cacheKey, 'someValue');
|
|
100
134
|
const context = { log: { info: (info) => { console.log(info); } } };
|
|
@@ -144,7 +178,7 @@ context('Test User Profile Middleware', () => {
|
|
|
144
178
|
});
|
|
145
179
|
|
|
146
180
|
it('close drains the redis pool', async () => {
|
|
147
|
-
const app1 =
|
|
181
|
+
const app1 = create(options);
|
|
148
182
|
await app1.setItem(cacheKey, 'someValue');
|
|
149
183
|
const value = await app1.getItem(cacheKey);
|
|
150
184
|
expect(value).to.eql('someValue');
|