@ti-engine/core 1.1.8 → 1.1.9

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/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  This document will contain the list of changes made to the framework. The format is based on the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.
4
4
 
5
+ ## Version 1.1.9
6
+ * feat(redis integration)!: change the way the `redis` client is initialized. Instead of happening automatically on class instantiation, it is now initialized on demand using the `initialize` method.
7
+ * feat(cache): change the way the main cache instance is initialized in compliance with the new redis integration
8
+ * feat(message memory cache)!: change the way the message memory cache is initialized in compliance with the new redis integration. The `initialize` method needs to be called explicitly to initialize the cache instance before it can be used.
9
+ * fix(message dispatcher): fix the way the `messageExchange` is initialized in the `MessageDispatcher` class (was not returning a promise)
10
+
5
11
  ## Version 1.1.8
6
12
  * feat(auditing): change export of the singleton class in an `instance` variable for consistency and clarity
7
13
  * feat(message dispatcher): change export of the singleton class in an `instance` variable for consistency and clarity
@@ -54,7 +54,7 @@ class MessageDispatcher {
54
54
 
55
55
  // Initialize the message tracer before enabling the message exchange:
56
56
  messageTracer.instance.initialize().then( () => {
57
- this.#messageExchange.enableMessaging( configureInbound, configureOutbound );
57
+ return this.#messageExchange.enableMessaging( configureInbound, configureOutbound );
58
58
  } ).then( () => {
59
59
  resolve();
60
60
  } ).catch( ( error ) => {
@@ -24,8 +24,22 @@ class MessageMemoryCache {
24
24
  /**
25
25
  * @constructor
26
26
  * @param {string} identifier The connection identifier for the Redis connection.
27
+ * @returns {MessageMemoryCache}
27
28
  */
28
29
  constructor( identifier ) {
30
+ this.#redisClient = redis.createRedisClient( identifier );
31
+ }
32
+
33
+ /* Public interface */
34
+
35
+ /**
36
+ * Used to initialize the cache service.
37
+ *
38
+ * @method
39
+ * @returns {Promise}
40
+ * @public
41
+ */
42
+ initialize() {
29
43
  let host = config.getSetting( config.setting.MEMORY_CACHE_REDIS_HOST );
30
44
  let port = config.getSetting( config.setting.MEMORY_CACHE_REDIS_PORT );
31
45
  let db = config.getSetting( config.setting.MEMORY_CACHE_REDIS_DB );
@@ -33,17 +47,16 @@ class MessageMemoryCache {
33
47
  let user = config.getSetting( config.setting.MEMORY_CACHE_USER );
34
48
  let retryMaxAttempts = config.getSetting( config.setting.MEMORY_CACHE_RETRY_MAX_ATTEMPTS );
35
49
  let retryMaxInterval = config.getSetting( config.setting.MEMORY_CACHE_RETRY_MAX_INTERVAL );
36
- this.#redisClient = redis.createRedisClient( identifier, host, port, authKey, user, db, retryMaxInterval, retryMaxAttempts );
37
- }
38
50
 
39
- /* Public interface */
51
+ return this.#redisClient.initialize( host, port, authKey, user, db, retryMaxInterval, retryMaxAttempts );
52
+ }
40
53
 
41
54
  /**
42
55
  * Used to gracefully shut down the cache service.
43
56
  *
44
57
  * @method
45
58
  * @param {number} [timeoutMs]
46
- * @return {Promise}
59
+ * @returns {Promise}
47
60
  * @public
48
61
  */
49
62
  shutDown( timeoutMs ) {
@@ -150,13 +150,16 @@ class ServiceInstance {
150
150
  */
151
151
  onStart() {
152
152
  return new Promise( ( resolve, reject ) => {
153
- const DefaultMessageExchange = require( "#default-message-exchange" );
154
- const ServiceProvider = require( "#service-provider" );
155
- const ServiceConsumer = require( "#service-consumer" );
153
+ cache.instance.initialize().then( () => {
154
+ const DefaultMessageExchange = require( "#default-message-exchange" );
155
+ const ServiceProvider = require( "#service-provider" );
156
+ const ServiceConsumer = require( "#service-consumer" );
156
157
 
157
- let configureInbound = ( this instanceof ServiceProvider );
158
- let configureOutbound = ( this instanceof ServiceConsumer );
159
- messageDispatcher.instance.initialize( new DefaultMessageExchange( ServiceInstance.instanceID, ServiceInstance.serviceDomainName ), configureInbound, configureOutbound ).then( () => {
158
+ let configureInbound = ( this instanceof ServiceProvider );
159
+ let configureOutbound = ( this instanceof ServiceConsumer );
160
+
161
+ return messageDispatcher.instance.initialize( new DefaultMessageExchange( ServiceInstance.instanceID, ServiceInstance.serviceDomainName ), configureInbound, configureOutbound );
162
+ } ).then( () => {
160
163
  resolve();
161
164
  } ).catch( ( error ) => {
162
165
  reject( exceptions.raise( error ) );
@@ -87,99 +87,10 @@ class RedisClient {
87
87
  /**
88
88
  * @constructor
89
89
  * @param {string} identifier
90
- * @param {string} host
91
- * @param {number} port
92
- * @param {string} authKey
93
- * @param {string} user
94
- * @param {number} defaultDB
95
- * @param {number} [retryMaxIntervalMs=1000] Optional max backoff interval.
96
- * @param {number|undefined} [retryMaxAttempts=undefined] Optional max (re)connection attempts before abort.
90
+ * @returns {RedisClient}
97
91
  */
98
- constructor( identifier, host, port, authKey, user, defaultDB, retryMaxIntervalMs = 1000, retryMaxAttempts = undefined ) {
92
+ constructor( identifier ) {
99
93
  this.#clientIdentifier = identifier || "redis-client-" + tools.getUUID();
100
- this.#retryMaxInterval = retryMaxIntervalMs;
101
- this.#retryMaxAttempts = retryMaxAttempts;
102
-
103
- let retryStrategy = ( attempt ) => {
104
- let result = Math.min( attempt * 50, this.#retryMaxInterval );
105
-
106
- if ( this.#retryMaxAttempts != null && attempt > this.#retryMaxAttempts ) {
107
- logger.log( "In Redis retry strategy: reached max attempts for command retry. Aborting...", logger.logSeverity.WARNING, { attempts: attempt } );
108
- result = exceptions.raise( exceptions.exceptionCode.E_COM_RETRY_ATTEMPTS_EXCEEDED );
109
- }
110
-
111
- return result;
112
- };
113
-
114
- let reconnectOnError = ( error ) => {
115
- logger.log( `In Redis reconnect on error strategy: ${ error.message }`, logger.logSeverity.ERROR, error );
116
- return !!error.message.includes( "READONLY" );
117
- };
118
-
119
- let options = {
120
- port: port,
121
- host: host,
122
- username: user,
123
- password: authKey,
124
- db: defaultDB,
125
- autoResendUnfulfilledCommands: true,
126
- maxRetriesPerRequest: null,
127
- retryStrategy: retryStrategy,
128
- reconnectOnError: reconnectOnError
129
- };
130
-
131
- /** @type Redis */
132
- this.#redisClient = new Redis( options );
133
-
134
- this.#redisClient.on( "ready", () => {
135
- logger.log( `Connection to Redis server ${ host }:${ port } (re)established by client '${ this.identifier }' and is ready to be used.`, logger.logSeverity.INFO );
136
-
137
- // notify all connection observers about this event:
138
- _.forEach( this.#connectionObservers, ( connectionObservers ) => {
139
- connectionObservers.onConnectionRecovered( this.#clientIdentifier );
140
- } );
141
-
142
- // fetch the server information and store it:
143
- this.#redisClient.info().then( ( result ) => {
144
- this.#serverInfo = {};
145
- if ( _.isString( result ) ) {
146
- let rawData = _.split( result, "\r\n" );
147
- _.forEach( rawData, ( entry ) => {
148
- let details = _.split( entry, ":" );
149
- if ( !_.startsWith( details[ 0 ], "#" ) && details[ 0 ] !== "" && details[ 0 ] ) {
150
- if ( _.isNaN( _.toNumber( details[ 1 ] ) ) ) {
151
- this.#serverInfo[ details[ 0 ] ] = details[ 1 ];
152
- } else {
153
- this.#serverInfo[ details[ 0 ] ] = _.toNumber( details[ 1 ] );
154
- }
155
- }
156
- } );
157
- }
158
- return this.#redisClient.module( "LIST" );
159
- } ).then( ( result ) => {
160
- this.#serverFeatures = {};
161
- if ( _.isArray( result ) ) {
162
- _.forEach( result, ( entry ) => {
163
- this.#serverFeatures[ entry[ 1 ] ] = entry[ 3 ];
164
- } );
165
- }
166
- } ).catch( ( error ) => {
167
- logger.log( `Failed to fetch server information by client '${ this.identifier }'!`, logger.logSeverity.WARNING, error );
168
- } );
169
- } );
170
- this.#redisClient.on( "error", ( error ) => {
171
- logger.log( `Error received in Redis client '${ this.identifier }'.`, logger.logSeverity.ERROR, error );
172
-
173
- // notify all connection observers about this event:
174
- _.forEach( this.#connectionObservers, ( connectionObservers ) => {
175
- connectionObservers.onConnectionDisrupted( this.#clientIdentifier );
176
- } );
177
- } );
178
- this.#redisClient.on( "reconnecting", ( info ) => {
179
- if ( info.attempt > 1 ) {
180
- logger.log( `Client '${ this.identifier }' reconnecting to Redis server after ${ info.delay } ms. This is attempt ${ info.attempt }.`, logger.logSeverity.DEBUG );
181
- }
182
- } );
183
94
  }
184
95
 
185
96
  /* Public interface */
@@ -188,7 +99,7 @@ class RedisClient {
188
99
  * Used to return the Redis client identifier.
189
100
  *
190
101
  * @property
191
- * @return {string}
102
+ * @returns {string}
192
103
  * @public
193
104
  */
194
105
  get identifier() {
@@ -199,7 +110,7 @@ class RedisClient {
199
110
  * Used to return the Redis server version.
200
111
  *
201
112
  * @property
202
- * @return {number}
113
+ * @returns {number}
203
114
  * @public
204
115
  */
205
116
  get serverVersion() {
@@ -218,6 +129,92 @@ class RedisClient {
218
129
  return !_.isNil( this.#serverFeatures[ "ReJSON" ] ) || !_.isNil( this.#serverFeatures[ "ReJSON2" ] );
219
130
  }
220
131
 
132
+ /**
133
+ * Used to initialize the Redis client.
134
+ *
135
+ * @method
136
+ * @param {string} host
137
+ * @param {number} port
138
+ * @param {string} authKey
139
+ * @param {string} user
140
+ * @param {number} defaultDB
141
+ * @param {number} [retryMaxIntervalMs=1000] Optional max backoff interval.
142
+ * @param {number|undefined} [retryMaxAttempts=undefined] Optional max (re)connection attempts before abort.
143
+ * @public
144
+ */
145
+ initialize( host, port, authKey, user, defaultDB, retryMaxIntervalMs = 1000, retryMaxAttempts = undefined ) {
146
+ return new Promise( ( resolve, reject ) => {
147
+ if ( this.#redisClient ) {
148
+ resolve();
149
+ } else {
150
+ try {
151
+ this.#retryMaxInterval = retryMaxIntervalMs;
152
+ this.#retryMaxAttempts = retryMaxAttempts;
153
+
154
+ let retryStrategy = ( attempt ) => {
155
+ let result = Math.min( attempt * 50, this.#retryMaxInterval );
156
+
157
+ if ( this.#retryMaxAttempts != null && attempt > this.#retryMaxAttempts ) {
158
+ logger.log( "In Redis retry strategy: reached max attempts for command retry. Aborting...", logger.logSeverity.WARNING, { attempts: attempt } );
159
+ result = exceptions.raise( exceptions.exceptionCode.E_COM_RETRY_ATTEMPTS_EXCEEDED );
160
+ }
161
+
162
+ return result;
163
+ };
164
+
165
+ let reconnectOnError = ( error ) => {
166
+ logger.log( `In Redis reconnect on error strategy: ${ error.message }`, logger.logSeverity.ERROR, error );
167
+ return !!error.message.includes( "READONLY" );
168
+ };
169
+
170
+ let options = {
171
+ port: port,
172
+ host: host,
173
+ username: user,
174
+ password: authKey,
175
+ db: defaultDB,
176
+ autoResendUnfulfilledCommands: true,
177
+ maxRetriesPerRequest: null,
178
+ retryStrategy: retryStrategy,
179
+ reconnectOnError: reconnectOnError
180
+ };
181
+
182
+ /** @type Redis */
183
+ this.#redisClient = new Redis( options );
184
+
185
+ this.#redisClient.on( "ready", () => {
186
+ logger.log( `Connection to Redis server ${ host }:${ port } (re)established by client '${ this.identifier }' and is ready to be used.`, logger.logSeverity.INFO );
187
+
188
+ // Notify all connection observers about this event:
189
+ _.forEach( this.#connectionObservers, ( connectionObservers ) => {
190
+ connectionObservers.onConnectionRecovered( this.#clientIdentifier );
191
+ } );
192
+
193
+ // Fetch the server information and store it:
194
+ this.#fetchServerInfo();
195
+
196
+ resolve();
197
+ } );
198
+ this.#redisClient.on( "error", ( error ) => {
199
+ logger.log( `Error received in Redis client '${ this.identifier }'.`, logger.logSeverity.ERROR, error );
200
+
201
+ // Notify all connection observers about this event:
202
+ _.forEach( this.#connectionObservers, ( connectionObservers ) => {
203
+ connectionObservers.onConnectionDisrupted( this.#clientIdentifier );
204
+ } );
205
+ } );
206
+ this.#redisClient.on( "reconnecting", ( info ) => {
207
+ if ( info.attempt > 1 ) {
208
+ logger.log( `Client '${ this.identifier }' reconnecting to Redis server after ${ info.delay } ms. This is attempt ${ info.attempt }.`, logger.logSeverity.DEBUG );
209
+ }
210
+ } );
211
+ } catch ( error ) {
212
+ reject( exceptions.raise( error ) );
213
+ }
214
+ }
215
+ } );
216
+ }
217
+
221
218
  /**
222
219
  * Used to register a new {@link ConnectionObserver} for events related to the Redis connection state.
223
220
  *
@@ -238,7 +235,7 @@ class RedisClient {
238
235
  *
239
236
  * @method
240
237
  * @param {Array[]} commands
241
- * @return {Promise<*>}
238
+ * @returns {Promise<*>}
242
239
  * @public
243
240
  */
244
241
  executeCommands( commands ) {
@@ -259,7 +256,7 @@ class RedisClient {
259
256
  * @method
260
257
  * @param {string} command
261
258
  * @param {Array} commandArguments
262
- * @return {Promise<*>}
259
+ * @returns {Promise<*>}
263
260
  * @public
264
261
  */
265
262
  blockingCommand( command, commandArguments ) {
@@ -278,7 +275,7 @@ class RedisClient {
278
275
  * @method
279
276
  * @param {string} channel
280
277
  * @param {(Object|string)} message
281
- * @return {Promise<number>}
278
+ * @returns {Promise<number>}
282
279
  * @public
283
280
  */
284
281
  publishCommand( channel, message ) {
@@ -299,7 +296,7 @@ class RedisClient {
299
296
  * @method
300
297
  * @param {string} channel Unique identifier of the channel to subscribe to.
301
298
  * @param {function( Object )} messageHandler Will execute this handler every time a new message is received.
302
- * @return {Promise}
299
+ * @returns {Promise}
303
300
  * @public
304
301
  */
305
302
  subscribeCommand( channel, messageHandler ) {
@@ -343,7 +340,7 @@ class RedisClient {
343
340
  *
344
341
  * @method
345
342
  * @param {string} channel Unique identifier of the channel to unsubscribe from.
346
- * @return {Promise}
343
+ * @returns {Promise}
347
344
  * @public
348
345
  */
349
346
  unsubscribeCommand( channel ) {
@@ -424,6 +421,43 @@ class RedisClient {
424
421
  } );
425
422
  }
426
423
 
424
+ /* Private interface */
425
+
426
+ /**
427
+ * Used to fetch and store Redis server information.
428
+ *
429
+ * @method
430
+ * @private
431
+ */
432
+ #fetchServerInfo() {
433
+ this.#redisClient.info().then( ( result ) => {
434
+ this.#serverInfo = {};
435
+ if ( _.isString( result ) ) {
436
+ let rawData = _.split( result, "\r\n" );
437
+ _.forEach( rawData, ( entry ) => {
438
+ let details = _.split( entry, ":" );
439
+ if ( !_.startsWith( details[ 0 ], "#" ) && details[ 0 ] !== "" && details[ 0 ] ) {
440
+ if ( _.isNaN( _.toNumber( details[ 1 ] ) ) ) {
441
+ this.#serverInfo[ details[ 0 ] ] = details[ 1 ];
442
+ } else {
443
+ this.#serverInfo[ details[ 0 ] ] = _.toNumber( details[ 1 ] );
444
+ }
445
+ }
446
+ } );
447
+ }
448
+ return this.#redisClient.module( "LIST" );
449
+ } ).then( ( result ) => {
450
+ this.#serverFeatures = {};
451
+ if ( _.isArray( result ) ) {
452
+ _.forEach( result, ( entry ) => {
453
+ this.#serverFeatures[ entry[ 1 ] ] = entry[ 3 ];
454
+ } );
455
+ }
456
+ } ).catch( ( error ) => {
457
+ logger.log( `Failed to fetch server information by client '${ this.identifier }'!`, logger.logSeverity.WARNING, error );
458
+ } );
459
+ }
460
+
427
461
  }
428
462
 
429
463
  /**
@@ -431,16 +465,9 @@ class RedisClient {
431
465
  *
432
466
  * @method
433
467
  * @param {string} identifier
434
- * @param {string} host
435
- * @param {number} [port=6379]
436
- * @param {string} [authKey=undefined]
437
- * @param {string} [user="default"]
438
- * @param {number} [defaultDB=0]
439
- * @param {number} [retryMaxIntervalMs=1000]
440
- * @param {number|undefined} [retryMaxAttempts=undefined]
441
468
  * @return {RedisClient}
442
469
  * @public
443
470
  */
444
- module.exports.createRedisClient = ( identifier, host, port = 6379, authKey = undefined, user = "default", defaultDB = 0, retryMaxIntervalMs = 1000, retryMaxAttempts = undefined ) => {
445
- return Object.freeze( new RedisClient( identifier, host, port, authKey, user, defaultDB, retryMaxIntervalMs, retryMaxAttempts ) );
471
+ module.exports.createRedisClient = ( identifier ) => {
472
+ return Object.freeze( new RedisClient( identifier ) );
446
473
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ti-engine/core",
3
- "version": "1.1.8",
3
+ "version": "1.1.9",
4
4
  "description": "The ti-engine is an open source, free to use—both for personal and commercial projects—framework for the creation of microservice-based solutions using node.js.",
5
5
  "author": "Boris Kostadinov <kostadinov.boris@gmail.com>",
6
6
  "license": "GPL-3.0-or-later",
package/testRedis.js ADDED
@@ -0,0 +1,22 @@
1
+ import Redis from "ioredis";
2
+
3
+ const redisClient = new Redis( {
4
+ port: 17467,
5
+ host: "redis-17467.c300.eu-central-1-1.ec2.redns.redis-cloud.com",
6
+ username: "default",
7
+ password: "o49Iyj2JYnmD7NSFWa32x2iYqulWkIM1",
8
+ db: 0,
9
+ autoResendUnfulfilledCommands: true,
10
+ maxRetriesPerRequest: null,
11
+ retryStrategy: ( attempt ) => {
12
+ let result = Math.min( attempt * 50, 1000 );
13
+ }
14
+ });
15
+
16
+ redisClient.on('error', err => console.log('Redis Client Error', err));
17
+
18
+ //await redisClient.connect();
19
+
20
+ await redisClient.set('foo', 'bar');
21
+ const result = await redisClient.get('foo');
22
+ console.log(result);
package/utils/cache.js CHANGED
@@ -36,13 +36,6 @@ class CommonMemoryCache extends ConnectionObserver {
36
36
  super();
37
37
 
38
38
  if ( !CommonMemoryCache.#instance ) {
39
- let host = config.getSetting( config.setting.MEMORY_CACHE_REDIS_HOST );
40
- let port = config.getSetting( config.setting.MEMORY_CACHE_REDIS_PORT );
41
- let db = config.getSetting( config.setting.MEMORY_CACHE_REDIS_DB );
42
- let authKey = config.getSetting( config.setting.MEMORY_CACHE_AUTH_KEY );
43
- let user = config.getSetting( config.setting.MEMORY_CACHE_USER );
44
- this.#redisClient = redis.createRedisClient( this.#connectionIdentifier, host, port, authKey, user, db );
45
- this.#redisClient.addConnectionObserver( this );
46
39
  CommonMemoryCache.#instance = this;
47
40
  }
48
41
  return CommonMemoryCache.#instance;
@@ -72,6 +65,26 @@ class CommonMemoryCache extends ConnectionObserver {
72
65
  return this.#connectionIdentifier;
73
66
  }
74
67
 
68
+ /**
69
+ * Used to initialize the cache service.
70
+ *
71
+ * @method
72
+ * @returns {Promise}
73
+ * @public
74
+ */
75
+ initialize() {
76
+ let host = config.getSetting( config.setting.MEMORY_CACHE_REDIS_HOST );
77
+ let port = config.getSetting( config.setting.MEMORY_CACHE_REDIS_PORT );
78
+ let db = config.getSetting( config.setting.MEMORY_CACHE_REDIS_DB );
79
+ let authKey = config.getSetting( config.setting.MEMORY_CACHE_AUTH_KEY );
80
+ let user = config.getSetting( config.setting.MEMORY_CACHE_USER );
81
+
82
+ this.#redisClient = redis.createRedisClient( this.#connectionIdentifier );
83
+ this.#redisClient.addConnectionObserver( this );
84
+
85
+ return this.#redisClient.initialize( host, port, authKey, user, db );
86
+ }
87
+
75
88
  /**
76
89
  * Used to gracefully shut down the cache service.
77
90
  *