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