@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
- constructor(queueName: string, options: Options, processEventsFunction: WorkerProcessFunction<EventEntity>, failureHandler: WorkerFailureHandler<EventEntity>);
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>;
@@ -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
- constructor(queueName: string, options: Options, processEventsFunction: WorkerProcessFunction<EventEntity>, failureHandler: WorkerFailureHandler<EventEntity>);
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>;
@@ -35,7 +35,8 @@ var KafkaWorkerConsumer = class {
35
35
  options;
36
36
  processEventsFunction;
37
37
  failureHandler;
38
- constructor(queueName, options, processEventsFunction, failureHandler) {
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
- await this.setupConsumer();
167
- await this.producer.connect();
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();
@@ -9,7 +9,8 @@ var KafkaWorkerConsumer = class {
9
9
  options;
10
10
  processEventsFunction;
11
11
  failureHandler;
12
- constructor(queueName, options, processEventsFunction, failureHandler) {
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
- await this.setupConsumer();
141
- await this.producer.connect();
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
- await this.setupConsumer();
183
- await this.producer.connect();
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forklaunch/implementation-worker-kafka",
3
- "version": "0.8.6",
3
+ "version": "0.9.0",
4
4
  "description": "Kafka implementation for forklaunch workers",
5
5
  "homepage": "https://github.com/forklaunch/forklaunch-js#readme",
6
6
  "bugs": {