bullmq 6.3.2 → 6.3.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/dist/cjs/classes/bun-redis-client.js +165 -53
- package/dist/cjs/classes/redis-queue-backend.js +56 -14
- package/dist/cjs/tsconfig-cjs.tsbuildinfo +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/classes/bun-redis-client.d.ts +1 -0
- package/dist/esm/classes/bun-redis-client.js +165 -53
- package/dist/esm/classes/redis-queue-backend.js +57 -15
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +1 -1
|
@@ -46,15 +46,21 @@ export function createBunRedisClient(client, opts) {
|
|
|
46
46
|
*/
|
|
47
47
|
class BunRedisAdapter extends EventEmitter {
|
|
48
48
|
get status() {
|
|
49
|
+
var _a;
|
|
49
50
|
if (this.statusOverride) {
|
|
50
51
|
return this.statusOverride;
|
|
51
52
|
}
|
|
52
53
|
if (this.closed) {
|
|
53
54
|
return 'end';
|
|
54
55
|
}
|
|
55
|
-
|
|
56
|
+
// `raw` may not exist yet on a duplicate whose raw client is created
|
|
57
|
+
// lazily (via `rawFactory`) on first connect.
|
|
58
|
+
if (this.ready) {
|
|
56
59
|
return 'ready';
|
|
57
60
|
}
|
|
61
|
+
if ((_a = this.raw) === null || _a === void 0 ? void 0 : _a.connected) {
|
|
62
|
+
return 'connect';
|
|
63
|
+
}
|
|
58
64
|
return this.hasConnected ? 'end' : 'wait';
|
|
59
65
|
}
|
|
60
66
|
set status(val) {
|
|
@@ -87,8 +93,14 @@ class BunRedisAdapter extends EventEmitter {
|
|
|
87
93
|
this.reconnectTimer = null;
|
|
88
94
|
this.reconnectAttempts = 0;
|
|
89
95
|
this.maxReconnectDelay = 20000; // cap at 20s (matches ioredis default)
|
|
96
|
+
this.ready = false;
|
|
90
97
|
this.isCluster = false;
|
|
91
|
-
this.
|
|
98
|
+
this.rawFactory = opts === null || opts === void 0 ? void 0 : opts.rawFactory;
|
|
99
|
+
// When a `rawFactory` is provided the raw client is created lazily on the
|
|
100
|
+
// first `connect()`; callbacks are wired up there once it exists.
|
|
101
|
+
if (this.raw) {
|
|
102
|
+
this._setupCallbacks();
|
|
103
|
+
}
|
|
92
104
|
// ioredis auto-connects by default. Mimic that behavior unless
|
|
93
105
|
// lazyConnect is set.
|
|
94
106
|
if (!(opts === null || opts === void 0 ? void 0 : opts.lazyConnect)) {
|
|
@@ -106,23 +118,10 @@ class BunRedisAdapter extends EventEmitter {
|
|
|
106
118
|
// event until CLIENT SETNAME completes so callers waiting for 'ready'
|
|
107
119
|
// see the name already applied.
|
|
108
120
|
this.raw.onconnect = () => {
|
|
109
|
-
this.
|
|
110
|
-
this.closed = false;
|
|
111
|
-
this.closing = false;
|
|
112
|
-
this.reconnecting = false;
|
|
113
|
-
this.reconnectAttempts = 0;
|
|
114
|
-
this.statusOverride = undefined;
|
|
115
|
-
// The server-side SCRIPT cache is gone for this (possibly new) raw
|
|
116
|
-
// connection. Force re-loading on next use.
|
|
117
|
-
this.loadedScriptShas.clear();
|
|
118
|
-
if (this.connectionName) {
|
|
119
|
-
this.clientSetName(this.connectionName).then(() => this.emit('ready'), () => this.emit('ready'));
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
this.emit('ready');
|
|
123
|
-
}
|
|
121
|
+
this._handleConnected();
|
|
124
122
|
};
|
|
125
123
|
this.raw.onclose = (error) => {
|
|
124
|
+
this.ready = false;
|
|
126
125
|
if (this.closing) {
|
|
127
126
|
// User-initiated close – no reconnect
|
|
128
127
|
this.closed = true;
|
|
@@ -139,6 +138,31 @@ class BunRedisAdapter extends EventEmitter {
|
|
|
139
138
|
this._scheduleReconnect();
|
|
140
139
|
};
|
|
141
140
|
}
|
|
141
|
+
_handleConnected() {
|
|
142
|
+
this.hasConnected = true;
|
|
143
|
+
this.ready = false;
|
|
144
|
+
this.closed = false;
|
|
145
|
+
this.closing = false;
|
|
146
|
+
this.reconnecting = false;
|
|
147
|
+
this.reconnectAttempts = 0;
|
|
148
|
+
this.statusOverride = undefined;
|
|
149
|
+
// The server-side SCRIPT cache is gone for this (possibly new) raw
|
|
150
|
+
// connection. Force re-loading on next use.
|
|
151
|
+
this.loadedScriptShas.clear();
|
|
152
|
+
const markReady = () => {
|
|
153
|
+
this.ready = true;
|
|
154
|
+
this.emit('ready');
|
|
155
|
+
};
|
|
156
|
+
const readying = this.connectionName
|
|
157
|
+
? this.clientSetName(this.connectionName).then(markReady, markReady)
|
|
158
|
+
: (markReady(), Promise.resolve());
|
|
159
|
+
this.readying = readying.finally(() => {
|
|
160
|
+
if (this.readying === readying) {
|
|
161
|
+
this.readying = undefined;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
return this.readying;
|
|
165
|
+
}
|
|
142
166
|
/**
|
|
143
167
|
* Schedule a reconnection attempt with exponential backoff.
|
|
144
168
|
*/
|
|
@@ -157,10 +181,16 @@ class BunRedisAdapter extends EventEmitter {
|
|
|
157
181
|
return;
|
|
158
182
|
}
|
|
159
183
|
try {
|
|
160
|
-
// Create a fresh raw client
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
184
|
+
// Create a fresh raw client aimed at the *same* server. Bun's native
|
|
185
|
+
// `duplicate()` preserves the connection target and options; the old
|
|
186
|
+
// `new BunRedisClient(this.raw.url)` produced a client pointed at Bun's
|
|
187
|
+
// default target because `url` is always undefined (#4582). If the raw
|
|
188
|
+
// client was never created (a duplicate reconnecting before its first
|
|
189
|
+
// connect), fall back to its lazy factory.
|
|
190
|
+
const newRaw = this.raw
|
|
191
|
+
? await this._duplicateRaw(this.raw)
|
|
192
|
+
: await this.rawFactory();
|
|
193
|
+
this.rawFactory = undefined;
|
|
164
194
|
// Swap the raw client reference
|
|
165
195
|
this.raw = newRaw;
|
|
166
196
|
this.closed = false;
|
|
@@ -183,6 +213,14 @@ class BunRedisAdapter extends EventEmitter {
|
|
|
183
213
|
// Connection lifecycle
|
|
184
214
|
// ---------------------------------------------------------------
|
|
185
215
|
async connect() {
|
|
216
|
+
var _a, _b;
|
|
217
|
+
// A duplicate created with a `rawFactory` builds its raw client lazily on
|
|
218
|
+
// the first connect (Bun's native `duplicate()` is async).
|
|
219
|
+
if (!this.raw && this.rawFactory) {
|
|
220
|
+
this.raw = await this.rawFactory();
|
|
221
|
+
this.rawFactory = undefined;
|
|
222
|
+
this._setupCallbacks();
|
|
223
|
+
}
|
|
186
224
|
const replaceRaw = this.hasConnected && (this.closed || !this.raw.connected);
|
|
187
225
|
if (this.reconnectTimer) {
|
|
188
226
|
clearTimeout(this.reconnectTimer);
|
|
@@ -194,6 +232,9 @@ class BunRedisAdapter extends EventEmitter {
|
|
|
194
232
|
this.closed = false;
|
|
195
233
|
this.closing = false;
|
|
196
234
|
this.statusOverride = undefined;
|
|
235
|
+
if (!this.ready) {
|
|
236
|
+
await ((_a = this.readying) !== null && _a !== void 0 ? _a : this._handleConnected());
|
|
237
|
+
}
|
|
197
238
|
return;
|
|
198
239
|
}
|
|
199
240
|
if (!this.connecting) {
|
|
@@ -201,11 +242,10 @@ class BunRedisAdapter extends EventEmitter {
|
|
|
201
242
|
this.closing = false;
|
|
202
243
|
this.statusOverride = undefined;
|
|
203
244
|
// If the raw client was previously closed, Bun doesn't support
|
|
204
|
-
// reconnecting on the same instance. Create a fresh raw client
|
|
245
|
+
// reconnecting on the same instance. Create a fresh raw client aimed at
|
|
246
|
+
// the same server via Bun's native `duplicate()` (see #4582).
|
|
205
247
|
if (replaceRaw) {
|
|
206
|
-
|
|
207
|
-
.constructor;
|
|
208
|
-
this.raw = new BunRedisClient(this.raw.url);
|
|
248
|
+
this.raw = await this._duplicateRaw(this.raw);
|
|
209
249
|
this._setupCallbacks();
|
|
210
250
|
}
|
|
211
251
|
this.connecting = this.raw
|
|
@@ -221,6 +261,31 @@ class BunRedisAdapter extends EventEmitter {
|
|
|
221
261
|
});
|
|
222
262
|
}
|
|
223
263
|
await this.connecting;
|
|
264
|
+
await this.readying;
|
|
265
|
+
// Bun may report the socket as connected before this adapter transitions
|
|
266
|
+
// to ready (for example while applying CLIENT SETNAME on duplicates).
|
|
267
|
+
// Keep connect() aligned with ioredis semantics by waiting until the
|
|
268
|
+
// adapter is either ready or closed.
|
|
269
|
+
if (!this.ready && !this.closed && !this.closing && ((_b = this.raw) === null || _b === void 0 ? void 0 : _b.connected)) {
|
|
270
|
+
await new Promise(resolve => {
|
|
271
|
+
var _a;
|
|
272
|
+
const cleanup = () => {
|
|
273
|
+
this.off('ready', onDone);
|
|
274
|
+
this.off('close', onDone);
|
|
275
|
+
this.off('end', onDone);
|
|
276
|
+
};
|
|
277
|
+
const onDone = () => {
|
|
278
|
+
cleanup();
|
|
279
|
+
resolve();
|
|
280
|
+
};
|
|
281
|
+
this.on('ready', onDone);
|
|
282
|
+
this.on('close', onDone);
|
|
283
|
+
this.on('end', onDone);
|
|
284
|
+
if (this.ready || this.closed || this.closing || !((_a = this.raw) === null || _a === void 0 ? void 0 : _a.connected)) {
|
|
285
|
+
onDone();
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
224
289
|
}
|
|
225
290
|
_closeRaw() {
|
|
226
291
|
// Cancel any pending reconnect
|
|
@@ -229,7 +294,12 @@ class BunRedisAdapter extends EventEmitter {
|
|
|
229
294
|
this.reconnectTimer = null;
|
|
230
295
|
}
|
|
231
296
|
this.reconnecting = false;
|
|
297
|
+
// A duplicate closed before it ever connected has no raw client yet.
|
|
298
|
+
this.rawFactory = undefined;
|
|
232
299
|
const raw = this.raw;
|
|
300
|
+
if (!raw) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
233
303
|
raw.onconnect = () => { };
|
|
234
304
|
raw.onclose = () => { };
|
|
235
305
|
raw.onerror = () => { };
|
|
@@ -259,18 +329,20 @@ class BunRedisAdapter extends EventEmitter {
|
|
|
259
329
|
this.closed = true;
|
|
260
330
|
this.statusOverride = undefined;
|
|
261
331
|
const raw = this.raw;
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
raw.
|
|
332
|
+
if (raw) {
|
|
333
|
+
raw.onclose = () => { };
|
|
334
|
+
if (raw.connected) {
|
|
335
|
+
setImmediate(() => {
|
|
336
|
+
try {
|
|
337
|
+
if (raw.connected) {
|
|
338
|
+
raw.close();
|
|
339
|
+
}
|
|
268
340
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
}
|
|
341
|
+
catch (_err) {
|
|
342
|
+
// swallow
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
}
|
|
274
346
|
}
|
|
275
347
|
this.emit('close');
|
|
276
348
|
this._scheduleReconnect();
|
|
@@ -303,15 +375,45 @@ class BunRedisAdapter extends EventEmitter {
|
|
|
303
375
|
});
|
|
304
376
|
return 'OK';
|
|
305
377
|
}
|
|
378
|
+
/**
|
|
379
|
+
* Create a fresh raw client aimed at the *same* Redis server as `src`.
|
|
380
|
+
*
|
|
381
|
+
* Bun's `RedisClient` exposes no public connection info (no `url`, host,
|
|
382
|
+
* port or options), so the target cannot be reconstructed from the instance.
|
|
383
|
+
* Its native `duplicate()` is the only reliable way to clone the target and
|
|
384
|
+
* options; we fall back to URL-based reconstruction for exotic raw clients
|
|
385
|
+
* that don't implement it. See #4582.
|
|
386
|
+
*/
|
|
387
|
+
async _duplicateRaw(src) {
|
|
388
|
+
if (typeof src.duplicate === 'function') {
|
|
389
|
+
return await src.duplicate();
|
|
390
|
+
}
|
|
391
|
+
const Ctor = src.constructor;
|
|
392
|
+
return new Ctor(src.url);
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Return the raw client, materializing it first when this adapter is a
|
|
396
|
+
* lazily-initialized duplicate (created via `duplicate()` with a
|
|
397
|
+
* `rawFactory`). Command paths that touch `this.raw` directly use this so a
|
|
398
|
+
* duplicate can be used immediately without an explicit `connect()`.
|
|
399
|
+
*/
|
|
400
|
+
async _ensureRaw() {
|
|
401
|
+
if (!this.raw) {
|
|
402
|
+
await this.connect();
|
|
403
|
+
}
|
|
404
|
+
return this.raw;
|
|
405
|
+
}
|
|
306
406
|
duplicate(...args) {
|
|
307
|
-
// Bun's duplicate() is async, but IRedisClient.duplicate() is sync.
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
-
// first
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const
|
|
314
|
-
const adapter = new BunRedisAdapter(
|
|
407
|
+
// Bun's duplicate() is async, but IRedisClient.duplicate() is sync. The
|
|
408
|
+
// duplicate adapter is therefore created immediately with a `rawFactory`
|
|
409
|
+
// that clones the connection target lazily (via Bun's native duplicate)
|
|
410
|
+
// on first connect. Rebuilding from `this.raw.url` is not possible because
|
|
411
|
+
// Bun never exposes the URL, which previously sent duplicates to the
|
|
412
|
+
// wrong (default) server (#4582).
|
|
413
|
+
const parentRaw = this.raw;
|
|
414
|
+
const adapter = new BunRedisAdapter(undefined, {
|
|
415
|
+
rawFactory: () => this._duplicateRaw(parentRaw),
|
|
416
|
+
});
|
|
315
417
|
// Copy registered scripts to the duplicate
|
|
316
418
|
for (const [name, script] of this.scripts) {
|
|
317
419
|
adapter.scripts.set(name, script);
|
|
@@ -408,6 +510,14 @@ class BunRedisAdapter extends EventEmitter {
|
|
|
408
510
|
if (this.closing || this.closed) {
|
|
409
511
|
return Promise.reject(new ConnectionClosedError('Connection is closed'));
|
|
410
512
|
}
|
|
513
|
+
// A duplicate created via `duplicate()` builds its raw client lazily on the
|
|
514
|
+
// first `connect()` (Bun's native `duplicate()` is async). Materialize it
|
|
515
|
+
// here so commands issued before an explicit `connect()` don't throw on an
|
|
516
|
+
// undefined `raw` — matching other adapters where duplicates connect
|
|
517
|
+
// implicitly on first use.
|
|
518
|
+
if (!this.raw) {
|
|
519
|
+
return this.connect().then(() => this.sendCommand(command, args));
|
|
520
|
+
}
|
|
411
521
|
// Send directly to the underlying Bun client. Redis protocol guarantees
|
|
412
522
|
// responses arrive in the same order as requests on a single connection,
|
|
413
523
|
// so concurrent send() calls are safe and enable implicit pipelining
|
|
@@ -437,10 +547,10 @@ class BunRedisAdapter extends EventEmitter {
|
|
|
437
547
|
// Pipeline / Transaction
|
|
438
548
|
// ---------------------------------------------------------------
|
|
439
549
|
multi() {
|
|
440
|
-
return new BunRedisTransaction(this.
|
|
550
|
+
return new BunRedisTransaction(this.scripts, true, this);
|
|
441
551
|
}
|
|
442
552
|
pipeline() {
|
|
443
|
-
return new BunRedisTransaction(this.
|
|
553
|
+
return new BunRedisTransaction(this.scripts, false, this);
|
|
444
554
|
}
|
|
445
555
|
// ---------------------------------------------------------------
|
|
446
556
|
// Hash commands
|
|
@@ -603,7 +713,7 @@ class BunRedisAdapter extends EventEmitter {
|
|
|
603
713
|
for (const [k, v] of Object.entries(fields)) {
|
|
604
714
|
args.push(k, String(v));
|
|
605
715
|
}
|
|
606
|
-
return await this.
|
|
716
|
+
return await (await this._ensureRaw()).send('XADD', args);
|
|
607
717
|
}
|
|
608
718
|
async xread(streams, options) {
|
|
609
719
|
const args = [];
|
|
@@ -830,8 +940,7 @@ class BunRedisAdapter extends EventEmitter {
|
|
|
830
940
|
// them within a MULTI/EXEC block using send().
|
|
831
941
|
// ---------------------------------------------------------------------------
|
|
832
942
|
class BunRedisTransaction {
|
|
833
|
-
constructor(
|
|
834
|
-
this.raw = raw;
|
|
943
|
+
constructor(scripts, transactional, adapter) {
|
|
835
944
|
this.scripts = scripts;
|
|
836
945
|
this.transactional = transactional;
|
|
837
946
|
this.adapter = adapter;
|
|
@@ -993,15 +1102,18 @@ class BunRedisTransaction {
|
|
|
993
1102
|
const swallow = (_) => {
|
|
994
1103
|
/* error surfaces via EXEC or the outer try/catch */
|
|
995
1104
|
};
|
|
1105
|
+
// Materialize the raw client (a lazily-initialized duplicate may not have
|
|
1106
|
+
// one yet) so the MULTI…EXEC frames can be written as a contiguous burst.
|
|
1107
|
+
const raw = await this.adapter._ensureRaw();
|
|
996
1108
|
try {
|
|
997
1109
|
// Fire MULTI without awaiting — no round-trip needed before commands.
|
|
998
|
-
|
|
1110
|
+
raw.send('MULTI', []).catch(swallow);
|
|
999
1111
|
// Fire all queued commands synchronously (no awaits).
|
|
1000
1112
|
for (const { cmd, args } of this.commands) {
|
|
1001
|
-
|
|
1113
|
+
raw.send(cmd, args).catch(swallow);
|
|
1002
1114
|
}
|
|
1003
1115
|
// EXEC is the only await — it returns the array of results.
|
|
1004
|
-
const results = await
|
|
1116
|
+
const results = await raw.send('EXEC', []);
|
|
1005
1117
|
if (!results) {
|
|
1006
1118
|
return null;
|
|
1007
1119
|
}
|
|
@@ -1018,7 +1130,7 @@ class BunRedisTransaction {
|
|
|
1018
1130
|
catch (err) {
|
|
1019
1131
|
// Try to discard the MULTI state on error
|
|
1020
1132
|
try {
|
|
1021
|
-
await
|
|
1133
|
+
await raw.send('DISCARD', []);
|
|
1022
1134
|
}
|
|
1023
1135
|
catch (_a) {
|
|
1024
1136
|
// ignore
|
|
@@ -9,7 +9,7 @@ const packer = new Packr({
|
|
|
9
9
|
encodeUndefinedAsNil: true,
|
|
10
10
|
});
|
|
11
11
|
const pack = packer.pack;
|
|
12
|
-
import { array2obj, clientCommandMessageReg, errorObject, getParentKey, isEmpty, isRedisVersionLowerThan, objectToFlatArray, optsDecodeMap, optsEncodeMap, parseObjectValues, tryCatch, } from '../utils';
|
|
12
|
+
import { array2obj, clientCommandMessageReg, errorObject, getParentKey, isEmpty, isRedisCluster, isRedisVersionLowerThan, objectToFlatArray, optsDecodeMap, optsEncodeMap, parseObjectValues, tryCatch, } from '../utils';
|
|
13
13
|
import { version as packageVersion } from '../version';
|
|
14
14
|
import { finishedErrors } from './finished-errors';
|
|
15
15
|
import { QueueKeys } from './queue-keys';
|
|
@@ -20,6 +20,44 @@ import { QueueKeys } from './queue-keys';
|
|
|
20
20
|
* scan the whole state unboundedly.
|
|
21
21
|
*/
|
|
22
22
|
const GET_JOBS_MAX_BACKFILL_ITERATIONS = 5;
|
|
23
|
+
/**
|
|
24
|
+
* Whether the client currently has a live connection. IORedis Cluster reports a
|
|
25
|
+
* usable connection as `connect` rather than `ready` (see
|
|
26
|
+
* `RedisConnection.waitUntilReady`).
|
|
27
|
+
*/
|
|
28
|
+
function isClientLive(client) {
|
|
29
|
+
return (client.status === 'ready' ||
|
|
30
|
+
(client.status === 'connect' && isRedisCluster(client)));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Resets a connection whose blocking command was abandoned by a watchdog.
|
|
34
|
+
*
|
|
35
|
+
* Under `maxRetriesPerRequest: null` IORedis silently re-queues and re-sends
|
|
36
|
+
* an interrupted blocking command instead of rejecting it, so the abandoned
|
|
37
|
+
* command outlives a reconnect and would be served ahead of the next blocking
|
|
38
|
+
* read. Getting rid of it requires tearing the socket down while it is live.
|
|
39
|
+
*
|
|
40
|
+
* If the client is currently socketless (`reconnecting`) we must NOT
|
|
41
|
+
* disconnect it: that clears IORedis' own retry timer without emitting a
|
|
42
|
+
* `close` event and parks it in `reconnecting` forever (#4585). Instead we
|
|
43
|
+
* wait (via `reconnect`) for it to reach a live state and only then tear the
|
|
44
|
+
* socket down, waiting for it to actually close (`disconnect(true)` awaits
|
|
45
|
+
* the `end` event) before reconnecting again: IORedis closes the socket
|
|
46
|
+
* asynchronously, so a bare `disconnect(false)` leaves `status` transiently
|
|
47
|
+
* `ready`, `reconnect()` would observe the stale status, return a no-op, and
|
|
48
|
+
* the pending close would kill the connection for good (#4585 ready-path
|
|
49
|
+
* race).
|
|
50
|
+
*/
|
|
51
|
+
async function resetBlockedConnection(connection, client, reconnect) {
|
|
52
|
+
if (!isClientLive(client)) {
|
|
53
|
+
// Let IORedis' own retry timer bring the connection back first.
|
|
54
|
+
await reconnect();
|
|
55
|
+
}
|
|
56
|
+
if (isClientLive(client)) {
|
|
57
|
+
await connection.disconnect(true);
|
|
58
|
+
await reconnect();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
23
61
|
export class RedisQueueBackend extends EventEmitter {
|
|
24
62
|
constructor(connection, name, keys, toKey, opts, blockingConnection, ownsConnection = true) {
|
|
25
63
|
var _a;
|
|
@@ -1826,7 +1864,7 @@ export class RedisQueueBackend extends EventEmitter {
|
|
|
1826
1864
|
// Worker blocking primitive (previously bzpopmin in Worker)
|
|
1827
1865
|
// ============================================================
|
|
1828
1866
|
async waitForJob(blockTimeout) {
|
|
1829
|
-
var _a;
|
|
1867
|
+
var _a, _b;
|
|
1830
1868
|
const conn = (_a = this.blockingConnection) !== null && _a !== void 0 ? _a : this.connection;
|
|
1831
1869
|
const bclient = (await this.queue.blockingClient);
|
|
1832
1870
|
const roundedTimeout = conn.capabilities.canDoubleTimeout
|
|
@@ -1855,7 +1893,9 @@ export class RedisQueueBackend extends EventEmitter {
|
|
|
1855
1893
|
const timeout = new Promise(resolve => {
|
|
1856
1894
|
watchdog = setTimeout(() => {
|
|
1857
1895
|
timedOut = true;
|
|
1858
|
-
|
|
1896
|
+
// Resolve the wait as a timeout so the worker loop always advances.
|
|
1897
|
+
// The (possibly stuck) command is abandoned and the connection is
|
|
1898
|
+
// re-established in the `finally` block — see the note there.
|
|
1859
1899
|
resolve(null);
|
|
1860
1900
|
}, roundedTimeout * 1000 + 1000);
|
|
1861
1901
|
});
|
|
@@ -1871,16 +1911,16 @@ export class RedisQueueBackend extends EventEmitter {
|
|
|
1871
1911
|
}
|
|
1872
1912
|
finally {
|
|
1873
1913
|
clearTimeout(watchdog);
|
|
1874
|
-
// The watchdog
|
|
1875
|
-
//
|
|
1876
|
-
//
|
|
1877
|
-
//
|
|
1878
|
-
//
|
|
1914
|
+
// The watchdog resolved the wait as a timeout because the awaited command
|
|
1915
|
+
// never settled. Reset the dedicated blocking connection so the next
|
|
1916
|
+
// `waitForJob` starts from a healthy, unblocked connection and the
|
|
1917
|
+
// abandoned command is actually dropped — see
|
|
1918
|
+
// `resetBlockedConnection()`.
|
|
1879
1919
|
if (timedOut && !this.closing) {
|
|
1880
1920
|
try {
|
|
1881
|
-
await this.reconnectBlocking();
|
|
1921
|
+
await resetBlockedConnection((_b = this.blockingConnection) !== null && _b !== void 0 ? _b : this.connection, bclient, () => this.reconnectBlocking());
|
|
1882
1922
|
}
|
|
1883
|
-
catch (
|
|
1923
|
+
catch (_c) {
|
|
1884
1924
|
// Ignored: the next waitForJob call will retry the reconnect.
|
|
1885
1925
|
}
|
|
1886
1926
|
}
|
|
@@ -1920,7 +1960,9 @@ export class RedisQueueBackend extends EventEmitter {
|
|
|
1920
1960
|
const timeout = new Promise(resolve => {
|
|
1921
1961
|
watchdog = setTimeout(() => {
|
|
1922
1962
|
timedOut = true;
|
|
1923
|
-
|
|
1963
|
+
// Resolve as a timeout so the consumer loop always advances. The
|
|
1964
|
+
// (possibly stuck) command is abandoned and the connection
|
|
1965
|
+
// re-established in the `finally` block — see the note there.
|
|
1924
1966
|
resolve(null);
|
|
1925
1967
|
}, blockTimeout + 1000);
|
|
1926
1968
|
});
|
|
@@ -1929,12 +1971,12 @@ export class RedisQueueBackend extends EventEmitter {
|
|
|
1929
1971
|
}
|
|
1930
1972
|
finally {
|
|
1931
1973
|
clearTimeout(watchdog);
|
|
1932
|
-
//
|
|
1933
|
-
//
|
|
1934
|
-
//
|
|
1974
|
+
// Reset the connection so the next read starts healthy and the abandoned
|
|
1975
|
+
// command is actually dropped. Mirrors the `waitForJob` reset — see
|
|
1976
|
+
// `resetBlockedConnection()`.
|
|
1935
1977
|
if (timedOut && !this.closing) {
|
|
1936
1978
|
try {
|
|
1937
|
-
await this.connection.reconnect();
|
|
1979
|
+
await resetBlockedConnection(this.connection, client, () => this.connection.reconnect());
|
|
1938
1980
|
}
|
|
1939
1981
|
catch (_a) {
|
|
1940
1982
|
// Ignored: the next readEvents call will retry the reconnect.
|