power-queues 2.0.5 → 2.0.7
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.cjs +115 -33
- package/dist/index.d.cts +15 -3
- package/dist/index.d.ts +15 -3
- package/dist/index.js +115 -33
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -264,8 +264,6 @@ var PowerQueues = class extends import_power_redis.PowerRedis {
|
|
|
264
264
|
this.scripts = {};
|
|
265
265
|
this.addingBatchTasksCount = 800;
|
|
266
266
|
this.addingBatchKeysLimit = 1e4;
|
|
267
|
-
this.idemOn = true;
|
|
268
|
-
this.idemKey = "";
|
|
269
267
|
this.workerExecuteLockTimeoutMs = 18e4;
|
|
270
268
|
this.workerCacheTaskTimeoutMs = 60;
|
|
271
269
|
this.approveBatchTasksCount = 2e3;
|
|
@@ -280,11 +278,13 @@ var PowerQueues = class extends import_power_redis.PowerRedis {
|
|
|
280
278
|
this.recoveryStuckTasksTimeoutMs = 6e4;
|
|
281
279
|
this.workerLoopIntervalMs = 5e3;
|
|
282
280
|
this.workerSelectionTimeoutMs = 80;
|
|
281
|
+
this.workerMaxRetries = 5;
|
|
282
|
+
this.workerClearAttemptsTimeoutMs = 864e5;
|
|
283
283
|
}
|
|
284
284
|
async onSelected(data) {
|
|
285
285
|
return data;
|
|
286
286
|
}
|
|
287
|
-
async onExecute(id, payload, createdAt, job, key) {
|
|
287
|
+
async onExecute(id, payload, createdAt, job, key, attempt) {
|
|
288
288
|
}
|
|
289
289
|
async onExecuted(data) {
|
|
290
290
|
}
|
|
@@ -309,6 +309,7 @@ var PowerQueues = class extends import_power_redis.PowerRedis {
|
|
|
309
309
|
await this.approve(ids);
|
|
310
310
|
}
|
|
311
311
|
} catch (err) {
|
|
312
|
+
await this.batchError(err);
|
|
312
313
|
await (0, import_full_utils.wait)(600);
|
|
313
314
|
}
|
|
314
315
|
}
|
|
@@ -320,7 +321,8 @@ var PowerQueues = class extends import_power_redis.PowerRedis {
|
|
|
320
321
|
if (typeof queueName !== "string" || !(queueName.length > 0)) {
|
|
321
322
|
throw new Error("Queue name is required.");
|
|
322
323
|
}
|
|
323
|
-
const
|
|
324
|
+
const job = (0, import_uuid.v4)();
|
|
325
|
+
const batches = this.buildBatches(data, job, opts.idem);
|
|
324
326
|
const result = new Array(data.length);
|
|
325
327
|
const promises = [];
|
|
326
328
|
let cursor = 0;
|
|
@@ -343,6 +345,16 @@ var PowerQueues = class extends import_power_redis.PowerRedis {
|
|
|
343
345
|
}
|
|
344
346
|
}
|
|
345
347
|
});
|
|
348
|
+
if (opts.status) {
|
|
349
|
+
await this.redis.set(`${queueName}:${job}:total`, data.length);
|
|
350
|
+
await this.redis.set(`${queueName}:${job}:ready`, 0);
|
|
351
|
+
await this.redis.set(`${queueName}:${job}:err`, 0);
|
|
352
|
+
await this.redis.set(`${queueName}:${job}:ok`, 0);
|
|
353
|
+
await this.redis.pexpire(`${queueName}:${job}:total`, opts.statusTimeoutMs || 864e5);
|
|
354
|
+
await this.redis.pexpire(`${queueName}:${job}:ready`, opts.statusTimeoutMs || 864e5);
|
|
355
|
+
await this.redis.pexpire(`${queueName}:${job}:err`, opts.statusTimeoutMs || 864e5);
|
|
356
|
+
await this.redis.pexpire(`${queueName}:${job}:ok`, opts.statusTimeoutMs || 864e5);
|
|
357
|
+
}
|
|
346
358
|
await Promise.all(runners);
|
|
347
359
|
return result;
|
|
348
360
|
}
|
|
@@ -452,35 +464,32 @@ var PowerQueues = class extends import_power_redis.PowerRedis {
|
|
|
452
464
|
}
|
|
453
465
|
return argv;
|
|
454
466
|
}
|
|
455
|
-
buildBatches(tasks) {
|
|
456
|
-
const job = (0, import_uuid.v4)();
|
|
467
|
+
buildBatches(tasks, job, idem) {
|
|
457
468
|
const batches = [];
|
|
458
469
|
let batch = [], realKeysLength = 0;
|
|
459
470
|
for (let task of tasks) {
|
|
471
|
+
const createdAt = task?.createdAt || Date.now();
|
|
460
472
|
let entry = task;
|
|
461
|
-
if (
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
473
|
+
if (typeof entry.payload === "object") {
|
|
474
|
+
entry = {
|
|
475
|
+
...entry,
|
|
476
|
+
payload: {
|
|
477
|
+
payload: JSON.stringify(entry.payload),
|
|
478
|
+
createdAt,
|
|
479
|
+
job
|
|
467
480
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
entry.flat.push("createdAt");
|
|
479
|
-
entry.flat.push(String(createdAt));
|
|
480
|
-
entry.flat.push("job");
|
|
481
|
-
entry.flat.push(job);
|
|
481
|
+
};
|
|
482
|
+
if (idem) {
|
|
483
|
+
entry.payload["idemKey"] = entry?.idemKey || (0, import_uuid.v4)();
|
|
484
|
+
}
|
|
485
|
+
} else if (Array.isArray(entry.flat)) {
|
|
486
|
+
entry.flat.push("createdAt");
|
|
487
|
+
entry.flat.push(String(createdAt));
|
|
488
|
+
entry.flat.push("job");
|
|
489
|
+
entry.flat.push(job);
|
|
490
|
+
if (idem) {
|
|
482
491
|
entry.flat.push("idemKey");
|
|
483
|
-
entry.flat.push(idemKey);
|
|
492
|
+
entry.flat.push(entry?.idemKey || (0, import_uuid.v4)());
|
|
484
493
|
}
|
|
485
494
|
}
|
|
486
495
|
const reqKeysLength = this.keysLength(entry);
|
|
@@ -500,6 +509,33 @@ var PowerQueues = class extends import_power_redis.PowerRedis {
|
|
|
500
509
|
keysLength(task) {
|
|
501
510
|
return 2 + ("flat" in task && Array.isArray(task.flat) && task.flat.length ? task.flat.length : Object.keys(task).length * 2);
|
|
502
511
|
}
|
|
512
|
+
attemptsKey(id) {
|
|
513
|
+
const safeStream = this.stream.replace(/[^\w:\-]/g, "_");
|
|
514
|
+
const safeId = id.replace(/[^\w:\-]/g, "_");
|
|
515
|
+
return `q:${safeStream}:attempts:${safeId}`;
|
|
516
|
+
}
|
|
517
|
+
async incrAttempts(id) {
|
|
518
|
+
try {
|
|
519
|
+
const key = this.attemptsKey(id);
|
|
520
|
+
const attempts = await this.redis.incr(key);
|
|
521
|
+
await this.redis.pexpire(key, this.workerClearAttemptsTimeoutMs);
|
|
522
|
+
return attempts;
|
|
523
|
+
} catch (err) {
|
|
524
|
+
}
|
|
525
|
+
return 0;
|
|
526
|
+
}
|
|
527
|
+
async getAttempts(id) {
|
|
528
|
+
const key = this.attemptsKey(id);
|
|
529
|
+
const v = await this.redis.get(key);
|
|
530
|
+
return Number(v || 0);
|
|
531
|
+
}
|
|
532
|
+
async clearAttempts(id) {
|
|
533
|
+
const key = this.attemptsKey(id);
|
|
534
|
+
try {
|
|
535
|
+
await this.redis.del(key);
|
|
536
|
+
} catch (e) {
|
|
537
|
+
}
|
|
538
|
+
}
|
|
503
539
|
async success(id, payload, createdAt, job, key) {
|
|
504
540
|
if (this.executeJobStatus) {
|
|
505
541
|
await this.status(id, payload, createdAt, job, key);
|
|
@@ -511,6 +547,18 @@ var PowerQueues = class extends import_power_redis.PowerRedis {
|
|
|
511
547
|
const { ready = 0, ok = 0 } = await this.getMany(prefix);
|
|
512
548
|
await this.setMany([{ key: `${prefix}ready`, value: ready + 1 }, { key: `${prefix}ok`, value: ok + 1 }], this.executeJobStatusTtlSec);
|
|
513
549
|
}
|
|
550
|
+
async batchError(err, tasks) {
|
|
551
|
+
}
|
|
552
|
+
async error(err, id, payload, createdAt, job, key) {
|
|
553
|
+
await this.onError(err, id, payload, createdAt, job, key);
|
|
554
|
+
}
|
|
555
|
+
async onError(err, id, payload, createdAt, job, key) {
|
|
556
|
+
}
|
|
557
|
+
async attempt(err, id, payload, createdAt, job, key, attempts) {
|
|
558
|
+
await this.onRetry(err, id, payload, createdAt, job, key, attempts);
|
|
559
|
+
}
|
|
560
|
+
async onRetry(err, id, payload, createdAt, job, key, attempts) {
|
|
561
|
+
}
|
|
514
562
|
async execute(tasks) {
|
|
515
563
|
const result = [];
|
|
516
564
|
let contended = 0, promises = [];
|
|
@@ -542,6 +590,7 @@ var PowerQueues = class extends import_power_redis.PowerRedis {
|
|
|
542
590
|
await this.waitAbortable(15 + Math.floor(Math.random() * 35) + Math.min(250, 15 * contended + Math.floor(Math.random() * 40)));
|
|
543
591
|
}
|
|
544
592
|
} catch (err) {
|
|
593
|
+
await this.batchError(err, tasks);
|
|
545
594
|
}
|
|
546
595
|
return result;
|
|
547
596
|
}
|
|
@@ -550,10 +599,27 @@ var PowerQueues = class extends import_power_redis.PowerRedis {
|
|
|
550
599
|
return await this.idempotency(id, payload, createdAt, job, key);
|
|
551
600
|
} else {
|
|
552
601
|
try {
|
|
553
|
-
await this.onExecute(id, payload, createdAt, job, key);
|
|
602
|
+
await this.onExecute(id, payload, createdAt, job, key, await this.getAttempts(id));
|
|
554
603
|
await this.success(id, payload, createdAt, job, key);
|
|
555
604
|
return { id };
|
|
556
605
|
} catch (err) {
|
|
606
|
+
const attempts = await this.incrAttempts(id);
|
|
607
|
+
await this.attempt(err, id, payload, createdAt, job, key, attempts);
|
|
608
|
+
await this.error(err, id, payload, createdAt, job, key);
|
|
609
|
+
if (attempts >= this.workerMaxRetries) {
|
|
610
|
+
await this.addTasks(`${this.stream}:dlq`, [{
|
|
611
|
+
payload: {
|
|
612
|
+
...payload,
|
|
613
|
+
error: String(err?.message || err),
|
|
614
|
+
createdAt,
|
|
615
|
+
job,
|
|
616
|
+
id,
|
|
617
|
+
attempts
|
|
618
|
+
}
|
|
619
|
+
}]);
|
|
620
|
+
await this.clearAttempts(id);
|
|
621
|
+
return { id };
|
|
622
|
+
}
|
|
557
623
|
}
|
|
558
624
|
}
|
|
559
625
|
return {};
|
|
@@ -593,12 +659,29 @@ var PowerQueues = class extends import_power_redis.PowerRedis {
|
|
|
593
659
|
const heartbeat = this.heartbeat(keys) || (() => {
|
|
594
660
|
});
|
|
595
661
|
try {
|
|
596
|
-
await this.onExecute(id, payload, createdAt, job, key);
|
|
662
|
+
await this.onExecute(id, payload, createdAt, job, key, await this.getAttempts(id));
|
|
597
663
|
await this.idempotencyDone(keys);
|
|
598
664
|
await this.success(id, payload, createdAt, job, key);
|
|
599
665
|
return { id };
|
|
600
666
|
} catch (err) {
|
|
667
|
+
const attempts = await this.incrAttempts(id);
|
|
601
668
|
try {
|
|
669
|
+
await this.attempt(err, id, payload, createdAt, job, key, attempts);
|
|
670
|
+
await this.error(err, id, payload, createdAt, job, key);
|
|
671
|
+
if (attempts >= this.workerMaxRetries) {
|
|
672
|
+
await this.addTasks(`${this.stream}:dlq`, [{
|
|
673
|
+
payload: {
|
|
674
|
+
...payload,
|
|
675
|
+
error: String(err?.message || err),
|
|
676
|
+
createdAt,
|
|
677
|
+
job,
|
|
678
|
+
id
|
|
679
|
+
}
|
|
680
|
+
}]);
|
|
681
|
+
await this.clearAttempts(id);
|
|
682
|
+
await this.idempotencyFree(keys);
|
|
683
|
+
return { id };
|
|
684
|
+
}
|
|
602
685
|
await this.idempotencyFree(keys);
|
|
603
686
|
} catch (err2) {
|
|
604
687
|
}
|
|
@@ -760,11 +843,10 @@ var PowerQueues = class extends import_power_redis.PowerRedis {
|
|
|
760
843
|
const id = Buffer.isBuffer(e?.[0]) ? e[0].toString() : e?.[0];
|
|
761
844
|
const kvRaw = e?.[1] ?? [];
|
|
762
845
|
const kv = Array.isArray(kvRaw) ? kvRaw.map((x) => Buffer.isBuffer(x) ? x.toString() : x) : [];
|
|
763
|
-
return [id, kv
|
|
846
|
+
return [id, kv];
|
|
764
847
|
}).filter(([id, kv]) => typeof id === "string" && id.length > 0 && Array.isArray(kv) && (kv.length & 1) === 0).map(([id, kv]) => {
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
return [id, data, createdAt, job, idemKey];
|
|
848
|
+
const { idemKey = "", job, createdAt, payload } = this.values(kv);
|
|
849
|
+
return [id, this.payload(payload), createdAt, job, idemKey];
|
|
768
850
|
});
|
|
769
851
|
}
|
|
770
852
|
values(value) {
|
package/dist/index.d.cts
CHANGED
|
@@ -13,6 +13,9 @@ type AddTasksOptions = {
|
|
|
13
13
|
exact?: boolean;
|
|
14
14
|
trimLimit?: number;
|
|
15
15
|
id?: string;
|
|
16
|
+
status?: boolean;
|
|
17
|
+
statusTimeoutMs?: number;
|
|
18
|
+
idem?: boolean;
|
|
16
19
|
};
|
|
17
20
|
type Task = {
|
|
18
21
|
job: string;
|
|
@@ -35,8 +38,6 @@ declare class PowerQueues extends PowerRedis {
|
|
|
35
38
|
readonly scripts: Record<string, SavedScript>;
|
|
36
39
|
readonly addingBatchTasksCount: number;
|
|
37
40
|
readonly addingBatchKeysLimit: number;
|
|
38
|
-
readonly idemOn: boolean;
|
|
39
|
-
readonly idemKey: string;
|
|
40
41
|
readonly workerExecuteLockTimeoutMs: number;
|
|
41
42
|
readonly workerCacheTaskTimeoutMs: number;
|
|
42
43
|
readonly approveBatchTasksCount: number;
|
|
@@ -51,8 +52,10 @@ declare class PowerQueues extends PowerRedis {
|
|
|
51
52
|
readonly recoveryStuckTasksTimeoutMs: number;
|
|
52
53
|
readonly workerLoopIntervalMs: number;
|
|
53
54
|
readonly workerSelectionTimeoutMs: number;
|
|
55
|
+
readonly workerMaxRetries: number;
|
|
56
|
+
readonly workerClearAttemptsTimeoutMs: number;
|
|
54
57
|
onSelected(data: Array<[string, any[], number, string, string]>): Promise<[string, any[], number, string, string][]>;
|
|
55
|
-
onExecute(id: string, payload: any, createdAt: number, job: string, key: string): Promise<void>;
|
|
58
|
+
onExecute(id: string, payload: any, createdAt: number, job: string, key: string, attempt: number): Promise<void>;
|
|
56
59
|
onExecuted(data: Array<[string, any[], number, string, string]>): Promise<void>;
|
|
57
60
|
onSuccess(id: string, payload: any, createdAt: number, job: string, key: string): Promise<void>;
|
|
58
61
|
runQueue(): Promise<void>;
|
|
@@ -66,8 +69,17 @@ declare class PowerQueues extends PowerRedis {
|
|
|
66
69
|
private payloadBatch;
|
|
67
70
|
private buildBatches;
|
|
68
71
|
private keysLength;
|
|
72
|
+
private attemptsKey;
|
|
73
|
+
private incrAttempts;
|
|
74
|
+
private getAttempts;
|
|
75
|
+
private clearAttempts;
|
|
69
76
|
private success;
|
|
70
77
|
private status;
|
|
78
|
+
private batchError;
|
|
79
|
+
private error;
|
|
80
|
+
onError(err: any, id: string, payload: any, createdAt: number, job: string, key: string): Promise<void>;
|
|
81
|
+
private attempt;
|
|
82
|
+
onRetry(err: any, id: string, payload: any, createdAt: number, job: string, key: string, attempts: number): Promise<void>;
|
|
71
83
|
private execute;
|
|
72
84
|
private executeProcess;
|
|
73
85
|
private approve;
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,9 @@ type AddTasksOptions = {
|
|
|
13
13
|
exact?: boolean;
|
|
14
14
|
trimLimit?: number;
|
|
15
15
|
id?: string;
|
|
16
|
+
status?: boolean;
|
|
17
|
+
statusTimeoutMs?: number;
|
|
18
|
+
idem?: boolean;
|
|
16
19
|
};
|
|
17
20
|
type Task = {
|
|
18
21
|
job: string;
|
|
@@ -35,8 +38,6 @@ declare class PowerQueues extends PowerRedis {
|
|
|
35
38
|
readonly scripts: Record<string, SavedScript>;
|
|
36
39
|
readonly addingBatchTasksCount: number;
|
|
37
40
|
readonly addingBatchKeysLimit: number;
|
|
38
|
-
readonly idemOn: boolean;
|
|
39
|
-
readonly idemKey: string;
|
|
40
41
|
readonly workerExecuteLockTimeoutMs: number;
|
|
41
42
|
readonly workerCacheTaskTimeoutMs: number;
|
|
42
43
|
readonly approveBatchTasksCount: number;
|
|
@@ -51,8 +52,10 @@ declare class PowerQueues extends PowerRedis {
|
|
|
51
52
|
readonly recoveryStuckTasksTimeoutMs: number;
|
|
52
53
|
readonly workerLoopIntervalMs: number;
|
|
53
54
|
readonly workerSelectionTimeoutMs: number;
|
|
55
|
+
readonly workerMaxRetries: number;
|
|
56
|
+
readonly workerClearAttemptsTimeoutMs: number;
|
|
54
57
|
onSelected(data: Array<[string, any[], number, string, string]>): Promise<[string, any[], number, string, string][]>;
|
|
55
|
-
onExecute(id: string, payload: any, createdAt: number, job: string, key: string): Promise<void>;
|
|
58
|
+
onExecute(id: string, payload: any, createdAt: number, job: string, key: string, attempt: number): Promise<void>;
|
|
56
59
|
onExecuted(data: Array<[string, any[], number, string, string]>): Promise<void>;
|
|
57
60
|
onSuccess(id: string, payload: any, createdAt: number, job: string, key: string): Promise<void>;
|
|
58
61
|
runQueue(): Promise<void>;
|
|
@@ -66,8 +69,17 @@ declare class PowerQueues extends PowerRedis {
|
|
|
66
69
|
private payloadBatch;
|
|
67
70
|
private buildBatches;
|
|
68
71
|
private keysLength;
|
|
72
|
+
private attemptsKey;
|
|
73
|
+
private incrAttempts;
|
|
74
|
+
private getAttempts;
|
|
75
|
+
private clearAttempts;
|
|
69
76
|
private success;
|
|
70
77
|
private status;
|
|
78
|
+
private batchError;
|
|
79
|
+
private error;
|
|
80
|
+
onError(err: any, id: string, payload: any, createdAt: number, job: string, key: string): Promise<void>;
|
|
81
|
+
private attempt;
|
|
82
|
+
onRetry(err: any, id: string, payload: any, createdAt: number, job: string, key: string, attempts: number): Promise<void>;
|
|
71
83
|
private execute;
|
|
72
84
|
private executeProcess;
|
|
73
85
|
private approve;
|
package/dist/index.js
CHANGED
|
@@ -238,8 +238,6 @@ var PowerQueues = class extends PowerRedis {
|
|
|
238
238
|
this.scripts = {};
|
|
239
239
|
this.addingBatchTasksCount = 800;
|
|
240
240
|
this.addingBatchKeysLimit = 1e4;
|
|
241
|
-
this.idemOn = true;
|
|
242
|
-
this.idemKey = "";
|
|
243
241
|
this.workerExecuteLockTimeoutMs = 18e4;
|
|
244
242
|
this.workerCacheTaskTimeoutMs = 60;
|
|
245
243
|
this.approveBatchTasksCount = 2e3;
|
|
@@ -254,11 +252,13 @@ var PowerQueues = class extends PowerRedis {
|
|
|
254
252
|
this.recoveryStuckTasksTimeoutMs = 6e4;
|
|
255
253
|
this.workerLoopIntervalMs = 5e3;
|
|
256
254
|
this.workerSelectionTimeoutMs = 80;
|
|
255
|
+
this.workerMaxRetries = 5;
|
|
256
|
+
this.workerClearAttemptsTimeoutMs = 864e5;
|
|
257
257
|
}
|
|
258
258
|
async onSelected(data) {
|
|
259
259
|
return data;
|
|
260
260
|
}
|
|
261
|
-
async onExecute(id, payload, createdAt, job, key) {
|
|
261
|
+
async onExecute(id, payload, createdAt, job, key, attempt) {
|
|
262
262
|
}
|
|
263
263
|
async onExecuted(data) {
|
|
264
264
|
}
|
|
@@ -283,6 +283,7 @@ var PowerQueues = class extends PowerRedis {
|
|
|
283
283
|
await this.approve(ids);
|
|
284
284
|
}
|
|
285
285
|
} catch (err) {
|
|
286
|
+
await this.batchError(err);
|
|
286
287
|
await wait(600);
|
|
287
288
|
}
|
|
288
289
|
}
|
|
@@ -294,7 +295,8 @@ var PowerQueues = class extends PowerRedis {
|
|
|
294
295
|
if (typeof queueName !== "string" || !(queueName.length > 0)) {
|
|
295
296
|
throw new Error("Queue name is required.");
|
|
296
297
|
}
|
|
297
|
-
const
|
|
298
|
+
const job = uuid();
|
|
299
|
+
const batches = this.buildBatches(data, job, opts.idem);
|
|
298
300
|
const result = new Array(data.length);
|
|
299
301
|
const promises = [];
|
|
300
302
|
let cursor = 0;
|
|
@@ -317,6 +319,16 @@ var PowerQueues = class extends PowerRedis {
|
|
|
317
319
|
}
|
|
318
320
|
}
|
|
319
321
|
});
|
|
322
|
+
if (opts.status) {
|
|
323
|
+
await this.redis.set(`${queueName}:${job}:total`, data.length);
|
|
324
|
+
await this.redis.set(`${queueName}:${job}:ready`, 0);
|
|
325
|
+
await this.redis.set(`${queueName}:${job}:err`, 0);
|
|
326
|
+
await this.redis.set(`${queueName}:${job}:ok`, 0);
|
|
327
|
+
await this.redis.pexpire(`${queueName}:${job}:total`, opts.statusTimeoutMs || 864e5);
|
|
328
|
+
await this.redis.pexpire(`${queueName}:${job}:ready`, opts.statusTimeoutMs || 864e5);
|
|
329
|
+
await this.redis.pexpire(`${queueName}:${job}:err`, opts.statusTimeoutMs || 864e5);
|
|
330
|
+
await this.redis.pexpire(`${queueName}:${job}:ok`, opts.statusTimeoutMs || 864e5);
|
|
331
|
+
}
|
|
320
332
|
await Promise.all(runners);
|
|
321
333
|
return result;
|
|
322
334
|
}
|
|
@@ -426,35 +438,32 @@ var PowerQueues = class extends PowerRedis {
|
|
|
426
438
|
}
|
|
427
439
|
return argv;
|
|
428
440
|
}
|
|
429
|
-
buildBatches(tasks) {
|
|
430
|
-
const job = uuid();
|
|
441
|
+
buildBatches(tasks, job, idem) {
|
|
431
442
|
const batches = [];
|
|
432
443
|
let batch = [], realKeysLength = 0;
|
|
433
444
|
for (let task of tasks) {
|
|
445
|
+
const createdAt = task?.createdAt || Date.now();
|
|
434
446
|
let entry = task;
|
|
435
|
-
if (
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
447
|
+
if (typeof entry.payload === "object") {
|
|
448
|
+
entry = {
|
|
449
|
+
...entry,
|
|
450
|
+
payload: {
|
|
451
|
+
payload: JSON.stringify(entry.payload),
|
|
452
|
+
createdAt,
|
|
453
|
+
job
|
|
441
454
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
entry.flat.push("createdAt");
|
|
453
|
-
entry.flat.push(String(createdAt));
|
|
454
|
-
entry.flat.push("job");
|
|
455
|
-
entry.flat.push(job);
|
|
455
|
+
};
|
|
456
|
+
if (idem) {
|
|
457
|
+
entry.payload["idemKey"] = entry?.idemKey || uuid();
|
|
458
|
+
}
|
|
459
|
+
} else if (Array.isArray(entry.flat)) {
|
|
460
|
+
entry.flat.push("createdAt");
|
|
461
|
+
entry.flat.push(String(createdAt));
|
|
462
|
+
entry.flat.push("job");
|
|
463
|
+
entry.flat.push(job);
|
|
464
|
+
if (idem) {
|
|
456
465
|
entry.flat.push("idemKey");
|
|
457
|
-
entry.flat.push(idemKey);
|
|
466
|
+
entry.flat.push(entry?.idemKey || uuid());
|
|
458
467
|
}
|
|
459
468
|
}
|
|
460
469
|
const reqKeysLength = this.keysLength(entry);
|
|
@@ -474,6 +483,33 @@ var PowerQueues = class extends PowerRedis {
|
|
|
474
483
|
keysLength(task) {
|
|
475
484
|
return 2 + ("flat" in task && Array.isArray(task.flat) && task.flat.length ? task.flat.length : Object.keys(task).length * 2);
|
|
476
485
|
}
|
|
486
|
+
attemptsKey(id) {
|
|
487
|
+
const safeStream = this.stream.replace(/[^\w:\-]/g, "_");
|
|
488
|
+
const safeId = id.replace(/[^\w:\-]/g, "_");
|
|
489
|
+
return `q:${safeStream}:attempts:${safeId}`;
|
|
490
|
+
}
|
|
491
|
+
async incrAttempts(id) {
|
|
492
|
+
try {
|
|
493
|
+
const key = this.attemptsKey(id);
|
|
494
|
+
const attempts = await this.redis.incr(key);
|
|
495
|
+
await this.redis.pexpire(key, this.workerClearAttemptsTimeoutMs);
|
|
496
|
+
return attempts;
|
|
497
|
+
} catch (err) {
|
|
498
|
+
}
|
|
499
|
+
return 0;
|
|
500
|
+
}
|
|
501
|
+
async getAttempts(id) {
|
|
502
|
+
const key = this.attemptsKey(id);
|
|
503
|
+
const v = await this.redis.get(key);
|
|
504
|
+
return Number(v || 0);
|
|
505
|
+
}
|
|
506
|
+
async clearAttempts(id) {
|
|
507
|
+
const key = this.attemptsKey(id);
|
|
508
|
+
try {
|
|
509
|
+
await this.redis.del(key);
|
|
510
|
+
} catch (e) {
|
|
511
|
+
}
|
|
512
|
+
}
|
|
477
513
|
async success(id, payload, createdAt, job, key) {
|
|
478
514
|
if (this.executeJobStatus) {
|
|
479
515
|
await this.status(id, payload, createdAt, job, key);
|
|
@@ -485,6 +521,18 @@ var PowerQueues = class extends PowerRedis {
|
|
|
485
521
|
const { ready = 0, ok = 0 } = await this.getMany(prefix);
|
|
486
522
|
await this.setMany([{ key: `${prefix}ready`, value: ready + 1 }, { key: `${prefix}ok`, value: ok + 1 }], this.executeJobStatusTtlSec);
|
|
487
523
|
}
|
|
524
|
+
async batchError(err, tasks) {
|
|
525
|
+
}
|
|
526
|
+
async error(err, id, payload, createdAt, job, key) {
|
|
527
|
+
await this.onError(err, id, payload, createdAt, job, key);
|
|
528
|
+
}
|
|
529
|
+
async onError(err, id, payload, createdAt, job, key) {
|
|
530
|
+
}
|
|
531
|
+
async attempt(err, id, payload, createdAt, job, key, attempts) {
|
|
532
|
+
await this.onRetry(err, id, payload, createdAt, job, key, attempts);
|
|
533
|
+
}
|
|
534
|
+
async onRetry(err, id, payload, createdAt, job, key, attempts) {
|
|
535
|
+
}
|
|
488
536
|
async execute(tasks) {
|
|
489
537
|
const result = [];
|
|
490
538
|
let contended = 0, promises = [];
|
|
@@ -516,6 +564,7 @@ var PowerQueues = class extends PowerRedis {
|
|
|
516
564
|
await this.waitAbortable(15 + Math.floor(Math.random() * 35) + Math.min(250, 15 * contended + Math.floor(Math.random() * 40)));
|
|
517
565
|
}
|
|
518
566
|
} catch (err) {
|
|
567
|
+
await this.batchError(err, tasks);
|
|
519
568
|
}
|
|
520
569
|
return result;
|
|
521
570
|
}
|
|
@@ -524,10 +573,27 @@ var PowerQueues = class extends PowerRedis {
|
|
|
524
573
|
return await this.idempotency(id, payload, createdAt, job, key);
|
|
525
574
|
} else {
|
|
526
575
|
try {
|
|
527
|
-
await this.onExecute(id, payload, createdAt, job, key);
|
|
576
|
+
await this.onExecute(id, payload, createdAt, job, key, await this.getAttempts(id));
|
|
528
577
|
await this.success(id, payload, createdAt, job, key);
|
|
529
578
|
return { id };
|
|
530
579
|
} catch (err) {
|
|
580
|
+
const attempts = await this.incrAttempts(id);
|
|
581
|
+
await this.attempt(err, id, payload, createdAt, job, key, attempts);
|
|
582
|
+
await this.error(err, id, payload, createdAt, job, key);
|
|
583
|
+
if (attempts >= this.workerMaxRetries) {
|
|
584
|
+
await this.addTasks(`${this.stream}:dlq`, [{
|
|
585
|
+
payload: {
|
|
586
|
+
...payload,
|
|
587
|
+
error: String(err?.message || err),
|
|
588
|
+
createdAt,
|
|
589
|
+
job,
|
|
590
|
+
id,
|
|
591
|
+
attempts
|
|
592
|
+
}
|
|
593
|
+
}]);
|
|
594
|
+
await this.clearAttempts(id);
|
|
595
|
+
return { id };
|
|
596
|
+
}
|
|
531
597
|
}
|
|
532
598
|
}
|
|
533
599
|
return {};
|
|
@@ -567,12 +633,29 @@ var PowerQueues = class extends PowerRedis {
|
|
|
567
633
|
const heartbeat = this.heartbeat(keys) || (() => {
|
|
568
634
|
});
|
|
569
635
|
try {
|
|
570
|
-
await this.onExecute(id, payload, createdAt, job, key);
|
|
636
|
+
await this.onExecute(id, payload, createdAt, job, key, await this.getAttempts(id));
|
|
571
637
|
await this.idempotencyDone(keys);
|
|
572
638
|
await this.success(id, payload, createdAt, job, key);
|
|
573
639
|
return { id };
|
|
574
640
|
} catch (err) {
|
|
641
|
+
const attempts = await this.incrAttempts(id);
|
|
575
642
|
try {
|
|
643
|
+
await this.attempt(err, id, payload, createdAt, job, key, attempts);
|
|
644
|
+
await this.error(err, id, payload, createdAt, job, key);
|
|
645
|
+
if (attempts >= this.workerMaxRetries) {
|
|
646
|
+
await this.addTasks(`${this.stream}:dlq`, [{
|
|
647
|
+
payload: {
|
|
648
|
+
...payload,
|
|
649
|
+
error: String(err?.message || err),
|
|
650
|
+
createdAt,
|
|
651
|
+
job,
|
|
652
|
+
id
|
|
653
|
+
}
|
|
654
|
+
}]);
|
|
655
|
+
await this.clearAttempts(id);
|
|
656
|
+
await this.idempotencyFree(keys);
|
|
657
|
+
return { id };
|
|
658
|
+
}
|
|
576
659
|
await this.idempotencyFree(keys);
|
|
577
660
|
} catch (err2) {
|
|
578
661
|
}
|
|
@@ -734,11 +817,10 @@ var PowerQueues = class extends PowerRedis {
|
|
|
734
817
|
const id = Buffer.isBuffer(e?.[0]) ? e[0].toString() : e?.[0];
|
|
735
818
|
const kvRaw = e?.[1] ?? [];
|
|
736
819
|
const kv = Array.isArray(kvRaw) ? kvRaw.map((x) => Buffer.isBuffer(x) ? x.toString() : x) : [];
|
|
737
|
-
return [id, kv
|
|
820
|
+
return [id, kv];
|
|
738
821
|
}).filter(([id, kv]) => typeof id === "string" && id.length > 0 && Array.isArray(kv) && (kv.length & 1) === 0).map(([id, kv]) => {
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
return [id, data, createdAt, job, idemKey];
|
|
822
|
+
const { idemKey = "", job, createdAt, payload } = this.values(kv);
|
|
823
|
+
return [id, this.payload(payload), createdAt, job, idemKey];
|
|
742
824
|
});
|
|
743
825
|
}
|
|
744
826
|
values(value) {
|
package/package.json
CHANGED