@aichatwar/shared 1.0.103 → 1.0.105

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.
@@ -8,8 +8,12 @@ export declare abstract class Listener<T extends BaseEvent> {
8
8
  protected ackDeadline: number;
9
9
  private currentPayload?;
10
10
  private retryCount;
11
- private readonly maxRetries;
11
+ private readonly maxInitialRetries;
12
+ private readonly maxRetryDelay;
13
+ private isListening;
14
+ private crashHandlerSetup;
12
15
  constructor(consumer: Consumer);
16
+ private setupCrashHandler;
13
17
  ack(): Promise<void>;
14
18
  listen(): Promise<void>;
15
19
  }
@@ -14,8 +14,29 @@ class Listener {
14
14
  constructor(consumer) {
15
15
  this.ackDeadline = 5 * 1000; // 5 seconds
16
16
  this.retryCount = 0;
17
- this.maxRetries = 5;
17
+ this.maxInitialRetries = 5; // Show detailed retry logs for first 5 attempts
18
+ this.maxRetryDelay = 60000; // Cap delay at 60 seconds
19
+ this.isListening = false; // Track if listener is active
20
+ this.crashHandlerSetup = false; // Track if crash handler has been set up
18
21
  this.consumer = consumer;
22
+ this.setupCrashHandler();
23
+ }
24
+ setupCrashHandler() {
25
+ // Only set up crash handler once to avoid memory leaks
26
+ if (this.crashHandlerSetup) {
27
+ return;
28
+ }
29
+ this.consumer.on('consumer.crash', (event) => {
30
+ var _a;
31
+ const error = event.payload.error;
32
+ if ((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('does not host this topic-partition')) {
33
+ console.warn(`[${this.topic}] Consumer partition error (non-fatal):`, error.message);
34
+ // Don't crash - this is often a transient error
35
+ return;
36
+ }
37
+ console.error(`[${this.topic}] Consumer crashed:`, error);
38
+ });
39
+ this.crashHandlerSetup = true;
19
40
  }
20
41
  // Manual acknowledgment method
21
42
  ack() {
@@ -34,26 +55,20 @@ class Listener {
34
55
  listen() {
35
56
  return __awaiter(this, void 0, void 0, function* () {
36
57
  var _a;
58
+ // Prevent multiple simultaneous listen attempts
59
+ if (this.isListening) {
60
+ return;
61
+ }
37
62
  try {
38
63
  yield this.consumer.connect();
39
- // Handle consumer errors (including partition errors)
40
- this.consumer.on('consumer.crash', (event) => {
41
- var _a;
42
- const error = event.payload.error;
43
- if ((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('does not host this topic-partition')) {
44
- console.warn(`[${this.topic}] Consumer partition error (non-fatal):`, error.message);
45
- // Don't crash - this is often a transient error
46
- return;
47
- }
48
- console.error(`[${this.topic}] Consumer crashed:`, error);
49
- });
64
+ // Crash handler is set up in constructor, no need to add it again
50
65
  yield this.consumer.subscribe({
51
66
  topic: this.topic,
52
67
  fromBeginning: false,
53
68
  });
54
69
  // Reset retry count on successful connection
55
70
  this.retryCount = 0;
56
- console.log(`Listening to topic: ${this.topic} with groupId: ${this.groupId}`);
71
+ console.log(`[${this.topic}] Listening to topic with groupId: ${this.groupId}`);
57
72
  yield this.consumer.run({
58
73
  eachMessage: (payload) => __awaiter(this, void 0, void 0, function* () {
59
74
  if (!payload.message.value)
@@ -78,27 +93,37 @@ class Listener {
78
93
  }
79
94
  })
80
95
  });
96
+ // Mark as successfully listening only after subscription and run are successful
97
+ this.isListening = true;
81
98
  }
82
99
  catch (error) {
100
+ // Reset listening flag on error so we can retry
101
+ this.isListening = false;
83
102
  // Handle connection/subscription errors gracefully
84
103
  // The "This server does not host this topic-partition" error is often harmless
85
104
  // and occurs when KafkaJS tries to list offsets for a topic that doesn't exist yet or has no messages
86
105
  if ((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('does not host this topic-partition')) {
87
106
  this.retryCount++;
88
- if (this.retryCount <= this.maxRetries) {
89
- console.warn(`Topic partition error for ${this.topic} (attempt ${this.retryCount}/${this.maxRetries}):`, error.message);
90
- // Retry subscription after a delay (exponential backoff)
91
- const delay = Math.min(5000 * Math.pow(2, this.retryCount - 1), 30000);
92
- setTimeout(() => this.listen(), delay);
93
- return;
107
+ // Calculate exponential backoff delay (cap at maxRetryDelay)
108
+ const delay = Math.min(5000 * Math.pow(2, Math.min(this.retryCount - 1, 6)), this.maxRetryDelay);
109
+ // Show detailed logs for first few attempts, then less frequently
110
+ if (this.retryCount <= this.maxInitialRetries) {
111
+ console.warn(`[${this.topic}] Topic partition error (attempt ${this.retryCount}/${this.maxInitialRetries}): ${error.message}. Retrying in ${delay}ms...`);
94
112
  }
95
- else {
96
- console.error(`Max retries reached for topic ${this.topic}. Error:`, error.message);
97
- // Don't throw - just log and continue (service will still work for other topics)
98
- return;
113
+ else if (this.retryCount % 10 === 0) {
114
+ // Log every 10th retry after initial attempts to avoid log spam
115
+ console.warn(`[${this.topic}] Still retrying subscription (attempt ${this.retryCount}). Topic may not exist yet or has no data. Retrying in ${delay}ms...`);
99
116
  }
117
+ // Retry indefinitely with exponential backoff
118
+ // This allows the listener to connect once the topic becomes available
119
+ setTimeout(() => {
120
+ if (!this.isListening) {
121
+ this.listen();
122
+ }
123
+ }, delay);
124
+ return;
100
125
  }
101
- console.error(`Error setting up listener for topic: ${this.topic}`, error);
126
+ console.error(`[${this.topic}] Error setting up listener:`, error);
102
127
  throw error;
103
128
  }
104
129
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aichatwar/shared",
3
- "version": "1.0.103",
3
+ "version": "1.0.105",
4
4
  "main": "./build/index.js",
5
5
  "typs": "./build/index.d.ts",
6
6
  "files": [