nodejs-task-scheduler 1.0.9 โ†’ 1.0.10

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.
@@ -14,7 +14,11 @@
14
14
  "Bash(npm test:*)",
15
15
  "Bash(node:*)",
16
16
  "Bash(touch:*)",
17
- "Bash(npx tsc:*)"
17
+ "Bash(npx tsc:*)",
18
+ "Bash(export RABBITMQ_URL=\"amqp://admin:password@localhost:5672\")",
19
+ "Bash(timeout 60 npx ts-node test-multinode.ts)",
20
+ "Bash(timeout 60 npx ts-node test-simple-election.ts)",
21
+ "Bash(timeout 45 npx ts-node test-quick-election.ts)"
18
22
  ],
19
23
  "deny": []
20
24
  }
@@ -0,0 +1,166 @@
1
+ # Multi-Node Master Election Testing
2
+
3
+ This directory contains tests to verify the master election functionality works correctly across multiple nodes using the `njs_testing_` prefix.
4
+
5
+ ## Test Files
6
+
7
+ - `test-quick-election.ts` - **Recommended** - Fast test with clean output
8
+ - `test-simple-election.ts` - Detailed test with full output
9
+ - `test-multinode.ts` - Comprehensive automated multi-node test
10
+ - `test-simple-multinode.ts` - Simple test for manual verification
11
+ - `run-multinode-test.sh` - Shell script to run automated tests
12
+
13
+ ## Prerequisites
14
+
15
+ 1. **RabbitMQ Server Running**
16
+ ```bash
17
+ # Using Docker
18
+ docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management
19
+
20
+ # Or start existing RabbitMQ service
21
+ brew services start rabbitmq # macOS
22
+ sudo systemctl start rabbitmq-server # Linux
23
+ ```
24
+
25
+ 2. **Project Built**
26
+ ```bash
27
+ npm run build
28
+ ```
29
+
30
+ ## Running Tests
31
+
32
+ ### Quick Test (Recommended)
33
+
34
+ Run the fast, clean test with minimal output:
35
+
36
+ ```bash
37
+ # Start RabbitMQ (if not running)
38
+ ./docker-rabbitmq.sh
39
+
40
+ # Run quick test
41
+ export RABBITMQ_URL="amqp://admin:password@localhost:5672"
42
+ npx ts-node test-quick-election.ts
43
+ ```
44
+
45
+ ### Automated Test Suite
46
+
47
+ Run the comprehensive automated test:
48
+
49
+ ```bash
50
+ # Make script executable (Linux/macOS)
51
+ chmod +x run-multinode-test.sh
52
+
53
+ # Run the test
54
+ ./run-multinode-test.sh
55
+
56
+ # Or run directly with ts-node
57
+ npx ts-node test-multinode.ts
58
+ ```
59
+
60
+ **What the automated test does:**
61
+ 1. Starts 3 nodes with `njs_testing_` prefix
62
+ 2. Verifies single leader election
63
+ 3. Tests job scheduling and distribution
64
+ 4. Tests leader failover (shuts down leader)
65
+ 5. Verifies new leader election
66
+ 6. Tests job distribution across remaining nodes
67
+ 7. Cleans up all resources
68
+
69
+ ### Manual Testing
70
+
71
+ For manual verification, run nodes in separate terminals:
72
+
73
+ **Terminal 1 (Leader + Scheduler):**
74
+ ```bash
75
+ npx ts-node test-simple-multinode.ts node1 scheduler
76
+ ```
77
+
78
+ **Terminal 2 (Worker):**
79
+ ```bash
80
+ npx ts-node test-simple-multinode.ts node2
81
+ ```
82
+
83
+ **Terminal 3 (Worker):**
84
+ ```bash
85
+ npx ts-node test-simple-multinode.ts node3
86
+ ```
87
+
88
+ **What you should see:**
89
+ - One node becomes leader (๐Ÿ‘‘ LEADER)
90
+ - Other nodes become followers (๐Ÿ‘ฅ FOLLOWER)
91
+ - Jobs scheduled by node1 are processed by any available node
92
+ - Status updates every 10 seconds
93
+ - Jobs scheduled every 15 seconds
94
+
95
+ ### Testing Leader Failover
96
+
97
+ 1. Start multiple nodes as shown above
98
+ 2. Note which node is the leader
99
+ 3. Stop the leader node (Ctrl+C)
100
+ 4. Watch other nodes - one should become the new leader
101
+ 5. Jobs should continue processing normally
102
+
103
+ ## Expected Behavior
104
+
105
+ ### โœ… Correct Behavior
106
+ - Only one leader at any time
107
+ - All nodes can process jobs from the queue
108
+ - Leader failover happens automatically
109
+ - Jobs are distributed across available nodes
110
+ - Queues use `njs_testing_` prefix
111
+
112
+ ### โŒ Issues to Watch For
113
+ - Multiple leaders elected simultaneously
114
+ - Split-brain scenarios
115
+ - Connection errors during election
116
+ - Jobs stuck in queue
117
+ - Resource leaks (queues not cleaned up)
118
+
119
+ ## Troubleshooting
120
+
121
+ ### Connection Errors
122
+ ```
123
+ Error: Not connected to RabbitMQ
124
+ ```
125
+ - Ensure RabbitMQ is running
126
+ - Check connection URL in environment variable
127
+ - Verify network connectivity
128
+
129
+ ### Access Refused Errors
130
+ ```
131
+ ACCESS_REFUSED - queue 'njs_testing_leader-election' in exclusive use
132
+ ```
133
+ - This is expected when multiple nodes compete for leadership
134
+ - Should resolve automatically with follower election
135
+
136
+ ### No Leader Elected
137
+ - Check RabbitMQ server is accessible
138
+ - Verify queue permissions
139
+ - Look for connection timeouts
140
+
141
+ ## Cleanup
142
+
143
+ The tests automatically clean up their resources, but if needed, you can manually clean up:
144
+
145
+ ```bash
146
+ # Connect to RabbitMQ management
147
+ http://localhost:15672
148
+
149
+ # Delete queues with njs_testing_ prefix
150
+ # Or use rabbitmqctl:
151
+ sudo rabbitmqctl list_queues | grep njs_testing
152
+ sudo rabbitmqctl delete_queue njs_testing_leader-election
153
+ sudo rabbitmqctl delete_queue njs_testing_leader-heartbeat
154
+ ```
155
+
156
+ ## Environment Variables
157
+
158
+ - `RABBITMQ_URL` - RabbitMQ connection URL (default: `amqp://localhost:5672`)
159
+
160
+ ## Test Results
161
+
162
+ When tests pass, you should see:
163
+ - โœ… Single leader elected
164
+ - โœ… Jobs scheduled successfully
165
+ - โœ… Leader failover completed successfully
166
+ - โœ… All multi-node tests completed successfully!
@@ -1 +1 @@
1
- {"version":3,"file":"load-balancer.d.ts","sourceRoot":"","sources":["../../src/utils/load-balancer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,aAAa,CAAS;gBAElB,UAAU,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;IAKpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAUrB,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5E,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAiBpD,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAetD,WAAW,IAAI,QAAQ,EAAE;IAIzB,cAAc,IAAI,QAAQ,EAAE;IAK5B,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;YAc5C,oBAAoB;YAapB,cAAc;YAQd,aAAa;YAgBb,oBAAoB;YAgBpB,mBAAmB;IAgBjC,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,gBAAgB;CAYzB"}
1
+ {"version":3,"file":"load-balancer.d.ts","sourceRoot":"","sources":["../../src/utils/load-balancer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,aAAa,CAAS;gBAElB,UAAU,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;IAKpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAUrB,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5E,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAiBpD,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAetD,WAAW,IAAI,QAAQ,EAAE;IAIzB,cAAc,IAAI,QAAQ,EAAE;IAK5B,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;YAc5C,oBAAoB;YAapB,cAAc;YAQd,aAAa;YAgBb,oBAAoB;YAqBpB,mBAAmB;IAgBjC,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,gBAAgB;CAYzB"}
@@ -102,6 +102,10 @@ class LoadBalancer {
102
102
  await channel.publish('heartbeat', '', Buffer.from(JSON.stringify(heartbeat)));
103
103
  }
104
104
  async sendOfflineHeartbeat() {
105
+ if (!this.connection.isConnected()) {
106
+ // Silently skip offline heartbeat when not connected - this is expected during shutdown
107
+ return;
108
+ }
105
109
  const channel = this.connection.getChannel();
106
110
  const heartbeat = {
107
111
  nodeId: this.nodeId,
@@ -1 +1 @@
1
- {"version":3,"file":"load-balancer.js","sourceRoot":"","sources":["../../src/utils/load-balancer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"load-balancer.js","sourceRoot":"","sources":["../../src/utils/load-balancer.ts"],"names":[],"mappings":"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"}
@@ -31,6 +31,7 @@ export declare class MasterElection extends EventEmitter {
31
31
  private heartbeatQueue;
32
32
  private lastHeartbeat?;
33
33
  private isRunning;
34
+ private consumerTag?;
34
35
  constructor(config: MasterElectionConfig);
35
36
  start(): Promise<void>;
36
37
  stop(): Promise<void>;
@@ -48,6 +49,7 @@ export declare class MasterElection extends EventEmitter {
48
49
  private resignLeadership;
49
50
  private startPeriodicCleanup;
50
51
  private cleanupHeartbeatQueue;
52
+ private waitForConnection;
51
53
  private getQueueName;
52
54
  }
53
55
  //# sourceMappingURL=master-election.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"master-election.d.ts","sourceRoot":"","sources":["../../src/utils/master-election.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,oBAAY,eAAe;IACzB,QAAQ,aAAa;IACrB,SAAS,cAAc;IACvB,MAAM,WAAW;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,qBAAa,cAAe,SAAQ,YAAY;IAC9C,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAA6C;IAC1D,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAiB;IACzC,OAAO,CAAC,YAAY,CAAC,CAAiB;IACtC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,aAAa,CAAC,CAAO;IAC7B,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,oBAAoB;IAYlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAWtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA6B3B,QAAQ,IAAI,OAAO;IAInB,SAAS,IAAI,MAAM;IAInB,QAAQ,IAAI,eAAe;YAIb,gBAAgB;YA+BhB,aAAa;YAYb,iBAAiB;YA2BjB,YAAY;YAoBZ,cAAc;YAuBd,aAAa;YAuCb,sBAAsB;IA8CpC,OAAO,CAAC,sBAAsB;YAahB,gBAAgB;IAuB9B,OAAO,CAAC,oBAAoB;YAmBd,qBAAqB;IAenC,OAAO,CAAC,YAAY;CAGrB"}
1
+ {"version":3,"file":"master-election.d.ts","sourceRoot":"","sources":["../../src/utils/master-election.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,oBAAY,eAAe;IACzB,QAAQ,aAAa;IACrB,SAAS,cAAc;IACvB,MAAM,WAAW;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,qBAAa,cAAe,SAAQ,YAAY;IAC9C,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAA6C;IAC1D,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAiB;IACzC,OAAO,CAAC,YAAY,CAAC,CAAiB;IACtC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,aAAa,CAAC,CAAO;IAC7B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAC,CAAS;gBAEjB,MAAM,EAAE,oBAAoB;IAYlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAetB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA6B3B,QAAQ,IAAI,OAAO;IAInB,SAAS,IAAI,MAAM;IAInB,QAAQ,IAAI,eAAe;YAIb,gBAAgB;YAmChB,aAAa;YAYb,iBAAiB;YAgCjB,YAAY;YAoBZ,cAAc;YAuBd,aAAa;YA2Cb,sBAAsB;IAkFpC,OAAO,CAAC,sBAAsB;YAahB,gBAAgB;IA0B9B,OAAO,CAAC,oBAAoB;YAwBd,qBAAqB;YAoBrB,iBAAiB;IAe/B,OAAO,CAAC,YAAY;CAGrB"}
@@ -27,6 +27,8 @@ class MasterElection extends events_1.EventEmitter {
27
27
  return;
28
28
  }
29
29
  this.isRunning = true;
30
+ // Wait for connection to be established
31
+ await this.waitForConnection();
30
32
  await this.initializeQueues();
31
33
  this.startPeriodicCleanup();
32
34
  await this.startElection();
@@ -62,6 +64,9 @@ class MasterElection extends events_1.EventEmitter {
62
64
  return this.state;
63
65
  }
64
66
  async initializeQueues() {
67
+ if (!this.connection.isConnected()) {
68
+ throw new Error('Not connected to RabbitMQ');
69
+ }
65
70
  const channel = this.connection.getChannel();
66
71
  // Election queue - exclusive, auto-delete when connection closes
67
72
  await channel.assertQueue(this.electionQueue, {
@@ -99,6 +104,9 @@ class MasterElection extends events_1.EventEmitter {
99
104
  }
100
105
  }
101
106
  async attemptLeadership() {
107
+ if (!this.connection.isConnected()) {
108
+ throw new Error('Not connected to RabbitMQ');
109
+ }
102
110
  const channel = this.connection.getChannel();
103
111
  try {
104
112
  // Try to consume from election queue exclusively
@@ -108,12 +116,13 @@ class MasterElection extends events_1.EventEmitter {
108
116
  noAck: true
109
117
  });
110
118
  if (consumerResult) {
119
+ this.consumerTag = consumerResult.consumerTag;
111
120
  await this.becomeLeader();
112
121
  }
113
122
  }
114
123
  catch (error) {
115
124
  if (error.message && (error.message.includes('exclusive') || error.code === 403)) {
116
- // Another node is already the leader
125
+ // Another node is already the leader - this is expected behavior
117
126
  await this.becomeFollower();
118
127
  }
119
128
  else {
@@ -162,6 +171,9 @@ class MasterElection extends events_1.EventEmitter {
162
171
  return;
163
172
  }
164
173
  try {
174
+ if (!this.connection.isConnected()) {
175
+ throw new Error('Not connected to RabbitMQ');
176
+ }
165
177
  const channel = this.connection.getChannel();
166
178
  const heartbeatMessage = {
167
179
  nodeId: this.nodeId,
@@ -191,34 +203,66 @@ class MasterElection extends events_1.EventEmitter {
191
203
  return;
192
204
  }
193
205
  try {
206
+ if (!this.connection.isConnected()) {
207
+ throw new Error('Not connected to RabbitMQ');
208
+ }
194
209
  const channel = this.connection.getChannel();
195
- await channel.consume(this.heartbeatQueue, async (msg) => {
196
- if (!msg || !this.isRunning) {
197
- return;
198
- }
199
- try {
200
- const heartbeat = JSON.parse(msg.content.toString());
201
- if (heartbeat.nodeId !== this.nodeId) {
202
- // Received heartbeat from current leader
203
- this.lastHeartbeat = new Date(heartbeat.timestamp);
204
- this.resetLeadershipTimeout();
210
+ try {
211
+ await channel.consume(this.heartbeatQueue, async (msg) => {
212
+ if (!msg || !this.isRunning) {
213
+ return;
205
214
  }
206
- channel.ack(msg);
207
- }
208
- catch (error) {
209
- console.error('Error processing heartbeat:', error);
210
- channel.nack(msg, false, false);
211
- }
212
- }, { noAck: false });
215
+ try {
216
+ const heartbeat = JSON.parse(msg.content.toString());
217
+ if (heartbeat.nodeId !== this.nodeId) {
218
+ // Received heartbeat from current leader
219
+ this.lastHeartbeat = new Date(heartbeat.timestamp);
220
+ this.resetLeadershipTimeout();
221
+ }
222
+ channel.ack(msg);
223
+ }
224
+ catch (error) {
225
+ console.error('Error processing heartbeat:', error);
226
+ try {
227
+ channel.nack(msg, false, false);
228
+ }
229
+ catch (nackError) {
230
+ // Ignore nack errors during cleanup
231
+ }
232
+ }
233
+ }, { noAck: false });
234
+ }
235
+ catch (consumeError) {
236
+ throw new Error(`Failed to consume heartbeat queue: ${consumeError.message}`);
237
+ }
213
238
  // Start timeout to detect leader failure
214
239
  this.resetLeadershipTimeout();
215
240
  }
216
241
  catch (error) {
217
- console.error(`Failed to monitor heartbeat for node ${this.nodeId}:`, error);
242
+ // Suppress expected errors during election and cleanup
243
+ const isExpectedError = error.message && (error.message.includes('NOT_FOUND') ||
244
+ error.message.includes('Not connected to RabbitMQ') ||
245
+ error.message.includes('not connected') ||
246
+ error.code === 404 ||
247
+ error.code === 403);
248
+ if (!isExpectedError) {
249
+ console.error(`Failed to monitor heartbeat for node ${this.nodeId}:`, error.message);
250
+ }
218
251
  // If we can't monitor heartbeat, try to start election after a delay
219
252
  setTimeout(async () => {
220
253
  if (this.isRunning && this.state === LeadershipState.FOLLOWER) {
221
- await this.startElection();
254
+ try {
255
+ await this.startElection();
256
+ }
257
+ catch (retryError) {
258
+ const isRetryExpectedError = retryError.message && (retryError.message.includes('NOT_FOUND') ||
259
+ retryError.message.includes('not connected') ||
260
+ retryError.code === 404 ||
261
+ retryError.code === 403);
262
+ if (!isRetryExpectedError) {
263
+ console.error(`Retry election failed for node ${this.nodeId}:`, retryError.message);
264
+ }
265
+ }
222
266
  }
223
267
  }, this.heartbeatInterval);
224
268
  }
@@ -245,11 +289,14 @@ class MasterElection extends events_1.EventEmitter {
245
289
  }
246
290
  // Cancel exclusive consumption to allow other nodes to become leader
247
291
  try {
248
- const channel = this.connection.getChannel();
249
- await channel.cancel(this.electionQueue);
292
+ if (this.consumerTag && this.connection.isConnected()) {
293
+ const channel = this.connection.getChannel();
294
+ await channel.cancel(this.consumerTag);
295
+ this.consumerTag = undefined;
296
+ }
250
297
  }
251
298
  catch (error) {
252
- console.warn('Error canceling election queue consumption:', error);
299
+ console.warn('Error canceling election queue consumption:', error.message);
253
300
  }
254
301
  await this.becomeFollower();
255
302
  }
@@ -258,6 +305,9 @@ class MasterElection extends events_1.EventEmitter {
258
305
  this.cleanupTimer = setInterval(async () => {
259
306
  if (this.isRunning && this.state === LeadershipState.FOLLOWER) {
260
307
  try {
308
+ if (!this.connection.isConnected()) {
309
+ return;
310
+ }
261
311
  const channel = this.connection.getChannel();
262
312
  // Only purge if queue has too many messages (check queue info)
263
313
  const queueInfo = await channel.checkQueue(this.heartbeatQueue);
@@ -267,13 +317,18 @@ class MasterElection extends events_1.EventEmitter {
267
317
  }
268
318
  }
269
319
  catch (error) {
270
- // Queue might not exist yet, ignore error
320
+ // Queue might not exist yet or connection issues, ignore error
321
+ console.debug(`Cleanup error (ignored): ${error.message}`);
271
322
  }
272
323
  }
273
324
  }, 60000); // Every minute
274
325
  }
275
326
  async cleanupHeartbeatQueue() {
276
327
  try {
328
+ if (!this.connection.isConnected()) {
329
+ // Silently skip cleanup when not connected - this is expected during shutdown
330
+ return;
331
+ }
277
332
  const channel = this.connection.getChannel();
278
333
  // Purge any remaining messages from heartbeat queue
279
334
  await channel.purgeQueue(this.heartbeatQueue);
@@ -281,7 +336,19 @@ class MasterElection extends events_1.EventEmitter {
281
336
  await channel.deleteQueue(this.heartbeatQueue, { ifEmpty: true });
282
337
  }
283
338
  catch (error) {
284
- console.warn(`Error cleaning up heartbeat queue for node ${this.nodeId}:`, error);
339
+ console.warn(`Error cleaning up heartbeat queue for node ${this.nodeId}:`, error.message);
340
+ }
341
+ }
342
+ async waitForConnection() {
343
+ const maxWaitTime = 30000; // 30 seconds
344
+ const checkInterval = 100; // 100ms
345
+ let waitTime = 0;
346
+ while (!this.connection.isConnected() && waitTime < maxWaitTime) {
347
+ await new Promise(resolve => setTimeout(resolve, checkInterval));
348
+ waitTime += checkInterval;
349
+ }
350
+ if (!this.connection.isConnected()) {
351
+ throw new Error('Failed to establish RabbitMQ connection within timeout period');
285
352
  }
286
353
  }
287
354
  getQueueName(baseName) {
@@ -1 +1 @@
1
- {"version":3,"file":"master-election.js","sourceRoot":"","sources":["../../src/utils/master-election.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"master-election.js","sourceRoot":"","sources":["../../src/utils/master-election.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"rabbitmq.d.ts","sourceRoot":"","sources":["../../src/utils/rabbitmq.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,oBAAoB,CAAM;IAClC,OAAO,CAAC,cAAc,CAAQ;gBAElB,MAAM,EAAE,gBAAgB;IAI9B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBjC,UAAU,IAAI,GAAG;IAOjB,WAAW,IAAI,OAAO;YAIR,qBAAqB;YAKrB,qBAAqB;YAOrB,eAAe;CAY9B"}
1
+ {"version":3,"file":"rabbitmq.d.ts","sourceRoot":"","sources":["../../src/utils/rabbitmq.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,oBAAoB,CAAM;IAClC,OAAO,CAAC,cAAc,CAAQ;gBAElB,MAAM,EAAE,gBAAgB;IAI9B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBjC,UAAU,IAAI,GAAG;IAOjB,WAAW,IAAI,OAAO;YAIR,qBAAqB;YAiBrB,qBAAqB;YAUrB,eAAe;CAgB9B"}
@@ -90,11 +90,22 @@ class RabbitMQConnection {
90
90
  return this.connection !== null && this.channel !== null;
91
91
  }
92
92
  async handleConnectionError(error) {
93
- console.error('RabbitMQ connection error:', error);
93
+ // Suppress expected election and cleanup errors
94
+ const isExpectedError = error.message && (error.message.includes('ACCESS_REFUSED') ||
95
+ error.message.includes('NOT_FOUND') ||
96
+ error.message.includes('exclusive use') ||
97
+ error.message.includes('404') ||
98
+ error.message.includes('403'));
99
+ if (!isExpectedError) {
100
+ console.error('RabbitMQ connection error:', error);
101
+ }
94
102
  await this.handleReconnect();
95
103
  }
96
104
  async handleConnectionClose() {
97
- console.log('RabbitMQ connection closed');
105
+ // Only log connection close if it's unexpected (not during shutdown)
106
+ if (this.reconnectAttempts < 2) {
107
+ console.log('RabbitMQ connection closed');
108
+ }
98
109
  this.connection = null;
99
110
  this.channel = null;
100
111
  await this.handleReconnect();
@@ -105,7 +116,10 @@ class RabbitMQConnection {
105
116
  return;
106
117
  }
107
118
  this.reconnectAttempts++;
108
- console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
119
+ // Only log reconnection attempts if they're taking a while (after attempt 3)
120
+ if (this.reconnectAttempts > 3) {
121
+ console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
122
+ }
109
123
  await new Promise(resolve => setTimeout(resolve, this.reconnectDelay));
110
124
  await this.connect();
111
125
  }
@@ -1 +1 @@
1
- {"version":3,"file":"rabbitmq.js","sourceRoot":"","sources":["../../src/utils/rabbitmq.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rabbitmq.js","sourceRoot":"","sources":["../../src/utils/rabbitmq.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ #!/bin/bash
2
+
3
+ # RabbitMQ Docker setup for testing
4
+ # This script starts RabbitMQ with management interface for local testing
5
+
6
+ echo "๐Ÿฐ Starting RabbitMQ with Docker for testing..."
7
+
8
+ # Stop and remove existing container if it exists
9
+ docker stop rabbitmq-test 2>/dev/null || true
10
+ docker rm rabbitmq-test 2>/dev/null || true
11
+
12
+ # Start RabbitMQ with management plugin
13
+ docker run -d \
14
+ --name rabbitmq-test \
15
+ -p 5672:5672 \
16
+ -p 15672:15672 \
17
+ -e RABBITMQ_DEFAULT_USER=admin \
18
+ -e RABBITMQ_DEFAULT_PASS=password \
19
+ rabbitmq:3-management
20
+
21
+ echo "โณ Waiting for RabbitMQ to start..."
22
+ sleep 10
23
+
24
+ # Check if RabbitMQ is ready
25
+ if curl -f http://localhost:15672/api/overview &>/dev/null; then
26
+ echo "โœ… RabbitMQ is running!"
27
+ echo ""
28
+ echo "๐Ÿ“Š Management UI: http://localhost:15672"
29
+ echo "๐Ÿ‘ค Username: admin"
30
+ echo "๐Ÿ”‘ Password: password"
31
+ echo "๐Ÿ”— Connection URL: amqp://admin:password@localhost:5672"
32
+ echo ""
33
+ echo "๐Ÿงช Ready for multi-node testing!"
34
+ else
35
+ echo "โŒ RabbitMQ failed to start properly"
36
+ exit 1
37
+ fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodejs-task-scheduler",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "Distributed task scheduler using RabbitMQ with cron and direct job execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,32 @@
1
+ #!/bin/bash
2
+
3
+ # Multi-node test runner script
4
+ # This script runs the multi-node test with proper environment setup
5
+
6
+ set -e
7
+
8
+ echo "๐Ÿš€ Multi-Node Master Election Test Runner"
9
+ echo "========================================"
10
+
11
+ # Check if RabbitMQ is running
12
+ echo "๐Ÿ“ก Checking RabbitMQ connection..."
13
+ if ! curl -f http://localhost:15672/api/overview &>/dev/null; then
14
+ echo "โŒ RabbitMQ management interface not accessible at localhost:15672"
15
+ echo "Please ensure RabbitMQ is running with management plugin enabled"
16
+ exit 1
17
+ fi
18
+
19
+ # Set default RabbitMQ URL if not provided
20
+ export RABBITMQ_URL=${RABBITMQ_URL:-"amqp://admin:password@localhost:5672"}
21
+
22
+ echo "๐Ÿ”— Using RabbitMQ URL: $RABBITMQ_URL"
23
+
24
+ # Build the project first
25
+ echo "๐Ÿ”จ Building project..."
26
+ npm run build
27
+
28
+ # Run the multi-node test
29
+ echo "๐Ÿงช Starting multi-node test..."
30
+ npx ts-node test-multinode.ts
31
+
32
+ echo "โœ… Multi-node test completed!"
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env ts-node
2
+
3
+ import { TaskScheduler } from './src';
4
+
5
+ interface NodeTestConfig {
6
+ nodeId: string;
7
+ port: number;
8
+ isLeaderExpected?: boolean;
9
+ }
10
+
11
+ class MultiNodeTester {
12
+ private nodes: Map<string, TaskScheduler> = new Map();
13
+ private nodeConfigs: NodeTestConfig[] = [
14
+ { nodeId: 'node-1', port: 3001, isLeaderExpected: true },
15
+ { nodeId: 'node-2', port: 3002 },
16
+ { nodeId: 'node-3', port: 3003 }
17
+ ];
18
+
19
+ async runTest(): Promise<void> {
20
+ console.log('๐Ÿš€ Starting multi-node master election test with prefix: njs_testing_');
21
+
22
+ try {
23
+ // Start all nodes
24
+ await this.startAllNodes();
25
+
26
+ // Wait for election to stabilize
27
+ console.log('โณ Waiting for master election to stabilize...');
28
+ await this.sleep(5000);
29
+
30
+ // Check initial leader
31
+ await this.checkLeaderStatus();
32
+
33
+ // Test basic job scheduling
34
+ await this.testJobScheduling();
35
+
36
+ console.log('โณ Waiting for jobs to process...');
37
+ await this.sleep(8000);
38
+
39
+ // Test leader failover (simplified)
40
+ await this.testLeaderFailover();
41
+
42
+ console.log('โœ… All multi-node tests completed successfully!');
43
+
44
+ } catch (error) {
45
+ console.error('โŒ Multi-node test failed:', error);
46
+ } finally {
47
+ await this.cleanup();
48
+ }
49
+ }
50
+
51
+ private async startAllNodes(): Promise<void> {
52
+ console.log('๐Ÿ“ก Starting nodes...');
53
+
54
+ for (const config of this.nodeConfigs) {
55
+ console.log(`Starting ${config.nodeId}...`);
56
+ const scheduler = new TaskScheduler({
57
+ url: process.env.RABBITMQ_URL || 'amqp://admin:password@localhost:5672',
58
+ queuePrefix: 'njs_testing_'
59
+ });
60
+
61
+ await scheduler.initialize();
62
+
63
+ // Wait a bit for full initialization
64
+ await this.sleep(2000);
65
+
66
+ this.nodes.set(config.nodeId, scheduler);
67
+
68
+ // Create a worker on each node
69
+ await scheduler.createWorker({
70
+ name: `${config.nodeId}-worker`,
71
+ concurrency: 2,
72
+ queues: ['test-jobs'],
73
+ handlers: {
74
+ 'test-job': async (data: any) => {
75
+ console.log(`๐Ÿ“‹ ${config.nodeId} processing job:`, data.jobId);
76
+ await this.sleep(1000); // Simulate work
77
+ return {
78
+ success: true,
79
+ processedBy: config.nodeId,
80
+ timestamp: new Date()
81
+ };
82
+ },
83
+ 'priority-job': async (data: any) => {
84
+ console.log(`โญ ${config.nodeId} processing priority job:`, data.jobId);
85
+ return {
86
+ success: true,
87
+ processedBy: config.nodeId,
88
+ priority: true
89
+ };
90
+ }
91
+ }
92
+ });
93
+
94
+ console.log(`โœ… ${config.nodeId} started and ready`);
95
+
96
+ // Stagger node startup to avoid race conditions
97
+ await this.sleep(1000);
98
+ }
99
+ }
100
+
101
+ private async checkLeaderStatus(): Promise<void> {
102
+ console.log('\n๐Ÿ‘‘ Checking leader election status...');
103
+
104
+ let leaderCount = 0;
105
+ let leaderNode = '';
106
+
107
+ for (const [nodeId, scheduler] of this.nodes) {
108
+ const nodeInfo = await scheduler.getNodeInfo();
109
+ // @ts-ignore
110
+ const isLeader = nodeInfo.isLeader;
111
+
112
+ console.log(`${nodeId}: ${isLeader ? '๐Ÿ‘‘ LEADER' : '๐Ÿ‘ฅ FOLLOWER'} (Node ID: ${nodeInfo.nodeId})`);
113
+
114
+ if (isLeader) {
115
+ leaderCount++;
116
+ leaderNode = nodeId;
117
+ }
118
+ }
119
+
120
+ if (leaderCount === 0) {
121
+ throw new Error('No leader elected!');
122
+ } else if (leaderCount > 1) {
123
+ throw new Error(`Multiple leaders detected: ${leaderCount}`);
124
+ } else {
125
+ console.log(`โœ… Single leader elected: ${leaderNode}`);
126
+ }
127
+ }
128
+
129
+ private async testJobScheduling(): Promise<void> {
130
+ console.log('\n๐Ÿ“‹ Testing job scheduling...');
131
+
132
+ // Get any scheduler to schedule jobs (should work from any node)
133
+ const scheduler = this.nodes.values().next().value;
134
+
135
+ // Schedule a few test jobs
136
+ for (let i = 1; i <= 3; i++) {
137
+ try {
138
+ await scheduler.scheduleJob({
139
+ id: `test-job-${i}`,
140
+ name: `Test Job ${i}`,
141
+ handler: 'test-job',
142
+ data: { jobId: `test-job-${i}`, message: `Hello from job ${i}` }
143
+ });
144
+ console.log(`โœ… Job ${i} scheduled`);
145
+ } catch (error) {
146
+ console.error(`โŒ Failed to schedule job ${i}:`, error.message);
147
+ }
148
+ }
149
+ }
150
+
151
+ private async testLeaderFailover(): Promise<void> {
152
+ console.log('\n๐Ÿ”„ Testing leader failover...');
153
+
154
+ // Find current leader
155
+ let currentLeader = '';
156
+ for (const [nodeId, scheduler] of this.nodes) {
157
+ const nodeInfo = await scheduler.getNodeInfo();
158
+ // @ts-ignore
159
+ if (nodeInfo.isLeader) {
160
+ currentLeader = nodeId;
161
+ break;
162
+ }
163
+ }
164
+
165
+ if (!currentLeader) {
166
+ throw new Error('No leader found for failover test');
167
+ }
168
+
169
+ console.log(`๐Ÿ›‘ Shutting down current leader: ${currentLeader}`);
170
+
171
+ // Shutdown the leader
172
+ const leaderScheduler = this.nodes.get(currentLeader)!;
173
+ await leaderScheduler.shutdown();
174
+ this.nodes.delete(currentLeader);
175
+
176
+ // Wait for new election
177
+ console.log('โณ Waiting for new leader election...');
178
+ await this.sleep(8000);
179
+
180
+ // Check new leader
181
+ await this.checkLeaderStatus();
182
+
183
+ console.log('โœ… Leader failover completed successfully');
184
+ }
185
+
186
+ private async testJobDistribution(): Promise<void> {
187
+ console.log('\nโš–๏ธ Testing job distribution across remaining nodes...');
188
+
189
+ // Get any remaining scheduler
190
+ const scheduler = this.nodes.values().next().value;
191
+
192
+ // Schedule jobs that should be distributed
193
+ const jobPromises = [];
194
+ for (let i = 1; i <= 6; i++) {
195
+ const jobPromise = scheduler.scheduleJob({
196
+ id: `distributed-job-${i}`,
197
+ name: `Distributed Job ${i}`,
198
+ handler: 'test-job',
199
+ data: { jobId: `distributed-job-${i}`, message: `Distributed job ${i}` }
200
+ });
201
+ jobPromises.push(jobPromise);
202
+ }
203
+
204
+ await Promise.all(jobPromises);
205
+ console.log('โœ… Distributed jobs scheduled');
206
+
207
+ // Wait for processing
208
+ await this.sleep(10000);
209
+ }
210
+
211
+ private async cleanup(): Promise<void> {
212
+ console.log('\n๐Ÿงน Cleaning up test resources...');
213
+
214
+ for (const [nodeId, scheduler] of this.nodes) {
215
+ try {
216
+ console.log(`Shutting down ${nodeId}...`);
217
+ await scheduler.shutdown();
218
+ } catch (error) {
219
+ console.warn(`Warning: Error shutting down ${nodeId}:`, error.message || error);
220
+ }
221
+ }
222
+
223
+ this.nodes.clear();
224
+
225
+ // Give time for cleanup
226
+ await this.sleep(2000);
227
+
228
+ console.log('โœ… Cleanup completed');
229
+ }
230
+
231
+ private sleep(ms: number): Promise<void> {
232
+ return new Promise(resolve => setTimeout(resolve, ms));
233
+ }
234
+ }
235
+
236
+ // Handle graceful shutdown
237
+ process.on('SIGINT', () => {
238
+ console.log('\n๐Ÿ›‘ Received SIGINT, shutting down test...');
239
+ process.exit(0);
240
+ });
241
+
242
+ process.on('SIGTERM', () => {
243
+ console.log('\n๐Ÿ›‘ Received SIGTERM, shutting down test...');
244
+ process.exit(0);
245
+ });
246
+
247
+ // Run the test
248
+ if (require.main === module) {
249
+ const tester = new MultiNodeTester();
250
+ tester.runTest().catch(error => {
251
+ console.error('โŒ Test execution failed:', error);
252
+ process.exit(1);
253
+ });
254
+ }
255
+
256
+ export { MultiNodeTester };
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env ts-node
2
+
3
+ // Quick test to verify master election works with minimal output
4
+ import { TaskScheduler } from './src';
5
+
6
+ async function quickElectionTest() {
7
+ console.log('๐Ÿš€ Quick master election test (njs_testing_ prefix)');
8
+
9
+ const nodes = [];
10
+ let testsPassed = 0;
11
+ const totalTests = 2;
12
+
13
+ try {
14
+ // Test 1: Single leader election
15
+ console.log('\n๐Ÿ“‹ Test 1: Single leader election');
16
+
17
+ const node1 = new TaskScheduler({
18
+ url: process.env.RABBITMQ_URL || 'amqp://admin:password@localhost:5672',
19
+ queuePrefix: 'njs_testing_'
20
+ });
21
+
22
+ const node2 = new TaskScheduler({
23
+ url: process.env.RABBITMQ_URL || 'amqp://admin:password@localhost:5672',
24
+ queuePrefix: 'njs_testing_'
25
+ });
26
+
27
+ await node1.initialize();
28
+ await node2.initialize();
29
+
30
+ nodes.push({ id: 'node1', scheduler: node1 });
31
+ nodes.push({ id: 'node2', scheduler: node2 });
32
+
33
+ // Wait for election
34
+ await sleep(6000);
35
+
36
+ const node1IsLeader = node1.isLeader();
37
+ const node2IsLeader = node2.isLeader();
38
+ const leaderCount = (node1IsLeader ? 1 : 0) + (node2IsLeader ? 1 : 0);
39
+
40
+ if (leaderCount === 1) {
41
+ console.log('โœ… Test 1 PASSED: Exactly one leader elected');
42
+ testsPassed++;
43
+ } else {
44
+ console.log(`โŒ Test 1 FAILED: ${leaderCount} leaders found`);
45
+ }
46
+
47
+ // Test 2: Leader failover
48
+ console.log('\n๐Ÿ“‹ Test 2: Leader failover');
49
+
50
+ const currentLeader = node1IsLeader ? nodes[0] : nodes[1];
51
+ const follower = node1IsLeader ? nodes[1] : nodes[0];
52
+
53
+ // Shutdown leader
54
+ await currentLeader.scheduler.shutdown();
55
+ nodes.splice(nodes.indexOf(currentLeader), 1);
56
+
57
+ // Wait for failover
58
+ await sleep(6000);
59
+
60
+ const newLeaderIsLeader = follower.scheduler.isLeader();
61
+
62
+ if (newLeaderIsLeader) {
63
+ console.log('โœ… Test 2 PASSED: Failover successful');
64
+ testsPassed++;
65
+ } else {
66
+ console.log('โŒ Test 2 FAILED: Failover failed');
67
+ }
68
+
69
+ // Summary
70
+ console.log(`\n๐Ÿ“Š Test Results: ${testsPassed}/${totalTests} tests passed`);
71
+
72
+ if (testsPassed === totalTests) {
73
+ console.log('๐ŸŽ‰ ALL TESTS PASSED! Master election is working correctly.');
74
+ console.log('โœ… njs_testing_ prefix multinode test SUCCESSFUL');
75
+ } else {
76
+ console.log('โŒ Some tests failed. Check master election implementation.');
77
+ }
78
+
79
+ } catch (error) {
80
+ console.error('โŒ Test execution failed:', error.message);
81
+ } finally {
82
+ // Quick cleanup
83
+ for (const node of nodes) {
84
+ try {
85
+ await node.scheduler.shutdown();
86
+ } catch (error) {
87
+ // Ignore cleanup errors
88
+ }
89
+ }
90
+
91
+ // Force exit
92
+ setTimeout(() => process.exit(testsPassed === totalTests ? 0 : 1), 1000);
93
+ }
94
+ }
95
+
96
+ function sleep(ms: number): Promise<void> {
97
+ return new Promise(resolve => setTimeout(resolve, ms));
98
+ }
99
+
100
+ // Suppress error logs during cleanup
101
+ process.on('uncaughtException', () => {});
102
+ process.on('unhandledRejection', () => {});
103
+
104
+ quickElectionTest();
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env ts-node
2
+
3
+ // Simplified test to verify master election works
4
+ import { TaskScheduler } from './src';
5
+
6
+ async function testBasicElection() {
7
+ console.log('๐Ÿš€ Testing basic master election with njs_testing_ prefix');
8
+
9
+ const nodes = [];
10
+
11
+ try {
12
+ // Start first node
13
+ console.log('Starting node 1...');
14
+ const node1 = new TaskScheduler({
15
+ url: process.env.RABBITMQ_URL || 'amqp://admin:password@localhost:5672',
16
+ queuePrefix: 'njs_testing_'
17
+ });
18
+
19
+ await node1.initialize();
20
+ nodes.push({ id: 'node1', scheduler: node1 });
21
+
22
+ // Wait for it to establish leadership
23
+ await sleep(5000);
24
+
25
+ // Check if it's the leader
26
+ const node1IsLeader = node1.isLeader();
27
+ console.log(`Node 1: ${node1IsLeader ? '๐Ÿ‘‘ LEADER' : '๐Ÿ‘ฅ FOLLOWER'}`);
28
+
29
+ // Start second node
30
+ console.log('Starting node 2...');
31
+ const node2 = new TaskScheduler({
32
+ url: process.env.RABBITMQ_URL || 'amqp://admin:password@localhost:5672',
33
+ queuePrefix: 'njs_testing_'
34
+ });
35
+
36
+ await node2.initialize();
37
+ nodes.push({ id: 'node2', scheduler: node2 });
38
+
39
+ // Wait for election to stabilize
40
+ await sleep(5000);
41
+
42
+ // Check leader status
43
+ const node1IsLeader2 = node1.isLeader();
44
+ const node2IsLeader = node2.isLeader();
45
+
46
+ console.log(`Node 1: ${node1IsLeader2 ? '๐Ÿ‘‘ LEADER' : '๐Ÿ‘ฅ FOLLOWER'}`);
47
+ console.log(`Node 2: ${node2IsLeader ? '๐Ÿ‘‘ LEADER' : '๐Ÿ‘ฅ FOLLOWER'}`);
48
+
49
+ // Verify only one leader
50
+ const leaderCount = (node1IsLeader2 ? 1 : 0) + (node2IsLeader ? 1 : 0);
51
+
52
+ if (leaderCount === 1) {
53
+ console.log('โœ… Election successful - exactly one leader');
54
+ } else {
55
+ console.log(`โŒ Election failed - ${leaderCount} leaders found`);
56
+ }
57
+
58
+ // Test leader shutdown and failover
59
+ console.log('\n๐Ÿ”„ Testing leader failover...');
60
+
61
+ let currentLeader = node1IsLeader2 ? nodes[0] : nodes[1];
62
+ let follower = node1IsLeader2 ? nodes[1] : nodes[0];
63
+
64
+ console.log(`Shutting down current leader: ${currentLeader.id}`);
65
+ await currentLeader.scheduler.shutdown();
66
+
67
+ // Remove from nodes array
68
+ const leaderIndex = nodes.indexOf(currentLeader);
69
+ nodes.splice(leaderIndex, 1);
70
+
71
+ // Wait for new election
72
+ await sleep(8000);
73
+
74
+ // Check if follower became leader
75
+ const newLeaderIsLeader = follower.scheduler.isLeader();
76
+ console.log(`${follower.id}: ${newLeaderIsLeader ? '๐Ÿ‘‘ NEW LEADER' : '๐Ÿ‘ฅ STILL FOLLOWER'}`);
77
+
78
+ if (newLeaderIsLeader) {
79
+ console.log('โœ… Failover successful');
80
+ console.log('โœ… All multinode tests PASSED!');
81
+ } else {
82
+ console.log('โŒ Failover failed');
83
+ }
84
+
85
+ } catch (error) {
86
+ console.error('โŒ Test failed:', error.message);
87
+ } finally {
88
+ // Cleanup
89
+ console.log('\n๐Ÿงน Cleaning up...');
90
+ for (const node of nodes) {
91
+ try {
92
+ await node.scheduler.shutdown();
93
+ } catch (error) {
94
+ console.warn(`Warning: cleanup error for ${node.id}`);
95
+ }
96
+ }
97
+ console.log('โœ… Test completed');
98
+
99
+ // Force exit to avoid hanging on reconnection attempts
100
+ setTimeout(() => {
101
+ console.log('๐Ÿšช Exiting test...');
102
+ process.exit(0);
103
+ }, 2000);
104
+ }
105
+ }
106
+
107
+ function sleep(ms: number): Promise<void> {
108
+ return new Promise(resolve => setTimeout(resolve, ms));
109
+ }
110
+
111
+ // Handle uncaught errors gracefully
112
+ process.on('uncaughtException', (error) => {
113
+ console.error('Uncaught Exception (non-fatal):', error.message);
114
+ // Don't exit, let test continue
115
+ });
116
+
117
+ process.on('unhandledRejection', (reason, promise) => {
118
+ console.error('Unhandled Rejection (non-fatal):', reason);
119
+ // Don't exit, let test continue
120
+ });
121
+
122
+ testBasicElection().catch(error => {
123
+ console.error('โŒ Test execution failed:', error.message);
124
+ process.exit(1);
125
+ });
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env ts-node
2
+
3
+ // Simple multi-node test that can be run manually in separate terminals
4
+ import { TaskScheduler } from './src';
5
+
6
+ const nodeId = process.argv[2] || `node-${Math.floor(Math.random() * 1000)}`;
7
+ const isScheduler = process.argv[3] === 'scheduler';
8
+
9
+ console.log(`๐Ÿš€ Starting ${nodeId} ${isScheduler ? '(with job scheduling)' : '(worker only)'}`);
10
+
11
+ async function runNode() {
12
+ const scheduler = new TaskScheduler({
13
+ url: process.env.RABBITMQ_URL || 'amqp://admin:password@localhost:5672',
14
+ queuePrefix: 'njs_testing_'
15
+ });
16
+
17
+ try {
18
+ await scheduler.initialize();
19
+ console.log(`โœ… ${nodeId} initialized`);
20
+
21
+ // Create worker
22
+ await scheduler.createWorker({
23
+ name: `${nodeId}-worker`,
24
+ concurrency: 2,
25
+ queues: ['test-jobs'],
26
+ handlers: {
27
+ 'test-job': async (data: any) => {
28
+ console.log(`๐Ÿ“‹ ${nodeId} processing:`, data.message);
29
+ await new Promise(resolve => setTimeout(resolve, 2000));
30
+ return {
31
+ success: true,
32
+ processedBy: nodeId,
33
+ timestamp: new Date().toISOString()
34
+ };
35
+ }
36
+ }
37
+ });
38
+
39
+ // Show node status every 10 seconds
40
+ setInterval(async () => {
41
+ try {
42
+ const nodeInfo = await scheduler.getNodeInfo();
43
+ // @ts-ignore
44
+ const status = nodeInfo.isLeader ? '๐Ÿ‘‘ LEADER' : '๐Ÿ‘ฅ FOLLOWER';
45
+ console.log(`${nodeId}: ${status} (ID: ${nodeInfo.nodeId.substring(0, 8)}...)`);
46
+ } catch (error) {
47
+ console.log(`${nodeId}: โŒ Status check failed`);
48
+ }
49
+ }, 10000);
50
+
51
+ // If this is the scheduler node, schedule some jobs
52
+ if (isScheduler) {
53
+ console.log(`๐Ÿ“‹ ${nodeId} will schedule jobs every 15 seconds`);
54
+
55
+ let jobCounter = 1;
56
+ setInterval(async () => {
57
+ try {
58
+ // @ts-ignore
59
+ await scheduler.scheduleJob({
60
+ id: `job-${jobCounter}`,
61
+ name: `Test Job ${jobCounter}`,
62
+ handler: 'test-job',
63
+ data: {
64
+ message: `Hello from job ${jobCounter}`,
65
+ scheduledBy: nodeId,
66
+ timestamp: new Date().toISOString()
67
+ }
68
+ });
69
+ console.log(`๐Ÿ“ค ${nodeId} scheduled job-${jobCounter}`);
70
+ jobCounter++;
71
+ } catch (error) {
72
+ console.error(`โŒ ${nodeId} failed to schedule job:`, error.message);
73
+ }
74
+ }, 15000);
75
+ }
76
+
77
+ console.log(`๐ŸŸข ${nodeId} is running and ready`);
78
+
79
+ } catch (error) {
80
+ console.error(`โŒ ${nodeId} failed to start:`, error);
81
+ process.exit(1);
82
+ }
83
+ }
84
+
85
+ // Graceful shutdown
86
+ process.on('SIGINT', async () => {
87
+ console.log(`\n๐Ÿ›‘ ${nodeId} shutting down...`);
88
+ process.exit(0);
89
+ });
90
+
91
+ runNode().catch(error => {
92
+ console.error(`โŒ ${nodeId} crashed:`, error);
93
+ process.exit(1);
94
+ });