@jetit/publisher 5.0.0 → 5.1.1
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/monitoring/adapters/prom.d.ts +5 -0
- package/src/lib/monitoring/adapters/prom.js +53 -0
- package/src/lib/monitoring/collector.d.ts +5 -0
- package/src/lib/monitoring/collector.js +57 -3
- package/src/lib/monitoring/tracker.d.ts +5 -0
- package/src/lib/monitoring/tracker.js +47 -0
- package/src/lib/monitoring/types.d.ts +22 -1
- package/src/lib/redis/duplication.js +3 -1
- package/src/lib/redis/scheduler.js +1 -1
- package/src/lib/redis/streams.js +25 -3
package/package.json
CHANGED
|
@@ -21,6 +21,11 @@ export declare class PrometheusAdapter {
|
|
|
21
21
|
private subscribeErrorCount;
|
|
22
22
|
private individualQueueDepth;
|
|
23
23
|
private duplicateEventCount;
|
|
24
|
+
private messageRatePublish;
|
|
25
|
+
private messageRateSubscribe;
|
|
26
|
+
private processingTimeHistogram;
|
|
27
|
+
private redisCommandLatency;
|
|
28
|
+
private consumerLag;
|
|
24
29
|
/**
|
|
25
30
|
*
|
|
26
31
|
* @param streams [Publisher]
|
|
@@ -75,6 +75,36 @@ class PrometheusAdapter {
|
|
|
75
75
|
labelNames: ['queue_name'],
|
|
76
76
|
registers: [this.registry],
|
|
77
77
|
});
|
|
78
|
+
this.messageRatePublish = new this.promClient.Gauge({
|
|
79
|
+
name: 'message_rate_publish',
|
|
80
|
+
help: 'Number of messages published per event type',
|
|
81
|
+
labelNames: ['event_type'],
|
|
82
|
+
registers: [this.registry],
|
|
83
|
+
});
|
|
84
|
+
this.messageRateSubscribe = new this.promClient.Gauge({
|
|
85
|
+
name: 'message_rate_subscribe',
|
|
86
|
+
help: 'Number of messages subscribed per event type',
|
|
87
|
+
labelNames: ['event_type'],
|
|
88
|
+
registers: [this.registry],
|
|
89
|
+
});
|
|
90
|
+
this.processingTimeHistogram = new this.promClient.Histogram({
|
|
91
|
+
name: 'processing_time_histogram',
|
|
92
|
+
help: 'Distribution of event processing times',
|
|
93
|
+
buckets: [0, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000],
|
|
94
|
+
registers: [this.registry],
|
|
95
|
+
});
|
|
96
|
+
this.redisCommandLatency = new this.promClient.Gauge({
|
|
97
|
+
name: 'redis_command_latency',
|
|
98
|
+
help: 'Latency of Redis commands',
|
|
99
|
+
labelNames: ['command'],
|
|
100
|
+
registers: [this.registry],
|
|
101
|
+
});
|
|
102
|
+
this.consumerLag = new this.promClient.Gauge({
|
|
103
|
+
name: 'consumer_lag',
|
|
104
|
+
help: 'Lag of consumers in milliseconds',
|
|
105
|
+
labelNames: ['consumer_group'],
|
|
106
|
+
registers: [this.registry],
|
|
107
|
+
});
|
|
78
108
|
this.registry.registerMetric(this.queueDepth);
|
|
79
109
|
this.registry.registerMetric(this.dlqSize);
|
|
80
110
|
this.registry.registerMetric(this.dlqRate);
|
|
@@ -87,6 +117,11 @@ class PrometheusAdapter {
|
|
|
87
117
|
this.registry.registerMetric(this.subscribeErrorCount);
|
|
88
118
|
this.registry.registerMetric(this.individualQueueDepth);
|
|
89
119
|
this.registry.registerMetric(this.duplicateEventCount);
|
|
120
|
+
this.registry.registerMetric(this.messageRatePublish);
|
|
121
|
+
this.registry.registerMetric(this.messageRateSubscribe);
|
|
122
|
+
this.registry.registerMetric(this.processingTimeHistogram);
|
|
123
|
+
this.registry.registerMetric(this.redisCommandLatency);
|
|
124
|
+
this.registry.registerMetric(this.consumerLag);
|
|
90
125
|
}
|
|
91
126
|
async updateMetrics() {
|
|
92
127
|
const metrics = await this.streams.getLatestMetrics();
|
|
@@ -111,6 +146,24 @@ class PrometheusAdapter {
|
|
|
111
146
|
this.individualQueueDepth.set({ queue_name: queueName }, depth);
|
|
112
147
|
});
|
|
113
148
|
}
|
|
149
|
+
Object.entries(metrics.messageRatePublish).forEach(([eventType, rate]) => {
|
|
150
|
+
this.messageRatePublish.set({ event_type: eventType }, rate);
|
|
151
|
+
});
|
|
152
|
+
Object.entries(metrics.messageRateSubscribe).forEach(([eventType, rate]) => {
|
|
153
|
+
this.messageRateSubscribe.set({ event_type: eventType }, rate);
|
|
154
|
+
});
|
|
155
|
+
Object.entries(metrics.processingTimeDistribution).forEach(([bucket, count]) => {
|
|
156
|
+
const [lower, upper] = bucket.split('-').map(Number);
|
|
157
|
+
for (let i = 0; i < count; i++) {
|
|
158
|
+
this.processingTimeHistogram.observe(upper || lower);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
Object.entries(metrics.redisCommandLatencies).forEach(([command, latency]) => {
|
|
162
|
+
this.redisCommandLatency.set({ command }, latency);
|
|
163
|
+
});
|
|
164
|
+
Object.entries(metrics.consumerLag).forEach(([consumerGroup, lag]) => {
|
|
165
|
+
this.consumerLag.set({ consumer_group: consumerGroup }, lag);
|
|
166
|
+
});
|
|
114
167
|
}
|
|
115
168
|
/**
|
|
116
169
|
* @param app This needs to be an instance of express or fastify something that supports the express api
|
|
@@ -10,6 +10,11 @@ export declare class MetricsCollector {
|
|
|
10
10
|
constructor(config: IMetricsCollectorConfig, dlq: DeadLetterQueue);
|
|
11
11
|
addMetrics(metrics: IPerformanceMetrics): void;
|
|
12
12
|
private aggregateAndStoreMetrics;
|
|
13
|
+
private aggregateMessageRates;
|
|
14
|
+
private aggregateHistogram;
|
|
15
|
+
private aggregateRedisCommandLatencies;
|
|
16
|
+
private aggregateConsumerLag;
|
|
17
|
+
private calculateAverages;
|
|
13
18
|
private getQueueDepth;
|
|
14
19
|
private storeMetrics;
|
|
15
20
|
getMetrics(startTime: number, endTime: number): Promise<IAggregatedMetrics[]>;
|
|
@@ -32,6 +32,11 @@ class MetricsCollector {
|
|
|
32
32
|
dlqRate: 0,
|
|
33
33
|
operationCount: this.metrics.length,
|
|
34
34
|
duplicateEventsCount: 0,
|
|
35
|
+
messageRatePublish: {},
|
|
36
|
+
messageRateSubscribe: {},
|
|
37
|
+
processingTimeDistribution: {},
|
|
38
|
+
redisCommandLatencies: {},
|
|
39
|
+
consumerLag: {},
|
|
35
40
|
};
|
|
36
41
|
for (const metric of this.metrics) {
|
|
37
42
|
aggregated.totalTime += metric.totalTime;
|
|
@@ -41,6 +46,11 @@ class MetricsCollector {
|
|
|
41
46
|
aggregated.publishErrorCount += metric.publishErrorCount;
|
|
42
47
|
aggregated.subscribeErrorCount += metric.subscribeErrorCount;
|
|
43
48
|
aggregated.duplicateEventsCount += metric.duplicateEventsCount;
|
|
49
|
+
this.aggregateMessageRates(aggregated.messageRatePublish, metric.messageRatePublish);
|
|
50
|
+
this.aggregateMessageRates(aggregated.messageRateSubscribe, metric.messageRateSubscribe);
|
|
51
|
+
this.aggregateHistogram(aggregated.processingTimeDistribution, metric.processingTimeDistribution);
|
|
52
|
+
this.aggregateRedisCommandLatencies(aggregated.redisCommandLatencies, metric.redisCommandLatencies);
|
|
53
|
+
this.aggregateConsumerLag(aggregated.consumerLag, metric.consumerLag);
|
|
44
54
|
}
|
|
45
55
|
// Calculate averages for time-based metrics
|
|
46
56
|
aggregated.totalTime /= aggregated.operationCount;
|
|
@@ -49,21 +59,65 @@ class MetricsCollector {
|
|
|
49
59
|
const dlqStats = await this.dlq.getDLQStats();
|
|
50
60
|
aggregated.dlqSize = dlqStats.size;
|
|
51
61
|
aggregated.dlqRate = dlqStats.additionRate;
|
|
62
|
+
this.calculateAverages(aggregated.redisCommandLatencies);
|
|
63
|
+
this.calculateAverages(aggregated.consumerLag);
|
|
52
64
|
await this.storeMetrics(aggregated);
|
|
53
65
|
this.metrics = [];
|
|
54
66
|
}
|
|
67
|
+
aggregateMessageRates(target, source) {
|
|
68
|
+
for (const [eventType, count] of Object.entries(source)) {
|
|
69
|
+
if (!target[eventType])
|
|
70
|
+
target[eventType] = 0;
|
|
71
|
+
target[eventType] += count;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
aggregateHistogram(target, source) {
|
|
75
|
+
for (const [bucket, count] of Object.entries(source)) {
|
|
76
|
+
if (!target[bucket])
|
|
77
|
+
target[bucket] = 0;
|
|
78
|
+
target[bucket] += count;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
aggregateRedisCommandLatencies(target, source) {
|
|
82
|
+
for (const [command, { total, count }] of Object.entries(source)) {
|
|
83
|
+
if (!target[command]) {
|
|
84
|
+
target[command] = { total: 0, count: 0 };
|
|
85
|
+
}
|
|
86
|
+
target[command].total += total;
|
|
87
|
+
target[command].count += count;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
aggregateConsumerLag(target, source) {
|
|
91
|
+
for (const [consumerGroup, { total, count }] of Object.entries(source)) {
|
|
92
|
+
if (!target[consumerGroup]) {
|
|
93
|
+
target[consumerGroup] = { total: 0, count: 0 };
|
|
94
|
+
}
|
|
95
|
+
target[consumerGroup].total += total;
|
|
96
|
+
target[consumerGroup].count += count;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
calculateAverages(data) {
|
|
100
|
+
for (const key in data) {
|
|
101
|
+
if (data[key].count > 0) {
|
|
102
|
+
data[key].total = data[key].total / data[key].count;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
data[key].total = 0;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
55
109
|
async getQueueDepth() {
|
|
56
110
|
let totalDepth = 0;
|
|
57
111
|
const individualDepths = {};
|
|
58
112
|
let cursor = '0';
|
|
59
113
|
do {
|
|
60
|
-
const [nextCursor, keys] = await this.redisClient.scan(cursor, 'MATCH',
|
|
114
|
+
const [nextCursor, keys] = await this.redisClient.scan(cursor, 'MATCH', `ack:*:cg-*`, 'COUNT', 100);
|
|
61
115
|
cursor = nextCursor;
|
|
62
116
|
if (keys.length > 0) {
|
|
63
117
|
const pipeline = this.redisClient.pipeline();
|
|
64
118
|
keys.forEach((key) => {
|
|
65
|
-
pipeline.xlen(key);
|
|
66
|
-
pipeline.zcard(
|
|
119
|
+
pipeline.xlen(key.slice(4));
|
|
120
|
+
pipeline.zcard(key);
|
|
67
121
|
});
|
|
68
122
|
const results = await pipeline.exec();
|
|
69
123
|
if (!results) {
|
|
@@ -11,5 +11,10 @@ export declare class MetricsTracker {
|
|
|
11
11
|
incrementDuplicateEvent(): void;
|
|
12
12
|
incrementErrorCount(type: 'publish' | 'subscribe'): void;
|
|
13
13
|
getMetrics(): IPerformanceMetrics;
|
|
14
|
+
incrementMessageRate(type: 'publish' | 'subscribe', eventType: string): void;
|
|
15
|
+
addProcessingTime(time: number): void;
|
|
16
|
+
private getBucket;
|
|
17
|
+
addRedisCommandLatency(command: string, time: number): void;
|
|
18
|
+
setConsumerLag(consumerGroup: string, lag: number): void;
|
|
14
19
|
reset(): void;
|
|
15
20
|
}
|
|
@@ -12,6 +12,11 @@ class MetricsTracker {
|
|
|
12
12
|
publishErrorCount: 0,
|
|
13
13
|
subscribeErrorCount: 0,
|
|
14
14
|
duplicateEventsCount: 0,
|
|
15
|
+
messageRatePublish: {},
|
|
16
|
+
messageRateSubscribe: {},
|
|
17
|
+
processingTimeDistribution: {},
|
|
18
|
+
redisCommandLatencies: {},
|
|
19
|
+
consumerLag: {},
|
|
15
20
|
};
|
|
16
21
|
this.startTime = (0, node_process_1.hrtime)();
|
|
17
22
|
}
|
|
@@ -43,6 +48,43 @@ class MetricsTracker {
|
|
|
43
48
|
this.metrics.totalTime = totalSeconds * 1000 + totalNanoseconds / 1e6;
|
|
44
49
|
return { ...this.metrics };
|
|
45
50
|
}
|
|
51
|
+
incrementMessageRate(type, eventType) {
|
|
52
|
+
const key = `messageRate${type.charAt(0).toUpperCase() + type.slice(1)}`;
|
|
53
|
+
if (!this.metrics[key][eventType]) {
|
|
54
|
+
this.metrics[key][eventType] = 0;
|
|
55
|
+
}
|
|
56
|
+
this.metrics[key][eventType]++;
|
|
57
|
+
}
|
|
58
|
+
addProcessingTime(time) {
|
|
59
|
+
const bucket = this.getBucket(time);
|
|
60
|
+
if (!this.metrics.processingTimeDistribution[bucket]) {
|
|
61
|
+
this.metrics.processingTimeDistribution[bucket] = 0;
|
|
62
|
+
}
|
|
63
|
+
this.metrics.processingTimeDistribution[bucket]++;
|
|
64
|
+
}
|
|
65
|
+
getBucket(time) {
|
|
66
|
+
const buckets = [0, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000];
|
|
67
|
+
for (let i = 0; i < buckets.length; i++) {
|
|
68
|
+
if (time <= buckets[i]) {
|
|
69
|
+
return i === 0 ? `0-${buckets[i]}` : `${buckets[i - 1] + 1}-${buckets[i]}`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return `>${buckets[buckets.length - 1]}`;
|
|
73
|
+
}
|
|
74
|
+
addRedisCommandLatency(command, time) {
|
|
75
|
+
if (!this.metrics.redisCommandLatencies[command]) {
|
|
76
|
+
this.metrics.redisCommandLatencies[command] = { total: 0, count: 0 };
|
|
77
|
+
}
|
|
78
|
+
this.metrics.redisCommandLatencies[command].total += time;
|
|
79
|
+
this.metrics.redisCommandLatencies[command].count++;
|
|
80
|
+
}
|
|
81
|
+
setConsumerLag(consumerGroup, lag) {
|
|
82
|
+
if (!this.metrics.consumerLag[consumerGroup]) {
|
|
83
|
+
this.metrics.consumerLag[consumerGroup] = { total: 0, count: 0 };
|
|
84
|
+
}
|
|
85
|
+
this.metrics.consumerLag[consumerGroup].total += lag;
|
|
86
|
+
this.metrics.consumerLag[consumerGroup].count++;
|
|
87
|
+
}
|
|
46
88
|
reset() {
|
|
47
89
|
this.startTime = (0, node_process_1.hrtime)();
|
|
48
90
|
this.metrics = {
|
|
@@ -53,6 +95,11 @@ class MetricsTracker {
|
|
|
53
95
|
publishErrorCount: 0,
|
|
54
96
|
subscribeErrorCount: 0,
|
|
55
97
|
duplicateEventsCount: 0,
|
|
98
|
+
messageRatePublish: {},
|
|
99
|
+
messageRateSubscribe: {},
|
|
100
|
+
processingTimeDistribution: {},
|
|
101
|
+
redisCommandLatencies: {},
|
|
102
|
+
consumerLag: {},
|
|
56
103
|
};
|
|
57
104
|
}
|
|
58
105
|
}
|
|
@@ -19,7 +19,7 @@ export interface IMetricsCollectorConfig {
|
|
|
19
19
|
collectionInterval: number;
|
|
20
20
|
retentionPeriod: number;
|
|
21
21
|
}
|
|
22
|
-
export type TQueryableMetrics = Omit<IAggregatedMetrics, 'timestamp' | 'individualQueueDepts'>;
|
|
22
|
+
export type TQueryableMetrics = Omit<IAggregatedMetrics, 'timestamp' | 'individualQueueDepts' | 'messageRatePublish' | 'messageRateSubscribe' | 'processingTimeDistribution' | 'redisCommandLatencies' | 'consumerLag'>;
|
|
23
23
|
export interface IQueueDepths {
|
|
24
24
|
total: number;
|
|
25
25
|
individual: {
|
|
@@ -34,4 +34,25 @@ export interface IPerformanceMetrics {
|
|
|
34
34
|
publishErrorCount: number;
|
|
35
35
|
subscribeErrorCount: number;
|
|
36
36
|
duplicateEventsCount: number;
|
|
37
|
+
messageRatePublish: {
|
|
38
|
+
[eventType: string]: number;
|
|
39
|
+
};
|
|
40
|
+
messageRateSubscribe: {
|
|
41
|
+
[eventType: string]: number;
|
|
42
|
+
};
|
|
43
|
+
processingTimeDistribution: {
|
|
44
|
+
[bucket: string]: number;
|
|
45
|
+
};
|
|
46
|
+
redisCommandLatencies: {
|
|
47
|
+
[command: string]: {
|
|
48
|
+
total: number;
|
|
49
|
+
count: number;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
consumerLag: {
|
|
53
|
+
[consumerGroup: string]: {
|
|
54
|
+
total: number;
|
|
55
|
+
count: number;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
37
58
|
}
|
|
@@ -15,7 +15,9 @@ class ContentBasedDeduplication {
|
|
|
15
15
|
return hmac.update(JSON.stringify(hashableData)).digest('hex');
|
|
16
16
|
}
|
|
17
17
|
async isDuplicate(event, consumerGroupName) {
|
|
18
|
-
|
|
18
|
+
if (event.data === null)
|
|
19
|
+
return false;
|
|
20
|
+
const eventHash = this.calculateEventHash(event);
|
|
19
21
|
const key = `processed:${consumerGroupName}:${eventHash}`;
|
|
20
22
|
const result = await this.redisClient.set(key, '1', 'EX', this.ttl, 'NX');
|
|
21
23
|
const isDuplicate = result === null;
|
|
@@ -59,7 +59,7 @@ class ScheduledProcessor {
|
|
|
59
59
|
// Publish the event to each consumer group's stream
|
|
60
60
|
const streamName = `${eventData.eventName}:${consumerGroup}`;
|
|
61
61
|
const generatedKey = await this.redisPublisher
|
|
62
|
-
.xadd(streamName,
|
|
62
|
+
.xadd(streamName, key, 'data', JSON.stringify(eventData))
|
|
63
63
|
.catch((e) => logger_1.PUBLISHER_LOGGER.error(`PUBLISHER: Publishing event ${eventData.eventName} to consumer groups: ${consumerGroups.join(', ')} failed with data ${JSON.stringify(eventData)}, ${e} `));
|
|
64
64
|
if (key === '*')
|
|
65
65
|
key = generatedKey ?? key;
|
package/src/lib/redis/streams.js
CHANGED
|
@@ -53,7 +53,7 @@ class Streams {
|
|
|
53
53
|
errorThresholdPercentage: 50,
|
|
54
54
|
openStateDuration: 30000,
|
|
55
55
|
halfOpenStateMaxAttempts: 10,
|
|
56
|
-
maxStoredEvents:
|
|
56
|
+
maxStoredEvents: 10000,
|
|
57
57
|
},
|
|
58
58
|
};
|
|
59
59
|
/** Initialise Config properties */
|
|
@@ -149,6 +149,7 @@ class Streams {
|
|
|
149
149
|
const generatedKey = await this.redisPublisher.xadd(streamName, key, 'data', JSON.stringify(data));
|
|
150
150
|
tracker.endRedisOperation();
|
|
151
151
|
tracker.incrementEventCount();
|
|
152
|
+
tracker.incrementMessageRate('publish', data.eventName);
|
|
152
153
|
if (this.metricsCollector) {
|
|
153
154
|
this.metricsCollector.addMetrics(tracker.getMetrics());
|
|
154
155
|
}
|
|
@@ -256,7 +257,13 @@ class Streams {
|
|
|
256
257
|
this.eventsListened.push(eventName);
|
|
257
258
|
try {
|
|
258
259
|
// Check if the consumer group already exists
|
|
259
|
-
|
|
260
|
+
let groupInfo = [];
|
|
261
|
+
try {
|
|
262
|
+
groupInfo = (await this.redisGroups.xinfo('GROUPS', streamName));
|
|
263
|
+
}
|
|
264
|
+
catch (e) {
|
|
265
|
+
// Do nothing
|
|
266
|
+
}
|
|
260
267
|
let groupExists = false;
|
|
261
268
|
for (const group of groupInfo) {
|
|
262
269
|
if (group[1] === this.consumerGroupName) {
|
|
@@ -276,7 +283,13 @@ class Streams {
|
|
|
276
283
|
logger_1.PUBLISHER_LOGGER.error(`PUBLISHER: Group creation failed with error ${e.message} for ${JSON.stringify({ streamName, cgn: this.consumerGroupName })}`);
|
|
277
284
|
}
|
|
278
285
|
// Check if the consumer already exists in the group
|
|
279
|
-
|
|
286
|
+
let consumers = [];
|
|
287
|
+
try {
|
|
288
|
+
consumers = (await this.redisGroups.xinfo('CONSUMERS', streamName, this.consumerGroupName));
|
|
289
|
+
}
|
|
290
|
+
catch (e) {
|
|
291
|
+
// Do nothing
|
|
292
|
+
}
|
|
280
293
|
let consumerExists = false;
|
|
281
294
|
for (const consumer of consumers) {
|
|
282
295
|
if (consumer[1] === this.instanceId) {
|
|
@@ -361,6 +374,10 @@ class Streams {
|
|
|
361
374
|
else {
|
|
362
375
|
const messages = (await redisClient.xreadgroup('GROUP', this.consumerGroupName, this.instanceId, 'COUNT', 1, 'STREAMS', streamName, '>'));
|
|
363
376
|
if (messages && messages.length) {
|
|
377
|
+
if (messageId === '0') {
|
|
378
|
+
messageId = messages[0][1][0][0];
|
|
379
|
+
logger_1.PUBLISHER_LOGGER.log(`PUBLISHER: Reprocessing unprocessed message with id: ${messageId}`);
|
|
380
|
+
}
|
|
364
381
|
eventData = JSON.parse(messages[0][1][0][1][1]);
|
|
365
382
|
}
|
|
366
383
|
}
|
|
@@ -412,6 +429,11 @@ class Streams {
|
|
|
412
429
|
};
|
|
413
430
|
await this.dlq.addToDLQ(dlqEvent);
|
|
414
431
|
}
|
|
432
|
+
tracker.incrementMessageRate('subscribe', eventData.eventName);
|
|
433
|
+
const processingTime = Date.now() - eventData.createdAt;
|
|
434
|
+
tracker.addProcessingTime(processingTime);
|
|
435
|
+
const lag = Date.now() - eventData.createdAt;
|
|
436
|
+
tracker.setConsumerLag(this.consumerGroupName, lag);
|
|
415
437
|
}
|
|
416
438
|
else {
|
|
417
439
|
logger_1.PUBLISHER_LOGGER.log(`PUBLISHER: Message ${messageId} not found for ${streamName}`);
|