bullmq 5.81.1 → 5.81.3

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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  <br/>
5
5
  <br/>
6
6
  <p>
7
- The fastest, most reliable, Redis-based distributed queue for Node.js, Python, Rust, and more. <br/>
7
+ The fastest, most reliable, Redis-based distributed queue for Node.js, Python, Elixir, Rust, PHP, and more. <br/>
8
8
  Carefully written for rock solid stability and atomicity.
9
9
  </p>
10
10
  Read the <a href="https://docs.bullmq.io">documentation</a>
@@ -44,7 +44,7 @@ BullMQ is available natively in multiple languages:
44
44
 
45
45
  - **Node.js / Bun** — This repository (`npm install bullmq`)
46
46
  - **Python** — [`python/`](./python) directory (`pip install bullmq`)
47
- - **Rust** — [`rust/`](./rust) directory (`cargo add bullmq`)
47
+ - **Rust** — [`rust/`](./rust) directory (`cargo add bullmq-official --rename bullmq`)
48
48
  - **Elixir** — [`elixir/`](./elixir) directory (`{:bullmq, "~> x.x"}`)
49
49
  - **PHP** — [`php/`](./php) directory
50
50
 
@@ -186,7 +186,13 @@ class BunRedisAdapter extends events_1.EventEmitter {
186
186
  // Connection lifecycle
187
187
  // ---------------------------------------------------------------
188
188
  async connect() {
189
- if (this.raw.connected) {
189
+ const replaceRaw = this.hasConnected && (this.closed || !this.raw.connected);
190
+ if (this.reconnectTimer) {
191
+ clearTimeout(this.reconnectTimer);
192
+ this.reconnectTimer = null;
193
+ }
194
+ this.reconnecting = false;
195
+ if (this.raw.connected && !replaceRaw) {
190
196
  this.hasConnected = true;
191
197
  this.closed = false;
192
198
  this.closing = false;
@@ -199,7 +205,7 @@ class BunRedisAdapter extends events_1.EventEmitter {
199
205
  this.statusOverride = undefined;
200
206
  // If the raw client was previously closed, Bun doesn't support
201
207
  // reconnecting on the same instance. Create a fresh raw client.
202
- if (this.hasConnected && !this.raw.connected) {
208
+ if (replaceRaw) {
203
209
  const BunRedisClient = this.raw
204
210
  .constructor;
205
211
  this.raw = new BunRedisClient(this.raw.url);
@@ -34,7 +34,7 @@ class FlowProducer extends events_1.EventEmitter {
34
34
  skipVersionCheck: opts.skipVersionCheck,
35
35
  skipWaitingForReady: opts.skipWaitingForReady,
36
36
  });
37
- this.connection.on('error', (error) => this.emit('error', error));
37
+ (0, utils_1.forwardConnectionError)(this, this.connection);
38
38
  this.connection.on('close', () => {
39
39
  this.queues.clear();
40
40
  if (!this.closing) {
@@ -593,9 +593,9 @@ class Job {
593
593
  /**
594
594
  * Records job metrics if a meter is configured in telemetry options.
595
595
  *
596
- * @param status - The job status
596
+ * @param state - The job state
597
597
  */
598
- recordJobMetrics(status) {
598
+ recordJobMetrics(state) {
599
599
  var _a, _b;
600
600
  const meter = (_b = (_a = this.queue.opts) === null || _a === void 0 ? void 0 : _a.telemetry) === null || _b === void 0 ? void 0 : _b.meter;
601
601
  if (!meter) {
@@ -604,9 +604,10 @@ class Job {
604
604
  const attributes = {
605
605
  [enums_1.TelemetryAttributes.QueueName]: this.queue.name,
606
606
  [enums_1.TelemetryAttributes.JobName]: this.name,
607
- [enums_1.TelemetryAttributes.JobStatus]: status,
607
+ [enums_1.TelemetryAttributes.JobStatus]: state,
608
+ [enums_1.TelemetryAttributes.JobState]: state,
608
609
  };
609
- // Record counter metric based on status
610
+ // Record counter metric based on state
610
611
  const statusToCounterName = {
611
612
  completed: enums_1.MetricNames.JobsCompleted,
612
613
  failed: enums_1.MetricNames.JobsFailed,
@@ -615,9 +616,9 @@ class Job {
615
616
  waiting: enums_1.MetricNames.JobsWaiting,
616
617
  'waiting-children': enums_1.MetricNames.JobsWaitingChildren,
617
618
  };
618
- const counterName = statusToCounterName[status];
619
+ const counterName = statusToCounterName[state];
619
620
  const counter = meter.createCounter(counterName, {
620
- description: `Number of jobs ${status}`,
621
+ description: `Number of jobs ${state}`,
621
622
  unit: '1',
622
623
  });
623
624
  counter.add(1, attributes);
@@ -40,7 +40,7 @@ class QueueBase extends events_1.EventEmitter {
40
40
  skipVersionCheck: opts.skipVersionCheck,
41
41
  skipWaitingForReady: opts.skipWaitingForReady,
42
42
  });
43
- this.connection.on('error', (error) => this.emit('error', error));
43
+ (0, utils_1.forwardConnectionError)(this, this.connection);
44
44
  this.connection.on('close', () => {
45
45
  if (!this.closing) {
46
46
  this.emit('ioredis:close');
@@ -393,7 +393,23 @@ class RedisConnection extends events_1.EventEmitter {
393
393
  }
394
394
  async reconnect() {
395
395
  const client = await this.client;
396
- return client.connect();
396
+ for (;;) {
397
+ if (client.status === 'ready' ||
398
+ (client.status === 'connect' && (0, utils_2.isRedisCluster)(client))) {
399
+ return;
400
+ }
401
+ if (client.status === 'wait' || client.status === 'end') {
402
+ return client.connect();
403
+ }
404
+ try {
405
+ await RedisConnection.waitUntilReady(client);
406
+ }
407
+ catch (error) {
408
+ if (!['end', 'connecting', 'connect', 'reconnecting'].includes(client.status)) {
409
+ throw error;
410
+ }
411
+ }
412
+ }
397
413
  }
398
414
  async close(force = false) {
399
415
  var _a;
@@ -126,7 +126,7 @@ class Worker extends queue_base_1.QueueBase {
126
126
  blocking: true,
127
127
  skipVersionCheck: opts.skipVersionCheck,
128
128
  });
129
- this.blockingConnection.on('error', error => this.emit('error', error));
129
+ (0, utils_1.forwardConnectionError)(this, this.blockingConnection);
130
130
  this.blockingConnection.on('ready', () => setTimeout(() => this.emit('ready'), 0));
131
131
  }
132
132
  /**
@@ -465,6 +465,19 @@ class Worker extends queue_base_1.QueueBase {
465
465
  if ((0, utils_1.isNotConnectionError)(error)) {
466
466
  this.emit('error', error);
467
467
  }
468
+ // The watchdog must abort every overdue blocking command, but doing so
469
+ // during socketless Sentinel resolution can leave ioredis terminally ended.
470
+ if (!this.closing && bclient.status === 'end') {
471
+ try {
472
+ await bclient.connect();
473
+ }
474
+ catch (reconnectError) {
475
+ if (bclient.status === 'end' &&
476
+ (0, utils_1.isNotConnectionError)(reconnectError)) {
477
+ this.emit('error', reconnectError);
478
+ }
479
+ }
480
+ }
468
481
  if (!this.closing) {
469
482
  await this.delay();
470
483
  }
@@ -42,7 +42,11 @@ var TelemetryAttributes;
42
42
  TelemetryAttributes["JobFailedReason"] = "bullmq.job.failed.reason";
43
43
  TelemetryAttributes["FlowName"] = "bullmq.flow.name";
44
44
  TelemetryAttributes["JobSchedulerId"] = "bullmq.job.scheduler.id";
45
+ /**
46
+ * @deprecated Use JobState instead. Will be removed in a future version.
47
+ */
45
48
  TelemetryAttributes["JobStatus"] = "bullmq.job.status";
49
+ TelemetryAttributes["JobState"] = "bullmq.job.state";
46
50
  })(TelemetryAttributes || (exports.TelemetryAttributes = TelemetryAttributes = {}));
47
51
  /**
48
52
  * Standard metric names for BullMQ telemetry