@holo-js/queue-redis 0.1.4 → 0.1.5

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/index.d.ts CHANGED
@@ -104,10 +104,14 @@ declare class RedisQueueDriver implements QueueAsyncDriver {
104
104
  private readonly managedRedisConnection?;
105
105
  private readonly queues;
106
106
  private readonly workers;
107
+ private readonly workerReadyPromises;
107
108
  private readonly reservations;
108
109
  private queueCursor;
109
110
  constructor(connection: NormalizedQueueRedisConnectionConfig, context: QueueDriverFactoryContext);
110
111
  private getQueue;
112
+ private createWorkerKey;
113
+ private normalizeReserveBlockFor;
114
+ private resolveWorkerDrainDelay;
111
115
  private getWorker;
112
116
  private normalizeQueueNames;
113
117
  private rotateQueueNames;
@@ -116,8 +120,9 @@ declare class RedisQueueDriver implements QueueAsyncDriver {
116
120
  private settleReservation;
117
121
  dispatch<TPayload extends QueueJsonValue = QueueJsonValue, TResult = unknown>(job: QueueJobEnvelope<TPayload>): Promise<QueueDriverDispatchResult<TResult>>;
118
122
  reserve<TPayload extends QueueJsonValue = QueueJsonValue>(input: {
119
- readonly queueNames: readonly string[];
120
- readonly workerId: string;
123
+ readonly queueNames?: readonly string[];
124
+ readonly workerId?: string;
125
+ readonly timeout?: number;
121
126
  }): Promise<QueueReservedJob<TPayload> | null>;
122
127
  acknowledge(job: QueueReservedJob): Promise<void>;
123
128
  release(job: QueueReservedJob, options?: QueueReleaseOptions): Promise<void>;
package/dist/index.mjs CHANGED
@@ -140,6 +140,7 @@ var RedisQueueDriver = class {
140
140
  managedRedisConnection;
141
141
  queues = /* @__PURE__ */ new Map();
142
142
  workers = /* @__PURE__ */ new Map();
143
+ workerReadyPromises = /* @__PURE__ */ new Map();
143
144
  reservations = /* @__PURE__ */ new Map();
144
145
  queueCursor = 0;
145
146
  getQueue(queueName) {
@@ -157,10 +158,30 @@ var RedisQueueDriver = class {
157
158
  this.queues.set(queueName, queue);
158
159
  return queue;
159
160
  }
160
- async getWorker(queueName) {
161
- const cached = this.workers.get(queueName);
161
+ createWorkerKey(queueName, blockFor) {
162
+ return `${queueName}:${blockFor}`;
163
+ }
164
+ normalizeReserveBlockFor(timeout) {
165
+ if (typeof timeout === "undefined") {
166
+ return this.connection.blockFor;
167
+ }
168
+ if (!Number.isFinite(timeout) || timeout < 0) {
169
+ throw new Error("[Holo Queue] Redis queue reservation timeout must be a non-negative finite number when provided.");
170
+ }
171
+ return timeout;
172
+ }
173
+ resolveWorkerDrainDelay(blockFor) {
174
+ return blockFor > 0 ? blockFor : 1;
175
+ }
176
+ async getWorker(queueName, blockFor = this.connection.blockFor) {
177
+ const workerKey = this.createWorkerKey(queueName, blockFor);
178
+ const pending = this.workerReadyPromises.get(workerKey);
179
+ if (pending) {
180
+ return (await pending).worker;
181
+ }
182
+ const cached = this.workers.get(workerKey);
162
183
  if (cached) {
163
- return cached;
184
+ return cached.worker;
164
185
  }
165
186
  const worker = new BullWorker(
166
187
  queueName,
@@ -169,22 +190,40 @@ var RedisQueueDriver = class {
169
190
  autorun: false,
170
191
  concurrency: 1,
171
192
  connection: this.bullConnection,
172
- drainDelay: this.connection.blockFor,
193
+ drainDelay: this.resolveWorkerDrainDelay(blockFor),
173
194
  lockDuration: this.connection.retryAfter * 1e3,
174
195
  removeOnComplete: { count: 0 },
175
196
  removeOnFail: { count: 0 }
176
197
  }
177
198
  );
178
- await worker.waitUntilReady();
179
- this.workers.set(queueName, worker);
180
- return worker;
199
+ const workerRecord = {
200
+ queueName,
201
+ worker
202
+ };
203
+ const ready = worker.waitUntilReady().then(() => workerRecord).catch(async (error) => {
204
+ if (this.workers.get(workerKey) === workerRecord) {
205
+ this.workers.delete(workerKey);
206
+ }
207
+ try {
208
+ await worker.close(true);
209
+ } catch {
210
+ }
211
+ throw error;
212
+ }).finally(() => {
213
+ if (this.workerReadyPromises.get(workerKey) === ready) {
214
+ this.workerReadyPromises.delete(workerKey);
215
+ }
216
+ });
217
+ this.workers.set(workerKey, workerRecord);
218
+ this.workerReadyPromises.set(workerKey, ready);
219
+ return (await ready).worker;
181
220
  }
182
221
  normalizeQueueNames(queueNames) {
183
222
  if (!queueNames || queueNames.length === 0) {
184
223
  return [.../* @__PURE__ */ new Set([
185
224
  this.connection.queue,
186
225
  ...this.queues.keys(),
187
- ...this.workers.keys()
226
+ ...[...this.workers.values()].map((worker) => worker.queueName)
188
227
  ])];
189
228
  }
190
229
  return [...new Set(queueNames)];
@@ -242,9 +281,8 @@ var RedisQueueDriver = class {
242
281
  await callback(reservation);
243
282
  } catch (error) {
244
283
  throw wrapRedisError(this.name, action, error);
245
- } finally {
246
- this.reservations.delete(reserved.reservationId);
247
284
  }
285
+ this.reservations.delete(reserved.reservationId);
248
286
  }
249
287
  async dispatch(job) {
250
288
  try {
@@ -268,8 +306,10 @@ var RedisQueueDriver = class {
268
306
  async reserve(input) {
269
307
  try {
270
308
  const queueNames = this.rotateQueueNames(this.normalizeQueueNames(input.queueNames));
309
+ const workerId = input.workerId ?? this.name;
310
+ const blockFor = this.normalizeReserveBlockFor(input.timeout);
271
311
  for (const queueName of queueNames) {
272
- const token2 = `${input.workerId}:${randomUUID()}`;
312
+ const token2 = `${workerId}:${randomUUID()}`;
273
313
  const worker2 = await this.getWorker(queueName);
274
314
  const job2 = await worker2.getNextJob(token2, { block: false });
275
315
  if (job2) {
@@ -277,11 +317,11 @@ var RedisQueueDriver = class {
277
317
  }
278
318
  }
279
319
  const [blockingQueue] = queueNames;
280
- if (!blockingQueue || this.connection.blockFor <= 0) {
320
+ if (!blockingQueue || blockFor <= 0) {
281
321
  return null;
282
322
  }
283
- const token = `${input.workerId}:${randomUUID()}`;
284
- const worker = await this.getWorker(blockingQueue);
323
+ const token = `${workerId}:${randomUUID()}`;
324
+ const worker = await this.getWorker(blockingQueue, blockFor);
285
325
  const job = await worker.getNextJob(token, { block: true });
286
326
  if (!job) {
287
327
  return null;
@@ -327,10 +367,12 @@ var RedisQueueDriver = class {
327
367
  }
328
368
  async close() {
329
369
  const resources = [
330
- ...this.workers.values(),
370
+ ...[...this.workers.values()].map((worker) => worker.worker),
331
371
  ...this.queues.values()
332
372
  ];
373
+ const workerReadyPromises = [...this.workerReadyPromises.values()];
333
374
  this.reservations.clear();
375
+ this.workerReadyPromises.clear();
334
376
  this.workers.clear();
335
377
  this.queues.clear();
336
378
  let closeRejection;
@@ -342,6 +384,7 @@ var RedisQueueDriver = class {
342
384
  }
343
385
  await resource.close();
344
386
  }));
387
+ await Promise.allSettled(workerReadyPromises);
345
388
  closeRejection = results.find((result) => result.status === "rejected");
346
389
  } finally {
347
390
  if (this.managedRedisConnection) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holo-js/queue-redis",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Holo-JS Framework - Redis queue driver",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,18 +23,18 @@
23
23
  "test": "vitest --run"
24
24
  },
25
25
  "peerDependencies": {
26
- "@holo-js/queue": "^0.1.4"
26
+ "@holo-js/queue": "^0.1.5"
27
27
  },
28
28
  "dependencies": {
29
29
  "bullmq": "^5.71.0",
30
- "ioredis": "catalog:",
30
+ "ioredis": "^5.4.2",
31
31
  "tslib": "^2.8.1"
32
32
  },
33
33
  "devDependencies": {
34
- "@holo-js/queue": "workspace:*",
34
+ "@holo-js/queue": "^0.1.5",
35
35
  "@types/node": "^22.10.2",
36
36
  "tsup": "^8.3.5",
37
37
  "typescript": "^5.7.2",
38
- "vitest": "^2.1.8"
38
+ "vitest": "^4.1.5"
39
39
  }
40
40
  }