exframe-cache-manager 2.0.3 → 2.0.4

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 CHANGED
@@ -1,4 +1,4 @@
1
- FROM node:14-alpine
1
+ FROM node:16-alpine
2
2
 
3
3
  LABEL maintainer=Exzeo
4
4
 
package/index.js CHANGED
@@ -4,6 +4,10 @@ const CacheManager = require('cache-manager');
4
4
  const RedisStore = require('cache-manager-redis-store');
5
5
  const util = require('util');
6
6
  const { lazyInstrument } = require('exframe-metrics');
7
+ const health = require('exframe-health');
8
+ const service = require('exframe-service');
9
+ const { generateShortId } = require('exframe-utilities');
10
+ const { setTimeout: waitTimeout } = require('timers/promises');
7
11
 
8
12
  const db = 0;
9
13
 
@@ -42,6 +46,7 @@ function cachemanager(options) {
42
46
  });
43
47
 
44
48
  const instance = {
49
+ id: generateShortId(),
45
50
  get cacheStorePool() {
46
51
  return redisClient;
47
52
  },
@@ -166,6 +171,28 @@ function cachemanager(options) {
166
171
  redisClient
167
172
  };
168
173
 
174
+ health.add(`redis-${instance.id}`, async () => {
175
+ try {
176
+ const controller = new AbortController();
177
+
178
+ const result = await Promise.race([
179
+ instance.healthCheck(),
180
+ waitTimeout(3000, false, { signal: controller.signal, ref: false })
181
+ ]);
182
+
183
+ if (!result) {
184
+ throw new Error('Timed out waiting to verify health status');
185
+ }
186
+
187
+ controller.abort();
188
+ return { status: 200, message: 'OK' };
189
+ } catch (ex) {
190
+ return { status: 503, message: 'Error connecting to Redis' };
191
+ }
192
+ }, { promotionTimeout: 60000 });
193
+
194
+ service.registerResource(`exframe-cache-manager-${instance.id}`, { onSignal: () => instance.close(), order: 'last' });
195
+
169
196
  Object.keys(instance).forEach((key) => {
170
197
  if (typeof instance[key] === 'function') {
171
198
  instance[key] = lazyInstrument(instance[key].bind(instance), { metricPrefix: 'cache' });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exframe-cache-manager",
3
- "version": "2.0.3",
3
+ "version": "2.0.4",
4
4
  "description": "Managing the cache",
5
5
  "main": "index.js",
6
6
  "config": {
@@ -13,7 +13,7 @@
13
13
  "pretest": "npm-install-peers && npm run lint",
14
14
  "lint": "eslint ./",
15
15
  "peers": "npm-install-peers",
16
- "test": "docker-compose -f docker-compose.yml up --abort-on-container-exit --exit-code-from exframe-cache-manager",
16
+ "test": "docker-compose -f docker-compose.yml up --build --abort-on-container-exit --exit-code-from exframe-cache-manager",
17
17
  "unit-test": "./node_modules/.bin/nyc -r lcov --report-dir ./documentation/coverage -t ./documentation/coverage mocha --exit --reporter $npm_package_config_reporter \"./test/**/*.test.js\""
18
18
  },
19
19
  "author": "Exzeo",
@@ -27,7 +27,8 @@
27
27
  "@types/cache-manager": "^3.4.2",
28
28
  "cache-manager": "^3.6.0",
29
29
  "cache-manager-redis-store": "^2.0.0",
30
- "exframe-metrics": "^1.1.0"
30
+ "exframe-metrics": "^1.1.0",
31
+ "exframe-utilities": "^1.1.0"
31
32
  },
32
33
  "devDependencies": {
33
34
  "chai": "*",
@@ -45,5 +46,5 @@
45
46
  "url": "https://bitbucket.org/exzeo-usa/exframe",
46
47
  "directory": "packages/exframe-cache-manager"
47
48
  },
48
- "gitHead": "846806709f00dedc764c794b3ee05ca3c7297244"
49
+ "gitHead": "6c09763669b90802745d12200803ac186a6dc5ae"
49
50
  }
@@ -1,135 +1,154 @@
1
1
  'use strict';
2
2
 
3
- const expect = require('chai').expect;
4
- const { prometheusClient } = require('exframe-service');
3
+ const { expect } = require('chai');
4
+ const sinon = require('sinon');
5
+ const service = require('exframe-service');
6
+ const health = require('exframe-health');
5
7
  const cacheManager = require('../index');
6
8
 
9
+ const { prometheusClient } = service;
7
10
  const userId = 'auth0|1234567890';
8
11
  const cacheKey = `${userId}_profiletest`;
9
12
 
10
- const options = {
11
- "host": process.env.HOST || "localhost",
12
- "port": process.env.PORT || 6379,
13
- "auth_pass": "",
14
- "db": 0,
15
- "ttl": 60
16
- };
17
-
18
- const userProfileResponse = {
19
- userId: userId,
20
- userName: 'CSRMickey',
21
- email: 'CMM@discsr.com',
22
- profile: {
23
- family_name: 'Mickey',
24
- given_name: ' Mouse CSR'
25
- },
26
- resources: [{
27
- uri: 'TTIC:FL:HO3:CSR1',
28
- right: 'READ'
29
- }, {
30
- uri: 'TTIC:FL:HO3:CSR2',
31
- right: 'WRITE'
32
- }]
13
+ const options = {
14
+ host: process.env.HOST || 'localhost',
15
+ port: process.env.PORT || 6379,
16
+ auth_pass: '',
17
+ db: 0,
18
+ ttl: 60
33
19
  };
34
20
 
35
21
  context('Test User Profile Middleware', () => {
36
22
  describe('Cache Manager', () => {
37
- const app = cacheManager.create(options);
38
-
39
- beforeEach(() => {
40
- prometheusClient.register.clear();
41
- expect(prometheusClient.register.getMetricsAsArray()).to.have.lengthOf(0);
42
- });
43
-
44
- it('validate the setItem function on the basis of key and value', async () => {
45
- const data = await app.setItem(cacheKey, "value1");
46
- expect(data).to.not.eql(null);
47
- expect(data).to.eql("value1");
48
- const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('setItem'));
49
- expect(metrics).to.be.an('array').and.to.have.lengthOf.above(0);
50
- await app.flushCache({log:{info:(info)=>{console.log(info)}}}, cacheKey);
51
- });
52
-
53
- it('validate the getItem function on the basis of key', async () => {
54
- await app.setItem(cacheKey,"someValue");
55
- const value = await app.getItem(cacheKey);
56
- expect(value).to.eql("someValue");
57
- const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('getItem'));
58
- expect(metrics).to.be.an('array').and.to.have.lengthOf.above(0);
59
- await app.flushCache({log:{info:(info)=>{console.log(info)}}}, cacheKey);
60
- });
61
-
62
- it('validate the addSetItem function on the basis of key', async () => {
63
- const key = 'SetTestKey';
64
- await app.addSetItem(key, "someSetValue");
65
- const value = await app.isSetMember(key, "someSetValue");
66
- expect(value).to.eql(true);
67
- const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('addSetItem'));
68
- expect(metrics).to.be.an('array').and.to.have.lengthOf.above(0);
69
- await app.flushCache({log:{info:(info)=>{console.log(info)}}}, key);
23
+ describe('create', () => {
24
+ let sinonInstance;
25
+
26
+ beforeEach(() => {
27
+ sinonInstance = sinon.createSandbox();
28
+ });
29
+
30
+ afterEach(() => {
31
+ sinonInstance.restore();
32
+ });
33
+
34
+ it('adds a health check with the instance ID in the name, check function, and liveness timeout of 60 seconds', () => {
35
+ const healthAddSpy = sinonInstance.spy(health, 'add');
36
+
37
+ const instance = cacheManager.create(options);
38
+ expect(healthAddSpy.calledOnce).to.equal(true);
39
+ expect(healthAddSpy.calledWithMatch(`redis-${instance.id}`, sinon.match.func, { promotionTimeout: 60000 })).to.be.true;
40
+ });
41
+
42
+ it('registers a service resource with the instance ID in the name, onSignal function, and order of "last"', () => {
43
+ const registerSpy = sinonInstance.spy(service, 'registerResource');
44
+
45
+ const instance = cacheManager.create(options);
46
+ expect(registerSpy.calledOnce).to.equal(true);
47
+ expect(registerSpy.calledWithMatch(`exframe-cache-manager-${instance.id}`, sinon.match({
48
+ onSignal: sinon.match.func,
49
+ order: 'last'
50
+ }))).to.be.true;
51
+ });
70
52
  });
71
53
 
72
- it('validate the isSetMember function on the basis of key', async () => {
73
- const key = 'SetTestKey2';
74
- await app.addSetItem(key, "someSetValue");
75
- const value1 = await app.isSetMember(key, "someSetValue");
76
- expect(value1).to.eql(true);
77
- const value2 = await app.isSetMember(key, "notSetValue");
78
- expect(value2).to.eql(false, 'expected non-member key to return false for isSetMember');
79
- const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('isSetMember'));
80
- expect(metrics).to.be.an('array').and.to.have.lengthOf.above(0);
81
- await app.flushCache({log:{info:(info)=>{console.log(info)}}}, key);
82
- });
54
+ describe('instance', () => {
55
+ const app = cacheManager.create(options);
83
56
 
84
- it('validate the removeSetMember function on the basis of key', async () => {
85
- const key = 'SetTestKey3';
86
- await app.addSetItem(key, "someSetValue");
87
- let value = await app.isSetMember(key, "someSetValue");
88
- expect(value).to.eql(true);
89
- await app.removeSetItem(key, "someSetValue");
90
- value = await app.isSetMember(key, "someSetValue");
91
- expect(value).to.eql(false, 'expected non-member key to return false for isSetMember');
92
- const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('removeSetItem'));
93
- expect(metrics).to.be.an('array').and.to.have.lengthOf.above(0);
94
- await app.flushCache({log:{info:(info)=>{console.log(info)}}}, key);
95
- });
57
+ beforeEach(() => {
58
+ prometheusClient.register.clear();
59
+ expect(prometheusClient.register.getMetricsAsArray()).to.have.lengthOf(0);
60
+ });
96
61
 
97
- it('close drains the redis pool', async () => {
98
- const app1 = cacheManager.create(options);
99
- await app1.setItem(cacheKey,"someValue");
100
- const value = await app1.getItem(cacheKey);
101
- expect(value).to.eql("someValue");
102
- await app1.close();
103
-
104
- try {
105
- const result = await app1.getItem(cacheKey);
106
- expect(result).to.not.be.ok();
107
- } catch (ex) {
108
- expect(ex.message).to.be.equal('GET can\'t be processed. The connection is already closed.');
109
- const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('close'));
62
+ it('validate the setItem function on the basis of key and value', async () => {
63
+ const data = await app.setItem(cacheKey, 'value1');
64
+ expect(data).to.not.eql(null);
65
+ expect(data).to.eql('value1');
66
+ const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('setItem'));
110
67
  expect(metrics).to.be.an('array').and.to.have.lengthOf.above(0);
111
- }
112
- });
113
-
114
- it('validate the flushCache function', async () => {
115
- await app.flushCache({log:{info:(info)=>{console.log(info)}}}, '*_profiletest');
116
- const value = await app.getItem(cacheKey);
117
- expect(value).to.eql(null);
118
- const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('flushCache'));
119
- expect(metrics).to.be.an('array').and.to.have.lengthOf.above(0);
120
- });
121
-
122
- it('validate the cacheStorePool', () => {
123
- const catchepool = app.cacheStorePool;
124
- expect(app).to.not.eql(null);
125
- expect(catchepool._maxListeners).to.eql(0);
126
- });
127
-
128
- it('validate the healthCheck function', async () => {
129
- const healthCheck = await app.healthCheck();
130
- expect(healthCheck).to.eql(true);
131
- const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('healthCheck'));
132
- expect(metrics).to.be.an('array').and.to.have.lengthOf.above(0);
68
+ await app.flushCache({ log: { info: (info) => { console.log(info); } } }, cacheKey);
69
+ });
70
+
71
+ it('validate the getItem function on the basis of key', async () => {
72
+ await app.setItem(cacheKey, 'someValue');
73
+ const value = await app.getItem(cacheKey);
74
+ expect(value).to.eql('someValue');
75
+ const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('getItem'));
76
+ expect(metrics).to.be.an('array').and.to.have.lengthOf.above(0);
77
+ await app.flushCache({ log: { info: (info) => { console.log(info); } } }, cacheKey);
78
+ });
79
+
80
+ it('validate the addSetItem function on the basis of key', async () => {
81
+ const key = 'SetTestKey';
82
+ await app.addSetItem(key, 'someSetValue');
83
+ const value = await app.isSetMember(key, 'someSetValue');
84
+ expect(value).to.eql(true);
85
+ const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('addSetItem'));
86
+ expect(metrics).to.be.an('array').and.to.have.lengthOf.above(0);
87
+ await app.flushCache({ log: { info: (info) => { console.log(info); } } }, key);
88
+ });
89
+
90
+ it('validate the isSetMember function on the basis of key', async () => {
91
+ const key = 'SetTestKey2';
92
+ await app.addSetItem(key, 'someSetValue');
93
+ const value1 = await app.isSetMember(key, 'someSetValue');
94
+ expect(value1).to.eql(true);
95
+ const value2 = await app.isSetMember(key, 'notSetValue');
96
+ expect(value2).to.eql(false, 'expected non-member key to return false for isSetMember');
97
+ const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('isSetMember'));
98
+ expect(metrics).to.be.an('array').and.to.have.lengthOf.above(0);
99
+ await app.flushCache({ log: { info: (info) => { console.log(info); } } }, key);
100
+ });
101
+
102
+ it('validate the removeSetMember function on the basis of key', async () => {
103
+ const key = 'SetTestKey3';
104
+ await app.addSetItem(key, 'someSetValue');
105
+ let value = await app.isSetMember(key, 'someSetValue');
106
+ expect(value).to.eql(true);
107
+ await app.removeSetItem(key, 'someSetValue');
108
+ value = await app.isSetMember(key, 'someSetValue');
109
+ expect(value).to.eql(false, 'expected non-member key to return false for isSetMember');
110
+ const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('removeSetItem'));
111
+ expect(metrics).to.be.an('array').and.to.have.lengthOf.above(0);
112
+ await app.flushCache({ log: { info: (info) => { console.log(info); } } }, key);
113
+ });
114
+
115
+ it('close drains the redis pool', async () => {
116
+ const app1 = cacheManager.create(options);
117
+ await app1.setItem(cacheKey, 'someValue');
118
+ const value = await app1.getItem(cacheKey);
119
+ expect(value).to.eql('someValue');
120
+ await app1.close();
121
+
122
+ try {
123
+ const result = await app1.getItem(cacheKey);
124
+ expect(result).to.not.be.ok();
125
+ } catch (ex) {
126
+ expect(ex.message).to.be.equal('GET can\'t be processed. The connection is already closed.');
127
+ const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('close'));
128
+ expect(metrics).to.be.an('array').and.to.have.lengthOf.above(0);
129
+ }
130
+ });
131
+
132
+ it('validate the flushCache function', async () => {
133
+ await app.flushCache({ log: { info: (info) => { console.log(info); } } }, '*_profiletest');
134
+ const value = await app.getItem(cacheKey);
135
+ expect(value).to.eql(null);
136
+ const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('flushCache'));
137
+ expect(metrics).to.be.an('array').and.to.have.lengthOf.above(0);
138
+ });
139
+
140
+ it('validate the cacheStorePool', () => {
141
+ const catchepool = app.cacheStorePool;
142
+ expect(app).to.not.eql(null);
143
+ expect(catchepool._maxListeners).to.eql(0);
144
+ });
145
+
146
+ it('validate the healthCheck function', async () => {
147
+ const healthCheck = await app.healthCheck();
148
+ expect(healthCheck).to.eql(true);
149
+ const metrics = prometheusClient.register.getMetricsAsArray().filter((m) => m.name.includes('healthCheck'));
150
+ expect(metrics).to.be.an('array').and.to.have.lengthOf.above(0);
151
+ });
133
152
  });
134
153
  });
135
- });
154
+ });