@sprout-idws/sprout-redis 1.0.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/dist/cache/cache-store.interface.d.ts +14 -0
- package/dist/cache/cache-store.interface.d.ts.map +1 -0
- package/dist/cache/cache-store.interface.js +5 -0
- package/dist/cache/cache-store.interface.js.map +1 -0
- package/dist/cache/cache.constants.d.ts +4 -0
- package/dist/cache/cache.constants.d.ts.map +1 -0
- package/dist/cache/cache.constants.js +7 -0
- package/dist/cache/cache.constants.js.map +1 -0
- package/dist/cache/cache.manager.d.ts +17 -0
- package/dist/cache/cache.manager.d.ts.map +1 -0
- package/dist/cache/cache.manager.js +59 -0
- package/dist/cache/cache.manager.js.map +1 -0
- package/dist/cache/cache.module.d.ts +3 -0
- package/dist/cache/cache.module.d.ts.map +1 -0
- package/dist/cache/cache.module.js +46 -0
- package/dist/cache/cache.module.js.map +1 -0
- package/dist/cache/index.d.ts +6 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +17 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/stores/in-memory-cache.store.d.ts +15 -0
- package/dist/cache/stores/in-memory-cache.store.d.ts.map +1 -0
- package/dist/cache/stores/in-memory-cache.store.js +44 -0
- package/dist/cache/stores/in-memory-cache.store.js.map +1 -0
- package/dist/cache/stores/redis-cache.store.d.ts +12 -0
- package/dist/cache/stores/redis-cache.store.d.ts.map +1 -0
- package/dist/cache/stores/redis-cache.store.js +48 -0
- package/dist/cache/stores/redis-cache.store.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/lock/redis-lock.service.d.ts +15 -0
- package/dist/lock/redis-lock.service.d.ts.map +1 -0
- package/dist/lock/redis-lock.service.js +97 -0
- package/dist/lock/redis-lock.service.js.map +1 -0
- package/dist/redis-base.module.d.ts +9 -0
- package/dist/redis-base.module.d.ts.map +1 -0
- package/dist/redis-base.module.js +88 -0
- package/dist/redis-base.module.js.map +1 -0
- package/dist/redis-stream-consumer.module.d.ts +3 -0
- package/dist/redis-stream-consumer.module.d.ts.map +1 -0
- package/dist/redis-stream-consumer.module.js +36 -0
- package/dist/redis-stream-consumer.module.js.map +1 -0
- package/dist/stream/lua/ack-delete.lua +6 -0
- package/dist/stream/lua/delayed-retry.lua +11 -0
- package/dist/stream/lua/move-to-dlq.lua +27 -0
- package/dist/stream/lua/republish-delayed-messages.lua +30 -0
- package/dist/stream/lua/republish-inactive-consumer-messages.lua +52 -0
- package/dist/stream/lua-scripts.loader.d.ts +11 -0
- package/dist/stream/lua-scripts.loader.d.ts.map +1 -0
- package/dist/stream/lua-scripts.loader.js +39 -0
- package/dist/stream/lua-scripts.loader.js.map +1 -0
- package/dist/stream/periodic-executor.service.d.ts +30 -0
- package/dist/stream/periodic-executor.service.d.ts.map +1 -0
- package/dist/stream/periodic-executor.service.js +130 -0
- package/dist/stream/periodic-executor.service.js.map +1 -0
- package/dist/stream/redis-stream.service.d.ts +47 -0
- package/dist/stream/redis-stream.service.d.ts.map +1 -0
- package/dist/stream/redis-stream.service.js +200 -0
- package/dist/stream/redis-stream.service.js.map +1 -0
- package/dist/stream/stream-consumer-monitor.service.d.ts +19 -0
- package/dist/stream/stream-consumer-monitor.service.d.ts.map +1 -0
- package/dist/stream/stream-consumer-monitor.service.js +85 -0
- package/dist/stream/stream-consumer-monitor.service.js.map +1 -0
- package/dist/stream/stream-consumer.service.d.ts +25 -0
- package/dist/stream/stream-consumer.service.d.ts.map +1 -0
- package/dist/stream/stream-consumer.service.js +78 -0
- package/dist/stream/stream-consumer.service.js.map +1 -0
- package/dist/stream/stream-message-handler.service.d.ts +28 -0
- package/dist/stream/stream-message-handler.service.d.ts.map +1 -0
- package/dist/stream/stream-message-handler.service.js +145 -0
- package/dist/stream/stream-message-handler.service.js.map +1 -0
- package/dist/stream/stream-publisher.service.d.ts +9 -0
- package/dist/stream/stream-publisher.service.d.ts.map +1 -0
- package/dist/stream/stream-publisher.service.js +39 -0
- package/dist/stream/stream-publisher.service.js.map +1 -0
- package/dist/stream/stream-retry.service.d.ts +25 -0
- package/dist/stream/stream-retry.service.d.ts.map +1 -0
- package/dist/stream/stream-retry.service.js +74 -0
- package/dist/stream/stream-retry.service.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
|
+
};
|
|
17
|
+
var RedisStreamService_1;
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.RedisStreamService = void 0;
|
|
20
|
+
const common_1 = require("@nestjs/common");
|
|
21
|
+
const sprout_context_1 = require("@sprout-idws/sprout-context");
|
|
22
|
+
const ioredis_1 = __importDefault(require("ioredis"));
|
|
23
|
+
const lua_scripts_loader_1 = require("./lua-scripts.loader");
|
|
24
|
+
let RedisStreamService = RedisStreamService_1 = class RedisStreamService {
|
|
25
|
+
constructor(redis, contextService) {
|
|
26
|
+
this.redis = redis;
|
|
27
|
+
this.contextService = contextService;
|
|
28
|
+
this.logger = new common_1.Logger(RedisStreamService_1.name);
|
|
29
|
+
this.luaScriptsLoader = new lua_scripts_loader_1.LuaScriptsLoader();
|
|
30
|
+
}
|
|
31
|
+
async publishToStream(streamName, message) {
|
|
32
|
+
message.context = message.context ?? this.contextService.getCurrentEncodedContext();
|
|
33
|
+
const args = ['*', 'message', JSON.stringify(message)];
|
|
34
|
+
return this.redis.xadd(streamName, ...args);
|
|
35
|
+
}
|
|
36
|
+
async createConsumerGroup(streamName, groupName, startId = '0') {
|
|
37
|
+
try {
|
|
38
|
+
await this.redis.xgroup('CREATE', streamName, groupName, startId, 'MKSTREAM');
|
|
39
|
+
this.logger.debug(`Created consumer group ${groupName} for stream ${streamName}`);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
const err = error;
|
|
43
|
+
if (!err.message?.includes('BUSYGROUP')) {
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async readFromConsumerGroup({ streamName, groupName, consumerName, count = 10, blockTime = 1000, }) {
|
|
49
|
+
const result = await this.redis.xreadgroup('GROUP', groupName, consumerName, 'COUNT', count, 'BLOCK', blockTime, 'STREAMS', streamName, '>');
|
|
50
|
+
if (!result || result.length === 0) {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
const entries = [];
|
|
54
|
+
for (const [_, messages] of result) {
|
|
55
|
+
for (const [id, fields] of messages) {
|
|
56
|
+
const messageIndex = fields.indexOf('message');
|
|
57
|
+
if (messageIndex !== -1) {
|
|
58
|
+
const messageValue = fields[messageIndex + 1];
|
|
59
|
+
const messageData = JSON.parse(messageValue);
|
|
60
|
+
entries.push({
|
|
61
|
+
id,
|
|
62
|
+
data: messageData.data ?? {},
|
|
63
|
+
context: messageData.context,
|
|
64
|
+
metadata: messageData.metadata,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return entries;
|
|
70
|
+
}
|
|
71
|
+
async sendToDlq(streamName, groupName, messageId) {
|
|
72
|
+
const dlqStream = `${streamName}:dlq`;
|
|
73
|
+
const moveToDlqScript = this.luaScriptsLoader.getScript('move-to-dlq.lua');
|
|
74
|
+
await this.redis.eval(moveToDlqScript, 1, streamName, dlqStream, groupName, messageId);
|
|
75
|
+
}
|
|
76
|
+
async ackDelete(streamName, groupName, messageId) {
|
|
77
|
+
const luaScript = this.luaScriptsLoader.getScript('ack-delete.lua');
|
|
78
|
+
await this.redis.eval(luaScript, 1, streamName, groupName, messageId);
|
|
79
|
+
}
|
|
80
|
+
async delayedRetry(streamName, groupName, messageId, delayedQueueKey, score, member) {
|
|
81
|
+
const luaScript = this.luaScriptsLoader.getScript('delayed-retry.lua');
|
|
82
|
+
await this.redis.eval(luaScript, 2, streamName, delayedQueueKey, groupName, messageId, `${score}`, member);
|
|
83
|
+
}
|
|
84
|
+
async getAllStreams(pattern = '*') {
|
|
85
|
+
try {
|
|
86
|
+
const streams = [];
|
|
87
|
+
let cursor = '0';
|
|
88
|
+
do {
|
|
89
|
+
const [nextCursor, keys] = await this.redis.scan(cursor, 'MATCH', pattern, 'COUNT', 100);
|
|
90
|
+
cursor = nextCursor;
|
|
91
|
+
for (const key of keys) {
|
|
92
|
+
const type = await this.redis.type(key);
|
|
93
|
+
if (type === 'stream') {
|
|
94
|
+
streams.push(key);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
} while (cursor !== '0');
|
|
98
|
+
return streams;
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
this.logger.warn(`Failed to get all streams:`, error);
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async getConsumerGroups(streamName) {
|
|
106
|
+
const result = await this.redis.xinfo('GROUPS', streamName);
|
|
107
|
+
const groups = [];
|
|
108
|
+
if (!result) {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
if (!Array.isArray(result)) {
|
|
112
|
+
this.logger.warn(`Unexpected result type from XINFO GROUPS for stream ${streamName}: ${typeof result}`);
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
for (const groupData of result) {
|
|
116
|
+
if (Array.isArray(groupData)) {
|
|
117
|
+
const group = { name: '' };
|
|
118
|
+
for (let j = 0; j < groupData.length; j += 2) {
|
|
119
|
+
const key = groupData[j];
|
|
120
|
+
const value = groupData[j + 1];
|
|
121
|
+
if (key === 'name') {
|
|
122
|
+
group.name = value;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (group.name) {
|
|
127
|
+
groups.push(group);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
this.logger.debug(`Unexpected group data format in XINFO GROUPS for stream ${streamName}: ${typeof groupData}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return groups;
|
|
135
|
+
}
|
|
136
|
+
async deleteConsumer(streamName, groupName, consumerName) {
|
|
137
|
+
await this.redis.xgroup('DELCONSUMER', streamName, groupName, consumerName);
|
|
138
|
+
}
|
|
139
|
+
async republishDelayedMessages(queueKey, currentTime, limit = 1000) {
|
|
140
|
+
this.logger.debug(`Checking for delayed messages to republish from queue ${queueKey} at ${currentTime}`);
|
|
141
|
+
const luaScript = this.luaScriptsLoader.getScript('republish-delayed-messages.lua');
|
|
142
|
+
const republishedCount = (await this.redis.eval(luaScript, 1, queueKey, `${currentTime}`, `${limit}`));
|
|
143
|
+
if (republishedCount > 0) {
|
|
144
|
+
this.logger.debug(`Republished ${republishedCount} delayed messages from queue ${queueKey}`);
|
|
145
|
+
}
|
|
146
|
+
return republishedCount;
|
|
147
|
+
}
|
|
148
|
+
async getConsumers(streamName, groupName) {
|
|
149
|
+
const result = await this.redis.xinfo('CONSUMERS', streamName, groupName);
|
|
150
|
+
const consumers = [];
|
|
151
|
+
if (!result) {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
if (!Array.isArray(result)) {
|
|
155
|
+
this.logger.warn(`Unexpected result type from XINFO CONSUMERS for stream ${streamName}, group ${groupName}: ${typeof result}`);
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
for (const consumerData of result) {
|
|
159
|
+
if (!Array.isArray(consumerData)) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const consumer = {
|
|
163
|
+
name: '',
|
|
164
|
+
pending: 0,
|
|
165
|
+
idle: 0,
|
|
166
|
+
};
|
|
167
|
+
for (let j = 0; j < consumerData.length; j += 2) {
|
|
168
|
+
const key = consumerData[j];
|
|
169
|
+
const value = consumerData[j + 1];
|
|
170
|
+
if (key === 'name') {
|
|
171
|
+
consumer.name = value;
|
|
172
|
+
}
|
|
173
|
+
else if (key === 'pending') {
|
|
174
|
+
consumer.pending = Number(value);
|
|
175
|
+
}
|
|
176
|
+
else if (key === 'idle') {
|
|
177
|
+
consumer.idle = Number(value);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (consumer.name) {
|
|
181
|
+
consumers.push(consumer);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return consumers;
|
|
185
|
+
}
|
|
186
|
+
async republishInactiveConsumerMessages(streamName, groupName, inactiveConsumerName) {
|
|
187
|
+
const luaScript = this.luaScriptsLoader.getScript('republish-inactive-consumer-messages.lua');
|
|
188
|
+
const args = [groupName, inactiveConsumerName];
|
|
189
|
+
const result = await this.redis.eval(luaScript, 1, streamName, ...args);
|
|
190
|
+
return { republishedCount: result };
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
exports.RedisStreamService = RedisStreamService;
|
|
194
|
+
exports.RedisStreamService = RedisStreamService = RedisStreamService_1 = __decorate([
|
|
195
|
+
(0, common_1.Injectable)(),
|
|
196
|
+
__param(0, (0, common_1.Inject)('REDIS_CLIENT')),
|
|
197
|
+
__metadata("design:paramtypes", [ioredis_1.default,
|
|
198
|
+
sprout_context_1.ContextService])
|
|
199
|
+
], RedisStreamService);
|
|
200
|
+
//# sourceMappingURL=redis-stream.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-stream.service.js","sourceRoot":"","sources":["../../src/stream/redis-stream.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAAA,2CAA4D;AAC5D,gEAA6D;AAC7D,sDAA4B;AAC5B,6DAAwD;AAejD,IAAM,kBAAkB,0BAAxB,MAAM,kBAAkB;IAI7B,YAC0B,KAA6B,EACpC,cAA8B;QADN,UAAK,GAAL,KAAK,CAAO;QACpC,mBAAc,GAAd,cAAc,CAAgB;QALhC,WAAM,GAAG,IAAI,eAAM,CAAC,oBAAkB,CAAC,IAAI,CAAC,CAAC;QAO5D,IAAI,CAAC,gBAAgB,GAAG,IAAI,qCAAgB,EAAE,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,UAAkB,EAAE,OAAsB;QAC9D,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC,wBAAwB,EAAE,CAAC;QACpF,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QACvD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,mBAAmB,CACvB,UAAkB,EAClB,SAAiB,EACjB,UAAkB,GAAG;QAErB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;YAC9E,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,SAAS,eAAe,UAAU,EAAE,CAAC,CAAC;QACpF,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,KAA6B,CAAC;YAC1C,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;gBACxC,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,EAC1B,UAAU,EACV,SAAS,EACT,YAAY,EACZ,KAAK,GAAG,EAAE,EACV,SAAS,GAAG,IAAI,GAOjB;QACC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CACxC,OAAO,EACP,SAAS,EACT,YAAY,EACZ,OAAO,EACP,KAAK,EACL,OAAO,EACP,SAAS,EACT,SAAS,EACT,UAAU,EACV,GAAG,CACJ,CAAC;QAEF,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,OAAO,GAAoB,EAAE,CAAC;QACpC,KAAK,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAI,MAAoD,EAAE,CAAC;YACjF,KAAK,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;gBACpC,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;gBAE/C,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;oBACxB,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;oBAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAI1C,CAAC;oBAEF,OAAO,CAAC,IAAI,CAAC;wBACX,EAAE;wBACF,IAAI,EAAE,WAAW,CAAC,IAAI,IAAI,EAAE;wBAC5B,OAAO,EAAE,WAAW,CAAC,OAAO;wBAC5B,QAAQ,EAAE,WAAW,CAAC,QAAQ;qBAC/B,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,UAAkB,EAAE,SAAiB,EAAE,SAAiB;QACtE,MAAM,SAAS,GAAG,GAAG,UAAU,MAAM,CAAC;QACtC,MAAM,eAAe,GAAG,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAE3E,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IACzF,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,UAAkB,EAAE,SAAiB,EAAE,SAAiB;QACtE,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAEpE,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IACxE,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,UAAkB,EAClB,SAAiB,EACjB,SAAiB,EACjB,eAAuB,EACvB,KAAa,EACb,MAAc;QAEd,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAEvE,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CACnB,SAAS,EACT,CAAC,EACD,UAAU,EACV,eAAe,EACf,SAAS,EACT,SAAS,EACT,GAAG,KAAK,EAAE,EACV,MAAM,CACP,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,UAAkB,GAAG;QACvC,IAAI,CAAC;YACH,MAAM,OAAO,GAAa,EAAE,CAAC;YAC7B,IAAI,MAAM,GAAG,GAAG,CAAC;YAEjB,GAAG,CAAC;gBACF,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;gBACzF,MAAM,GAAG,UAAU,CAAC;gBAEpB,KAAK,MAAM,GAAG,IAAI,IAAgB,EAAE,CAAC;oBACnC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBACxC,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;wBACtB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBACpB,CAAC;gBACH,CAAC;YACH,CAAC,QAAQ,MAAM,KAAK,GAAG,EAAE;YAEzB,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;YACtD,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,UAAkB;QACxC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAC5D,MAAM,MAAM,GAA4B,EAAE,CAAC;QAE3C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,uDAAuD,UAAU,KAAK,OAAO,MAAM,EAAE,CACtF,CAAC;YACF,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,KAAK,MAAM,SAAS,IAAI,MAAM,EAAE,CAAC;YAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC7B,MAAM,KAAK,GAAqB,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;gBAE7C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC7C,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;oBACzB,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;oBAE/B,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;wBACnB,KAAK,CAAC,IAAI,GAAG,KAAe,CAAC;wBAC7B,MAAM;oBACR,CAAC;gBACH,CAAC;gBAED,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;oBACf,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACrB,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,2DAA2D,UAAU,KAAK,OAAO,SAAS,EAAE,CAC7F,CAAC;YACJ,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,UAAkB,EAAE,SAAiB,EAAE,YAAoB;QAC9E,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;IAC9E,CAAC;IAED,KAAK,CAAC,wBAAwB,CAC5B,QAAgB,EAChB,WAAmB,EACnB,QAAgB,IAAI;QAEpB,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,yDAAyD,QAAQ,OAAO,WAAW,EAAE,CACtF,CAAC;QAEF,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,gCAAgC,CAAC,CAAC;QACpF,MAAM,gBAAgB,GAAG,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAC7C,SAAS,EACT,CAAC,EACD,QAAQ,EACR,GAAG,WAAW,EAAE,EAChB,GAAG,KAAK,EAAE,CACX,CAAW,CAAC;QAEb,IAAI,gBAAgB,GAAG,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,gBAAgB,gCAAgC,QAAQ,EAAE,CAAC,CAAC;QAC/F,CAAC;QAED,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,UAAkB,EAClB,SAAiB;QAEjB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;QAC1E,MAAM,SAAS,GAA2D,EAAE,CAAC;QAE7E,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,0DAA0D,UAAU,WAAW,SAAS,KAAK,OAAO,MAAM,EAAE,CAC7G,CAAC;YACF,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,KAAK,MAAM,YAAY,IAAI,MAAM,EAAE,CAAC;YAClC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;gBACjC,SAAS;YACX,CAAC;YAED,MAAM,QAAQ,GAAoD;gBAChE,IAAI,EAAE,EAAE;gBACR,OAAO,EAAE,CAAC;gBACV,IAAI,EAAE,CAAC;aACR,CAAC;YAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChD,MAAM,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;gBAC5B,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBAElC,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;oBACnB,QAAQ,CAAC,IAAI,GAAG,KAAe,CAAC;gBAClC,CAAC;qBAAM,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;oBAC7B,QAAQ,CAAC,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;gBACnC,CAAC;qBAAM,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;oBAC1B,QAAQ,CAAC,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;gBAChC,CAAC;YACH,CAAC;YAED,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAClB,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,iCAAiC,CACrC,UAAkB,EAClB,SAAiB,EACjB,oBAA4B;QAE5B,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,0CAA0C,CAAC,CAAC;QAE9F,MAAM,IAAI,GAAG,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;QAE/C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,CAAC;QACxE,OAAO,EAAE,gBAAgB,EAAE,MAAgB,EAAE,CAAC;IAChD,CAAC;CACF,CAAA;AAzRY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,mBAAU,GAAE;IAMR,WAAA,IAAA,eAAM,EAAC,cAAc,CAAC,CAAA;qCAAyB,iBAAK;QACpB,+BAAc;GANtC,kBAAkB,CAyR9B"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { OnModuleInit } from '@nestjs/common';
|
|
2
|
+
import { PeriodicExecutorService } from './periodic-executor.service';
|
|
3
|
+
import { RedisStreamService } from './redis-stream.service';
|
|
4
|
+
export declare class StreamConsumerMonitorService implements OnModuleInit {
|
|
5
|
+
private readonly redisStreamService;
|
|
6
|
+
private readonly periodicExecutorService;
|
|
7
|
+
private readonly logger;
|
|
8
|
+
private executorInstance;
|
|
9
|
+
private readonly CONSUMER_MONITOR_INTERNAL_MS;
|
|
10
|
+
private readonly CONSUMER_IDLE_TIME_THRESHOLD_MULTIPLIER;
|
|
11
|
+
private get minIdleTimeMs();
|
|
12
|
+
constructor(redisStreamService: RedisStreamService, periodicExecutorService: PeriodicExecutorService);
|
|
13
|
+
onModuleInit(): Promise<void>;
|
|
14
|
+
start(): Promise<void>;
|
|
15
|
+
private monitorInactiveConsumers;
|
|
16
|
+
private checkAndClaimInactiveMessages;
|
|
17
|
+
private republishInactiveConsumerMessages;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=stream-consumer-monitor.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-consumer-monitor.service.d.ts","sourceRoot":"","sources":["../../src/stream/stream-consumer-monitor.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAA4B,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAG5D,qBACa,4BAA6B,YAAW,YAAY;IAc7D,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,uBAAuB;IAd1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiD;IAExE,OAAO,CAAC,gBAAgB,CAAyC;IAEjE,OAAO,CAAC,QAAQ,CAAC,4BAA4B,CAAa;IAE1D,OAAO,CAAC,QAAQ,CAAC,uCAAuC,CAAM;IAE9D,OAAO,KAAK,aAAa,GAExB;gBAGkB,kBAAkB,EAAE,kBAAkB,EACtC,uBAAuB,EAAE,uBAAuB;IAG7D,YAAY;IAKZ,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAed,wBAAwB;YAgBxB,6BAA6B;YAuB7B,iCAAiC;CAuBhD"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var StreamConsumerMonitorService_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.StreamConsumerMonitorService = void 0;
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const periodic_executor_service_1 = require("./periodic-executor.service");
|
|
16
|
+
const redis_stream_service_1 = require("./redis-stream.service");
|
|
17
|
+
const stream_consumer_service_1 = require("./stream-consumer.service");
|
|
18
|
+
let StreamConsumerMonitorService = StreamConsumerMonitorService_1 = class StreamConsumerMonitorService {
|
|
19
|
+
get minIdleTimeMs() {
|
|
20
|
+
return stream_consumer_service_1.CONSUMER_INTERVAL_TIME * this.CONSUMER_IDLE_TIME_THRESHOLD_MULTIPLIER;
|
|
21
|
+
}
|
|
22
|
+
constructor(redisStreamService, periodicExecutorService) {
|
|
23
|
+
this.redisStreamService = redisStreamService;
|
|
24
|
+
this.periodicExecutorService = periodicExecutorService;
|
|
25
|
+
this.logger = new common_1.Logger(StreamConsumerMonitorService_1.name);
|
|
26
|
+
this.executorInstance = null;
|
|
27
|
+
this.CONSUMER_MONITOR_INTERNAL_MS = 60 * 1000;
|
|
28
|
+
this.CONSUMER_IDLE_TIME_THRESHOLD_MULTIPLIER = 10;
|
|
29
|
+
}
|
|
30
|
+
async onModuleInit() {
|
|
31
|
+
await this.start();
|
|
32
|
+
this.logger.log('StreamConsumerMonitorService started');
|
|
33
|
+
}
|
|
34
|
+
async start() {
|
|
35
|
+
if (this.executorInstance?.isActive) {
|
|
36
|
+
this.logger.warn('Consumer monitor is already running');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
this.executorInstance = await this.periodicExecutorService.startExecutor({
|
|
40
|
+
intervalMs: this.CONSUMER_MONITOR_INTERNAL_MS,
|
|
41
|
+
name: 'stream-consumer-monitor',
|
|
42
|
+
}, () => this.monitorInactiveConsumers());
|
|
43
|
+
}
|
|
44
|
+
async monitorInactiveConsumers() {
|
|
45
|
+
if (!this.executorInstance?.isActive) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const streams = await this.redisStreamService.getAllStreams();
|
|
49
|
+
for (const streamName of streams) {
|
|
50
|
+
const groups = await this.redisStreamService.getConsumerGroups(streamName);
|
|
51
|
+
for (const group of groups) {
|
|
52
|
+
await this.checkAndClaimInactiveMessages(streamName, group.name);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async checkAndClaimInactiveMessages(streamName, groupName) {
|
|
57
|
+
const consumers = await this.redisStreamService.getConsumers(streamName, groupName);
|
|
58
|
+
for (const consumer of consumers) {
|
|
59
|
+
if (consumer.idle < this.minIdleTimeMs) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (consumer.pending > 0) {
|
|
63
|
+
await this.republishInactiveConsumerMessages(streamName, groupName, consumer);
|
|
64
|
+
}
|
|
65
|
+
await this.redisStreamService.deleteConsumer(streamName, groupName, consumer.name);
|
|
66
|
+
this.logger.warn(`Deleted inactive consumer ${consumer.name} from group ${groupName} in stream ${streamName}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async republishInactiveConsumerMessages(streamName, groupName, consumer) {
|
|
70
|
+
let totalRepublishedCount = 0;
|
|
71
|
+
do {
|
|
72
|
+
const { republishedCount: currentRepublishedCount } = await this.redisStreamService.republishInactiveConsumerMessages(streamName, groupName, consumer.name);
|
|
73
|
+
totalRepublishedCount += currentRepublishedCount;
|
|
74
|
+
} while (totalRepublishedCount < consumer.pending);
|
|
75
|
+
this.logger.warn(`Republished ${totalRepublishedCount} messages from inactive consumer ${consumer.name} in group ${groupName} for stream ${streamName}`);
|
|
76
|
+
return totalRepublishedCount;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
exports.StreamConsumerMonitorService = StreamConsumerMonitorService;
|
|
80
|
+
exports.StreamConsumerMonitorService = StreamConsumerMonitorService = StreamConsumerMonitorService_1 = __decorate([
|
|
81
|
+
(0, common_1.Injectable)(),
|
|
82
|
+
__metadata("design:paramtypes", [redis_stream_service_1.RedisStreamService,
|
|
83
|
+
periodic_executor_service_1.PeriodicExecutorService])
|
|
84
|
+
], StreamConsumerMonitorService);
|
|
85
|
+
//# sourceMappingURL=stream-consumer-monitor.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-consumer-monitor.service.js","sourceRoot":"","sources":["../../src/stream/stream-consumer-monitor.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAkE;AAClE,2EAAgG;AAChG,iEAA4D;AAC5D,uEAA2F;AAGpF,IAAM,4BAA4B,oCAAlC,MAAM,4BAA4B;IASvC,IAAY,aAAa;QACvB,OAAO,gDAAoB,GAAG,IAAI,CAAC,uCAAuC,CAAC;IAC7E,CAAC;IAED,YACmB,kBAAsC,EACtC,uBAAgD;QADhD,uBAAkB,GAAlB,kBAAkB,CAAoB;QACtC,4BAAuB,GAAvB,uBAAuB,CAAyB;QAdlD,WAAM,GAAG,IAAI,eAAM,CAAC,8BAA4B,CAAC,IAAI,CAAC,CAAC;QAEhE,qBAAgB,GAAoC,IAAI,CAAC;QAEhD,iCAA4B,GAAG,EAAE,GAAG,IAAI,CAAC;QAEzC,4CAAuC,GAAG,EAAE,CAAC;IAS3D,CAAC;IAEJ,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,gBAAgB,EAAE,QAAQ,EAAE,CAAC;YACpC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;YACxD,OAAO;QACT,CAAC;QAED,IAAI,CAAC,gBAAgB,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,aAAa,CACtE;YACE,UAAU,EAAE,IAAI,CAAC,4BAA4B;YAC7C,IAAI,EAAE,yBAAyB;SAChC,EACD,GAAG,EAAE,CAAC,IAAI,CAAC,wBAAwB,EAAE,CACtC,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,wBAAwB;QACpC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,QAAQ,EAAE,CAAC;YACrC,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,aAAa,EAAE,CAAC;QAE9D,KAAK,MAAM,UAAU,IAAI,OAAO,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;YAE3E,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,MAAM,IAAI,CAAC,6BAA6B,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACnE,CAAC;QACH,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,6BAA6B,CACzC,UAAkB,EAClB,SAAiB;QAEjB,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QAEpF,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;gBACvC,SAAS;YACX,CAAC;YAED,IAAI,QAAQ,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;gBACzB,MAAM,IAAI,CAAC,iCAAiC,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;YAChF,CAAC;YAED,MAAM,IAAI,CAAC,kBAAkB,CAAC,cAAc,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;YAEnF,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,6BAA6B,QAAQ,CAAC,IAAI,eAAe,SAAS,cAAc,UAAU,EAAE,CAC7F,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,iCAAiC,CAC7C,UAAkB,EAClB,SAAiB,EACjB,QAAyD;QAEzD,IAAI,qBAAqB,GAAG,CAAC,CAAC;QAE9B,GAAG,CAAC;YACF,MAAM,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,GACjD,MAAM,IAAI,CAAC,kBAAkB,CAAC,iCAAiC,CAC7D,UAAU,EACV,SAAS,EACT,QAAQ,CAAC,IAAI,CACd,CAAC;YAEJ,qBAAqB,IAAI,uBAAuB,CAAC;QACnD,CAAC,QAAQ,qBAAqB,GAAG,QAAQ,CAAC,OAAO,EAAE;QAEnD,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,eAAe,qBAAqB,oCAAoC,QAAQ,CAAC,IAAI,aAAa,SAAS,eAAe,UAAU,EAAE,CACvI,CAAC;QACF,OAAO,qBAAqB,CAAC;IAC/B,CAAC;CACF,CAAA;AApGY,oEAA4B;uCAA5B,4BAA4B;IADxC,IAAA,mBAAU,GAAE;qCAe4B,yCAAkB;QACb,mDAAuB;GAfxD,4BAA4B,CAoGxC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { PeriodicExecutorService } from './periodic-executor.service';
|
|
2
|
+
import { RedisStreamService, StreamMessage } from './redis-stream.service';
|
|
3
|
+
import { StreamMessageHandlerService } from './stream-message-handler.service';
|
|
4
|
+
import { StreamRetryService } from './stream-retry.service';
|
|
5
|
+
export interface ConsumerConfig {
|
|
6
|
+
streamName: string;
|
|
7
|
+
groupName: string;
|
|
8
|
+
consumerName: string;
|
|
9
|
+
maxRetries?: number;
|
|
10
|
+
retryDelay?: number;
|
|
11
|
+
blockTime?: number;
|
|
12
|
+
maxConcurrency?: number;
|
|
13
|
+
prefetchCount?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const CONSUMER_INTERVAL_TIME = 1000;
|
|
16
|
+
export declare class StreamConsumerService {
|
|
17
|
+
private readonly redisStreamService;
|
|
18
|
+
private readonly messageHandlerService;
|
|
19
|
+
private readonly streamRetryService;
|
|
20
|
+
private readonly periodicExecutorService;
|
|
21
|
+
private readonly logger;
|
|
22
|
+
constructor(redisStreamService: RedisStreamService, messageHandlerService: StreamMessageHandlerService, streamRetryService: StreamRetryService, periodicExecutorService: PeriodicExecutorService);
|
|
23
|
+
consumeMessages(config: ConsumerConfig, messageHandler: (entry: StreamMessage) => Promise<void>): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=stream-consumer.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-consumer.service.d.ts","sourceRoot":"","sources":["../../src/stream/stream-consumer.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAEL,2BAA2B,EAC5B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAI5D,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAE3C,qBACa,qBAAqB;IAI9B,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IACtC,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,uBAAuB;IAN1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;gBAG9C,kBAAkB,EAAE,kBAAkB,EACtC,qBAAqB,EAAE,2BAA2B,EAClD,kBAAkB,EAAE,kBAAkB,EACtC,uBAAuB,EAAE,uBAAuB;IAG7D,eAAe,CACnB,MAAM,EAAE,cAAc,EACtB,cAAc,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,GACtD,OAAO,CAAC,IAAI,CAAC;CAmEjB"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var StreamConsumerService_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.StreamConsumerService = exports.CONSUMER_INTERVAL_TIME = void 0;
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const periodic_executor_service_1 = require("./periodic-executor.service");
|
|
16
|
+
const redis_stream_service_1 = require("./redis-stream.service");
|
|
17
|
+
const stream_message_handler_service_1 = require("./stream-message-handler.service");
|
|
18
|
+
const stream_retry_service_1 = require("./stream-retry.service");
|
|
19
|
+
const generateConsumerId = () => process.hrtime.bigint().toString();
|
|
20
|
+
exports.CONSUMER_INTERVAL_TIME = 1000;
|
|
21
|
+
let StreamConsumerService = StreamConsumerService_1 = class StreamConsumerService {
|
|
22
|
+
constructor(redisStreamService, messageHandlerService, streamRetryService, periodicExecutorService) {
|
|
23
|
+
this.redisStreamService = redisStreamService;
|
|
24
|
+
this.messageHandlerService = messageHandlerService;
|
|
25
|
+
this.streamRetryService = streamRetryService;
|
|
26
|
+
this.periodicExecutorService = periodicExecutorService;
|
|
27
|
+
this.logger = new common_1.Logger(StreamConsumerService_1.name);
|
|
28
|
+
}
|
|
29
|
+
async consumeMessages(config, messageHandler) {
|
|
30
|
+
const { streamName, groupName, consumerName, maxRetries = 3, retryDelay = 60 * 5000, blockTime = 100, prefetchCount = 100, maxConcurrency = 25, } = {
|
|
31
|
+
...config,
|
|
32
|
+
...{ consumerName: `${config.consumerName}:${generateConsumerId()}` },
|
|
33
|
+
};
|
|
34
|
+
await this.streamRetryService.addDelayedQueueKey(streamName);
|
|
35
|
+
await this.redisStreamService.createConsumerGroup(streamName, groupName);
|
|
36
|
+
this.logger.debug(`Starting consumer ${consumerName} for stream ${streamName} with concurrency ${maxConcurrency}`);
|
|
37
|
+
const processBatchRequest = {
|
|
38
|
+
messages: [],
|
|
39
|
+
streamName,
|
|
40
|
+
groupName,
|
|
41
|
+
messageHandler,
|
|
42
|
+
maxRetries,
|
|
43
|
+
retryDelay,
|
|
44
|
+
maxConcurrency,
|
|
45
|
+
};
|
|
46
|
+
const processMessages = async () => {
|
|
47
|
+
this.logger.debug(`Consumer ${consumerName} is running, fetching messages from stream ${streamName}`);
|
|
48
|
+
const messages = await this.redisStreamService.readFromConsumerGroup({
|
|
49
|
+
streamName,
|
|
50
|
+
groupName,
|
|
51
|
+
consumerName,
|
|
52
|
+
count: prefetchCount,
|
|
53
|
+
blockTime,
|
|
54
|
+
});
|
|
55
|
+
if (!messages || messages.length === 0) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.logger.debug(`Consumer ${consumerName} fetched ${messages.length} messages from stream ${streamName}`);
|
|
59
|
+
await this.messageHandlerService.processMessages({
|
|
60
|
+
...processBatchRequest,
|
|
61
|
+
messages,
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
await this.periodicExecutorService.startExecutor({
|
|
65
|
+
intervalMs: exports.CONSUMER_INTERVAL_TIME,
|
|
66
|
+
name: `consumer-${consumerName}`,
|
|
67
|
+
}, processMessages);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
exports.StreamConsumerService = StreamConsumerService;
|
|
71
|
+
exports.StreamConsumerService = StreamConsumerService = StreamConsumerService_1 = __decorate([
|
|
72
|
+
(0, common_1.Injectable)(),
|
|
73
|
+
__metadata("design:paramtypes", [redis_stream_service_1.RedisStreamService,
|
|
74
|
+
stream_message_handler_service_1.StreamMessageHandlerService,
|
|
75
|
+
stream_retry_service_1.StreamRetryService,
|
|
76
|
+
periodic_executor_service_1.PeriodicExecutorService])
|
|
77
|
+
], StreamConsumerService);
|
|
78
|
+
//# sourceMappingURL=stream-consumer.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-consumer.service.js","sourceRoot":"","sources":["../../src/stream/stream-consumer.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAoD;AACpD,2EAAsE;AACtE,iEAA2E;AAC3E,qFAG0C;AAC1C,iEAA4D;AAE5D,MAAM,kBAAkB,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC;AAavD,QAAA,sBAAsB,GAAG,IAAI,CAAC;AAGpC,IAAM,qBAAqB,6BAA3B,MAAM,qBAAqB;IAGhC,YACmB,kBAAsC,EACtC,qBAAkD,EAClD,kBAAsC,EACtC,uBAAgD;QAHhD,uBAAkB,GAAlB,kBAAkB,CAAoB;QACtC,0BAAqB,GAArB,qBAAqB,CAA6B;QAClD,uBAAkB,GAAlB,kBAAkB,CAAoB;QACtC,4BAAuB,GAAvB,uBAAuB,CAAyB;QANlD,WAAM,GAAG,IAAI,eAAM,CAAC,uBAAqB,CAAC,IAAI,CAAC,CAAC;IAO9D,CAAC;IAEJ,KAAK,CAAC,eAAe,CACnB,MAAsB,EACtB,cAAuD;QAEvD,MAAM,EACJ,UAAU,EACV,SAAS,EACT,YAAY,EACZ,UAAU,GAAG,CAAC,EACd,UAAU,GAAG,EAAE,GAAG,IAAI,EACtB,SAAS,GAAG,GAAG,EACf,aAAa,GAAG,GAAG,EACnB,cAAc,GAAG,EAAE,GACpB,GAAG;YACF,GAAG,MAAM;YACT,GAAG,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC,YAAY,IAAI,kBAAkB,EAAE,EAAE,EAAE;SACtE,CAAC;QAEF,MAAM,IAAI,CAAC,kBAAkB,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC;QAC7D,MAAM,IAAI,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QAEzE,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,qBAAqB,YAAY,eAAe,UAAU,qBAAqB,cAAc,EAAE,CAChG,CAAC;QAEF,MAAM,mBAAmB,GAAyB;YAChD,QAAQ,EAAE,EAAE;YACZ,UAAU;YACV,SAAS;YACT,cAAc;YACd,UAAU;YACV,UAAU;YACV,cAAc;SACf,CAAC;QAEF,MAAM,eAAe,GAAG,KAAK,IAAI,EAAE;YACjC,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,YAAY,YAAY,8CAA8C,UAAU,EAAE,CACnF,CAAC;YAEF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,qBAAqB,CAAC;gBACnE,UAAU;gBACV,SAAS;gBACT,YAAY;gBACZ,KAAK,EAAE,aAAa;gBACpB,SAAS;aACV,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvC,OAAO;YACT,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,YAAY,YAAY,YAAY,QAAQ,CAAC,MAAM,yBAAyB,UAAU,EAAE,CACzF,CAAC;YAEF,MAAM,IAAI,CAAC,qBAAqB,CAAC,eAAe,CAAC;gBAC/C,GAAG,mBAAmB;gBACtB,QAAQ;aACT,CAAC,CAAC;QACL,CAAC,CAAC;QAEF,MAAM,IAAI,CAAC,uBAAuB,CAAC,aAAa,CAC9C;YACE,UAAU,EAAE,8BAAsB;YAClC,IAAI,EAAE,YAAY,YAAY,EAAE;SACjC,EACD,eAAe,CAChB,CAAC;IACJ,CAAC;CACF,CAAA;AAhFY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;qCAK4B,yCAAkB;QACf,4DAA2B;QAC9B,yCAAkB;QACb,mDAAuB;GAPxD,qBAAqB,CAgFjC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ContextService } from '@sprout-idws/sprout-context';
|
|
2
|
+
import { RedisStreamService, StreamMessage } from './redis-stream.service';
|
|
3
|
+
import { StreamRetryService } from './stream-retry.service';
|
|
4
|
+
export interface ProcessMessagesInput {
|
|
5
|
+
streamName: string;
|
|
6
|
+
groupName: string;
|
|
7
|
+
maxRetries: number;
|
|
8
|
+
retryDelay: number;
|
|
9
|
+
maxConcurrency: number;
|
|
10
|
+
messages: StreamMessage[];
|
|
11
|
+
messageHandler: (entry: StreamMessage) => Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export declare class StreamMessageHandlerService {
|
|
14
|
+
private readonly redisStreamService;
|
|
15
|
+
private readonly retryService;
|
|
16
|
+
private readonly contextService;
|
|
17
|
+
private readonly logger;
|
|
18
|
+
private processingMessages;
|
|
19
|
+
constructor(redisStreamService: RedisStreamService, retryService: StreamRetryService, contextService: ContextService);
|
|
20
|
+
processMessages(processBatchInput: ProcessMessagesInput): Promise<void>;
|
|
21
|
+
private processMessage;
|
|
22
|
+
private handleFailedMessage;
|
|
23
|
+
private sendToDlq;
|
|
24
|
+
private sendRetryMessage;
|
|
25
|
+
getProcessingMessagesCount(): number;
|
|
26
|
+
isMessageProcessing(messageId: string): boolean;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=stream-message-handler.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-message-handler.service.d.ts","sourceRoot":"","sources":["../../src/stream/stream-message-handler.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,cAAc,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAWD,qBACa,2BAA2B;IAKpC,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,cAAc;IANjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgD;IACvE,OAAO,CAAC,kBAAkB,CAAqB;gBAG5B,kBAAkB,EAAE,kBAAkB,EACtC,YAAY,EAAE,kBAAkB,EAChC,cAAc,EAAE,cAAc;IAG3C,eAAe,CAAC,iBAAiB,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;YA6C/D,cAAc;YAiCd,mBAAmB;YAuBnB,SAAS;YAKT,gBAAgB;IAW9B,0BAA0B,IAAI,MAAM;IAIpC,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;CAGhD"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var StreamMessageHandlerService_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.StreamMessageHandlerService = void 0;
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const sprout_context_1 = require("@sprout-idws/sprout-context");
|
|
16
|
+
const redis_stream_service_1 = require("./redis-stream.service");
|
|
17
|
+
const stream_retry_service_1 = require("./stream-retry.service");
|
|
18
|
+
let StreamMessageHandlerService = StreamMessageHandlerService_1 = class StreamMessageHandlerService {
|
|
19
|
+
constructor(redisStreamService, retryService, contextService) {
|
|
20
|
+
this.redisStreamService = redisStreamService;
|
|
21
|
+
this.retryService = retryService;
|
|
22
|
+
this.contextService = contextService;
|
|
23
|
+
this.logger = new common_1.Logger(StreamMessageHandlerService_1.name);
|
|
24
|
+
this.processingMessages = new Set();
|
|
25
|
+
}
|
|
26
|
+
async processMessages(processBatchInput) {
|
|
27
|
+
const { messages, streamName, groupName, maxRetries, retryDelay, maxConcurrency, messageHandler, } = processBatchInput;
|
|
28
|
+
const semaphore = new Semaphore(Math.min(maxConcurrency, messages.length));
|
|
29
|
+
const processMessageInput = {
|
|
30
|
+
streamName,
|
|
31
|
+
groupName,
|
|
32
|
+
messageHandler,
|
|
33
|
+
maxRetries,
|
|
34
|
+
retryDelay,
|
|
35
|
+
};
|
|
36
|
+
const promises = messages.map(async (message) => {
|
|
37
|
+
if (this.processingMessages.has(message.id)) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
this.processingMessages.add(message.id);
|
|
41
|
+
const release = await semaphore.acquire();
|
|
42
|
+
try {
|
|
43
|
+
const startTime = Date.now();
|
|
44
|
+
await this.processMessage({
|
|
45
|
+
...processMessageInput,
|
|
46
|
+
message: message,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
this.processingMessages.delete(message.id);
|
|
51
|
+
release();
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
await Promise.allSettled(promises);
|
|
55
|
+
}
|
|
56
|
+
async processMessage(request) {
|
|
57
|
+
try {
|
|
58
|
+
const encodedContext = request.message.context;
|
|
59
|
+
const runCallback = async () => {
|
|
60
|
+
await request.messageHandler(request.message);
|
|
61
|
+
await this.redisStreamService.ackDelete(request.streamName, request.groupName, request.message.id);
|
|
62
|
+
};
|
|
63
|
+
if (encodedContext) {
|
|
64
|
+
const context = this.contextService.getDecodedContext(encodedContext);
|
|
65
|
+
await this.contextService.run(context, runCallback);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
await runCallback();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const typedError = error;
|
|
73
|
+
this.logger.error(`Error processing message ${request.message.id}: ${typedError.message}`, typedError.stack);
|
|
74
|
+
this.logger.error(`Message payload for ${request.message.id}: ${JSON.stringify(request.message.data)}`);
|
|
75
|
+
await this.handleFailedMessage(request, error);
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async handleFailedMessage(inFlightMessage, error) {
|
|
80
|
+
const { maxRetries, message, streamName, groupName } = inFlightMessage;
|
|
81
|
+
const retryCount = (inFlightMessage.message.metadata?.retryCount || 0) + 1;
|
|
82
|
+
if (retryCount < maxRetries) {
|
|
83
|
+
this.logger.warn(`Message ${message.id} in stream ${streamName} and group ${groupName} failed with error: ${error.message}, retrying...`);
|
|
84
|
+
await this.sendRetryMessage({
|
|
85
|
+
...inFlightMessage,
|
|
86
|
+
message: { ...message, metadata: { ...message.metadata, retryCount } },
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
this.logger.error(`Message ${message.id} in stream ${streamName} and group ${groupName} exceeded max retries (${maxRetries}), moving to DLQ. Final error:`, error.stack || error.message);
|
|
91
|
+
await this.sendToDlq(inFlightMessage);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async sendToDlq(request) {
|
|
95
|
+
const { message, streamName, groupName } = request;
|
|
96
|
+
await this.redisStreamService.sendToDlq(streamName, groupName, message.id);
|
|
97
|
+
}
|
|
98
|
+
async sendRetryMessage(inFlightMessage) {
|
|
99
|
+
const { message, maxRetries } = inFlightMessage;
|
|
100
|
+
const retryCount = message.metadata?.retryCount || 0;
|
|
101
|
+
this.logger.warn(`Scheduling retry for message ${message.id} (attempt ${retryCount}/${maxRetries})`);
|
|
102
|
+
await this.retryService.delayedRetry(inFlightMessage);
|
|
103
|
+
}
|
|
104
|
+
getProcessingMessagesCount() {
|
|
105
|
+
return this.processingMessages.size;
|
|
106
|
+
}
|
|
107
|
+
isMessageProcessing(messageId) {
|
|
108
|
+
return this.processingMessages.has(messageId);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
exports.StreamMessageHandlerService = StreamMessageHandlerService;
|
|
112
|
+
exports.StreamMessageHandlerService = StreamMessageHandlerService = StreamMessageHandlerService_1 = __decorate([
|
|
113
|
+
(0, common_1.Injectable)(),
|
|
114
|
+
__metadata("design:paramtypes", [redis_stream_service_1.RedisStreamService,
|
|
115
|
+
stream_retry_service_1.StreamRetryService,
|
|
116
|
+
sprout_context_1.ContextService])
|
|
117
|
+
], StreamMessageHandlerService);
|
|
118
|
+
class Semaphore {
|
|
119
|
+
constructor(permits) {
|
|
120
|
+
this.waitQueue = [];
|
|
121
|
+
this.permits = permits;
|
|
122
|
+
}
|
|
123
|
+
async acquire() {
|
|
124
|
+
if (this.permits > 0) {
|
|
125
|
+
this.permits--;
|
|
126
|
+
return () => this.release();
|
|
127
|
+
}
|
|
128
|
+
return new Promise((resolve) => {
|
|
129
|
+
this.waitQueue.push(() => {
|
|
130
|
+
this.permits--;
|
|
131
|
+
resolve(() => this.release());
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
release() {
|
|
136
|
+
this.permits++;
|
|
137
|
+
if (this.waitQueue.length > 0) {
|
|
138
|
+
const next = this.waitQueue.shift();
|
|
139
|
+
if (next) {
|
|
140
|
+
next();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=stream-message-handler.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-message-handler.service.js","sourceRoot":"","sources":["../../src/stream/stream-message-handler.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { RedisStreamService } from './redis-stream.service';
|
|
2
|
+
import { ContextService } from '@sprout-idws/sprout-context';
|
|
3
|
+
export declare class StreamPublisherService {
|
|
4
|
+
private readonly redisStreamService;
|
|
5
|
+
private readonly contextService;
|
|
6
|
+
constructor(redisStreamService: RedisStreamService, contextService: ContextService);
|
|
7
|
+
publishToStream(streamName: string, data: object): Promise<string>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=stream-publisher.service.d.ts.map
|