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