pg-boss 10.3.3 → 10.4.1
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/.claude/settings.local.json +25 -0
- package/dist/attorney.d.ts +19 -0
- package/dist/attorney.d.ts.map +1 -0
- package/dist/attorney.js +227 -0
- package/dist/bam.d.ts +14 -0
- package/dist/bam.d.ts.map +1 -0
- package/dist/bam.js +114 -0
- package/dist/boss.d.ts +16 -0
- package/dist/boss.d.ts.map +1 -0
- package/dist/boss.js +163 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +333 -0
- package/dist/contractor.d.ts +22 -0
- package/dist/contractor.d.ts.map +1 -0
- package/dist/contractor.js +81 -0
- package/dist/db.d.ts +16 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +46 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +266 -0
- package/dist/manager.d.ts +93 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +746 -0
- package/dist/migrationStore.d.ts +7 -0
- package/dist/migrationStore.d.ts.map +1 -0
- package/dist/migrationStore.js +425 -0
- package/dist/plans.d.ts +102 -0
- package/dist/plans.d.ts.map +1 -0
- package/dist/plans.js +1220 -0
- package/dist/spy.d.ts +23 -0
- package/dist/spy.d.ts.map +1 -0
- package/dist/spy.js +73 -0
- package/dist/timekeeper.d.ts +34 -0
- package/dist/timekeeper.d.ts.map +1 -0
- package/dist/timekeeper.js +158 -0
- package/dist/tools.d.ts +18 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +45 -0
- package/dist/types.d.ts +301 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/worker.d.ts +43 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +113 -0
- package/package.json +1 -1
- package/src/plans.js +22 -7
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type * as types from './types.ts';
|
|
2
|
+
interface WorkerOptions<T> {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
options: types.WorkOptions;
|
|
6
|
+
interval: number;
|
|
7
|
+
fetch: () => Promise<types.Job<T>[]>;
|
|
8
|
+
onFetch: (jobs: types.Job<T>[]) => Promise<void>;
|
|
9
|
+
onError: (err: any) => void;
|
|
10
|
+
}
|
|
11
|
+
declare class Worker<T = unknown> {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly options: types.WorkOptions;
|
|
15
|
+
readonly fetch: () => Promise<types.Job<T>[]>;
|
|
16
|
+
readonly onFetch: (jobs: types.Job<T>[]) => Promise<void>;
|
|
17
|
+
readonly onError: (err: any) => void;
|
|
18
|
+
readonly interval: number;
|
|
19
|
+
jobs: types.Job<T>[];
|
|
20
|
+
createdOn: number;
|
|
21
|
+
state: types.WorkerState;
|
|
22
|
+
lastFetchedOn: number | null;
|
|
23
|
+
lastJobStartedOn: number | null;
|
|
24
|
+
lastJobEndedOn: number | null;
|
|
25
|
+
lastJobDuration: number | null;
|
|
26
|
+
lastError: any;
|
|
27
|
+
lastErrorOn: number | null;
|
|
28
|
+
stopping: boolean;
|
|
29
|
+
stopped: boolean;
|
|
30
|
+
abortController: AbortController | null;
|
|
31
|
+
private loopDelayPromise;
|
|
32
|
+
private beenNotified;
|
|
33
|
+
private runPromise;
|
|
34
|
+
constructor({ id, name, options, interval, fetch, onFetch, onError }: WorkerOptions<T>);
|
|
35
|
+
start(): void;
|
|
36
|
+
private run;
|
|
37
|
+
notify(): void;
|
|
38
|
+
stop(): Promise<void>;
|
|
39
|
+
abort(): void;
|
|
40
|
+
toWipData(): types.WipData;
|
|
41
|
+
}
|
|
42
|
+
export default Worker;
|
|
43
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,KAAK,MAAM,YAAY,CAAA;AASxC,UAAU,aAAa,CAAC,CAAC;IACvB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,KAAK,CAAC,WAAW,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IACpC,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAChD,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;CAC5B;AAED,cAAM,MAAM,CAAC,CAAC,GAAG,OAAO;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,WAAW,CAAA;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC7C,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACzD,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IAEzB,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAK;IACzB,SAAS,SAAa;IACtB,KAAK,EAAE,KAAK,CAAC,WAAW,CAAwB;IAChD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAO;IACnC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAO;IACtC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAO;IACpC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAO;IACrC,SAAS,EAAE,GAAG,CAAO;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAO;IACjC,QAAQ,UAAQ;IAChB,OAAO,UAAQ;IACf,eAAe,EAAE,eAAe,GAAG,IAAI,CAAO;IAC9C,OAAO,CAAC,gBAAgB,CAAsC;IAC9D,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,UAAU,CAA6B;gBAElC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC;IAUvF,KAAK;YAIS,GAAG;IAgDjB,MAAM;IAQA,IAAI,IAAK,OAAO,CAAC,IAAI,CAAC;IAW5B,KAAK,IAAK,IAAI;IAMd,SAAS,IAAK,KAAK,CAAC,OAAO;CAgB5B;AAED,eAAe,MAAM,CAAA"}
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { delay } from "./tools.js";
|
|
2
|
+
const WORKER_STATES = {
|
|
3
|
+
created: 'created',
|
|
4
|
+
active: 'active',
|
|
5
|
+
stopping: 'stopping',
|
|
6
|
+
stopped: 'stopped'
|
|
7
|
+
};
|
|
8
|
+
class Worker {
|
|
9
|
+
id;
|
|
10
|
+
name;
|
|
11
|
+
options;
|
|
12
|
+
fetch;
|
|
13
|
+
onFetch;
|
|
14
|
+
onError;
|
|
15
|
+
interval;
|
|
16
|
+
jobs = [];
|
|
17
|
+
createdOn = Date.now();
|
|
18
|
+
state = WORKER_STATES.created;
|
|
19
|
+
lastFetchedOn = null;
|
|
20
|
+
lastJobStartedOn = null;
|
|
21
|
+
lastJobEndedOn = null;
|
|
22
|
+
lastJobDuration = null;
|
|
23
|
+
lastError = null;
|
|
24
|
+
lastErrorOn = null;
|
|
25
|
+
stopping = false;
|
|
26
|
+
stopped = false;
|
|
27
|
+
abortController = null;
|
|
28
|
+
loopDelayPromise = null;
|
|
29
|
+
beenNotified = false;
|
|
30
|
+
runPromise = null;
|
|
31
|
+
constructor({ id, name, options, interval, fetch, onFetch, onError }) {
|
|
32
|
+
this.id = id;
|
|
33
|
+
this.name = name;
|
|
34
|
+
this.options = options;
|
|
35
|
+
this.fetch = fetch;
|
|
36
|
+
this.onFetch = onFetch;
|
|
37
|
+
this.onError = onError;
|
|
38
|
+
this.interval = interval;
|
|
39
|
+
}
|
|
40
|
+
start() {
|
|
41
|
+
this.runPromise = this.run();
|
|
42
|
+
}
|
|
43
|
+
async run() {
|
|
44
|
+
this.state = WORKER_STATES.active;
|
|
45
|
+
while (!this.stopping) {
|
|
46
|
+
const started = Date.now();
|
|
47
|
+
try {
|
|
48
|
+
this.beenNotified = false;
|
|
49
|
+
const jobs = await this.fetch();
|
|
50
|
+
this.lastFetchedOn = Date.now();
|
|
51
|
+
if (jobs) {
|
|
52
|
+
this.jobs = jobs;
|
|
53
|
+
this.lastJobStartedOn = this.lastFetchedOn;
|
|
54
|
+
await this.onFetch(jobs);
|
|
55
|
+
this.lastJobEndedOn = Date.now();
|
|
56
|
+
this.jobs = [];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
this.lastErrorOn = Date.now();
|
|
61
|
+
this.lastError = err;
|
|
62
|
+
err.message = `${err.message} (Queue: ${this.name}, Worker: ${this.id})`;
|
|
63
|
+
this.onError(err);
|
|
64
|
+
}
|
|
65
|
+
const duration = Date.now() - started;
|
|
66
|
+
this.lastJobDuration = duration;
|
|
67
|
+
if (!this.stopping && !this.beenNotified && (this.interval - duration) > 100) {
|
|
68
|
+
this.loopDelayPromise = delay(this.interval - duration);
|
|
69
|
+
await this.loopDelayPromise;
|
|
70
|
+
this.loopDelayPromise = null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
this.stopping = false;
|
|
74
|
+
this.stopped = true;
|
|
75
|
+
this.state = WORKER_STATES.stopped;
|
|
76
|
+
}
|
|
77
|
+
notify() {
|
|
78
|
+
this.beenNotified = true;
|
|
79
|
+
if (this.loopDelayPromise) {
|
|
80
|
+
this.loopDelayPromise.abort();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async stop() {
|
|
84
|
+
this.stopping = true;
|
|
85
|
+
this.state = WORKER_STATES.stopping;
|
|
86
|
+
if (this.loopDelayPromise) {
|
|
87
|
+
this.loopDelayPromise.abort();
|
|
88
|
+
}
|
|
89
|
+
await this.runPromise;
|
|
90
|
+
}
|
|
91
|
+
abort() {
|
|
92
|
+
if (this.abortController && !this.abortController.signal.aborted) {
|
|
93
|
+
this.abortController.abort();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
toWipData() {
|
|
97
|
+
return {
|
|
98
|
+
id: this.id,
|
|
99
|
+
name: this.name,
|
|
100
|
+
options: this.options,
|
|
101
|
+
state: this.state,
|
|
102
|
+
count: this.jobs.length,
|
|
103
|
+
createdOn: this.createdOn,
|
|
104
|
+
lastFetchedOn: this.lastFetchedOn,
|
|
105
|
+
lastJobStartedOn: this.lastJobStartedOn,
|
|
106
|
+
lastJobEndedOn: this.lastJobEndedOn,
|
|
107
|
+
lastError: this.lastError,
|
|
108
|
+
lastErrorOn: this.lastErrorOn,
|
|
109
|
+
lastJobDuration: this.lastJobDuration
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export default Worker;
|
package/package.json
CHANGED
package/src/plans.js
CHANGED
|
@@ -506,7 +506,7 @@ function insertVersion (schema, version) {
|
|
|
506
506
|
function fetchNextJob (schema) {
|
|
507
507
|
return ({ includeMetadata, priority = true, ignoreStartAfter = false } = {}) => `
|
|
508
508
|
WITH next as (
|
|
509
|
-
SELECT id
|
|
509
|
+
SELECT id, singleton_key, policy
|
|
510
510
|
FROM ${schema}.job
|
|
511
511
|
WHERE name = $1
|
|
512
512
|
AND state < '${JOB_STATES.active}'
|
|
@@ -514,13 +514,28 @@ function fetchNextJob (schema) {
|
|
|
514
514
|
ORDER BY ${priority ? 'priority desc, ' : ''}created_on, id
|
|
515
515
|
LIMIT $2
|
|
516
516
|
FOR UPDATE SKIP LOCKED
|
|
517
|
+
), grouped as (
|
|
518
|
+
SELECT
|
|
519
|
+
n.id,
|
|
520
|
+
n.policy as grouped_policy,
|
|
521
|
+
row_number() OVER (
|
|
522
|
+
PARTITION BY CASE
|
|
523
|
+
WHEN $2 > 1 AND n.policy in ('${QUEUE_POLICIES.singleton}', '${QUEUE_POLICIES.stately}') THEN n.singleton_key -- gate singleton/stately by key per batch
|
|
524
|
+
ELSE n.id::text
|
|
525
|
+
END
|
|
526
|
+
) as row_number
|
|
527
|
+
FROM next n
|
|
517
528
|
)
|
|
518
529
|
UPDATE ${schema}.job j SET
|
|
519
530
|
state = '${JOB_STATES.active}',
|
|
520
531
|
started_on = now(),
|
|
521
532
|
retry_count = CASE WHEN started_on IS NOT NULL THEN retry_count + 1 ELSE retry_count END
|
|
522
|
-
FROM
|
|
523
|
-
WHERE name = $1 AND j.id =
|
|
533
|
+
FROM grouped
|
|
534
|
+
WHERE name = $1 AND j.id = grouped.id
|
|
535
|
+
AND (
|
|
536
|
+
(grouped.grouped_policy IN ('${QUEUE_POLICIES.singleton}', '${QUEUE_POLICIES.stately}') AND grouped.row_number = 1) -- only one per singleton key
|
|
537
|
+
OR COALESCE(grouped.grouped_policy, '') NOT IN ('${QUEUE_POLICIES.singleton}', '${QUEUE_POLICIES.stately}') -- other policies unaffected
|
|
538
|
+
)
|
|
524
539
|
RETURNING j.${includeMetadata ? allJobColumns : baseJobColumns}
|
|
525
540
|
`
|
|
526
541
|
}
|
|
@@ -777,9 +792,9 @@ function insertJob (schema) {
|
|
|
777
792
|
singleton_on,
|
|
778
793
|
COALESCE(j.dead_letter, q.dead_letter) as dead_letter,
|
|
779
794
|
CASE
|
|
780
|
-
WHEN expire_in IS NOT NULL THEN
|
|
795
|
+
WHEN expire_in IS NOT NULL THEN expire_in::numeric * interval '1s'
|
|
781
796
|
WHEN q.expire_seconds IS NOT NULL THEN q.expire_seconds * interval '1s'
|
|
782
|
-
WHEN expire_in_default IS NOT NULL THEN
|
|
797
|
+
WHEN expire_in_default IS NOT NULL THEN expire_in_default::numeric * interval '1s'
|
|
783
798
|
ELSE interval '15 minutes'
|
|
784
799
|
END as expire_in,
|
|
785
800
|
CASE
|
|
@@ -865,9 +880,9 @@ function insertJobs (schema) {
|
|
|
865
880
|
END as singleton_on,
|
|
866
881
|
COALESCE("deadLetter", q.dead_letter) as dead_letter,
|
|
867
882
|
CASE
|
|
868
|
-
WHEN "expireInSeconds" IS NOT NULL THEN "expireInSeconds" *
|
|
883
|
+
WHEN "expireInSeconds" IS NOT NULL THEN "expireInSeconds"::numeric * interval '1s'
|
|
869
884
|
WHEN q.expire_seconds IS NOT NULL THEN q.expire_seconds * interval '1s'
|
|
870
|
-
WHEN defaults.expire_in IS NOT NULL THEN
|
|
885
|
+
WHEN defaults.expire_in IS NOT NULL THEN defaults.expire_in::numeric * interval '1s'
|
|
871
886
|
ELSE interval '15 minutes'
|
|
872
887
|
END as expire_in,
|
|
873
888
|
CASE
|