@pg-boss/dashboard 1.3.0 → 1.3.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/build/server/index.js +383 -95
- package/package.json +2 -2
package/build/server/index.js
CHANGED
|
@@ -1568,7 +1568,8 @@ function createTableVersion(schema) {
|
|
|
1568
1568
|
CREATE TABLE ${schema}.version (
|
|
1569
1569
|
version int primary key,
|
|
1570
1570
|
cron_on timestamp with time zone,
|
|
1571
|
-
bam_on timestamp with time zone
|
|
1571
|
+
bam_on timestamp with time zone,
|
|
1572
|
+
flow_on timestamp with time zone
|
|
1572
1573
|
)
|
|
1573
1574
|
`;
|
|
1574
1575
|
}
|
|
@@ -1843,6 +1844,7 @@ function createTableJobCommon(schema) {
|
|
|
1843
1844
|
SELECT ${schema}.job_table_run($cmd$${createIndexJobThrottle(schema)}$cmd$, '${COMMON_JOB_TABLE}');
|
|
1844
1845
|
SELECT ${schema}.job_table_run($cmd$${createIndexJobFetch(schema)}$cmd$, '${COMMON_JOB_TABLE}');
|
|
1845
1846
|
SELECT ${schema}.job_table_run($cmd$${createIndexJobGroupConcurrency(schema)}$cmd$, '${COMMON_JOB_TABLE}');
|
|
1847
|
+
SELECT ${schema}.job_table_run($cmd$${createIndexJobBlocking(schema)}$cmd$, '${COMMON_JOB_TABLE}');
|
|
1846
1848
|
|
|
1847
1849
|
ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.${COMMON_JOB_TABLE} DEFAULT;
|
|
1848
1850
|
`;
|
|
@@ -1860,6 +1862,7 @@ function createTableJobIndexes(schema, noDeferrableConstraints = false, noCoveri
|
|
|
1860
1862
|
${createIndexJobThrottle(schema)};
|
|
1861
1863
|
${createIndexJobFetch(schema, noCoveringIndex)};
|
|
1862
1864
|
${createIndexJobGroupConcurrency(schema)};
|
|
1865
|
+
${createIndexJobBlocking(schema)};
|
|
1863
1866
|
`;
|
|
1864
1867
|
}
|
|
1865
1868
|
function createQueueFunction(schema, noPartitioning = false) {
|
|
@@ -1970,6 +1973,7 @@ function createQueueFunction(schema, noPartitioning = false) {
|
|
|
1970
1973
|
EXECUTE ${schema}.job_table_format($cmd$${createIndexJobFetch(schema)}$cmd$, tablename);
|
|
1971
1974
|
EXECUTE ${schema}.job_table_format($cmd$${createIndexJobThrottle(schema)}$cmd$, tablename);
|
|
1972
1975
|
EXECUTE ${schema}.job_table_format($cmd$${createIndexJobGroupConcurrency(schema)}$cmd$, tablename);
|
|
1976
|
+
EXECUTE ${schema}.job_table_format($cmd$${createIndexJobBlocking(schema)}$cmd$, tablename);
|
|
1973
1977
|
|
|
1974
1978
|
IF options->>'policy' = 'short' THEN
|
|
1975
1979
|
EXECUTE ${schema}.job_table_format($cmd$${createIndexJobPolicyShort(schema)}$cmd$, tablename);
|
|
@@ -2053,7 +2057,7 @@ function createIndexJobThrottle(schema) {
|
|
|
2053
2057
|
return `CREATE UNIQUE INDEX job_i4 ON ${schema}.job (name, singleton_on, COALESCE(singleton_key, '')) WHERE state <> '${JOB_STATES.cancelled}' AND singleton_on IS NOT NULL`;
|
|
2054
2058
|
}
|
|
2055
2059
|
function createIndexJobFetch(schema, noCoveringIndex = false) {
|
|
2056
|
-
return `CREATE INDEX job_i5 ON ${schema}.job (name, start_after)
|
|
2060
|
+
return `CREATE INDEX job_i5 ON ${schema}.job (name, start_after) WHERE state < '${JOB_STATES.active}' AND NOT blocked`;
|
|
2057
2061
|
}
|
|
2058
2062
|
function createIndexJobPolicyExclusive(schema) {
|
|
2059
2063
|
return `CREATE UNIQUE INDEX job_i6 ON ${schema}.job (name, COALESCE(singleton_key, '')) WHERE state <= '${JOB_STATES.active}' AND policy = '${QUEUE_POLICIES.exclusive}'`;
|
|
@@ -2067,6 +2071,9 @@ function createCheckConstraintKeyStrictFifo(schema) {
|
|
|
2067
2071
|
function createIndexJobGroupConcurrency(schema) {
|
|
2068
2072
|
return `CREATE INDEX job_i7 ON ${schema}.job (name, group_id) WHERE state = '${JOB_STATES.active}' AND group_id IS NOT NULL`;
|
|
2069
2073
|
}
|
|
2074
|
+
function createIndexJobBlocking(schema) {
|
|
2075
|
+
return `CREATE INDEX job_i9 ON ${schema}.job (name, id) WHERE blocking AND state = '${JOB_STATES.completed}'`;
|
|
2076
|
+
}
|
|
2070
2077
|
function trySetQueueMonitorTime(schema, queues, seconds) {
|
|
2071
2078
|
return trySetQueueTimestamp(schema, queues, "monitor_on", seconds);
|
|
2072
2079
|
}
|
|
@@ -2079,6 +2086,9 @@ function trySetCronTime(schema, seconds) {
|
|
|
2079
2086
|
function trySetBamTime(schema, seconds) {
|
|
2080
2087
|
return trySetTimestamp(schema, "bam_on", seconds);
|
|
2081
2088
|
}
|
|
2089
|
+
function trySetFlowTime(schema, seconds) {
|
|
2090
|
+
return trySetTimestamp(schema, "flow_on", seconds);
|
|
2091
|
+
}
|
|
2082
2092
|
function trySetTimestamp(schema, column, seconds) {
|
|
2083
2093
|
return `
|
|
2084
2094
|
UPDATE ${schema}.version
|
|
@@ -2163,7 +2173,7 @@ function deleteJobsById(schema, table) {
|
|
|
2163
2173
|
WITH results as (
|
|
2164
2174
|
DELETE FROM ${schema}.${table}
|
|
2165
2175
|
WHERE name = $1
|
|
2166
|
-
AND id
|
|
2176
|
+
AND id = ANY($2::uuid[])
|
|
2167
2177
|
RETURNING 1
|
|
2168
2178
|
)
|
|
2169
2179
|
SELECT COUNT(*) from results
|
|
@@ -2426,7 +2436,7 @@ function completeJobsUpdate(schema, table, includeQueued) {
|
|
|
2426
2436
|
blocked = ${includeQueued ? "false" : "blocked"},
|
|
2427
2437
|
pending_dependencies = ${includeQueued ? "0" : "pending_dependencies"}
|
|
2428
2438
|
WHERE name = $1
|
|
2429
|
-
AND id
|
|
2439
|
+
AND id = ANY($2::uuid[])
|
|
2430
2440
|
AND ${includeQueued ? `state < '${JOB_STATES.completed}'` : `state = '${JOB_STATES.active}'`}`;
|
|
2431
2441
|
}
|
|
2432
2442
|
function lockedChildrenCte(schema) {
|
|
@@ -2450,24 +2460,11 @@ function unblockChildrenUpdate(schema) {
|
|
|
2450
2460
|
}
|
|
2451
2461
|
function completeJobs(schema, table, includeQueued) {
|
|
2452
2462
|
return `
|
|
2453
|
-
WITH
|
|
2463
|
+
WITH results AS (
|
|
2454
2464
|
${completeJobsUpdate(schema, table, includeQueued)}
|
|
2455
|
-
RETURNING name, id, blocking
|
|
2456
|
-
),
|
|
2457
|
-
decremented AS (
|
|
2458
|
-
SELECT d.child_name, d.child_id, COUNT(*)::int AS n
|
|
2459
|
-
FROM ${schema}.job_dependency d
|
|
2460
|
-
JOIN completed c ON c.blocking
|
|
2461
|
-
AND d.parent_name = c.name
|
|
2462
|
-
AND d.parent_id = c.id
|
|
2463
|
-
GROUP BY d.child_name, d.child_id
|
|
2464
|
-
),
|
|
2465
|
-
${lockedChildrenCte(schema)},
|
|
2466
|
-
unblocked AS (
|
|
2467
|
-
${unblockChildrenUpdate(schema)}
|
|
2468
2465
|
RETURNING 1
|
|
2469
2466
|
)
|
|
2470
|
-
SELECT COUNT(*) FROM
|
|
2467
|
+
SELECT COUNT(*) FROM results
|
|
2471
2468
|
`;
|
|
2472
2469
|
}
|
|
2473
2470
|
function completeJobsWithOutputs(schema, table) {
|
|
@@ -2475,7 +2472,7 @@ function completeJobsWithOutputs(schema, table) {
|
|
|
2475
2472
|
WITH input AS (
|
|
2476
2473
|
SELECT * FROM json_to_recordset($2::json) AS x (id uuid, output jsonb)
|
|
2477
2474
|
),
|
|
2478
|
-
|
|
2475
|
+
results AS (
|
|
2479
2476
|
UPDATE ${schema}.${table} j
|
|
2480
2477
|
SET completed_on = now(),
|
|
2481
2478
|
state = '${JOB_STATES.completed}',
|
|
@@ -2484,22 +2481,9 @@ function completeJobsWithOutputs(schema, table) {
|
|
|
2484
2481
|
WHERE j.name = $1
|
|
2485
2482
|
AND j.id = i.id
|
|
2486
2483
|
AND j.state = '${JOB_STATES.active}'
|
|
2487
|
-
RETURNING j.name, j.id, j.blocking
|
|
2488
|
-
),
|
|
2489
|
-
decremented AS (
|
|
2490
|
-
SELECT d.child_name, d.child_id, COUNT(*)::int AS n
|
|
2491
|
-
FROM ${schema}.job_dependency d
|
|
2492
|
-
JOIN completed c ON c.blocking
|
|
2493
|
-
AND d.parent_name = c.name
|
|
2494
|
-
AND d.parent_id = c.id
|
|
2495
|
-
GROUP BY d.child_name, d.child_id
|
|
2496
|
-
),
|
|
2497
|
-
${lockedChildrenCte(schema)},
|
|
2498
|
-
unblocked AS (
|
|
2499
|
-
${unblockChildrenUpdate(schema)}
|
|
2500
2484
|
RETURNING 1
|
|
2501
2485
|
)
|
|
2502
|
-
SELECT COUNT(*) FROM
|
|
2486
|
+
SELECT COUNT(*) FROM results
|
|
2503
2487
|
`;
|
|
2504
2488
|
}
|
|
2505
2489
|
function completeJobsWithOutputsDistributed(schema, table) {
|
|
@@ -2515,7 +2499,7 @@ function completeJobsWithOutputsDistributed(schema, table) {
|
|
|
2515
2499
|
WHERE j.name = $1
|
|
2516
2500
|
AND j.id = i.id
|
|
2517
2501
|
AND j.state = '${JOB_STATES.active}'
|
|
2518
|
-
RETURNING j.id
|
|
2502
|
+
RETURNING j.id
|
|
2519
2503
|
`;
|
|
2520
2504
|
}
|
|
2521
2505
|
function cancelJobs(schema, table) {
|
|
@@ -2525,7 +2509,7 @@ function cancelJobs(schema, table) {
|
|
|
2525
2509
|
SET completed_on = now(),
|
|
2526
2510
|
state = '${JOB_STATES.cancelled}'
|
|
2527
2511
|
WHERE name = $1
|
|
2528
|
-
AND id
|
|
2512
|
+
AND id = ANY($2::uuid[])
|
|
2529
2513
|
AND state < '${JOB_STATES.completed}'
|
|
2530
2514
|
RETURNING 1
|
|
2531
2515
|
)
|
|
@@ -2539,7 +2523,7 @@ function resumeJobs(schema, table) {
|
|
|
2539
2523
|
SET completed_on = NULL,
|
|
2540
2524
|
state = '${JOB_STATES.created}'
|
|
2541
2525
|
WHERE name = $1
|
|
2542
|
-
AND id
|
|
2526
|
+
AND id = ANY($2::uuid[])
|
|
2543
2527
|
AND state = '${JOB_STATES.cancelled}'
|
|
2544
2528
|
RETURNING 1
|
|
2545
2529
|
)
|
|
@@ -2553,7 +2537,7 @@ function restoreJobs(schema, table) {
|
|
|
2553
2537
|
started_on = NULL,
|
|
2554
2538
|
heartbeat_on = NULL
|
|
2555
2539
|
WHERE name = $1
|
|
2556
|
-
AND id
|
|
2540
|
+
AND id = ANY($2::uuid[])
|
|
2557
2541
|
`;
|
|
2558
2542
|
}
|
|
2559
2543
|
function insertJobs(schema, { table, name, returnId = true, notify = false }) {
|
|
@@ -2668,7 +2652,7 @@ function insertFlowJobs(schema, { table, name }, jobs) {
|
|
|
2668
2652
|
`;
|
|
2669
2653
|
}
|
|
2670
2654
|
function failJobsById(schema, table) {
|
|
2671
|
-
return failJobs(schema, table, `name = $1 AND id
|
|
2655
|
+
return failJobs(schema, table, `name = $1 AND id = ANY($2::uuid[]) AND state < '${JOB_STATES.completed}'`, "$3::jsonb");
|
|
2672
2656
|
}
|
|
2673
2657
|
function failJobsByTimeout(schema, table, queues, noAdvisoryLocks) {
|
|
2674
2658
|
return locked(schema, failJobs(schema, table, `state = '${JOB_STATES.active}'
|
|
@@ -2687,7 +2671,7 @@ function touchJobs(schema, table) {
|
|
|
2687
2671
|
UPDATE ${schema}.${table}
|
|
2688
2672
|
SET heartbeat_on = now()
|
|
2689
2673
|
WHERE name = $1
|
|
2690
|
-
AND id
|
|
2674
|
+
AND id = ANY($2::uuid[])
|
|
2691
2675
|
AND state = '${JOB_STATES.active}'
|
|
2692
2676
|
RETURNING 1
|
|
2693
2677
|
)
|
|
@@ -2891,13 +2875,13 @@ function deadLetterJobsByIdWithOutputs(schema, table) {
|
|
|
2891
2875
|
}
|
|
2892
2876
|
function selectJobsToFailById(schema, table) {
|
|
2893
2877
|
return {
|
|
2894
|
-
text: `SELECT * FROM ${schema}.${table} WHERE name = $1 AND id
|
|
2878
|
+
text: `SELECT * FROM ${schema}.${table} WHERE name = $1 AND id = ANY($2::uuid[]) AND state < '${JOB_STATES.completed}'`,
|
|
2895
2879
|
values: []
|
|
2896
2880
|
};
|
|
2897
2881
|
}
|
|
2898
2882
|
function deleteJobsToFail(schema, table) {
|
|
2899
2883
|
return {
|
|
2900
|
-
text: `DELETE FROM ${schema}.${table} WHERE name = $1 AND id
|
|
2884
|
+
text: `DELETE FROM ${schema}.${table} WHERE name = $1 AND id = ANY($2::uuid[])`,
|
|
2901
2885
|
values: []
|
|
2902
2886
|
};
|
|
2903
2887
|
}
|
|
@@ -2922,14 +2906,14 @@ function selectJobsToFailByHeartbeat(schema, table, queues) {
|
|
|
2922
2906
|
}
|
|
2923
2907
|
function deleteJobsByIds(schema, table) {
|
|
2924
2908
|
return {
|
|
2925
|
-
text: `DELETE FROM ${schema}.${table} WHERE id
|
|
2909
|
+
text: `DELETE FROM ${schema}.${table} WHERE id = ANY($1::uuid[])`,
|
|
2926
2910
|
values: []
|
|
2927
2911
|
};
|
|
2928
2912
|
}
|
|
2929
2913
|
function completeJobsDistributed(schema, table, includeQueued) {
|
|
2930
2914
|
return `
|
|
2931
2915
|
${completeJobsUpdate(schema, table, includeQueued)}
|
|
2932
|
-
RETURNING id
|
|
2916
|
+
RETURNING id
|
|
2933
2917
|
`;
|
|
2934
2918
|
}
|
|
2935
2919
|
function decrementDependents(schema) {
|
|
@@ -2938,13 +2922,75 @@ function decrementDependents(schema) {
|
|
|
2938
2922
|
SELECT d.child_name, d.child_id, COUNT(*)::int AS n
|
|
2939
2923
|
FROM ${schema}.job_dependency d
|
|
2940
2924
|
WHERE d.parent_name = $1
|
|
2941
|
-
AND d.parent_id
|
|
2925
|
+
AND d.parent_id = ANY($2::uuid[])
|
|
2942
2926
|
GROUP BY d.child_name, d.child_id
|
|
2943
2927
|
),
|
|
2944
2928
|
${lockedChildrenCte(schema)}
|
|
2945
2929
|
${unblockChildrenUpdate(schema)}
|
|
2946
2930
|
`;
|
|
2947
2931
|
}
|
|
2932
|
+
var FLOW_BATCH_SIZE = 1e3;
|
|
2933
|
+
function resolveFlowJobs(schema, table, names) {
|
|
2934
|
+
return {
|
|
2935
|
+
text: `
|
|
2936
|
+
WITH locked_parents AS (
|
|
2937
|
+
SELECT j.name, j.id
|
|
2938
|
+
FROM ${schema}.${table} j
|
|
2939
|
+
WHERE j.blocking
|
|
2940
|
+
AND j.state = '${JOB_STATES.completed}'
|
|
2941
|
+
AND j.name = ANY($1::text[])
|
|
2942
|
+
ORDER BY j.name, j.id
|
|
2943
|
+
FOR UPDATE OF j SKIP LOCKED
|
|
2944
|
+
LIMIT ${FLOW_BATCH_SIZE}
|
|
2945
|
+
),
|
|
2946
|
+
decremented AS (
|
|
2947
|
+
SELECT d.child_name, d.child_id, COUNT(*)::int AS n
|
|
2948
|
+
FROM ${schema}.job_dependency d
|
|
2949
|
+
JOIN locked_parents p ON d.parent_name = p.name
|
|
2950
|
+
AND d.parent_id = p.id
|
|
2951
|
+
GROUP BY d.child_name, d.child_id
|
|
2952
|
+
),
|
|
2953
|
+
${lockedChildrenCte(schema)},
|
|
2954
|
+
unblocked AS (
|
|
2955
|
+
${unblockChildrenUpdate(schema)}
|
|
2956
|
+
RETURNING 1
|
|
2957
|
+
),
|
|
2958
|
+
cleared AS (
|
|
2959
|
+
UPDATE ${schema}.${table} j
|
|
2960
|
+
SET blocking = false
|
|
2961
|
+
FROM locked_parents p
|
|
2962
|
+
WHERE j.name = p.name
|
|
2963
|
+
AND j.id = p.id
|
|
2964
|
+
RETURNING 1
|
|
2965
|
+
)
|
|
2966
|
+
SELECT COUNT(*)::int AS resolved FROM cleared
|
|
2967
|
+
`,
|
|
2968
|
+
values: [names]
|
|
2969
|
+
};
|
|
2970
|
+
}
|
|
2971
|
+
function selectBlockingParents(schema, table, names, noSkipLocked) {
|
|
2972
|
+
return {
|
|
2973
|
+
text: `
|
|
2974
|
+
SELECT name, id
|
|
2975
|
+
FROM ${schema}.${table}
|
|
2976
|
+
WHERE blocking
|
|
2977
|
+
AND state = '${JOB_STATES.completed}'
|
|
2978
|
+
AND name = ANY($1::text[])
|
|
2979
|
+
ORDER BY name, id
|
|
2980
|
+
FOR UPDATE${noSkipLocked ? "" : " SKIP LOCKED"}
|
|
2981
|
+
LIMIT ${FLOW_BATCH_SIZE}
|
|
2982
|
+
`,
|
|
2983
|
+
values: [names]
|
|
2984
|
+
};
|
|
2985
|
+
}
|
|
2986
|
+
function clearBlocking(schema) {
|
|
2987
|
+
return `
|
|
2988
|
+
UPDATE ${schema}.job
|
|
2989
|
+
SET blocking = false
|
|
2990
|
+
WHERE name = $1
|
|
2991
|
+
AND id = ANY($2::uuid[])
|
|
2992
|
+
`;
|
|
2993
|
+
}
|
|
2948
2994
|
function insertRetryJob(schema, table) {
|
|
2949
2995
|
return `
|
|
2950
2996
|
INSERT INTO ${schema}.${table} (
|
|
@@ -2986,7 +3032,7 @@ function retryJobs(schema, table) {
|
|
|
2986
3032
|
SET state = '${JOB_STATES.retry}',
|
|
2987
3033
|
retry_limit = retry_limit + 1
|
|
2988
3034
|
WHERE name = $1
|
|
2989
|
-
AND id
|
|
3035
|
+
AND id = ANY($2::uuid[])
|
|
2990
3036
|
AND state = '${JOB_STATES.failed}'
|
|
2991
3037
|
RETURNING 1
|
|
2992
3038
|
)
|
|
@@ -3464,6 +3510,7 @@ function getConfig(value) {
|
|
|
3464
3510
|
applyOpsConfig(config);
|
|
3465
3511
|
applyScheduleConfig(config);
|
|
3466
3512
|
applyBamConfig(config);
|
|
3513
|
+
applyFlowConfig(config);
|
|
3467
3514
|
validateWarningConfig(config);
|
|
3468
3515
|
return config;
|
|
3469
3516
|
}
|
|
@@ -3566,6 +3613,11 @@ function applyBamConfig(config) {
|
|
|
3566
3613
|
assert(!("bamIntervalSeconds" in config) || config.bamIntervalSeconds >= minInterval, `configuration assert: bamIntervalSeconds must be at least ${minInterval} seconds`);
|
|
3567
3614
|
config.bamIntervalSeconds = config.bamIntervalSeconds || 60;
|
|
3568
3615
|
}
|
|
3616
|
+
function applyFlowConfig(config) {
|
|
3617
|
+
const minInterval = config.__test__bypass_flow_interval_check ? .5 : 1;
|
|
3618
|
+
assert(!("flowIntervalSeconds" in config) || config.flowIntervalSeconds >= minInterval, `configuration assert: flowIntervalSeconds must be at least ${minInterval} seconds`);
|
|
3619
|
+
config.flowIntervalSeconds = config.flowIntervalSeconds || 5;
|
|
3620
|
+
}
|
|
3569
3621
|
//#endregion
|
|
3570
3622
|
//#region ../../src/migrationStore.ts
|
|
3571
3623
|
function formatJobTable(command, table) {
|
|
@@ -4113,6 +4165,92 @@ var createQueueFn = {
|
|
|
4113
4165
|
END;
|
|
4114
4166
|
$$
|
|
4115
4167
|
LANGUAGE plpgsql;
|
|
4168
|
+
`,
|
|
4169
|
+
33: (schema) => `
|
|
4170
|
+
CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
4171
|
+
RETURNS VOID AS
|
|
4172
|
+
$$
|
|
4173
|
+
DECLARE
|
|
4174
|
+
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
4175
|
+
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
4176
|
+
ELSE 'job_common'
|
|
4177
|
+
END;
|
|
4178
|
+
queue_created_on timestamptz;
|
|
4179
|
+
BEGIN
|
|
4180
|
+
|
|
4181
|
+
WITH q as (
|
|
4182
|
+
INSERT INTO ${schema}.queue (
|
|
4183
|
+
name,
|
|
4184
|
+
policy,
|
|
4185
|
+
retry_limit,
|
|
4186
|
+
retry_delay,
|
|
4187
|
+
retry_backoff,
|
|
4188
|
+
retry_delay_max,
|
|
4189
|
+
expire_seconds,
|
|
4190
|
+
retention_seconds,
|
|
4191
|
+
deletion_seconds,
|
|
4192
|
+
warning_queued,
|
|
4193
|
+
dead_letter,
|
|
4194
|
+
partition,
|
|
4195
|
+
table_name,
|
|
4196
|
+
heartbeat_seconds,
|
|
4197
|
+
notify
|
|
4198
|
+
)
|
|
4199
|
+
VALUES (
|
|
4200
|
+
queue_name,
|
|
4201
|
+
options->>'policy',
|
|
4202
|
+
COALESCE((options->>'retryLimit')::int, 2),
|
|
4203
|
+
COALESCE((options->>'retryDelay')::int, 0),
|
|
4204
|
+
COALESCE((options->>'retryBackoff')::bool, false),
|
|
4205
|
+
(options->>'retryDelayMax')::int,
|
|
4206
|
+
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
4207
|
+
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
4208
|
+
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
4209
|
+
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
4210
|
+
options->>'deadLetter',
|
|
4211
|
+
COALESCE((options->>'partition')::bool, false),
|
|
4212
|
+
tablename,
|
|
4213
|
+
(options->>'heartbeatSeconds')::int,
|
|
4214
|
+
COALESCE((options->>'notify')::bool, false)
|
|
4215
|
+
)
|
|
4216
|
+
ON CONFLICT DO NOTHING
|
|
4217
|
+
RETURNING created_on
|
|
4218
|
+
)
|
|
4219
|
+
SELECT created_on into queue_created_on from q;
|
|
4220
|
+
|
|
4221
|
+
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
4222
|
+
RETURN;
|
|
4223
|
+
END IF;
|
|
4224
|
+
|
|
4225
|
+
EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
|
|
4226
|
+
|
|
4227
|
+
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
|
|
4228
|
+
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD CONSTRAINT q_fkey FOREIGN KEY (name) REFERENCES ${schema}.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED$cmd$, tablename);
|
|
4229
|
+
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD CONSTRAINT dlq_fkey FOREIGN KEY (dead_letter) REFERENCES ${schema}.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED$cmd$, tablename);
|
|
4230
|
+
|
|
4231
|
+
EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) WHERE state < 'active' AND NOT blocked$cmd$, tablename);
|
|
4232
|
+
EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i4 ON ${schema}.job (name, singleton_on, COALESCE(singleton_key, '')) WHERE state <> 'cancelled' AND singleton_on IS NOT NULL$cmd$, tablename);
|
|
4233
|
+
EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i7 ON ${schema}.job (name, group_id) WHERE state = 'active' AND group_id IS NOT NULL$cmd$, tablename);
|
|
4234
|
+
EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i9 ON ${schema}.job (name, id) WHERE blocking AND state = 'completed'$cmd$, tablename);
|
|
4235
|
+
|
|
4236
|
+
IF options->>'policy' = 'short' THEN
|
|
4237
|
+
EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i1 ON ${schema}.job (name, COALESCE(singleton_key, '')) WHERE state = 'created' AND policy = 'short'$cmd$, tablename);
|
|
4238
|
+
ELSIF options->>'policy' = 'singleton' THEN
|
|
4239
|
+
EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i2 ON ${schema}.job (name, COALESCE(singleton_key, '')) WHERE state = 'active' AND policy = 'singleton'$cmd$, tablename);
|
|
4240
|
+
ELSIF options->>'policy' = 'stately' THEN
|
|
4241
|
+
EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i3 ON ${schema}.job (name, state, COALESCE(singleton_key, '')) WHERE state <= 'active' AND policy = 'stately'$cmd$, tablename);
|
|
4242
|
+
ELSIF options->>'policy' = 'exclusive' THEN
|
|
4243
|
+
EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i6 ON ${schema}.job (name, COALESCE(singleton_key, '')) WHERE state <= 'active' AND policy = 'exclusive'$cmd$, tablename);
|
|
4244
|
+
ELSIF options->>'policy' = 'key_strict_fifo' THEN
|
|
4245
|
+
EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i8 ON ${schema}.job (name, singleton_key) WHERE state IN ('active', 'retry', 'failed') AND policy = 'key_strict_fifo'$cmd$, tablename);
|
|
4246
|
+
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD CONSTRAINT job_key_strict_fifo_singleton_key_check CHECK (NOT (policy = 'key_strict_fifo' AND singleton_key IS NULL))$cmd$, tablename);
|
|
4247
|
+
END IF;
|
|
4248
|
+
|
|
4249
|
+
EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
4250
|
+
EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
4251
|
+
END;
|
|
4252
|
+
$$
|
|
4253
|
+
LANGUAGE plpgsql;
|
|
4116
4254
|
`
|
|
4117
4255
|
};
|
|
4118
4256
|
function getAll(schema) {
|
|
@@ -4361,6 +4499,25 @@ function getAll(schema) {
|
|
|
4361
4499
|
createQueueFn[31](schema),
|
|
4362
4500
|
`ALTER TABLE ${schema}.queue DROP COLUMN notify`
|
|
4363
4501
|
]
|
|
4502
|
+
},
|
|
4503
|
+
{
|
|
4504
|
+
release: "12.22.0",
|
|
4505
|
+
version: 33,
|
|
4506
|
+
previous: 32,
|
|
4507
|
+
install: [
|
|
4508
|
+
`ALTER TABLE ${schema}.version ADD COLUMN IF NOT EXISTS flow_on timestamp with time zone`,
|
|
4509
|
+
`SELECT ${schema}.job_table_run($cmd$CREATE INDEX job_i9 ON ${schema}.job (name, id) WHERE blocking AND state = 'completed'$cmd$)`,
|
|
4510
|
+
`SELECT ${schema}.job_table_run($cmd$DROP INDEX IF EXISTS ${schema}.job_i5$cmd$)`,
|
|
4511
|
+
`SELECT ${schema}.job_table_run($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) WHERE state < 'active' AND NOT blocked$cmd$)`,
|
|
4512
|
+
createQueueFn[33](schema)
|
|
4513
|
+
],
|
|
4514
|
+
uninstall: [
|
|
4515
|
+
createQueueFn[32](schema),
|
|
4516
|
+
`SELECT ${schema}.job_table_run($cmd$DROP INDEX IF EXISTS ${schema}.job_i5$cmd$)`,
|
|
4517
|
+
`SELECT ${schema}.job_table_run($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active' AND NOT blocked$cmd$)`,
|
|
4518
|
+
`SELECT ${schema}.job_table_run($cmd$DROP INDEX IF EXISTS ${schema}.job_i9$cmd$)`,
|
|
4519
|
+
`ALTER TABLE ${schema}.version DROP COLUMN flow_on`
|
|
4520
|
+
]
|
|
4364
4521
|
}
|
|
4365
4522
|
];
|
|
4366
4523
|
}
|
|
@@ -4368,7 +4525,7 @@ function getAll(schema) {
|
|
|
4368
4525
|
//#region ../../src/contractor.ts
|
|
4369
4526
|
var schemaVersion = {
|
|
4370
4527
|
name: "pg-boss",
|
|
4371
|
-
version: "12.
|
|
4528
|
+
version: "12.22.0",
|
|
4372
4529
|
description: "Queueing jobs in Postgres from Node.js like a boss",
|
|
4373
4530
|
type: "module",
|
|
4374
4531
|
main: "./dist/index.js",
|
|
@@ -4376,7 +4533,7 @@ var schemaVersion = {
|
|
|
4376
4533
|
bin: { "pg-boss": "./dist/cli.js" },
|
|
4377
4534
|
engines: { "node": ">=22.12.0" },
|
|
4378
4535
|
dependencies: {
|
|
4379
|
-
"cron-parser": "^5.6.
|
|
4536
|
+
"cron-parser": "^5.6.1",
|
|
4380
4537
|
"pg": "^8.22.0",
|
|
4381
4538
|
"serialize-error": "^13.0.1"
|
|
4382
4539
|
},
|
|
@@ -4424,7 +4581,7 @@ var schemaVersion = {
|
|
|
4424
4581
|
"docs": "npm run docs:dev --prefix docs",
|
|
4425
4582
|
"docs:readme": "node ./scripts/sync-readme.js"
|
|
4426
4583
|
},
|
|
4427
|
-
pgboss: { "schema":
|
|
4584
|
+
pgboss: { "schema": 33 },
|
|
4428
4585
|
repository: {
|
|
4429
4586
|
"type": "git",
|
|
4430
4587
|
"url": "git+https://github.com/timgit/pg-boss.git"
|
|
@@ -4938,7 +5095,7 @@ var NUMERIC_QUEUE_FIELDS = [
|
|
|
4938
5095
|
"activeCount",
|
|
4939
5096
|
"totalCount"
|
|
4940
5097
|
];
|
|
4941
|
-
var events$
|
|
5098
|
+
var events$5 = {
|
|
4942
5099
|
error: "error",
|
|
4943
5100
|
wip: "wip"
|
|
4944
5101
|
};
|
|
@@ -4947,7 +5104,7 @@ function rethrowWriteError(err) {
|
|
|
4947
5104
|
throw err;
|
|
4948
5105
|
}
|
|
4949
5106
|
var Manager = class extends EventEmitter {
|
|
4950
|
-
events = events$
|
|
5107
|
+
events = events$5;
|
|
4951
5108
|
db;
|
|
4952
5109
|
config;
|
|
4953
5110
|
wipTs;
|
|
@@ -5110,17 +5267,15 @@ var Manager = class extends EventEmitter {
|
|
|
5110
5267
|
output: this.mapCompletionDataArg(item.output)
|
|
5111
5268
|
}));
|
|
5112
5269
|
const ids = items.map((item) => item.id);
|
|
5113
|
-
if (this.config.noMultiMutationCte)
|
|
5270
|
+
if (this.config.noMultiMutationCte) {
|
|
5114
5271
|
const sql = completeJobsWithOutputsDistributed(this.config.schema, table);
|
|
5115
|
-
const { rows } = await
|
|
5116
|
-
const blockingIds = rows.filter((row) => row.blocking).map((row) => row.id);
|
|
5117
|
-
if (blockingIds.length > 0) await tx.executeSql(decrementDependents(this.config.schema), [name, blockingIds]);
|
|
5272
|
+
const { rows } = await this.db.executeSql(sql, [name, JSON.stringify(payload)]);
|
|
5118
5273
|
return {
|
|
5119
5274
|
jobs: ids,
|
|
5120
5275
|
requested: ids.length,
|
|
5121
5276
|
affected: rows.length
|
|
5122
5277
|
};
|
|
5123
|
-
}
|
|
5278
|
+
}
|
|
5124
5279
|
const sql = completeJobsWithOutputs(this.config.schema, table);
|
|
5125
5280
|
const result = await this.db.executeSql(sql, [name, JSON.stringify(payload)]);
|
|
5126
5281
|
return this.mapCommandResponse(ids, result);
|
|
@@ -5208,7 +5363,7 @@ var Manager = class extends EventEmitter {
|
|
|
5208
5363
|
try {
|
|
5209
5364
|
await this.touch(name, jobIds);
|
|
5210
5365
|
} catch (err) {
|
|
5211
|
-
this.emit(events$
|
|
5366
|
+
this.emit(events$5.error, err);
|
|
5212
5367
|
}
|
|
5213
5368
|
}, intervalMs);
|
|
5214
5369
|
}
|
|
@@ -5245,7 +5400,7 @@ var Manager = class extends EventEmitter {
|
|
|
5245
5400
|
if (now - this.wipTs < 2e3) return;
|
|
5246
5401
|
const wip = this.getWipData();
|
|
5247
5402
|
if (wip.some((w) => w.count > 0)) {
|
|
5248
|
-
this.emit(events$
|
|
5403
|
+
this.emit(events$5.wip, wip);
|
|
5249
5404
|
this.wipTs = now;
|
|
5250
5405
|
}
|
|
5251
5406
|
}, 2e3);
|
|
@@ -5260,7 +5415,7 @@ var Manager = class extends EventEmitter {
|
|
|
5260
5415
|
return acc;
|
|
5261
5416
|
}, {});
|
|
5262
5417
|
} catch (error) {
|
|
5263
|
-
emit && this.emit(events$
|
|
5418
|
+
emit && this.emit(events$5.error, {
|
|
5264
5419
|
...error,
|
|
5265
5420
|
message: error.message,
|
|
5266
5421
|
stack: error.stack
|
|
@@ -5340,7 +5495,7 @@ var Manager = class extends EventEmitter {
|
|
|
5340
5495
|
this.emitWip(name);
|
|
5341
5496
|
};
|
|
5342
5497
|
const onError = (error) => {
|
|
5343
|
-
this.emit(events$
|
|
5498
|
+
this.emit(events$5.error, {
|
|
5344
5499
|
...error,
|
|
5345
5500
|
message: error.message,
|
|
5346
5501
|
stack: error.stack,
|
|
@@ -5379,7 +5534,7 @@ var Manager = class extends EventEmitter {
|
|
|
5379
5534
|
if (!INTERNAL_QUEUES[name]) {
|
|
5380
5535
|
const now = Date.now();
|
|
5381
5536
|
if (now - this.wipTs > 2e3) {
|
|
5382
|
-
this.emit(events$
|
|
5537
|
+
this.emit(events$5.wip, this.getWipData());
|
|
5383
5538
|
this.wipTs = now;
|
|
5384
5539
|
}
|
|
5385
5540
|
}
|
|
@@ -5714,24 +5869,17 @@ var Manager = class extends EventEmitter {
|
|
|
5714
5869
|
return fn(db);
|
|
5715
5870
|
}
|
|
5716
5871
|
async completeDistributed(name, ids, outputData, table, db, includeQueued) {
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
}
|
|
5729
|
-
return {
|
|
5730
|
-
jobs: ids,
|
|
5731
|
-
requested: ids.length,
|
|
5732
|
-
affected: rows.length
|
|
5733
|
-
};
|
|
5734
|
-
});
|
|
5872
|
+
const sql = completeJobsDistributed(this.config.schema, table, includeQueued);
|
|
5873
|
+
const { rows } = await db.executeSql(sql, [
|
|
5874
|
+
name,
|
|
5875
|
+
ids,
|
|
5876
|
+
outputData
|
|
5877
|
+
]);
|
|
5878
|
+
return {
|
|
5879
|
+
jobs: ids,
|
|
5880
|
+
requested: ids.length,
|
|
5881
|
+
affected: rows.length
|
|
5882
|
+
};
|
|
5735
5883
|
}
|
|
5736
5884
|
async fail(name, id, data, options = {}) {
|
|
5737
5885
|
assertQueueName(name);
|
|
@@ -5775,6 +5923,26 @@ var Manager = class extends EventEmitter {
|
|
|
5775
5923
|
const select = selectJobsToFailByHeartbeat(this.config.schema, table, queues);
|
|
5776
5924
|
return this.expireJobsDistributed(table, select, { value: { message: "job heartbeat timeout" } });
|
|
5777
5925
|
}
|
|
5926
|
+
async resolveFlowJobsDistributed(table, names) {
|
|
5927
|
+
const select = selectBlockingParents(this.config.schema, table, names, this.config.noSkipLocked);
|
|
5928
|
+
return this.withDistributedTransaction(this.db, async (tx) => {
|
|
5929
|
+
const { rows } = await tx.executeSql(select.text, select.values);
|
|
5930
|
+
if (rows.length === 0) return 0;
|
|
5931
|
+
const idsByName = /* @__PURE__ */ new Map();
|
|
5932
|
+
for (const row of rows) {
|
|
5933
|
+
const list = idsByName.get(row.name) || [];
|
|
5934
|
+
list.push(row.id);
|
|
5935
|
+
idsByName.set(row.name, list);
|
|
5936
|
+
}
|
|
5937
|
+
const decrementSql = decrementDependents(this.config.schema);
|
|
5938
|
+
const clearSql = clearBlocking(this.config.schema);
|
|
5939
|
+
for (const [name, ids] of idsByName) {
|
|
5940
|
+
await tx.executeSql(decrementSql, [name, ids]);
|
|
5941
|
+
await tx.executeSql(clearSql, [name, ids]);
|
|
5942
|
+
}
|
|
5943
|
+
return rows.length;
|
|
5944
|
+
});
|
|
5945
|
+
}
|
|
5778
5946
|
async expireJobsDistributed(table, select, outputData) {
|
|
5779
5947
|
return this.withDistributedTransaction(this.db, async (tx) => {
|
|
5780
5948
|
const { rows: jobs } = await tx.executeSql(select.text, []);
|
|
@@ -6099,7 +6267,7 @@ var Manager = class extends EventEmitter {
|
|
|
6099
6267
|
};
|
|
6100
6268
|
//#endregion
|
|
6101
6269
|
//#region ../../src/boss.ts
|
|
6102
|
-
var events$
|
|
6270
|
+
var events$4 = {
|
|
6103
6271
|
error: "error",
|
|
6104
6272
|
warning: "warning"
|
|
6105
6273
|
};
|
|
@@ -6125,7 +6293,7 @@ var Boss = class extends EventEmitter {
|
|
|
6125
6293
|
#db;
|
|
6126
6294
|
#config;
|
|
6127
6295
|
#manager;
|
|
6128
|
-
events = events$
|
|
6296
|
+
events = events$4;
|
|
6129
6297
|
constructor(db, manager, config) {
|
|
6130
6298
|
super();
|
|
6131
6299
|
this.#db = db;
|
|
@@ -6160,8 +6328,8 @@ var Boss = class extends EventEmitter {
|
|
|
6160
6328
|
db: this.#db,
|
|
6161
6329
|
schema: this.#config.schema,
|
|
6162
6330
|
persistWarnings: this.#config.persistWarnings,
|
|
6163
|
-
warningEvent: events$
|
|
6164
|
-
errorEvent: events$
|
|
6331
|
+
warningEvent: events$4.warning,
|
|
6332
|
+
errorEvent: events$4.error
|
|
6165
6333
|
};
|
|
6166
6334
|
}
|
|
6167
6335
|
async #executeQuery(query) {
|
|
@@ -6190,7 +6358,7 @@ var Boss = class extends EventEmitter {
|
|
|
6190
6358
|
!this.#stopped && await this.supervise(queues);
|
|
6191
6359
|
!this.#stopped && await this.#maintainWarnings();
|
|
6192
6360
|
} catch (err) {
|
|
6193
|
-
this.emit(events$
|
|
6361
|
+
this.emit(events$4.error, err);
|
|
6194
6362
|
} finally {
|
|
6195
6363
|
this.#maintaining = false;
|
|
6196
6364
|
}
|
|
@@ -6268,7 +6436,7 @@ var Boss = class extends EventEmitter {
|
|
|
6268
6436
|
};
|
|
6269
6437
|
//#endregion
|
|
6270
6438
|
//#region ../../src/bam.ts
|
|
6271
|
-
var events$
|
|
6439
|
+
var events$3 = {
|
|
6272
6440
|
error: "error",
|
|
6273
6441
|
bam: "bam"
|
|
6274
6442
|
};
|
|
@@ -6278,7 +6446,7 @@ var Bam = class extends EventEmitter {
|
|
|
6278
6446
|
#pollInterval;
|
|
6279
6447
|
#db;
|
|
6280
6448
|
#config;
|
|
6281
|
-
events = events$
|
|
6449
|
+
events = events$3;
|
|
6282
6450
|
constructor(db, config) {
|
|
6283
6451
|
super();
|
|
6284
6452
|
this.#db = db;
|
|
@@ -6314,7 +6482,7 @@ var Bam = class extends EventEmitter {
|
|
|
6314
6482
|
const { rows } = await this.#db.executeSql(sql);
|
|
6315
6483
|
if (rows.length === 1) await this.#processCommands();
|
|
6316
6484
|
} catch (err) {
|
|
6317
|
-
this.emit(events$
|
|
6485
|
+
this.emit(events$3.error, err);
|
|
6318
6486
|
} finally {
|
|
6319
6487
|
this.#working = false;
|
|
6320
6488
|
}
|
|
@@ -6323,7 +6491,7 @@ var Bam = class extends EventEmitter {
|
|
|
6323
6491
|
if (this.#stopped) return;
|
|
6324
6492
|
const entry = await this.#getNextCommand();
|
|
6325
6493
|
if (!entry || this.#stopped) return;
|
|
6326
|
-
this.emit(events$
|
|
6494
|
+
this.emit(events$3.bam, {
|
|
6327
6495
|
id: entry.id,
|
|
6328
6496
|
name: entry.name,
|
|
6329
6497
|
status: "in_progress",
|
|
@@ -6334,7 +6502,7 @@ var Bam = class extends EventEmitter {
|
|
|
6334
6502
|
await this.#db.executeSql(entry.command);
|
|
6335
6503
|
if (this.#stopped) return;
|
|
6336
6504
|
await this.#markCompleted(entry.id);
|
|
6337
|
-
this.emit(events$
|
|
6505
|
+
this.emit(events$3.bam, {
|
|
6338
6506
|
id: entry.id,
|
|
6339
6507
|
name: entry.name,
|
|
6340
6508
|
status: "completed",
|
|
@@ -6344,8 +6512,8 @@ var Bam = class extends EventEmitter {
|
|
|
6344
6512
|
} catch (err) {
|
|
6345
6513
|
if (this.#stopped) return;
|
|
6346
6514
|
await this.#markFailed(entry.id, err);
|
|
6347
|
-
this.emit(events$
|
|
6348
|
-
this.emit(events$
|
|
6515
|
+
this.emit(events$3.error, err);
|
|
6516
|
+
this.emit(events$3.bam, {
|
|
6349
6517
|
id: entry.id,
|
|
6350
6518
|
name: entry.name,
|
|
6351
6519
|
status: "failed",
|
|
@@ -6370,6 +6538,111 @@ var Bam = class extends EventEmitter {
|
|
|
6370
6538
|
}
|
|
6371
6539
|
};
|
|
6372
6540
|
//#endregion
|
|
6541
|
+
//#region ../../src/navigator.ts
|
|
6542
|
+
var events$2 = {
|
|
6543
|
+
error: "error",
|
|
6544
|
+
flow: "flow"
|
|
6545
|
+
};
|
|
6546
|
+
var MAX_BATCHES_PER_PASS = 100;
|
|
6547
|
+
var Navigator = class extends EventEmitter {
|
|
6548
|
+
#stopped;
|
|
6549
|
+
#stopping;
|
|
6550
|
+
#working;
|
|
6551
|
+
#pollInterval;
|
|
6552
|
+
#db;
|
|
6553
|
+
#manager;
|
|
6554
|
+
#config;
|
|
6555
|
+
events = events$2;
|
|
6556
|
+
constructor(db, manager, config) {
|
|
6557
|
+
super();
|
|
6558
|
+
this.#db = db;
|
|
6559
|
+
this.#manager = manager;
|
|
6560
|
+
this.#config = config;
|
|
6561
|
+
this.#stopped = true;
|
|
6562
|
+
this.#stopping = false;
|
|
6563
|
+
this.#working = false;
|
|
6564
|
+
}
|
|
6565
|
+
get working() {
|
|
6566
|
+
return this.#working;
|
|
6567
|
+
}
|
|
6568
|
+
async start() {
|
|
6569
|
+
if (!this.#stopped) return;
|
|
6570
|
+
this.#stopped = false;
|
|
6571
|
+
this.#stopping = false;
|
|
6572
|
+
setImmediate(() => this.#onPoll());
|
|
6573
|
+
this.#pollInterval = setInterval(() => this.#onPoll(), this.#config.flowIntervalSeconds * 1e3);
|
|
6574
|
+
}
|
|
6575
|
+
async stop() {
|
|
6576
|
+
if (this.#stopped) return;
|
|
6577
|
+
this.#stopping = true;
|
|
6578
|
+
this.#stopped = true;
|
|
6579
|
+
if (this.#pollInterval) {
|
|
6580
|
+
clearInterval(this.#pollInterval);
|
|
6581
|
+
this.#pollInterval = void 0;
|
|
6582
|
+
}
|
|
6583
|
+
while (this.#working) await delay(10);
|
|
6584
|
+
}
|
|
6585
|
+
async #onPoll() {
|
|
6586
|
+
if (this.#stopped || this.#working) return;
|
|
6587
|
+
this.#working = true;
|
|
6588
|
+
try {
|
|
6589
|
+
if (this.#config.__test__throw_flow) throw new Error(this.#config.__test__throw_flow);
|
|
6590
|
+
if (this.#config.__test__delay_flow_ms) await delay(this.#config.__test__delay_flow_ms);
|
|
6591
|
+
const gate = trySetFlowTime(this.#config.schema, this.#config.flowIntervalSeconds);
|
|
6592
|
+
const { rows } = await this.#db.executeSql(gate);
|
|
6593
|
+
if (rows.length === 1) await this.#resolve();
|
|
6594
|
+
} catch (err) {
|
|
6595
|
+
this.emit(events$2.error, err);
|
|
6596
|
+
} finally {
|
|
6597
|
+
this.#working = false;
|
|
6598
|
+
}
|
|
6599
|
+
}
|
|
6600
|
+
async resolveNow() {
|
|
6601
|
+
while (this.#working) await delay(10);
|
|
6602
|
+
if (this.#stopping) return;
|
|
6603
|
+
this.#working = true;
|
|
6604
|
+
try {
|
|
6605
|
+
await this.#resolve();
|
|
6606
|
+
} finally {
|
|
6607
|
+
this.#working = false;
|
|
6608
|
+
}
|
|
6609
|
+
}
|
|
6610
|
+
async #resolve() {
|
|
6611
|
+
const queueGroups = (await this.#manager.getQueues()).reduce((acc, q) => {
|
|
6612
|
+
acc[q.table] = acc[q.table] || {
|
|
6613
|
+
table: q.table,
|
|
6614
|
+
names: []
|
|
6615
|
+
};
|
|
6616
|
+
acc[q.table].names.push(q.name);
|
|
6617
|
+
return acc;
|
|
6618
|
+
}, {});
|
|
6619
|
+
for (const group of Object.values(queueGroups)) {
|
|
6620
|
+
if (this.#stopping) return;
|
|
6621
|
+
const { table } = group;
|
|
6622
|
+
const names = [...group.names];
|
|
6623
|
+
while (names.length) {
|
|
6624
|
+
if (this.#stopping) return;
|
|
6625
|
+
const chunk = names.splice(0, 100);
|
|
6626
|
+
let batches = 0;
|
|
6627
|
+
let resolved = 0;
|
|
6628
|
+
do {
|
|
6629
|
+
if (this.#stopping) return;
|
|
6630
|
+
resolved = this.#config.noMultiMutationCte ? await this.#manager.resolveFlowJobsDistributed(table, chunk) : await this.#resolveStandard(table, chunk);
|
|
6631
|
+
if (resolved > 0) this.emit(events$2.flow, {
|
|
6632
|
+
table,
|
|
6633
|
+
resolved
|
|
6634
|
+
});
|
|
6635
|
+
} while (resolved >= 1e3 && ++batches < MAX_BATCHES_PER_PASS && !this.#stopping);
|
|
6636
|
+
}
|
|
6637
|
+
}
|
|
6638
|
+
}
|
|
6639
|
+
async #resolveStandard(table, names) {
|
|
6640
|
+
const query = resolveFlowJobs(this.#config.schema, table, names);
|
|
6641
|
+
const { rows } = await this.#db.executeSql(query.text, query.values);
|
|
6642
|
+
return Number(rows[0]?.resolved ?? 0);
|
|
6643
|
+
}
|
|
6644
|
+
};
|
|
6645
|
+
//#endregion
|
|
6373
6646
|
//#region ../../src/notifier.ts
|
|
6374
6647
|
var events$1 = {
|
|
6375
6648
|
error: "error",
|
|
@@ -6450,7 +6723,7 @@ var Db = class extends EventEmitter {
|
|
|
6450
6723
|
constructor(config) {
|
|
6451
6724
|
super();
|
|
6452
6725
|
config.application_name = config.application_name || "pgboss";
|
|
6453
|
-
config.connectionTimeoutMillis
|
|
6726
|
+
config.connectionTimeoutMillis ??= 1e4;
|
|
6454
6727
|
this.config = config;
|
|
6455
6728
|
this._pgbdb = true;
|
|
6456
6729
|
this.opened = false;
|
|
@@ -6551,7 +6824,8 @@ var events = Object.freeze({
|
|
|
6551
6824
|
warning: "warning",
|
|
6552
6825
|
wip: "wip",
|
|
6553
6826
|
stopped: "stopped",
|
|
6554
|
-
bam: "bam"
|
|
6827
|
+
bam: "bam",
|
|
6828
|
+
flow: "flow"
|
|
6555
6829
|
});
|
|
6556
6830
|
var PgBoss = class extends EventEmitter {
|
|
6557
6831
|
#stoppingOn;
|
|
@@ -6565,6 +6839,7 @@ var PgBoss = class extends EventEmitter {
|
|
|
6565
6839
|
#manager;
|
|
6566
6840
|
#timekeeper;
|
|
6567
6841
|
#bam;
|
|
6842
|
+
#navigator;
|
|
6568
6843
|
#notifier;
|
|
6569
6844
|
constructor(value) {
|
|
6570
6845
|
super();
|
|
@@ -6581,18 +6856,21 @@ var PgBoss = class extends EventEmitter {
|
|
|
6581
6856
|
const timekeeper = new Timekeeper(db, manager, config);
|
|
6582
6857
|
manager.timekeeper = timekeeper;
|
|
6583
6858
|
const bam = new Bam(db, config);
|
|
6859
|
+
const navigator = new Navigator(db, manager, config);
|
|
6584
6860
|
const notifier = new Notifier(db, manager, config);
|
|
6585
6861
|
manager.notifier = notifier;
|
|
6586
6862
|
this.#promoteEvents(manager);
|
|
6587
6863
|
this.#promoteEvents(boss);
|
|
6588
6864
|
this.#promoteEvents(timekeeper);
|
|
6589
6865
|
this.#promoteEvents(bam);
|
|
6866
|
+
this.#promoteEvents(navigator);
|
|
6590
6867
|
this.#promoteEvents(notifier);
|
|
6591
6868
|
this.#boss = boss;
|
|
6592
6869
|
this.#contractor = contractor;
|
|
6593
6870
|
this.#manager = manager;
|
|
6594
6871
|
this.#timekeeper = timekeeper;
|
|
6595
6872
|
this.#bam = bam;
|
|
6873
|
+
this.#navigator = navigator;
|
|
6596
6874
|
this.#notifier = notifier;
|
|
6597
6875
|
}
|
|
6598
6876
|
#promoteEvents(emitter) {
|
|
@@ -6608,7 +6886,10 @@ var PgBoss = class extends EventEmitter {
|
|
|
6608
6886
|
else await this.#contractor.check();
|
|
6609
6887
|
await this.#manager.start();
|
|
6610
6888
|
if (this.#config.useListenNotify) await this.#notifier.start();
|
|
6611
|
-
if (this.#config.supervise)
|
|
6889
|
+
if (this.#config.supervise) {
|
|
6890
|
+
await this.#boss.start();
|
|
6891
|
+
await this.#navigator.start();
|
|
6892
|
+
}
|
|
6612
6893
|
if (this.#config.schedule) await this.#timekeeper.start();
|
|
6613
6894
|
if (this.#config.migrate) await this.#bam.start();
|
|
6614
6895
|
} catch (err) {
|
|
@@ -6641,6 +6922,7 @@ var PgBoss = class extends EventEmitter {
|
|
|
6641
6922
|
await this.#manager.stop();
|
|
6642
6923
|
await this.#timekeeper.stop();
|
|
6643
6924
|
await this.#boss.stop();
|
|
6925
|
+
await this.#navigator.stop();
|
|
6644
6926
|
await this.#bam.stop();
|
|
6645
6927
|
const shutdown = async () => {
|
|
6646
6928
|
await this.#manager.failWip();
|
|
@@ -6754,7 +7036,7 @@ var PgBoss = class extends EventEmitter {
|
|
|
6754
7036
|
return this.#manager.deleteQueue(name);
|
|
6755
7037
|
}
|
|
6756
7038
|
getQueues(names) {
|
|
6757
|
-
return this.#manager.getQueues();
|
|
7039
|
+
return this.#manager.getQueues(names);
|
|
6758
7040
|
}
|
|
6759
7041
|
getQueue(name) {
|
|
6760
7042
|
return this.#manager.getQueue(name);
|
|
@@ -6768,12 +7050,18 @@ var PgBoss = class extends EventEmitter {
|
|
|
6768
7050
|
isBamWorking() {
|
|
6769
7051
|
return this.#bam.working;
|
|
6770
7052
|
}
|
|
7053
|
+
isResolvingFlow() {
|
|
7054
|
+
return this.#navigator.working;
|
|
7055
|
+
}
|
|
6771
7056
|
isCheckingSkew() {
|
|
6772
7057
|
return this.#timekeeper.checkingSkew;
|
|
6773
7058
|
}
|
|
6774
7059
|
supervise(name) {
|
|
6775
7060
|
return this.#boss.supervise(name);
|
|
6776
7061
|
}
|
|
7062
|
+
resolveFlow() {
|
|
7063
|
+
return this.#navigator.resolveNow();
|
|
7064
|
+
}
|
|
6777
7065
|
getWipData(options) {
|
|
6778
7066
|
return this.#manager.getWipData(options);
|
|
6779
7067
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pg-boss/dashboard",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Web dashboard for monitoring and managing pg-boss job queues",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pg-boss",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"isbot": "^5.1.44",
|
|
54
54
|
"lucide-react": "^1.21.0",
|
|
55
55
|
"pg": "^8.22.0",
|
|
56
|
-
"pg-boss": "^12.
|
|
56
|
+
"pg-boss": "^12.22.0",
|
|
57
57
|
"react": "^19.2.7",
|
|
58
58
|
"react-dom": "^19.2.7",
|
|
59
59
|
"react-router": "^8.0.1",
|