@jetit/publisher 1.2.0 → 1.3.2
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/package.json +1 -1
- package/src/lib/redis/streams.d.ts +4 -1
- package/src/lib/redis/streams.js +62 -42
package/package.json
CHANGED
|
@@ -7,6 +7,7 @@ export declare class Streams {
|
|
|
7
7
|
private _redisGroups?;
|
|
8
8
|
private consumerGroupName;
|
|
9
9
|
private instanceId;
|
|
10
|
+
private instanceUniqueId;
|
|
10
11
|
private cleanUpTimer;
|
|
11
12
|
private eventsListened;
|
|
12
13
|
get redisPublisher(): RedisType;
|
|
@@ -148,7 +149,9 @@ export declare class Streams {
|
|
|
148
149
|
*/
|
|
149
150
|
close(): Promise<void>;
|
|
150
151
|
private clearSubscribedEvents;
|
|
152
|
+
private registerSubscribedEvent;
|
|
151
153
|
private registerConsumerGroup;
|
|
152
|
-
|
|
154
|
+
private registerConsumerGroupName;
|
|
155
|
+
releaseAllClaims(streamName: string): Promise<void>;
|
|
153
156
|
private cleanupAcknowledgedMessages;
|
|
154
157
|
}
|
package/src/lib/redis/streams.js
CHANGED
|
@@ -40,13 +40,14 @@ class Streams {
|
|
|
40
40
|
* const streams = new Streams('POS');
|
|
41
41
|
*/
|
|
42
42
|
constructor(serviceName) {
|
|
43
|
-
var _a;
|
|
43
|
+
var _a, _b;
|
|
44
44
|
this.eventsListened = [];
|
|
45
|
-
this.
|
|
45
|
+
this.instanceUniqueId = (_a = process.env['INSTANCE_ID']) !== null && _a !== void 0 ? _a : (0, id_1.generateID)('HEX', 'FE');
|
|
46
|
+
this.instanceId = `${serviceName}:${this.instanceUniqueId}`;
|
|
46
47
|
console.log(this.instanceId);
|
|
47
48
|
this.consumerGroupName = `cg-${serviceName}`;
|
|
48
|
-
const cleanUpInterval = (
|
|
49
|
-
setTimeout(() => this.runClear(cleanUpInterval),
|
|
49
|
+
const cleanUpInterval = (_b = parseInt(process.env['CLEANUP_INTERVAL'] || `${1000 * 60 * 60}`, 10)) !== null && _b !== void 0 ? _b : 1000 * 60 * 60;
|
|
50
|
+
setTimeout(() => this.runClear(cleanUpInterval), 60000);
|
|
50
51
|
this.cleanUpTimer = setInterval(() => {
|
|
51
52
|
this.runClear(cleanUpInterval);
|
|
52
53
|
}, cleanUpInterval);
|
|
@@ -199,6 +200,7 @@ class Streams {
|
|
|
199
200
|
*/
|
|
200
201
|
listen(eventName, maxRetries = 5, initialDelay = 1000) {
|
|
201
202
|
this.registerConsumerGroup(eventName); // Registers the consumer group for listening to the message
|
|
203
|
+
this.registerConsumerGroupName().then();
|
|
202
204
|
this.eventsListened.push(eventName);
|
|
203
205
|
return this.listenInternals(eventName).pipe((0, rxjs_1.retry)({
|
|
204
206
|
count: maxRetries,
|
|
@@ -214,7 +216,8 @@ class Streams {
|
|
|
214
216
|
}
|
|
215
217
|
listenInternals(eventName) {
|
|
216
218
|
try {
|
|
217
|
-
this.createConsumerGroup(eventName);
|
|
219
|
+
this.createConsumerGroup(eventName).then();
|
|
220
|
+
this.registerSubscribedEvent(eventName).then();
|
|
218
221
|
const bs = new rxjs_1.BehaviorSubject(null);
|
|
219
222
|
const observable = bs.asObservable().pipe((0, rxjs_1.skip)(1));
|
|
220
223
|
const streamName = `${eventName}:${this.consumerGroupName}`;
|
|
@@ -272,24 +275,25 @@ class Streams {
|
|
|
272
275
|
*/
|
|
273
276
|
republishUnprocessedEvents(eventName) {
|
|
274
277
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
278
|
+
console.log('Republishing Unprocessed Events.');
|
|
275
279
|
const streamName = `${eventName}:${this.consumerGroupName}`;
|
|
276
280
|
const result = yield this.redisGroups.xreadgroup('GROUP', this.consumerGroupName, this.instanceId, 'STREAMS', streamName, '>');
|
|
277
|
-
if (result)
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
281
|
+
if (!result)
|
|
282
|
+
return;
|
|
283
|
+
const [, streamMessages] = result[0];
|
|
284
|
+
if (!streamMessages)
|
|
285
|
+
return;
|
|
286
|
+
console.log(`Unprocessed events: ${streamMessages.length}`);
|
|
287
|
+
for (const [id, data] of streamMessages) {
|
|
288
|
+
const transaction = this.redisGroups.multi({ pipeline: true });
|
|
289
|
+
const eventData = JSON.parse(data[1]);
|
|
290
|
+
// Republishing the events
|
|
291
|
+
transaction.xadd(streamName, '*', 'data', JSON.stringify(eventData));
|
|
292
|
+
transaction.publish(eventName, '');
|
|
293
|
+
transaction.xack(streamName, this.consumerGroupName, id);
|
|
294
|
+
yield transaction.exec().catch(publisherErrorHandler);
|
|
295
|
+
console.log(`Event ${eventName} with ID: ${id} published`);
|
|
296
|
+
yield transaction.exec();
|
|
293
297
|
}
|
|
294
298
|
});
|
|
295
299
|
}
|
|
@@ -309,6 +313,7 @@ class Streams {
|
|
|
309
313
|
*/
|
|
310
314
|
recoverCrashedConsumerMessages(eventName, idleTimeout = 30000) {
|
|
311
315
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
316
|
+
console.log(`PUBLISHER: Running recoverCrashedConsumerMessages`);
|
|
312
317
|
const streamName = `${eventName}:${this.consumerGroupName}`;
|
|
313
318
|
const pendingMessages = (yield this.redisGroups.xpending(streamName, this.consumerGroupName));
|
|
314
319
|
if (!pendingMessages)
|
|
@@ -317,24 +322,24 @@ class Streams {
|
|
|
317
322
|
if (!consumers || consumers.length === 0)
|
|
318
323
|
return;
|
|
319
324
|
for (const [consumer, pendingCount] of consumers) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
325
|
+
if (parseInt(pendingCount) < 0)
|
|
326
|
+
return;
|
|
327
|
+
const pending = (yield this.redisGroups.xpending(streamName, this.consumerGroupName, minId, maxId, Number(pendingCount), consumer));
|
|
328
|
+
if (!pending)
|
|
329
|
+
return;
|
|
330
|
+
for (const [messageId] of pending) {
|
|
331
|
+
const claimedMessage = (yield this.redisGroups.xclaim(streamName, this.consumerGroupName, this.instanceId, idleTimeout, messageId));
|
|
332
|
+
if (claimedMessage) {
|
|
333
|
+
console.log({ claimedMessage: JSON.stringify(claimedMessage) });
|
|
334
|
+
const [, data] = claimedMessage[0];
|
|
335
|
+
const eventData = JSON.parse(data[1]);
|
|
336
|
+
const transaction = this.redisGroups.multi();
|
|
337
|
+
transaction.xadd(streamName, '*', 'data', JSON.stringify(eventData));
|
|
338
|
+
transaction.publish(eventName, '');
|
|
339
|
+
transaction.xack(streamName, this.consumerGroupName, messageId);
|
|
340
|
+
yield transaction.exec().catch(publisherErrorHandler);
|
|
335
341
|
}
|
|
336
342
|
}
|
|
337
|
-
yield transaction.exec();
|
|
338
343
|
}
|
|
339
344
|
});
|
|
340
345
|
}
|
|
@@ -381,36 +386,51 @@ class Streams {
|
|
|
381
386
|
let x = this.eventsListened.length;
|
|
382
387
|
for (const eventName of this.eventsListened) {
|
|
383
388
|
console.log(`${eventName} is being cleared in publisher`);
|
|
389
|
+
const streamName = `${eventName}:${this.consumerGroupName}`;
|
|
384
390
|
yield this.redisGroups.srem(`${eventName}`, this.consumerGroupName);
|
|
385
391
|
console.log(`${eventName} is removed from ${this.consumerGroupName}`);
|
|
386
392
|
// Releasing all claims based on info from: https://redis.io/commands/xgroup-delconsumer/
|
|
387
|
-
yield this.releaseAllClaims(
|
|
393
|
+
yield this.releaseAllClaims(streamName);
|
|
388
394
|
console.log(`${eventName} removes all claims`);
|
|
389
|
-
yield this.redisGroups.xgroup('DELCONSUMER',
|
|
395
|
+
yield this.redisGroups.xgroup('DELCONSUMER', streamName, this.consumerGroupName, this.instanceId);
|
|
390
396
|
console.log(`${eventName} is deleted as a consumer from ${this.consumerGroupName}, ${this.instanceId}`);
|
|
391
397
|
console.log(x--);
|
|
392
398
|
}
|
|
393
399
|
});
|
|
394
400
|
}
|
|
401
|
+
registerSubscribedEvent(eventName) {
|
|
402
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
403
|
+
const key = `instance:${this.instanceId}:subscribedEvents`;
|
|
404
|
+
console.log(`Registering event name for ${this.consumerGroupName} with key : ${key}`);
|
|
405
|
+
yield this.redisGroups.sadd(key, eventName);
|
|
406
|
+
});
|
|
407
|
+
}
|
|
395
408
|
registerConsumerGroup(eventName) {
|
|
396
409
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
397
410
|
yield this.redisGroups.sadd(`${eventName}`, this.consumerGroupName);
|
|
398
411
|
});
|
|
399
412
|
}
|
|
400
|
-
|
|
413
|
+
registerConsumerGroupName() {
|
|
414
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
415
|
+
const key = `instance:${this.instanceUniqueId}:consumerGroupName`;
|
|
416
|
+
console.log(`Registering service name ${this.consumerGroupName} for key : ${key}`);
|
|
417
|
+
yield this.redisGroups.set(key, this.consumerGroupName);
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
releaseAllClaims(streamName) {
|
|
401
421
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
402
422
|
/**
|
|
403
423
|
* Retrieve the pending messages for the consumer. Note this only fetches the last
|
|
404
424
|
* 10000 events assigned to this consumer. This function has been modified to make sure
|
|
405
425
|
* that there is a temp instance that claims all this messages
|
|
406
426
|
*/
|
|
407
|
-
const pendingMessages = (yield this.redisGroups.xpending(
|
|
427
|
+
const pendingMessages = (yield this.redisGroups.xpending(streamName, this.consumerGroupName, '-', '+', 10000, this.instanceId));
|
|
408
428
|
if (pendingMessages && pendingMessages.length > 0) {
|
|
409
429
|
console.log(`${pendingMessages.length} messages to clean up.`);
|
|
410
430
|
const transaction = this.redisGroups.multi({ pipeline: true });
|
|
411
431
|
const tempConsumerId = `${this.instanceId}-temp`;
|
|
412
432
|
for (const [messageId] of pendingMessages) {
|
|
413
|
-
transaction.xclaim(
|
|
433
|
+
transaction.xclaim(streamName, this.consumerGroupName, tempConsumerId, 10, messageId);
|
|
414
434
|
}
|
|
415
435
|
yield transaction.exec();
|
|
416
436
|
}
|