@forklaunch/implementation-worker-kafka 0.8.6 → 0.9.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.
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { OpenTelemetryCollector, MetricsDefinition } from '@forklaunch/core/http';
|
|
1
2
|
import { WorkerConsumer } from '@forklaunch/interfaces-worker/interfaces';
|
|
2
3
|
import { WorkerEventEntity, WorkerProcessFunction, WorkerFailureHandler } from '@forklaunch/interfaces-worker/types';
|
|
3
4
|
import { KafkaWorkerOptions } from '../domain/types/index.mjs';
|
|
@@ -11,7 +12,8 @@ declare class KafkaWorkerConsumer<EventEntity extends WorkerEventEntity, Options
|
|
|
11
12
|
protected readonly options: Options;
|
|
12
13
|
protected readonly processEventsFunction: WorkerProcessFunction<EventEntity>;
|
|
13
14
|
protected readonly failureHandler: WorkerFailureHandler<EventEntity>;
|
|
14
|
-
|
|
15
|
+
protected readonly openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>;
|
|
16
|
+
constructor(queueName: string, options: Options, processEventsFunction: WorkerProcessFunction<EventEntity>, failureHandler: WorkerFailureHandler<EventEntity>, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>);
|
|
15
17
|
private setupConsumer;
|
|
16
18
|
peekEvents(): Promise<EventEntity[]>;
|
|
17
19
|
start(): Promise<void>;
|
package/lib/consumers/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { OpenTelemetryCollector, MetricsDefinition } from '@forklaunch/core/http';
|
|
1
2
|
import { WorkerConsumer } from '@forklaunch/interfaces-worker/interfaces';
|
|
2
3
|
import { WorkerEventEntity, WorkerProcessFunction, WorkerFailureHandler } from '@forklaunch/interfaces-worker/types';
|
|
3
4
|
import { KafkaWorkerOptions } from '../domain/types/index.js';
|
|
@@ -11,7 +12,8 @@ declare class KafkaWorkerConsumer<EventEntity extends WorkerEventEntity, Options
|
|
|
11
12
|
protected readonly options: Options;
|
|
12
13
|
protected readonly processEventsFunction: WorkerProcessFunction<EventEntity>;
|
|
13
14
|
protected readonly failureHandler: WorkerFailureHandler<EventEntity>;
|
|
14
|
-
|
|
15
|
+
protected readonly openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>;
|
|
16
|
+
constructor(queueName: string, options: Options, processEventsFunction: WorkerProcessFunction<EventEntity>, failureHandler: WorkerFailureHandler<EventEntity>, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>);
|
|
15
17
|
private setupConsumer;
|
|
16
18
|
peekEvents(): Promise<EventEntity[]>;
|
|
17
19
|
start(): Promise<void>;
|
package/lib/consumers/index.js
CHANGED
|
@@ -35,7 +35,8 @@ var KafkaWorkerConsumer = class {
|
|
|
35
35
|
options;
|
|
36
36
|
processEventsFunction;
|
|
37
37
|
failureHandler;
|
|
38
|
-
|
|
38
|
+
openTelemetryCollector;
|
|
39
|
+
constructor(queueName, options, processEventsFunction, failureHandler, openTelemetryCollector) {
|
|
39
40
|
this.queueName = queueName;
|
|
40
41
|
this.options = options;
|
|
41
42
|
this.processEventsFunction = processEventsFunction;
|
|
@@ -48,6 +49,7 @@ var KafkaWorkerConsumer = class {
|
|
|
48
49
|
this.consumer = this.kafka.consumer({
|
|
49
50
|
groupId: this.options.groupId
|
|
50
51
|
});
|
|
52
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
|
51
53
|
}
|
|
52
54
|
async setupConsumer() {
|
|
53
55
|
await this.consumer.connect();
|
|
@@ -163,8 +165,29 @@ var KafkaWorkerConsumer = class {
|
|
|
163
165
|
}
|
|
164
166
|
}
|
|
165
167
|
async start() {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
+
const maxAttempts = 30;
|
|
169
|
+
const delayMs = 2e3;
|
|
170
|
+
let attempt = 1;
|
|
171
|
+
while (attempt <= maxAttempts) {
|
|
172
|
+
try {
|
|
173
|
+
await this.setupConsumer();
|
|
174
|
+
await this.producer.connect();
|
|
175
|
+
return;
|
|
176
|
+
} catch (error) {
|
|
177
|
+
const err = error;
|
|
178
|
+
const isUnknownTopic = err?.code === 3 || err?.type === "UNKNOWN_TOPIC_OR_PARTITION" || (err?.message || "").includes(
|
|
179
|
+
"This server does not host this topic-partition"
|
|
180
|
+
);
|
|
181
|
+
if (!isUnknownTopic || attempt >= maxAttempts) {
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
this.openTelemetryCollector.warn(
|
|
185
|
+
`Kafka not ready for topic ${this.queueName} (attempt ${attempt}/${maxAttempts}). Retrying in ${delayMs}ms...`
|
|
186
|
+
);
|
|
187
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
188
|
+
attempt += 1;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
168
191
|
}
|
|
169
192
|
async close() {
|
|
170
193
|
await this.producer.disconnect();
|
package/lib/consumers/index.mjs
CHANGED
|
@@ -9,7 +9,8 @@ var KafkaWorkerConsumer = class {
|
|
|
9
9
|
options;
|
|
10
10
|
processEventsFunction;
|
|
11
11
|
failureHandler;
|
|
12
|
-
|
|
12
|
+
openTelemetryCollector;
|
|
13
|
+
constructor(queueName, options, processEventsFunction, failureHandler, openTelemetryCollector) {
|
|
13
14
|
this.queueName = queueName;
|
|
14
15
|
this.options = options;
|
|
15
16
|
this.processEventsFunction = processEventsFunction;
|
|
@@ -22,6 +23,7 @@ var KafkaWorkerConsumer = class {
|
|
|
22
23
|
this.consumer = this.kafka.consumer({
|
|
23
24
|
groupId: this.options.groupId
|
|
24
25
|
});
|
|
26
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
|
25
27
|
}
|
|
26
28
|
async setupConsumer() {
|
|
27
29
|
await this.consumer.connect();
|
|
@@ -137,8 +139,29 @@ var KafkaWorkerConsumer = class {
|
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
141
|
async start() {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
+
const maxAttempts = 30;
|
|
143
|
+
const delayMs = 2e3;
|
|
144
|
+
let attempt = 1;
|
|
145
|
+
while (attempt <= maxAttempts) {
|
|
146
|
+
try {
|
|
147
|
+
await this.setupConsumer();
|
|
148
|
+
await this.producer.connect();
|
|
149
|
+
return;
|
|
150
|
+
} catch (error) {
|
|
151
|
+
const err = error;
|
|
152
|
+
const isUnknownTopic = err?.code === 3 || err?.type === "UNKNOWN_TOPIC_OR_PARTITION" || (err?.message || "").includes(
|
|
153
|
+
"This server does not host this topic-partition"
|
|
154
|
+
);
|
|
155
|
+
if (!isUnknownTopic || attempt >= maxAttempts) {
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
this.openTelemetryCollector.warn(
|
|
159
|
+
`Kafka not ready for topic ${this.queueName} (attempt ${attempt}/${maxAttempts}). Retrying in ${delayMs}ms...`
|
|
160
|
+
);
|
|
161
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
162
|
+
attempt += 1;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
142
165
|
}
|
|
143
166
|
async close() {
|
|
144
167
|
await this.producer.disconnect();
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MetricsDefinition,
|
|
3
|
+
OpenTelemetryCollector
|
|
4
|
+
} from '@forklaunch/core/http';
|
|
1
5
|
import { WorkerConsumer } from '@forklaunch/interfaces-worker/interfaces';
|
|
2
6
|
import {
|
|
3
7
|
WorkerEventEntity,
|
|
@@ -20,12 +24,14 @@ export class KafkaWorkerConsumer<
|
|
|
20
24
|
protected readonly options: Options;
|
|
21
25
|
protected readonly processEventsFunction: WorkerProcessFunction<EventEntity>;
|
|
22
26
|
protected readonly failureHandler: WorkerFailureHandler<EventEntity>;
|
|
27
|
+
protected readonly openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>;
|
|
23
28
|
|
|
24
29
|
constructor(
|
|
25
30
|
queueName: string,
|
|
26
31
|
options: Options,
|
|
27
32
|
processEventsFunction: WorkerProcessFunction<EventEntity>,
|
|
28
|
-
failureHandler: WorkerFailureHandler<EventEntity
|
|
33
|
+
failureHandler: WorkerFailureHandler<EventEntity>,
|
|
34
|
+
openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>
|
|
29
35
|
) {
|
|
30
36
|
this.queueName = queueName;
|
|
31
37
|
this.options = options;
|
|
@@ -40,6 +46,7 @@ export class KafkaWorkerConsumer<
|
|
|
40
46
|
this.consumer = this.kafka.consumer({
|
|
41
47
|
groupId: this.options.groupId
|
|
42
48
|
});
|
|
49
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
private async setupConsumer() {
|
|
@@ -179,8 +186,39 @@ export class KafkaWorkerConsumer<
|
|
|
179
186
|
}
|
|
180
187
|
|
|
181
188
|
async start(): Promise<void> {
|
|
182
|
-
|
|
183
|
-
|
|
189
|
+
const maxAttempts = 30;
|
|
190
|
+
const delayMs = 2000;
|
|
191
|
+
let attempt = 1;
|
|
192
|
+
|
|
193
|
+
while (attempt <= maxAttempts) {
|
|
194
|
+
try {
|
|
195
|
+
await this.setupConsumer();
|
|
196
|
+
await this.producer.connect();
|
|
197
|
+
return;
|
|
198
|
+
} catch (error) {
|
|
199
|
+
const err = error as Error & {
|
|
200
|
+
code?: number;
|
|
201
|
+
type?: string;
|
|
202
|
+
message?: string;
|
|
203
|
+
};
|
|
204
|
+
const isUnknownTopic =
|
|
205
|
+
err?.code === 3 ||
|
|
206
|
+
err?.type === 'UNKNOWN_TOPIC_OR_PARTITION' ||
|
|
207
|
+
(err?.message || '').includes(
|
|
208
|
+
'This server does not host this topic-partition'
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
if (!isUnknownTopic || attempt >= maxAttempts) {
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
this.openTelemetryCollector.warn(
|
|
216
|
+
`Kafka not ready for topic ${this.queueName} (attempt ${attempt}/${maxAttempts}). Retrying in ${delayMs}ms...`
|
|
217
|
+
);
|
|
218
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
219
|
+
attempt += 1;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
184
222
|
}
|
|
185
223
|
|
|
186
224
|
async close(): Promise<void> {
|
package/package.json
CHANGED