@pg-boss/dashboard 1.2.1 → 1.3.0
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/client/assets/MenuTrigger-BNvpjhsQ.js +1 -0
- package/build/client/assets/_index-DqpFaaQw.js +1 -0
- package/build/client/assets/{badge-DCQvSdiR.js → badge-CMnQO7Lq.js} +1 -1
- package/build/client/assets/{button-BxLcuaPM.js → button-9NpSS9Ow.js} +1 -1
- package/build/client/assets/check-7jwc5sb1.js +1 -0
- package/build/client/assets/{chevron-down-Byq-CYG9.js → chevron-down-BFFjfYD4.js} +1 -1
- package/build/client/assets/chevron-right-DGk5QFJF.js +1 -0
- package/build/client/assets/{createLucideIcon-DVP_i62f.js → createLucideIcon-C-LI4enx.js} +1 -1
- package/build/client/assets/db-link-BajQ1v8I.js +1 -0
- package/build/client/assets/dialog-D-oczDM2.js +1 -0
- package/build/client/assets/{entry.client-DL_oPh96.js → entry.client-CqyjuPDB.js} +3 -3
- package/build/client/assets/{error-card-B0ANyjh3.js → error-card-BH7i86fH.js} +1 -1
- package/build/client/assets/{filter-select--qLjbs9m.js → filter-select-Bn_oSiip.js} +1 -1
- package/build/client/assets/jobs-CAd_qqLH.js +1 -0
- package/build/client/assets/jsx-runtime-RQyiN6Nr.js +16 -0
- package/build/client/assets/manifest-27e8e133.js +1 -0
- package/build/client/assets/migrations-D5l0n4Jn.js +1 -0
- package/build/client/assets/{pagination-Bzx8wbXG.js → pagination-C-ohiBmY.js} +1 -1
- package/build/client/assets/queues._index-8YriSqbQ.js +1 -0
- package/build/client/assets/queues._name-Cb17IB2u.js +1 -0
- package/build/client/assets/{queues._name.jobs._jobId-BkG9y75k.js → queues._name.jobs._jobId-Bkv8POBj.js} +1 -1
- package/build/client/assets/{queues.create-CMqQVLup.js → queues.create-DsY0Sc19.js} +1 -1
- package/build/client/assets/{react-dom-QnGHOQwT.js → react-dom-D_m_Zgd3.js} +1 -1
- package/build/client/assets/root-B0MB8jZH.css +2 -0
- package/build/client/assets/root-qxoeL6W3.js +40 -0
- package/build/client/assets/{schedules-DPXQoaEE.js → schedules-iYfIJxOD.js} +1 -1
- package/build/client/assets/{schedules._name._key-B_luxy1w.js → schedules._name._key-CJVu73XY.js} +1 -1
- package/build/client/assets/{schedules.new-BQV7GWzs.js → schedules.new-Cq0Mxa7G.js} +1 -1
- package/build/client/assets/send-8X9ZisG-.js +1 -0
- package/build/client/assets/{stat-card-DLtQnscf.js → stat-card-dyg1wY5p.js} +1 -1
- package/build/client/assets/{table-DqqzSNik.js → table-Cz7ujmH_.js} +1 -1
- package/build/client/assets/useOpenInteractionType-BQ1arb0B.js +1 -0
- package/build/client/assets/{warnings-CHKaRfIW.js → warnings-C1R_RzIe.js} +1 -1
- package/build/client/assets/x-AhXI_F1j.js +1 -0
- package/build/server/index.js +1712 -1061
- package/package.json +11 -8
- package/build/client/assets/MenuTrigger-BhalG0aG.js +0 -1
- package/build/client/assets/_index-D1-nZ7Th.js +0 -1
- package/build/client/assets/check-Ch42cXMT.js +0 -1
- package/build/client/assets/chevron-right-CKAGD7DJ.js +0 -1
- package/build/client/assets/db-link-BWWnHM0k.js +0 -1
- package/build/client/assets/dialog-Bik519zD.js +0 -1
- package/build/client/assets/jobs-D0a6Lwq0.js +0 -1
- package/build/client/assets/jsx-runtime-BgbGXvsu.js +0 -16
- package/build/client/assets/manifest-ef81a0f9.js +0 -1
- package/build/client/assets/queues._index-D8903DTa.js +0 -1
- package/build/client/assets/queues._name-BVt_4pav.js +0 -1
- package/build/client/assets/root-C0MdPLOa.css +0 -2
- package/build/client/assets/root-Df70GAY3.js +0 -40
- package/build/client/assets/send-DJBsfnx_.js +0 -1
- package/build/client/assets/useOpenInteractionType-D3JsvupP.js +0 -1
- package/build/client/assets/x-BPKZwOn9.js +0 -1
package/build/server/index.js
CHANGED
|
@@ -347,6 +347,38 @@ var WARNING_TYPE_OPTIONS = [{
|
|
|
347
347
|
value: type,
|
|
348
348
|
label: WARNING_TYPE_LABELS[type]
|
|
349
349
|
}))];
|
|
350
|
+
var BAM_STATUSES = [
|
|
351
|
+
"pending",
|
|
352
|
+
"in_progress",
|
|
353
|
+
"completed",
|
|
354
|
+
"failed"
|
|
355
|
+
];
|
|
356
|
+
/**
|
|
357
|
+
* Validate a BAM status filter value
|
|
358
|
+
*/
|
|
359
|
+
function isValidBamStatus(value) {
|
|
360
|
+
if (value === null) return true;
|
|
361
|
+
return BAM_STATUSES.includes(value);
|
|
362
|
+
}
|
|
363
|
+
var BAM_STATUS_VARIANTS = {
|
|
364
|
+
pending: "gray",
|
|
365
|
+
in_progress: "primary",
|
|
366
|
+
completed: "success",
|
|
367
|
+
failed: "error"
|
|
368
|
+
};
|
|
369
|
+
var BAM_STATUS_LABELS = {
|
|
370
|
+
pending: "Pending",
|
|
371
|
+
in_progress: "In Progress",
|
|
372
|
+
completed: "Completed",
|
|
373
|
+
failed: "Failed"
|
|
374
|
+
};
|
|
375
|
+
var BAM_STATUS_OPTIONS = [{
|
|
376
|
+
value: null,
|
|
377
|
+
label: "All Statuses"
|
|
378
|
+
}, ...BAM_STATUSES.map((status) => ({
|
|
379
|
+
value: status,
|
|
380
|
+
label: BAM_STATUS_LABELS[status]
|
|
381
|
+
}))];
|
|
350
382
|
/**
|
|
351
383
|
* Format warning data for display
|
|
352
384
|
*/
|
|
@@ -840,6 +872,11 @@ var navigation = [
|
|
|
840
872
|
href: "/schedules",
|
|
841
873
|
icon: SchedulesIcon
|
|
842
874
|
},
|
|
875
|
+
{
|
|
876
|
+
name: "Migrations",
|
|
877
|
+
href: "/migrations",
|
|
878
|
+
icon: MigrationsIcon
|
|
879
|
+
},
|
|
843
880
|
{
|
|
844
881
|
name: "Warnings",
|
|
845
882
|
href: "/warnings",
|
|
@@ -916,6 +953,20 @@ function WarningIcon$1({ className }) {
|
|
|
916
953
|
})
|
|
917
954
|
});
|
|
918
955
|
}
|
|
956
|
+
function MigrationsIcon({ className }) {
|
|
957
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
958
|
+
className,
|
|
959
|
+
fill: "none",
|
|
960
|
+
viewBox: "0 0 24 24",
|
|
961
|
+
strokeWidth: 1.5,
|
|
962
|
+
stroke: "currentColor",
|
|
963
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
964
|
+
strokeLinecap: "round",
|
|
965
|
+
strokeLinejoin: "round",
|
|
966
|
+
d: "M7.5 21 3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5"
|
|
967
|
+
})
|
|
968
|
+
});
|
|
969
|
+
}
|
|
919
970
|
function DatabaseIcon({ className }) {
|
|
920
971
|
return /* @__PURE__ */ jsx("svg", {
|
|
921
972
|
className,
|
|
@@ -1208,11 +1259,11 @@ var dbContext = globalStore[TOKEN_KEY] ??= createContext();
|
|
|
1208
1259
|
//#endregion
|
|
1209
1260
|
//#region app/root.tsx
|
|
1210
1261
|
var root_exports = /* @__PURE__ */ __exportAll({
|
|
1211
|
-
ErrorBoundary: () => ErrorBoundary$
|
|
1262
|
+
ErrorBoundary: () => ErrorBoundary$12,
|
|
1212
1263
|
Layout: () => Layout,
|
|
1213
1264
|
default: () => root_default,
|
|
1214
1265
|
links: () => links,
|
|
1215
|
-
loader: () => loader$
|
|
1266
|
+
loader: () => loader$12,
|
|
1216
1267
|
meta: () => meta
|
|
1217
1268
|
});
|
|
1218
1269
|
function MainContent({ children }) {
|
|
@@ -1288,7 +1339,7 @@ var themeScript = `
|
|
|
1288
1339
|
link.href = 'data:image/svg+xml,' + encodeURIComponent(svg);
|
|
1289
1340
|
})();
|
|
1290
1341
|
`;
|
|
1291
|
-
async function loader$
|
|
1342
|
+
async function loader$12({ context }) {
|
|
1292
1343
|
const { databases, currentDb } = context.get(dbContext);
|
|
1293
1344
|
return {
|
|
1294
1345
|
databases,
|
|
@@ -1321,7 +1372,7 @@ function Layout({ children }) {
|
|
|
1321
1372
|
var root_default = UNSAFE_withComponentProps(function App() {
|
|
1322
1373
|
return /* @__PURE__ */ jsx(Outlet, {});
|
|
1323
1374
|
});
|
|
1324
|
-
var ErrorBoundary$
|
|
1375
|
+
var ErrorBoundary$12 = UNSAFE_withErrorBoundaryProps(function ErrorBoundary({ error }) {
|
|
1325
1376
|
let message = "Oops!";
|
|
1326
1377
|
let details = "An unexpected error occurred.";
|
|
1327
1378
|
let stack;
|
|
@@ -1538,10 +1589,13 @@ function createTableQueue(schema) {
|
|
|
1538
1589
|
table_name text NOT NULL,
|
|
1539
1590
|
deferred_count int NOT NULL default 0,
|
|
1540
1591
|
queued_count int NOT NULL default 0,
|
|
1592
|
+
ready_count int NOT NULL default 0,
|
|
1541
1593
|
warning_queued int NOT NULL default 0,
|
|
1542
1594
|
active_count int NOT NULL default 0,
|
|
1595
|
+
failed_count int NOT NULL default 0,
|
|
1543
1596
|
total_count int NOT NULL default 0,
|
|
1544
1597
|
heartbeat_seconds int,
|
|
1598
|
+
notify bool NOT NULL DEFAULT false,
|
|
1545
1599
|
singletons_active text[],
|
|
1546
1600
|
monitor_on timestamp with time zone,
|
|
1547
1601
|
maintain_on timestamp with time zone,
|
|
@@ -1878,7 +1932,8 @@ function createQueueFunction(schema, noPartitioning = false) {
|
|
|
1878
1932
|
dead_letter,
|
|
1879
1933
|
partition,
|
|
1880
1934
|
table_name,
|
|
1881
|
-
heartbeat_seconds
|
|
1935
|
+
heartbeat_seconds,
|
|
1936
|
+
notify
|
|
1882
1937
|
)
|
|
1883
1938
|
VALUES (
|
|
1884
1939
|
queue_name,
|
|
@@ -1894,7 +1949,8 @@ function createQueueFunction(schema, noPartitioning = false) {
|
|
|
1894
1949
|
options->>'deadLetter',
|
|
1895
1950
|
COALESCE((options->>'partition')::bool, ${QUEUE_DEFAULTS.partition}),
|
|
1896
1951
|
tablename,
|
|
1897
|
-
(options->>'heartbeatSeconds')::int
|
|
1952
|
+
(options->>'heartbeatSeconds')::int,
|
|
1953
|
+
COALESCE((options->>'notify')::bool, false)
|
|
1898
1954
|
)
|
|
1899
1955
|
ON CONFLICT DO NOTHING
|
|
1900
1956
|
RETURNING created_on
|
|
@@ -1966,6 +2022,12 @@ function deleteQueueFunction(schema, noPartitioning = false) {
|
|
|
1966
2022
|
function createQueue$1(schema, name, options, noAdvisoryLocks) {
|
|
1967
2023
|
return locked(schema, `SELECT ${schema}.create_queue('${name}', '${JSON.stringify(options)}'::jsonb)`, "create-queue", noAdvisoryLocks);
|
|
1968
2024
|
}
|
|
2025
|
+
function notifyChannelSql(schema) {
|
|
2026
|
+
return `('pgboss_' || left(encode(sha224('${schema}'::bytea), 'hex'), 24))`;
|
|
2027
|
+
}
|
|
2028
|
+
function notifyQueue(schema, name) {
|
|
2029
|
+
return `SELECT pg_notify(${notifyChannelSql(schema)}, '${name}')`;
|
|
2030
|
+
}
|
|
1969
2031
|
function deleteQueue(schema, name, noAdvisoryLocks) {
|
|
1970
2032
|
return locked(schema, `SELECT ${schema}.delete_queue('${name}')`, "delete-queue", noAdvisoryLocks);
|
|
1971
2033
|
}
|
|
@@ -2054,6 +2116,7 @@ function updateQueue(schema, { deadLetter } = {}) {
|
|
|
2054
2116
|
heartbeat_seconds = CASE WHEN o.data ? 'heartbeatSeconds'
|
|
2055
2117
|
THEN (o.data->>'heartbeatSeconds')::int
|
|
2056
2118
|
ELSE heartbeat_seconds END,
|
|
2119
|
+
notify = COALESCE((o.data->>'notify')::bool, notify),
|
|
2057
2120
|
${deadLetter === void 0 ? "" : `dead_letter = CASE WHEN '${deadLetter}' IS DISTINCT FROM dead_letter THEN '${deadLetter}' ELSE dead_letter END,`}
|
|
2058
2121
|
updated_on = now()
|
|
2059
2122
|
FROM options o
|
|
@@ -2076,11 +2139,14 @@ function getQueues$1(schema, names) {
|
|
|
2076
2139
|
q.deletion_seconds as "deleteAfterSeconds",
|
|
2077
2140
|
q.partition,
|
|
2078
2141
|
q.heartbeat_seconds as "heartbeatSeconds",
|
|
2142
|
+
q.notify,
|
|
2079
2143
|
q.dead_letter as "deadLetter",
|
|
2080
2144
|
q.deferred_count as "deferredCount",
|
|
2081
2145
|
q.warning_queued as "warningQueueSize",
|
|
2082
2146
|
q.queued_count as "queuedCount",
|
|
2147
|
+
q.ready_count as "readyCount",
|
|
2083
2148
|
q.active_count as "activeCount",
|
|
2149
|
+
q.failed_count as "failedCount",
|
|
2084
2150
|
q.total_count as "totalCount",
|
|
2085
2151
|
q.singletons_active as "singletonsActive",
|
|
2086
2152
|
q.table_name as "table",
|
|
@@ -2404,6 +2470,54 @@ function completeJobs(schema, table, includeQueued) {
|
|
|
2404
2470
|
SELECT COUNT(*) FROM completed
|
|
2405
2471
|
`;
|
|
2406
2472
|
}
|
|
2473
|
+
function completeJobsWithOutputs(schema, table) {
|
|
2474
|
+
return `
|
|
2475
|
+
WITH input AS (
|
|
2476
|
+
SELECT * FROM json_to_recordset($2::json) AS x (id uuid, output jsonb)
|
|
2477
|
+
),
|
|
2478
|
+
completed AS (
|
|
2479
|
+
UPDATE ${schema}.${table} j
|
|
2480
|
+
SET completed_on = now(),
|
|
2481
|
+
state = '${JOB_STATES.completed}',
|
|
2482
|
+
output = i.output
|
|
2483
|
+
FROM input i
|
|
2484
|
+
WHERE j.name = $1
|
|
2485
|
+
AND j.id = i.id
|
|
2486
|
+
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
|
+
RETURNING 1
|
|
2501
|
+
)
|
|
2502
|
+
SELECT COUNT(*) FROM completed
|
|
2503
|
+
`;
|
|
2504
|
+
}
|
|
2505
|
+
function completeJobsWithOutputsDistributed(schema, table) {
|
|
2506
|
+
return `
|
|
2507
|
+
WITH input AS (
|
|
2508
|
+
SELECT * FROM json_to_recordset($2::json) AS x (id uuid, output jsonb)
|
|
2509
|
+
)
|
|
2510
|
+
UPDATE ${schema}.${table} j
|
|
2511
|
+
SET completed_on = now(),
|
|
2512
|
+
state = '${JOB_STATES.completed}',
|
|
2513
|
+
output = i.output
|
|
2514
|
+
FROM input i
|
|
2515
|
+
WHERE j.name = $1
|
|
2516
|
+
AND j.id = i.id
|
|
2517
|
+
AND j.state = '${JOB_STATES.active}'
|
|
2518
|
+
RETURNING j.id, j.blocking
|
|
2519
|
+
`;
|
|
2520
|
+
}
|
|
2407
2521
|
function cancelJobs(schema, table) {
|
|
2408
2522
|
return `
|
|
2409
2523
|
WITH results as (
|
|
@@ -2442,8 +2556,8 @@ function restoreJobs(schema, table) {
|
|
|
2442
2556
|
AND id IN (SELECT UNNEST($2::uuid[]))
|
|
2443
2557
|
`;
|
|
2444
2558
|
}
|
|
2445
|
-
function insertJobs(schema, { table, name, returnId = true }) {
|
|
2446
|
-
|
|
2559
|
+
function insertJobs(schema, { table, name, returnId = true, notify = false }) {
|
|
2560
|
+
const insert = `
|
|
2447
2561
|
INSERT INTO ${schema}.${table} (
|
|
2448
2562
|
id,
|
|
2449
2563
|
name,
|
|
@@ -2526,7 +2640,31 @@ function insertJobs(schema, { table, name, returnId = true }) {
|
|
|
2526
2640
|
) j
|
|
2527
2641
|
JOIN ${schema}.queue q ON q.name = '${name}'
|
|
2528
2642
|
ON CONFLICT DO NOTHING
|
|
2529
|
-
${returnId ? "RETURNING id" : ""}
|
|
2643
|
+
${notify ? "RETURNING id, start_after" : returnId ? "RETURNING id" : ""}
|
|
2644
|
+
`;
|
|
2645
|
+
if (!notify) return insert;
|
|
2646
|
+
const comparator = returnId ? ">= 0" : "< 0";
|
|
2647
|
+
return `
|
|
2648
|
+
WITH ins AS (
|
|
2649
|
+
${insert}
|
|
2650
|
+
),
|
|
2651
|
+
notified AS (
|
|
2652
|
+
SELECT pg_notify(${notifyChannelSql(schema)}, '${name}')
|
|
2653
|
+
FROM ins WHERE start_after <= now() LIMIT 1
|
|
2654
|
+
)
|
|
2655
|
+
SELECT id FROM ins WHERE (SELECT count(*) FROM notified) ${comparator}
|
|
2656
|
+
`;
|
|
2657
|
+
}
|
|
2658
|
+
function insertFlowJobs(schema, { table, name }, jobs) {
|
|
2659
|
+
return `
|
|
2660
|
+
WITH ins AS (
|
|
2661
|
+
${insertJobs(schema, {
|
|
2662
|
+
table,
|
|
2663
|
+
name,
|
|
2664
|
+
returnId: true
|
|
2665
|
+
}).replace("$1", () => serializeJsonParam(jobs))}
|
|
2666
|
+
)
|
|
2667
|
+
SELECT 1 / (CASE WHEN (SELECT count(*) FROM ins) = ${jobs.length} THEN 1 ELSE 0 END)
|
|
2530
2668
|
`;
|
|
2531
2669
|
}
|
|
2532
2670
|
function failJobsById(schema, table) {
|
|
@@ -2558,7 +2696,12 @@ function touchJobs(schema, table) {
|
|
|
2558
2696
|
}
|
|
2559
2697
|
function failJobs(schema, table, where, output) {
|
|
2560
2698
|
return `
|
|
2561
|
-
WITH
|
|
2699
|
+
WITH ${failJobsBody(schema, table, where, output)}
|
|
2700
|
+
SELECT COUNT(*) FROM results
|
|
2701
|
+
`;
|
|
2702
|
+
}
|
|
2703
|
+
function failJobsBody(schema, table, where, output, forceTerminal = false) {
|
|
2704
|
+
return `deleted_jobs AS (
|
|
2562
2705
|
DELETE FROM ${schema}.${table}
|
|
2563
2706
|
WHERE ${where}
|
|
2564
2707
|
RETURNING *
|
|
@@ -2600,10 +2743,10 @@ function failJobs(schema, table, where, output) {
|
|
|
2600
2743
|
name,
|
|
2601
2744
|
priority,
|
|
2602
2745
|
data,
|
|
2603
|
-
CASE
|
|
2746
|
+
${forceTerminal ? `'${JOB_STATES.failed}'::${schema}.job_state` : `CASE
|
|
2604
2747
|
WHEN retry_count < retry_limit THEN '${JOB_STATES.retry}'::${schema}.job_state
|
|
2605
2748
|
ELSE '${JOB_STATES.failed}'::${schema}.job_state
|
|
2606
|
-
END as state,
|
|
2749
|
+
END`} as state,
|
|
2607
2750
|
retry_limit,
|
|
2608
2751
|
retry_count,
|
|
2609
2752
|
retry_delay,
|
|
@@ -2627,7 +2770,7 @@ function failJobs(schema, table, where, output) {
|
|
|
2627
2770
|
expire_seconds,
|
|
2628
2771
|
deletion_seconds,
|
|
2629
2772
|
created_on,
|
|
2630
|
-
CASE WHEN retry_count < retry_limit THEN NULL ELSE now() END as completed_on,
|
|
2773
|
+
${forceTerminal ? "now()" : "CASE WHEN retry_count < retry_limit THEN NULL ELSE now() END"} as completed_on,
|
|
2631
2774
|
keep_until,
|
|
2632
2775
|
policy,
|
|
2633
2776
|
${output},
|
|
@@ -2726,7 +2869,23 @@ function failJobs(schema, table, where, output) {
|
|
|
2726
2869
|
FROM results r
|
|
2727
2870
|
JOIN ${schema}.queue q ON q.name = r.dead_letter
|
|
2728
2871
|
WHERE state = '${JOB_STATES.failed}'
|
|
2729
|
-
)
|
|
2872
|
+
)`;
|
|
2873
|
+
}
|
|
2874
|
+
function failJobsByIdWithOutputs(schema, table) {
|
|
2875
|
+
return `
|
|
2876
|
+
WITH output_map AS (
|
|
2877
|
+
SELECT * FROM json_to_recordset($2::json) AS x (id uuid, output jsonb)
|
|
2878
|
+
),
|
|
2879
|
+
${failJobsBody(schema, table, `name = $1 AND id IN (SELECT id FROM output_map) AND state < '${JOB_STATES.completed}'`, "(SELECT om.output FROM output_map om WHERE om.id = deleted_jobs.id)")}
|
|
2880
|
+
SELECT COUNT(*) FROM results
|
|
2881
|
+
`;
|
|
2882
|
+
}
|
|
2883
|
+
function deadLetterJobsByIdWithOutputs(schema, table) {
|
|
2884
|
+
return `
|
|
2885
|
+
WITH output_map AS (
|
|
2886
|
+
SELECT * FROM json_to_recordset($2::json) AS x (id uuid, output jsonb)
|
|
2887
|
+
),
|
|
2888
|
+
${failJobsBody(schema, table, `name = $1 AND id IN (SELECT id FROM output_map) AND state < '${JOB_STATES.completed}'`, "(SELECT om.output FROM output_map om WHERE om.id = deleted_jobs.id)", true)}
|
|
2730
2889
|
SELECT COUNT(*) FROM results
|
|
2731
2890
|
`;
|
|
2732
2891
|
}
|
|
@@ -2839,14 +2998,26 @@ function getQueueStats$1(schema, table, queues) {
|
|
|
2839
2998
|
text: `
|
|
2840
2999
|
SELECT
|
|
2841
3000
|
name,
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
(
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
3001
|
+
"deferredCount",
|
|
3002
|
+
"queuedCount",
|
|
3003
|
+
GREATEST("queuedCount" - "deferredCount", 0) as "readyCount",
|
|
3004
|
+
"activeCount",
|
|
3005
|
+
"failedCount",
|
|
3006
|
+
"totalCount",
|
|
3007
|
+
"singletonsActive"
|
|
3008
|
+
FROM (
|
|
3009
|
+
SELECT
|
|
3010
|
+
name,
|
|
3011
|
+
(count(*) FILTER (WHERE start_after > now()))::int as "deferredCount",
|
|
3012
|
+
(count(*) FILTER (WHERE state < '${JOB_STATES.active}'))::int as "queuedCount",
|
|
3013
|
+
(count(*) FILTER (WHERE state = '${JOB_STATES.active}'))::int as "activeCount",
|
|
3014
|
+
(count(*) FILTER (WHERE state = '${JOB_STATES.failed}'))::int as "failedCount",
|
|
3015
|
+
count(*)::int as "totalCount",
|
|
3016
|
+
array_agg(singleton_key) FILTER (WHERE policy IN ('${QUEUE_POLICIES.singleton}','${QUEUE_POLICIES.stately}') AND state = '${JOB_STATES.active}') as "singletonsActive"
|
|
3017
|
+
FROM ${schema}.${table}
|
|
3018
|
+
WHERE name = ANY($1::text[])
|
|
3019
|
+
GROUP BY 1
|
|
3020
|
+
) stats
|
|
2850
3021
|
`,
|
|
2851
3022
|
values: [queues]
|
|
2852
3023
|
};
|
|
@@ -2857,7 +3028,9 @@ function cacheQueueStats(schema, table, queues, noAdvisoryLocks) {
|
|
|
2857
3028
|
UPDATE ${schema}.queue SET
|
|
2858
3029
|
deferred_count = COALESCE(stats."deferredCount", 0),
|
|
2859
3030
|
queued_count = COALESCE(stats."queuedCount", 0),
|
|
3031
|
+
ready_count = COALESCE(stats."readyCount", 0),
|
|
2860
3032
|
active_count = COALESCE(stats."activeCount", 0),
|
|
3033
|
+
failed_count = COALESCE(stats."failedCount", 0),
|
|
2861
3034
|
total_count = COALESCE(stats."totalCount", 0),
|
|
2862
3035
|
singletons_active = stats."singletonsActive"
|
|
2863
3036
|
FROM (
|
|
@@ -2931,8 +3104,8 @@ function getJobById$1(schema, table) {
|
|
|
2931
3104
|
AND id = $2
|
|
2932
3105
|
`;
|
|
2933
3106
|
}
|
|
2934
|
-
function insertDependencies(schema) {
|
|
2935
|
-
|
|
3107
|
+
function insertDependencies(schema, deps) {
|
|
3108
|
+
const sql = `
|
|
2936
3109
|
INSERT INTO ${schema}.job_dependency (child_name, child_id, parent_name, parent_id)
|
|
2937
3110
|
SELECT child_name, child_id, parent_name, parent_id
|
|
2938
3111
|
FROM json_to_recordset($1::json) AS x (
|
|
@@ -2943,6 +3116,7 @@ function insertDependencies(schema) {
|
|
|
2943
3116
|
)
|
|
2944
3117
|
ON CONFLICT DO NOTHING
|
|
2945
3118
|
`;
|
|
3119
|
+
return deps ? sql.replace("$1", () => serializeJsonParam(deps)) : sql;
|
|
2946
3120
|
}
|
|
2947
3121
|
function getDependencies(schema) {
|
|
2948
3122
|
return `
|
|
@@ -3018,7 +3192,7 @@ function getBamStatus(schema) {
|
|
|
3018
3192
|
GROUP BY status
|
|
3019
3193
|
`;
|
|
3020
3194
|
}
|
|
3021
|
-
function getBamEntries(schema) {
|
|
3195
|
+
function getBamEntries$1(schema) {
|
|
3022
3196
|
return `
|
|
3023
3197
|
SELECT id, name, version, status, queue, table_name as "table", command, error,
|
|
3024
3198
|
created_on as "createdOn", started_on as "startedOn", completed_on as "completedOn"
|
|
@@ -3039,7 +3213,8 @@ var COMPATIBILITY_FLAGS = [
|
|
|
3039
3213
|
"noTablePartitioning",
|
|
3040
3214
|
"noDeferrableConstraints",
|
|
3041
3215
|
"noAdvisoryLocks",
|
|
3042
|
-
"noCoveringIndexes"
|
|
3216
|
+
"noCoveringIndexes",
|
|
3217
|
+
"noListenNotify"
|
|
3043
3218
|
];
|
|
3044
3219
|
var BACKEND_PROFILES = {
|
|
3045
3220
|
postgres: {
|
|
@@ -3054,7 +3229,8 @@ var BACKEND_PROFILES = {
|
|
|
3054
3229
|
noTablePartitioning: true,
|
|
3055
3230
|
noDeferrableConstraints: true,
|
|
3056
3231
|
noAdvisoryLocks: true,
|
|
3057
|
-
noCoveringIndexes: true
|
|
3232
|
+
noCoveringIndexes: true,
|
|
3233
|
+
noListenNotify: true
|
|
3058
3234
|
}
|
|
3059
3235
|
},
|
|
3060
3236
|
yugabytedb: {
|
|
@@ -3078,6 +3254,7 @@ function assertObjectName(value, name = "Name") {
|
|
|
3078
3254
|
}
|
|
3079
3255
|
function validateQueueArgs(config = {}) {
|
|
3080
3256
|
assert(!("deadLetter" in config) || config.deadLetter === null || typeof config.deadLetter === "string", "deadLetter must be a string");
|
|
3257
|
+
assert(!("notify" in config) || typeof config.notify === "boolean", "notify must be a boolean");
|
|
3081
3258
|
if (config.deadLetter) assertObjectName(config.deadLetter, "deadLetter");
|
|
3082
3259
|
validateRetryConfig(config);
|
|
3083
3260
|
validateExpirationConfig(config);
|
|
@@ -3257,6 +3434,7 @@ function checkWorkArgs(name, args) {
|
|
|
3257
3434
|
assert(!("includeMetadata" in options) || typeof options.includeMetadata === "boolean", "includeMetadata must be a boolean");
|
|
3258
3435
|
assert(!("priority" in options) || typeof options.priority === "boolean", "priority must be a boolean");
|
|
3259
3436
|
assert(!("localConcurrency" in options) || Number.isInteger(options.localConcurrency) && options.localConcurrency >= 1, "localConcurrency must be an integer >= 1");
|
|
3437
|
+
assert(!("perJobResults" in options) || typeof options.perJobResults === "boolean", "perJobResults must be a boolean");
|
|
3260
3438
|
validatePriorityRangeConfig(options);
|
|
3261
3439
|
validateGroupConcurrencyConfig(options);
|
|
3262
3440
|
validateHeartbeatRefreshConfig(options);
|
|
@@ -3280,6 +3458,7 @@ function getConfig(value) {
|
|
|
3280
3458
|
config.supervise = "supervise" in config ? config.supervise : true;
|
|
3281
3459
|
config.migrate = "migrate" in config ? config.migrate : true;
|
|
3282
3460
|
config.createSchema = "createSchema" in config ? config.createSchema : true;
|
|
3461
|
+
config.useListenNotify = "useListenNotify" in config ? config.useListenNotify : false;
|
|
3283
3462
|
resolveBackend(config);
|
|
3284
3463
|
applySchemaConfig(config);
|
|
3285
3464
|
applyOpsConfig(config);
|
|
@@ -3349,6 +3528,13 @@ function validateHeartbeatRefreshConfig(config) {
|
|
|
3349
3528
|
function applyPollingInterval(config) {
|
|
3350
3529
|
assert(!("pollingIntervalSeconds" in config) || config.pollingIntervalSeconds >= POLICY.MIN_POLLING_INTERVAL_MS / 1e3, `configuration assert: pollingIntervalSeconds must be at least every ${POLICY.MIN_POLLING_INTERVAL_MS}ms`);
|
|
3351
3530
|
config.pollingInterval = "pollingIntervalSeconds" in config ? config.pollingIntervalSeconds * 1e3 : 2e3;
|
|
3531
|
+
assert(!("notifyPollingIntervalSeconds" in config) || config.notifyPollingIntervalSeconds >= POLICY.MIN_POLLING_INTERVAL_MS / 1e3, `configuration assert: notifyPollingIntervalSeconds must be at least every ${POLICY.MIN_POLLING_INTERVAL_MS}ms`);
|
|
3532
|
+
if ("notifyPollingIntervalSeconds" in config) {
|
|
3533
|
+
config.notifyPollingInterval = config.notifyPollingIntervalSeconds * 1e3;
|
|
3534
|
+
assert(config.notifyPollingInterval >= config.pollingInterval, "configuration assert: notifyPollingIntervalSeconds must be at least pollingIntervalSeconds");
|
|
3535
|
+
} else config.notifyPollingInterval = Math.max(3e4, config.pollingInterval);
|
|
3536
|
+
assert(!("burstWhenReadyExceeds" in config) || Number.isInteger(config.burstWhenReadyExceeds) && config.burstWhenReadyExceeds >= 1, "configuration assert: burstWhenReadyExceeds must be an integer >= 1");
|
|
3537
|
+
assert(!("burstWhenBatchFull" in config) || typeof config.burstWhenBatchFull === "boolean", "configuration assert: burstWhenBatchFull must be a boolean");
|
|
3352
3538
|
}
|
|
3353
3539
|
function applyOpsConfig(config) {
|
|
3354
3540
|
assert(!("superviseIntervalSeconds" in config) || config.superviseIntervalSeconds >= 1, "configuration assert: superviseIntervalSeconds must be at least every second");
|
|
@@ -3382,6 +3568,21 @@ function applyBamConfig(config) {
|
|
|
3382
3568
|
}
|
|
3383
3569
|
//#endregion
|
|
3384
3570
|
//#region ../../src/migrationStore.ts
|
|
3571
|
+
function formatJobTable(command, table) {
|
|
3572
|
+
return command.replaceAll(".job", `.${table}`).replaceAll("job_i", `${table}_i`);
|
|
3573
|
+
}
|
|
3574
|
+
function inlineAsyncCommand(schema, asyncCommand, version, partitionTables) {
|
|
3575
|
+
const nameMatch = asyncCommand.match(/job_table_run_async\(\s*'([^']+)'/);
|
|
3576
|
+
const bodyMatch = asyncCommand.match(/\$\$([\s\S]*?)\$\$/);
|
|
3577
|
+
const tableMatch = asyncCommand.match(/\$\$\s*,\s*'([^']+)'/);
|
|
3578
|
+
assert(nameMatch && bodyMatch, `Unable to inline async migration command: ${asyncCommand}`);
|
|
3579
|
+
const commandName = nameMatch[1];
|
|
3580
|
+
const body = bodyMatch[1].trim();
|
|
3581
|
+
return (tableMatch ? [tableMatch[1]] : ["job_common", ...partitionTables]).map((table) => {
|
|
3582
|
+
const ddl = formatJobTable(body, table).replace(/(CREATE (?:UNIQUE )?INDEX CONCURRENTLY) /, "$1 IF NOT EXISTS ");
|
|
3583
|
+
return `${`-- inlined from ${schema}.job_table_run_async (migration v${version}, command: ${commandName})`}\n${ddl}`;
|
|
3584
|
+
});
|
|
3585
|
+
}
|
|
3385
3586
|
function flatten(schema, commands, version, noAdvisoryLocks) {
|
|
3386
3587
|
commands.unshift(assertMigration(schema, version));
|
|
3387
3588
|
commands.push(setVersion(schema, version));
|
|
@@ -3399,11 +3600,13 @@ function next(schema, version, migrations, noAdvisoryLocks) {
|
|
|
3399
3600
|
assert(result, `Version ${version} not found.`);
|
|
3400
3601
|
return flatten(schema, result.install, result.version, noAdvisoryLocks);
|
|
3401
3602
|
}
|
|
3402
|
-
function
|
|
3603
|
+
function migrateCommands(schema, version, migrations, noAdvisoryLocks, options = {}) {
|
|
3403
3604
|
migrations = migrations || getAll(schema);
|
|
3605
|
+
const concurrent = [];
|
|
3404
3606
|
const result = migrations.filter((i) => i.previous >= version).sort((a, b) => a.version - b.version).reduce((acc, migration) => {
|
|
3405
3607
|
acc.install = acc.install.concat(migration.install);
|
|
3406
|
-
if (migration.async)
|
|
3608
|
+
if (migration.async) if (options.inlineAsync) for (const cmd of migration.async) concurrent.push(...inlineAsyncCommand(schema, cmd, migration.version, options.partitionTables || []));
|
|
3609
|
+
else {
|
|
3407
3610
|
const bamCommands = migration.async.map((cmd) => cmd.replace(/\$VERSION\$/g, String(migration.version)));
|
|
3408
3611
|
acc.install = acc.install.concat(bamCommands);
|
|
3409
3612
|
}
|
|
@@ -3414,156 +3617,576 @@ function migrate(schema, version, migrations, noAdvisoryLocks) {
|
|
|
3414
3617
|
version
|
|
3415
3618
|
});
|
|
3416
3619
|
assert(result.install.length > 0, `Version ${version} not found.`);
|
|
3417
|
-
return
|
|
3620
|
+
return {
|
|
3621
|
+
sql: flatten(schema, result.install, result.version, noAdvisoryLocks),
|
|
3622
|
+
concurrent
|
|
3623
|
+
};
|
|
3418
3624
|
}
|
|
3419
|
-
function
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
BEGIN
|
|
3625
|
+
function migrate(schema, version, migrations, noAdvisoryLocks, options = {}) {
|
|
3626
|
+
const { sql, concurrent } = migrateCommands(schema, version, migrations, noAdvisoryLocks, options);
|
|
3627
|
+
return concurrent.length ? `${sql}\n${concurrent.join(";\n")};` : sql;
|
|
3628
|
+
}
|
|
3629
|
+
var createQueueFn = {
|
|
3630
|
+
26: (schema) => `
|
|
3631
|
+
CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
3632
|
+
RETURNS VOID AS
|
|
3633
|
+
$$
|
|
3634
|
+
DECLARE
|
|
3635
|
+
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
3636
|
+
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
3637
|
+
ELSE 'job_common'
|
|
3638
|
+
END;
|
|
3639
|
+
queue_created_on timestamptz;
|
|
3640
|
+
BEGIN
|
|
3436
3641
|
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3642
|
+
WITH q as (
|
|
3643
|
+
INSERT INTO ${schema}.queue (
|
|
3644
|
+
name,
|
|
3645
|
+
policy,
|
|
3646
|
+
retry_limit,
|
|
3647
|
+
retry_delay,
|
|
3648
|
+
retry_backoff,
|
|
3649
|
+
retry_delay_max,
|
|
3650
|
+
expire_seconds,
|
|
3651
|
+
retention_seconds,
|
|
3652
|
+
deletion_seconds,
|
|
3653
|
+
warning_queued,
|
|
3654
|
+
dead_letter,
|
|
3655
|
+
partition,
|
|
3656
|
+
table_name
|
|
3657
|
+
)
|
|
3658
|
+
VALUES (
|
|
3659
|
+
queue_name,
|
|
3660
|
+
options->>'policy',
|
|
3661
|
+
COALESCE((options->>'retryLimit')::int, 2),
|
|
3662
|
+
COALESCE((options->>'retryDelay')::int, 0),
|
|
3663
|
+
COALESCE((options->>'retryBackoff')::bool, false),
|
|
3664
|
+
(options->>'retryDelayMax')::int,
|
|
3665
|
+
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
3666
|
+
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
3667
|
+
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
3668
|
+
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
3669
|
+
options->>'deadLetter',
|
|
3670
|
+
COALESCE((options->>'partition')::bool, false),
|
|
3671
|
+
tablename
|
|
3672
|
+
)
|
|
3673
|
+
ON CONFLICT DO NOTHING
|
|
3674
|
+
RETURNING created_on
|
|
3675
|
+
)
|
|
3676
|
+
SELECT created_on into queue_created_on from q;
|
|
3472
3677
|
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3678
|
+
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
3679
|
+
RETURN;
|
|
3680
|
+
END IF;
|
|
3476
3681
|
|
|
3477
|
-
|
|
3682
|
+
EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
|
|
3478
3683
|
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3684
|
+
EXECUTE format('ALTER TABLE ${schema}.%1$I ADD PRIMARY KEY (name, id)', tablename);
|
|
3685
|
+
EXECUTE format('ALTER TABLE ${schema}.%1$I ADD CONSTRAINT q_fkey FOREIGN KEY (name) REFERENCES ${schema}.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED', tablename);
|
|
3686
|
+
EXECUTE format('ALTER TABLE ${schema}.%1$I ADD CONSTRAINT dlq_fkey FOREIGN KEY (dead_letter) REFERENCES ${schema}.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED', tablename);
|
|
3482
3687
|
|
|
3483
|
-
|
|
3484
|
-
|
|
3688
|
+
EXECUTE format('CREATE INDEX %1$s_i5 ON ${schema}.%1$I (name, start_after) INCLUDE (priority, created_on, id) WHERE state < ''active''', tablename);
|
|
3689
|
+
EXECUTE format('CREATE UNIQUE INDEX %1$s_i4 ON ${schema}.%1$I (name, singleton_on, COALESCE(singleton_key, '''')) WHERE state <> ''cancelled'' AND singleton_on IS NOT NULL', tablename);
|
|
3485
3690
|
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3691
|
+
IF options->>'policy' = 'short' THEN
|
|
3692
|
+
EXECUTE format('CREATE UNIQUE INDEX %1$s_i1 ON ${schema}.%1$I (name, COALESCE(singleton_key, '''')) WHERE state = ''created'' AND policy = ''short''', tablename);
|
|
3693
|
+
ELSIF options->>'policy' = 'singleton' THEN
|
|
3694
|
+
EXECUTE format('CREATE UNIQUE INDEX %1$s_i2 ON ${schema}.%1$I (name, COALESCE(singleton_key, '''')) WHERE state = ''active'' AND policy = ''singleton''', tablename);
|
|
3695
|
+
ELSIF options->>'policy' = 'stately' THEN
|
|
3696
|
+
EXECUTE format('CREATE UNIQUE INDEX %1$s_i3 ON ${schema}.%1$I (name, state, COALESCE(singleton_key, '''')) WHERE state <= ''active'' AND policy = ''stately''', tablename);
|
|
3697
|
+
ELSIF options->>'policy' = 'exclusive' THEN
|
|
3698
|
+
EXECUTE format('CREATE UNIQUE INDEX %1$s_i6 ON ${schema}.%1$I (name, COALESCE(singleton_key, '''')) WHERE state <= ''active'' AND policy = ''exclusive''', tablename);
|
|
3699
|
+
END IF;
|
|
3495
3700
|
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3701
|
+
EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
3702
|
+
EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
3703
|
+
END;
|
|
3704
|
+
$$
|
|
3705
|
+
LANGUAGE plpgsql;
|
|
3706
|
+
`,
|
|
3707
|
+
27: (schema) => `
|
|
3708
|
+
CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
3709
|
+
RETURNS VOID AS
|
|
3710
|
+
$$
|
|
3711
|
+
DECLARE
|
|
3712
|
+
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
3713
|
+
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
3714
|
+
ELSE 'job_common'
|
|
3715
|
+
END;
|
|
3716
|
+
queue_created_on timestamptz;
|
|
3717
|
+
BEGIN
|
|
3718
|
+
|
|
3719
|
+
WITH q as (
|
|
3720
|
+
INSERT INTO ${schema}.queue (
|
|
3721
|
+
name,
|
|
3722
|
+
policy,
|
|
3723
|
+
retry_limit,
|
|
3724
|
+
retry_delay,
|
|
3725
|
+
retry_backoff,
|
|
3726
|
+
retry_delay_max,
|
|
3727
|
+
expire_seconds,
|
|
3728
|
+
retention_seconds,
|
|
3729
|
+
deletion_seconds,
|
|
3730
|
+
warning_queued,
|
|
3731
|
+
dead_letter,
|
|
3732
|
+
partition,
|
|
3733
|
+
table_name
|
|
3523
3734
|
)
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
IF queue_name IS NOT NULL THEN
|
|
3544
|
-
SELECT table_name INTO tbl_name FROM ${schema}.queue WHERE name = queue_name;
|
|
3545
|
-
END IF;
|
|
3735
|
+
VALUES (
|
|
3736
|
+
queue_name,
|
|
3737
|
+
options->>'policy',
|
|
3738
|
+
COALESCE((options->>'retryLimit')::int, 2),
|
|
3739
|
+
COALESCE((options->>'retryDelay')::int, 0),
|
|
3740
|
+
COALESCE((options->>'retryBackoff')::bool, false),
|
|
3741
|
+
(options->>'retryDelayMax')::int,
|
|
3742
|
+
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
3743
|
+
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
3744
|
+
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
3745
|
+
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
3746
|
+
options->>'deadLetter',
|
|
3747
|
+
COALESCE((options->>'partition')::bool, false),
|
|
3748
|
+
tablename
|
|
3749
|
+
)
|
|
3750
|
+
ON CONFLICT DO NOTHING
|
|
3751
|
+
RETURNING created_on
|
|
3752
|
+
)
|
|
3753
|
+
SELECT created_on into queue_created_on from q;
|
|
3546
3754
|
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
command_name,
|
|
3551
|
-
version,
|
|
3552
|
-
'pending',
|
|
3553
|
-
queue_name,
|
|
3554
|
-
tbl_name,
|
|
3555
|
-
${schema}.job_table_format(command, tbl_name)
|
|
3556
|
-
);
|
|
3557
|
-
RETURN;
|
|
3558
|
-
END IF;
|
|
3755
|
+
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
3756
|
+
RETURN;
|
|
3757
|
+
END IF;
|
|
3559
3758
|
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3759
|
+
EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
|
|
3760
|
+
|
|
3761
|
+
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
|
|
3762
|
+
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);
|
|
3763
|
+
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);
|
|
3764
|
+
|
|
3765
|
+
EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active'$cmd$, tablename);
|
|
3766
|
+
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);
|
|
3767
|
+
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);
|
|
3768
|
+
|
|
3769
|
+
IF options->>'policy' = 'short' THEN
|
|
3770
|
+
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);
|
|
3771
|
+
ELSIF options->>'policy' = 'singleton' THEN
|
|
3772
|
+
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);
|
|
3773
|
+
ELSIF options->>'policy' = 'stately' THEN
|
|
3774
|
+
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);
|
|
3775
|
+
ELSIF options->>'policy' = 'exclusive' THEN
|
|
3776
|
+
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);
|
|
3777
|
+
END IF;
|
|
3778
|
+
|
|
3779
|
+
EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
3780
|
+
EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
3781
|
+
END;
|
|
3782
|
+
$$
|
|
3783
|
+
LANGUAGE plpgsql;
|
|
3784
|
+
`,
|
|
3785
|
+
28: (schema) => `
|
|
3786
|
+
CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
3787
|
+
RETURNS VOID AS
|
|
3788
|
+
$$
|
|
3789
|
+
DECLARE
|
|
3790
|
+
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
3791
|
+
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
3792
|
+
ELSE 'job_common'
|
|
3793
|
+
END;
|
|
3794
|
+
queue_created_on timestamptz;
|
|
3795
|
+
BEGIN
|
|
3796
|
+
|
|
3797
|
+
WITH q as (
|
|
3798
|
+
INSERT INTO ${schema}.queue (
|
|
3799
|
+
name,
|
|
3800
|
+
policy,
|
|
3801
|
+
retry_limit,
|
|
3802
|
+
retry_delay,
|
|
3803
|
+
retry_backoff,
|
|
3804
|
+
retry_delay_max,
|
|
3805
|
+
expire_seconds,
|
|
3806
|
+
retention_seconds,
|
|
3807
|
+
deletion_seconds,
|
|
3808
|
+
warning_queued,
|
|
3809
|
+
dead_letter,
|
|
3810
|
+
partition,
|
|
3811
|
+
table_name
|
|
3812
|
+
)
|
|
3813
|
+
VALUES (
|
|
3814
|
+
queue_name,
|
|
3815
|
+
options->>'policy',
|
|
3816
|
+
COALESCE((options->>'retryLimit')::int, 2),
|
|
3817
|
+
COALESCE((options->>'retryDelay')::int, 0),
|
|
3818
|
+
COALESCE((options->>'retryBackoff')::bool, false),
|
|
3819
|
+
(options->>'retryDelayMax')::int,
|
|
3820
|
+
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
3821
|
+
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
3822
|
+
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
3823
|
+
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
3824
|
+
options->>'deadLetter',
|
|
3825
|
+
COALESCE((options->>'partition')::bool, false),
|
|
3826
|
+
tablename
|
|
3827
|
+
)
|
|
3828
|
+
ON CONFLICT DO NOTHING
|
|
3829
|
+
RETURNING created_on
|
|
3830
|
+
)
|
|
3831
|
+
SELECT created_on into queue_created_on from q;
|
|
3832
|
+
|
|
3833
|
+
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
3834
|
+
RETURN;
|
|
3835
|
+
END IF;
|
|
3836
|
+
|
|
3837
|
+
EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
|
|
3838
|
+
|
|
3839
|
+
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
|
|
3840
|
+
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);
|
|
3841
|
+
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);
|
|
3842
|
+
|
|
3843
|
+
EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active'$cmd$, tablename);
|
|
3844
|
+
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);
|
|
3845
|
+
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);
|
|
3846
|
+
|
|
3847
|
+
IF options->>'policy' = 'short' THEN
|
|
3848
|
+
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);
|
|
3849
|
+
ELSIF options->>'policy' = 'singleton' THEN
|
|
3850
|
+
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);
|
|
3851
|
+
ELSIF options->>'policy' = 'stately' THEN
|
|
3852
|
+
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);
|
|
3853
|
+
ELSIF options->>'policy' = 'exclusive' THEN
|
|
3854
|
+
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);
|
|
3855
|
+
ELSIF options->>'policy' = 'key_strict_fifo' THEN
|
|
3856
|
+
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);
|
|
3857
|
+
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);
|
|
3858
|
+
END IF;
|
|
3859
|
+
|
|
3860
|
+
EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
3861
|
+
EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
3862
|
+
END;
|
|
3863
|
+
$$
|
|
3864
|
+
LANGUAGE plpgsql;
|
|
3865
|
+
`,
|
|
3866
|
+
30: (schema) => `
|
|
3867
|
+
CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
3868
|
+
RETURNS VOID AS
|
|
3869
|
+
$$
|
|
3870
|
+
DECLARE
|
|
3871
|
+
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
3872
|
+
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
3873
|
+
ELSE 'job_common'
|
|
3874
|
+
END;
|
|
3875
|
+
queue_created_on timestamptz;
|
|
3876
|
+
BEGIN
|
|
3877
|
+
|
|
3878
|
+
WITH q as (
|
|
3879
|
+
INSERT INTO ${schema}.queue (
|
|
3880
|
+
name,
|
|
3881
|
+
policy,
|
|
3882
|
+
retry_limit,
|
|
3883
|
+
retry_delay,
|
|
3884
|
+
retry_backoff,
|
|
3885
|
+
retry_delay_max,
|
|
3886
|
+
expire_seconds,
|
|
3887
|
+
retention_seconds,
|
|
3888
|
+
deletion_seconds,
|
|
3889
|
+
warning_queued,
|
|
3890
|
+
dead_letter,
|
|
3891
|
+
partition,
|
|
3892
|
+
table_name,
|
|
3893
|
+
heartbeat_seconds
|
|
3894
|
+
)
|
|
3895
|
+
VALUES (
|
|
3896
|
+
queue_name,
|
|
3897
|
+
options->>'policy',
|
|
3898
|
+
COALESCE((options->>'retryLimit')::int, 2),
|
|
3899
|
+
COALESCE((options->>'retryDelay')::int, 0),
|
|
3900
|
+
COALESCE((options->>'retryBackoff')::bool, false),
|
|
3901
|
+
(options->>'retryDelayMax')::int,
|
|
3902
|
+
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
3903
|
+
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
3904
|
+
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
3905
|
+
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
3906
|
+
options->>'deadLetter',
|
|
3907
|
+
COALESCE((options->>'partition')::bool, false),
|
|
3908
|
+
tablename,
|
|
3909
|
+
(options->>'heartbeatSeconds')::int
|
|
3910
|
+
)
|
|
3911
|
+
ON CONFLICT DO NOTHING
|
|
3912
|
+
RETURNING created_on
|
|
3913
|
+
)
|
|
3914
|
+
SELECT created_on into queue_created_on from q;
|
|
3915
|
+
|
|
3916
|
+
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
3917
|
+
RETURN;
|
|
3918
|
+
END IF;
|
|
3919
|
+
|
|
3920
|
+
EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
|
|
3921
|
+
|
|
3922
|
+
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
|
|
3923
|
+
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);
|
|
3924
|
+
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);
|
|
3925
|
+
|
|
3926
|
+
EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active'$cmd$, tablename);
|
|
3927
|
+
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);
|
|
3928
|
+
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);
|
|
3929
|
+
|
|
3930
|
+
IF options->>'policy' = 'short' THEN
|
|
3931
|
+
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);
|
|
3932
|
+
ELSIF options->>'policy' = 'singleton' THEN
|
|
3933
|
+
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);
|
|
3934
|
+
ELSIF options->>'policy' = 'stately' THEN
|
|
3935
|
+
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);
|
|
3936
|
+
ELSIF options->>'policy' = 'exclusive' THEN
|
|
3937
|
+
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);
|
|
3938
|
+
ELSIF options->>'policy' = 'key_strict_fifo' THEN
|
|
3939
|
+
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);
|
|
3940
|
+
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);
|
|
3941
|
+
END IF;
|
|
3942
|
+
|
|
3943
|
+
EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
3944
|
+
EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
3945
|
+
END;
|
|
3946
|
+
$$
|
|
3947
|
+
LANGUAGE plpgsql;
|
|
3948
|
+
`,
|
|
3949
|
+
31: (schema) => `
|
|
3950
|
+
CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
3951
|
+
RETURNS VOID AS
|
|
3952
|
+
$$
|
|
3953
|
+
DECLARE
|
|
3954
|
+
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
3955
|
+
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
3956
|
+
ELSE 'job_common'
|
|
3957
|
+
END;
|
|
3958
|
+
queue_created_on timestamptz;
|
|
3959
|
+
BEGIN
|
|
3960
|
+
|
|
3961
|
+
WITH q as (
|
|
3962
|
+
INSERT INTO ${schema}.queue (
|
|
3963
|
+
name,
|
|
3964
|
+
policy,
|
|
3965
|
+
retry_limit,
|
|
3966
|
+
retry_delay,
|
|
3967
|
+
retry_backoff,
|
|
3968
|
+
retry_delay_max,
|
|
3969
|
+
expire_seconds,
|
|
3970
|
+
retention_seconds,
|
|
3971
|
+
deletion_seconds,
|
|
3972
|
+
warning_queued,
|
|
3973
|
+
dead_letter,
|
|
3974
|
+
partition,
|
|
3975
|
+
table_name,
|
|
3976
|
+
heartbeat_seconds
|
|
3977
|
+
)
|
|
3978
|
+
VALUES (
|
|
3979
|
+
queue_name,
|
|
3980
|
+
options->>'policy',
|
|
3981
|
+
COALESCE((options->>'retryLimit')::int, 2),
|
|
3982
|
+
COALESCE((options->>'retryDelay')::int, 0),
|
|
3983
|
+
COALESCE((options->>'retryBackoff')::bool, false),
|
|
3984
|
+
(options->>'retryDelayMax')::int,
|
|
3985
|
+
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
3986
|
+
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
3987
|
+
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
3988
|
+
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
3989
|
+
options->>'deadLetter',
|
|
3990
|
+
COALESCE((options->>'partition')::bool, false),
|
|
3991
|
+
tablename,
|
|
3992
|
+
(options->>'heartbeatSeconds')::int
|
|
3993
|
+
)
|
|
3994
|
+
ON CONFLICT DO NOTHING
|
|
3995
|
+
RETURNING created_on
|
|
3996
|
+
)
|
|
3997
|
+
SELECT created_on into queue_created_on from q;
|
|
3998
|
+
|
|
3999
|
+
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
4000
|
+
RETURN;
|
|
4001
|
+
END IF;
|
|
4002
|
+
|
|
4003
|
+
EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
|
|
4004
|
+
|
|
4005
|
+
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
|
|
4006
|
+
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);
|
|
4007
|
+
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);
|
|
4008
|
+
|
|
4009
|
+
EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active' AND NOT blocked$cmd$, tablename);
|
|
4010
|
+
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);
|
|
4011
|
+
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);
|
|
4012
|
+
|
|
4013
|
+
IF options->>'policy' = 'short' THEN
|
|
4014
|
+
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);
|
|
4015
|
+
ELSIF options->>'policy' = 'singleton' THEN
|
|
4016
|
+
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);
|
|
4017
|
+
ELSIF options->>'policy' = 'stately' THEN
|
|
4018
|
+
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);
|
|
4019
|
+
ELSIF options->>'policy' = 'exclusive' THEN
|
|
4020
|
+
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);
|
|
4021
|
+
ELSIF options->>'policy' = 'key_strict_fifo' THEN
|
|
4022
|
+
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);
|
|
4023
|
+
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);
|
|
4024
|
+
END IF;
|
|
4025
|
+
|
|
4026
|
+
EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
4027
|
+
EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
4028
|
+
END;
|
|
4029
|
+
$$
|
|
4030
|
+
LANGUAGE plpgsql;
|
|
4031
|
+
`,
|
|
4032
|
+
32: (schema) => `
|
|
4033
|
+
CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
4034
|
+
RETURNS VOID AS
|
|
4035
|
+
$$
|
|
4036
|
+
DECLARE
|
|
4037
|
+
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
4038
|
+
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
4039
|
+
ELSE 'job_common'
|
|
4040
|
+
END;
|
|
4041
|
+
queue_created_on timestamptz;
|
|
4042
|
+
BEGIN
|
|
4043
|
+
|
|
4044
|
+
WITH q as (
|
|
4045
|
+
INSERT INTO ${schema}.queue (
|
|
4046
|
+
name,
|
|
4047
|
+
policy,
|
|
4048
|
+
retry_limit,
|
|
4049
|
+
retry_delay,
|
|
4050
|
+
retry_backoff,
|
|
4051
|
+
retry_delay_max,
|
|
4052
|
+
expire_seconds,
|
|
4053
|
+
retention_seconds,
|
|
4054
|
+
deletion_seconds,
|
|
4055
|
+
warning_queued,
|
|
4056
|
+
dead_letter,
|
|
4057
|
+
partition,
|
|
4058
|
+
table_name,
|
|
4059
|
+
heartbeat_seconds,
|
|
4060
|
+
notify
|
|
4061
|
+
)
|
|
4062
|
+
VALUES (
|
|
4063
|
+
queue_name,
|
|
4064
|
+
options->>'policy',
|
|
4065
|
+
COALESCE((options->>'retryLimit')::int, 2),
|
|
4066
|
+
COALESCE((options->>'retryDelay')::int, 0),
|
|
4067
|
+
COALESCE((options->>'retryBackoff')::bool, false),
|
|
4068
|
+
(options->>'retryDelayMax')::int,
|
|
4069
|
+
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
4070
|
+
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
4071
|
+
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
4072
|
+
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
4073
|
+
options->>'deadLetter',
|
|
4074
|
+
COALESCE((options->>'partition')::bool, false),
|
|
4075
|
+
tablename,
|
|
4076
|
+
(options->>'heartbeatSeconds')::int,
|
|
4077
|
+
COALESCE((options->>'notify')::bool, false)
|
|
4078
|
+
)
|
|
4079
|
+
ON CONFLICT DO NOTHING
|
|
4080
|
+
RETURNING created_on
|
|
4081
|
+
)
|
|
4082
|
+
SELECT created_on into queue_created_on from q;
|
|
4083
|
+
|
|
4084
|
+
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
4085
|
+
RETURN;
|
|
4086
|
+
END IF;
|
|
4087
|
+
|
|
4088
|
+
EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
|
|
4089
|
+
|
|
4090
|
+
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
|
|
4091
|
+
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);
|
|
4092
|
+
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);
|
|
4093
|
+
|
|
4094
|
+
EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active' AND NOT blocked$cmd$, tablename);
|
|
4095
|
+
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);
|
|
4096
|
+
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);
|
|
4097
|
+
|
|
4098
|
+
IF options->>'policy' = 'short' THEN
|
|
4099
|
+
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);
|
|
4100
|
+
ELSIF options->>'policy' = 'singleton' THEN
|
|
4101
|
+
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);
|
|
4102
|
+
ELSIF options->>'policy' = 'stately' THEN
|
|
4103
|
+
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);
|
|
4104
|
+
ELSIF options->>'policy' = 'exclusive' THEN
|
|
4105
|
+
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);
|
|
4106
|
+
ELSIF options->>'policy' = 'key_strict_fifo' THEN
|
|
4107
|
+
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);
|
|
4108
|
+
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);
|
|
4109
|
+
END IF;
|
|
4110
|
+
|
|
4111
|
+
EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
4112
|
+
EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
4113
|
+
END;
|
|
4114
|
+
$$
|
|
4115
|
+
LANGUAGE plpgsql;
|
|
4116
|
+
`
|
|
4117
|
+
};
|
|
4118
|
+
function getAll(schema) {
|
|
4119
|
+
return [
|
|
4120
|
+
{
|
|
4121
|
+
release: "11.1.0",
|
|
4122
|
+
version: 26,
|
|
4123
|
+
previous: 25,
|
|
4124
|
+
install: [createQueueFn[26](schema), `CREATE UNIQUE INDEX job_i6 ON ${schema}.job_common (name, COALESCE(singleton_key, '')) WHERE state <= 'active' AND policy = 'exclusive'`],
|
|
4125
|
+
uninstall: [`DROP INDEX ${schema}.job_i6`]
|
|
4126
|
+
},
|
|
4127
|
+
{
|
|
4128
|
+
release: "12.6.0",
|
|
4129
|
+
version: 27,
|
|
4130
|
+
previous: 26,
|
|
4131
|
+
install: [
|
|
4132
|
+
`ALTER TABLE ${schema}.version ADD COLUMN IF NOT EXISTS bam_on timestamp with time zone`,
|
|
4133
|
+
`
|
|
4134
|
+
CREATE TABLE IF NOT EXISTS ${schema}.bam (
|
|
4135
|
+
id uuid PRIMARY KEY default gen_random_uuid(),
|
|
4136
|
+
name text NOT NULL,
|
|
4137
|
+
version int NOT NULL,
|
|
4138
|
+
status text NOT NULL DEFAULT 'pending',
|
|
4139
|
+
queue text,
|
|
4140
|
+
table_name text NOT NULL,
|
|
4141
|
+
command text NOT NULL,
|
|
4142
|
+
error text,
|
|
4143
|
+
created_on timestamp with time zone NOT NULL DEFAULT now(),
|
|
4144
|
+
started_on timestamp with time zone,
|
|
4145
|
+
completed_on timestamp with time zone
|
|
4146
|
+
)
|
|
4147
|
+
`,
|
|
4148
|
+
`CREATE FUNCTION ${schema}.job_table_format(command text, table_name text)
|
|
4149
|
+
RETURNS text AS
|
|
4150
|
+
$$
|
|
4151
|
+
SELECT format(
|
|
4152
|
+
replace(
|
|
4153
|
+
replace(command, '.job', '.%1$I'),
|
|
4154
|
+
'job_i', '%1$s_i'
|
|
4155
|
+
),
|
|
4156
|
+
table_name
|
|
4157
|
+
);
|
|
4158
|
+
$$
|
|
4159
|
+
LANGUAGE sql IMMUTABLE;
|
|
4160
|
+
`,
|
|
4161
|
+
`
|
|
4162
|
+
CREATE OR REPLACE FUNCTION ${schema}.job_table_run_async(command_name text, version int, command text, tbl_name text DEFAULT NULL, queue_name text DEFAULT NULL)
|
|
4163
|
+
RETURNS VOID AS
|
|
4164
|
+
$$
|
|
4165
|
+
BEGIN
|
|
4166
|
+
IF queue_name IS NOT NULL THEN
|
|
4167
|
+
SELECT table_name INTO tbl_name FROM ${schema}.queue WHERE name = queue_name;
|
|
4168
|
+
END IF;
|
|
4169
|
+
|
|
4170
|
+
IF tbl_name IS NOT NULL THEN
|
|
4171
|
+
INSERT INTO ${schema}.bam (name, version, status, queue, table_name, command)
|
|
4172
|
+
VALUES (
|
|
4173
|
+
command_name,
|
|
4174
|
+
version,
|
|
4175
|
+
'pending',
|
|
4176
|
+
queue_name,
|
|
4177
|
+
tbl_name,
|
|
4178
|
+
${schema}.job_table_format(command, tbl_name)
|
|
4179
|
+
);
|
|
4180
|
+
RETURN;
|
|
4181
|
+
END IF;
|
|
4182
|
+
|
|
4183
|
+
INSERT INTO ${schema}.bam (name, version, status, queue, table_name, command)
|
|
4184
|
+
SELECT
|
|
4185
|
+
command_name,
|
|
4186
|
+
version,
|
|
4187
|
+
'pending',
|
|
4188
|
+
NULL,
|
|
4189
|
+
'job_common',
|
|
3567
4190
|
${schema}.job_table_format(command, 'job_common')
|
|
3568
4191
|
UNION ALL
|
|
3569
4192
|
SELECT
|
|
@@ -3605,86 +4228,9 @@ function getAll(schema) {
|
|
|
3605
4228
|
$$
|
|
3606
4229
|
LANGUAGE plpgsql;
|
|
3607
4230
|
`,
|
|
3608
|
-
`ALTER TABLE ${schema}.job ADD COLUMN IF NOT EXISTS group_id text`,
|
|
3609
|
-
`ALTER TABLE ${schema}.job ADD COLUMN IF NOT EXISTS group_tier text`,
|
|
3610
|
-
|
|
3611
|
-
CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
3612
|
-
RETURNS VOID AS
|
|
3613
|
-
$$
|
|
3614
|
-
DECLARE
|
|
3615
|
-
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
3616
|
-
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
3617
|
-
ELSE 'job_common'
|
|
3618
|
-
END;
|
|
3619
|
-
queue_created_on timestamptz;
|
|
3620
|
-
BEGIN
|
|
3621
|
-
|
|
3622
|
-
WITH q as (
|
|
3623
|
-
INSERT INTO ${schema}.queue (
|
|
3624
|
-
name,
|
|
3625
|
-
policy,
|
|
3626
|
-
retry_limit,
|
|
3627
|
-
retry_delay,
|
|
3628
|
-
retry_backoff,
|
|
3629
|
-
retry_delay_max,
|
|
3630
|
-
expire_seconds,
|
|
3631
|
-
retention_seconds,
|
|
3632
|
-
deletion_seconds,
|
|
3633
|
-
warning_queued,
|
|
3634
|
-
dead_letter,
|
|
3635
|
-
partition,
|
|
3636
|
-
table_name
|
|
3637
|
-
)
|
|
3638
|
-
VALUES (
|
|
3639
|
-
queue_name,
|
|
3640
|
-
options->>'policy',
|
|
3641
|
-
COALESCE((options->>'retryLimit')::int, 2),
|
|
3642
|
-
COALESCE((options->>'retryDelay')::int, 0),
|
|
3643
|
-
COALESCE((options->>'retryBackoff')::bool, false),
|
|
3644
|
-
(options->>'retryDelayMax')::int,
|
|
3645
|
-
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
3646
|
-
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
3647
|
-
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
3648
|
-
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
3649
|
-
options->>'deadLetter',
|
|
3650
|
-
COALESCE((options->>'partition')::bool, false),
|
|
3651
|
-
tablename
|
|
3652
|
-
)
|
|
3653
|
-
ON CONFLICT DO NOTHING
|
|
3654
|
-
RETURNING created_on
|
|
3655
|
-
)
|
|
3656
|
-
SELECT created_on into queue_created_on from q;
|
|
3657
|
-
|
|
3658
|
-
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
3659
|
-
RETURN;
|
|
3660
|
-
END IF;
|
|
3661
|
-
|
|
3662
|
-
EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
|
|
3663
|
-
|
|
3664
|
-
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
|
|
3665
|
-
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);
|
|
3666
|
-
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);
|
|
3667
|
-
|
|
3668
|
-
EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active'$cmd$, tablename);
|
|
3669
|
-
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);
|
|
3670
|
-
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);
|
|
3671
|
-
|
|
3672
|
-
IF options->>'policy' = 'short' THEN
|
|
3673
|
-
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);
|
|
3674
|
-
ELSIF options->>'policy' = 'singleton' THEN
|
|
3675
|
-
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);
|
|
3676
|
-
ELSIF options->>'policy' = 'stately' THEN
|
|
3677
|
-
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);
|
|
3678
|
-
ELSIF options->>'policy' = 'exclusive' THEN
|
|
3679
|
-
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);
|
|
3680
|
-
END IF;
|
|
3681
|
-
|
|
3682
|
-
EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
3683
|
-
EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
3684
|
-
END;
|
|
3685
|
-
$$
|
|
3686
|
-
LANGUAGE plpgsql;
|
|
3687
|
-
`,
|
|
4231
|
+
`ALTER TABLE ${schema}.job ADD COLUMN IF NOT EXISTS group_id text`,
|
|
4232
|
+
`ALTER TABLE ${schema}.job ADD COLUMN IF NOT EXISTS group_tier text`,
|
|
4233
|
+
createQueueFn[27](schema),
|
|
3688
4234
|
`ALTER INDEX IF EXISTS ${schema}.job_i1 RENAME TO job_common_i1`,
|
|
3689
4235
|
`ALTER INDEX IF EXISTS ${schema}.job_i2 RENAME TO job_common_i2`,
|
|
3690
4236
|
`ALTER INDEX IF EXISTS ${schema}.job_i3 RENAME TO job_common_i3`,
|
|
@@ -3708,83 +4254,7 @@ function getAll(schema) {
|
|
|
3708
4254
|
`ALTER INDEX ${schema}.job_common_i2 RENAME TO job_i2`,
|
|
3709
4255
|
`ALTER INDEX ${schema}.job_common_i1 RENAME TO job_i1`,
|
|
3710
4256
|
`SELECT ${schema}.job_table_run('DROP INDEX ${schema}.job_i7')`,
|
|
3711
|
-
|
|
3712
|
-
CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
3713
|
-
RETURNS VOID AS
|
|
3714
|
-
$$
|
|
3715
|
-
DECLARE
|
|
3716
|
-
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
3717
|
-
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
3718
|
-
ELSE 'job_common'
|
|
3719
|
-
END;
|
|
3720
|
-
queue_created_on timestamptz;
|
|
3721
|
-
BEGIN
|
|
3722
|
-
|
|
3723
|
-
WITH q as (
|
|
3724
|
-
INSERT INTO ${schema}.queue (
|
|
3725
|
-
name,
|
|
3726
|
-
policy,
|
|
3727
|
-
retry_limit,
|
|
3728
|
-
retry_delay,
|
|
3729
|
-
retry_backoff,
|
|
3730
|
-
retry_delay_max,
|
|
3731
|
-
expire_seconds,
|
|
3732
|
-
retention_seconds,
|
|
3733
|
-
deletion_seconds,
|
|
3734
|
-
warning_queued,
|
|
3735
|
-
dead_letter,
|
|
3736
|
-
partition,
|
|
3737
|
-
table_name
|
|
3738
|
-
)
|
|
3739
|
-
VALUES (
|
|
3740
|
-
queue_name,
|
|
3741
|
-
options->>'policy',
|
|
3742
|
-
COALESCE((options->>'retryLimit')::int, 2),
|
|
3743
|
-
COALESCE((options->>'retryDelay')::int, 0),
|
|
3744
|
-
COALESCE((options->>'retryBackoff')::bool, false),
|
|
3745
|
-
(options->>'retryDelayMax')::int,
|
|
3746
|
-
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
3747
|
-
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
3748
|
-
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
3749
|
-
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
3750
|
-
options->>'deadLetter',
|
|
3751
|
-
COALESCE((options->>'partition')::bool, false),
|
|
3752
|
-
tablename
|
|
3753
|
-
)
|
|
3754
|
-
ON CONFLICT DO NOTHING
|
|
3755
|
-
RETURNING created_on
|
|
3756
|
-
)
|
|
3757
|
-
SELECT created_on into queue_created_on from q;
|
|
3758
|
-
|
|
3759
|
-
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
3760
|
-
RETURN;
|
|
3761
|
-
END IF;
|
|
3762
|
-
|
|
3763
|
-
EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
|
|
3764
|
-
|
|
3765
|
-
EXECUTE format('ALTER TABLE ${schema}.%1$I ADD PRIMARY KEY (name, id)', tablename);
|
|
3766
|
-
EXECUTE format('ALTER TABLE ${schema}.%1$I ADD CONSTRAINT q_fkey FOREIGN KEY (name) REFERENCES ${schema}.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED', tablename);
|
|
3767
|
-
EXECUTE format('ALTER TABLE ${schema}.%1$I ADD CONSTRAINT dlq_fkey FOREIGN KEY (dead_letter) REFERENCES ${schema}.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED', tablename);
|
|
3768
|
-
|
|
3769
|
-
EXECUTE format('CREATE INDEX %1$s_i5 ON ${schema}.%1$I (name, start_after) INCLUDE (priority, created_on, id) WHERE state < ''active''', tablename);
|
|
3770
|
-
EXECUTE format('CREATE UNIQUE INDEX %1$s_i4 ON ${schema}.%1$I (name, singleton_on, COALESCE(singleton_key, '''')) WHERE state <> ''cancelled'' AND singleton_on IS NOT NULL', tablename);
|
|
3771
|
-
|
|
3772
|
-
IF options->>'policy' = 'short' THEN
|
|
3773
|
-
EXECUTE format('CREATE UNIQUE INDEX %1$s_i1 ON ${schema}.%1$I (name, COALESCE(singleton_key, '''')) WHERE state = ''created'' AND policy = ''short''', tablename);
|
|
3774
|
-
ELSIF options->>'policy' = 'singleton' THEN
|
|
3775
|
-
EXECUTE format('CREATE UNIQUE INDEX %1$s_i2 ON ${schema}.%1$I (name, COALESCE(singleton_key, '''')) WHERE state = ''active'' AND policy = ''singleton''', tablename);
|
|
3776
|
-
ELSIF options->>'policy' = 'stately' THEN
|
|
3777
|
-
EXECUTE format('CREATE UNIQUE INDEX %1$s_i3 ON ${schema}.%1$I (name, state, COALESCE(singleton_key, '''')) WHERE state <= ''active'' AND policy = ''stately''', tablename);
|
|
3778
|
-
ELSIF options->>'policy' = 'exclusive' THEN
|
|
3779
|
-
EXECUTE format('CREATE UNIQUE INDEX %1$s_i6 ON ${schema}.%1$I (name, COALESCE(singleton_key, '''')) WHERE state <= ''active'' AND policy = ''exclusive''', tablename);
|
|
3780
|
-
END IF;
|
|
3781
|
-
|
|
3782
|
-
EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
3783
|
-
EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
3784
|
-
END;
|
|
3785
|
-
$$
|
|
3786
|
-
LANGUAGE plpgsql;
|
|
3787
|
-
`,
|
|
4257
|
+
createQueueFn[26](schema),
|
|
3788
4258
|
`DROP FUNCTION ${schema}.job_table_run(text, text, text)`,
|
|
3789
4259
|
`DROP FUNCTION ${schema}.job_table_run_async(text, int, text, text, text)`,
|
|
3790
4260
|
`DROP FUNCTION ${schema}.job_table_format(text, text)`,
|
|
@@ -3798,87 +4268,7 @@ function getAll(schema) {
|
|
|
3798
4268
|
release: "12.10.0",
|
|
3799
4269
|
version: 28,
|
|
3800
4270
|
previous: 27,
|
|
3801
|
-
install: [`SELECT ${schema}.job_table_run($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$, 'job_common')`,
|
|
3802
|
-
CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
3803
|
-
RETURNS VOID AS
|
|
3804
|
-
$$
|
|
3805
|
-
DECLARE
|
|
3806
|
-
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
3807
|
-
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
3808
|
-
ELSE 'job_common'
|
|
3809
|
-
END;
|
|
3810
|
-
queue_created_on timestamptz;
|
|
3811
|
-
BEGIN
|
|
3812
|
-
|
|
3813
|
-
WITH q as (
|
|
3814
|
-
INSERT INTO ${schema}.queue (
|
|
3815
|
-
name,
|
|
3816
|
-
policy,
|
|
3817
|
-
retry_limit,
|
|
3818
|
-
retry_delay,
|
|
3819
|
-
retry_backoff,
|
|
3820
|
-
retry_delay_max,
|
|
3821
|
-
expire_seconds,
|
|
3822
|
-
retention_seconds,
|
|
3823
|
-
deletion_seconds,
|
|
3824
|
-
warning_queued,
|
|
3825
|
-
dead_letter,
|
|
3826
|
-
partition,
|
|
3827
|
-
table_name
|
|
3828
|
-
)
|
|
3829
|
-
VALUES (
|
|
3830
|
-
queue_name,
|
|
3831
|
-
options->>'policy',
|
|
3832
|
-
COALESCE((options->>'retryLimit')::int, 2),
|
|
3833
|
-
COALESCE((options->>'retryDelay')::int, 0),
|
|
3834
|
-
COALESCE((options->>'retryBackoff')::bool, false),
|
|
3835
|
-
(options->>'retryDelayMax')::int,
|
|
3836
|
-
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
3837
|
-
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
3838
|
-
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
3839
|
-
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
3840
|
-
options->>'deadLetter',
|
|
3841
|
-
COALESCE((options->>'partition')::bool, false),
|
|
3842
|
-
tablename
|
|
3843
|
-
)
|
|
3844
|
-
ON CONFLICT DO NOTHING
|
|
3845
|
-
RETURNING created_on
|
|
3846
|
-
)
|
|
3847
|
-
SELECT created_on into queue_created_on from q;
|
|
3848
|
-
|
|
3849
|
-
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
3850
|
-
RETURN;
|
|
3851
|
-
END IF;
|
|
3852
|
-
|
|
3853
|
-
EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
|
|
3854
|
-
|
|
3855
|
-
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
|
|
3856
|
-
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);
|
|
3857
|
-
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);
|
|
3858
|
-
|
|
3859
|
-
EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active'$cmd$, tablename);
|
|
3860
|
-
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);
|
|
3861
|
-
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);
|
|
3862
|
-
|
|
3863
|
-
IF options->>'policy' = 'short' THEN
|
|
3864
|
-
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);
|
|
3865
|
-
ELSIF options->>'policy' = 'singleton' THEN
|
|
3866
|
-
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);
|
|
3867
|
-
ELSIF options->>'policy' = 'stately' THEN
|
|
3868
|
-
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);
|
|
3869
|
-
ELSIF options->>'policy' = 'exclusive' THEN
|
|
3870
|
-
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);
|
|
3871
|
-
ELSIF options->>'policy' = 'key_strict_fifo' THEN
|
|
3872
|
-
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);
|
|
3873
|
-
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);
|
|
3874
|
-
END IF;
|
|
3875
|
-
|
|
3876
|
-
EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
3877
|
-
EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
3878
|
-
END;
|
|
3879
|
-
$$
|
|
3880
|
-
LANGUAGE plpgsql;
|
|
3881
|
-
`],
|
|
4271
|
+
install: [`SELECT ${schema}.job_table_run($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$, 'job_common')`, createQueueFn[28](schema)],
|
|
3882
4272
|
async: [`SELECT ${schema}.job_table_run_async(
|
|
3883
4273
|
'key_strict_fifo_index',
|
|
3884
4274
|
$VERSION$,
|
|
@@ -3889,84 +4279,7 @@ function getAll(schema) {
|
|
|
3889
4279
|
uninstall: [
|
|
3890
4280
|
`SELECT ${schema}.job_table_run('DROP INDEX IF EXISTS ${schema}.job_i8')`,
|
|
3891
4281
|
`SELECT ${schema}.job_table_run('ALTER TABLE ${schema}.job DROP CONSTRAINT IF EXISTS job_key_strict_fifo_singleton_key_check')`,
|
|
3892
|
-
|
|
3893
|
-
CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
3894
|
-
RETURNS VOID AS
|
|
3895
|
-
$$
|
|
3896
|
-
DECLARE
|
|
3897
|
-
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
3898
|
-
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
3899
|
-
ELSE 'job_common'
|
|
3900
|
-
END;
|
|
3901
|
-
queue_created_on timestamptz;
|
|
3902
|
-
BEGIN
|
|
3903
|
-
|
|
3904
|
-
WITH q as (
|
|
3905
|
-
INSERT INTO ${schema}.queue (
|
|
3906
|
-
name,
|
|
3907
|
-
policy,
|
|
3908
|
-
retry_limit,
|
|
3909
|
-
retry_delay,
|
|
3910
|
-
retry_backoff,
|
|
3911
|
-
retry_delay_max,
|
|
3912
|
-
expire_seconds,
|
|
3913
|
-
retention_seconds,
|
|
3914
|
-
deletion_seconds,
|
|
3915
|
-
warning_queued,
|
|
3916
|
-
dead_letter,
|
|
3917
|
-
partition,
|
|
3918
|
-
table_name
|
|
3919
|
-
)
|
|
3920
|
-
VALUES (
|
|
3921
|
-
queue_name,
|
|
3922
|
-
options->>'policy',
|
|
3923
|
-
COALESCE((options->>'retryLimit')::int, 2),
|
|
3924
|
-
COALESCE((options->>'retryDelay')::int, 0),
|
|
3925
|
-
COALESCE((options->>'retryBackoff')::bool, false),
|
|
3926
|
-
(options->>'retryDelayMax')::int,
|
|
3927
|
-
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
3928
|
-
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
3929
|
-
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
3930
|
-
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
3931
|
-
options->>'deadLetter',
|
|
3932
|
-
COALESCE((options->>'partition')::bool, false),
|
|
3933
|
-
tablename
|
|
3934
|
-
)
|
|
3935
|
-
ON CONFLICT DO NOTHING
|
|
3936
|
-
RETURNING created_on
|
|
3937
|
-
)
|
|
3938
|
-
SELECT created_on into queue_created_on from q;
|
|
3939
|
-
|
|
3940
|
-
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
3941
|
-
RETURN;
|
|
3942
|
-
END IF;
|
|
3943
|
-
|
|
3944
|
-
EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
|
|
3945
|
-
|
|
3946
|
-
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
|
|
3947
|
-
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);
|
|
3948
|
-
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);
|
|
3949
|
-
|
|
3950
|
-
EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active'$cmd$, tablename);
|
|
3951
|
-
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);
|
|
3952
|
-
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);
|
|
3953
|
-
|
|
3954
|
-
IF options->>'policy' = 'short' THEN
|
|
3955
|
-
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);
|
|
3956
|
-
ELSIF options->>'policy' = 'singleton' THEN
|
|
3957
|
-
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);
|
|
3958
|
-
ELSIF options->>'policy' = 'stately' THEN
|
|
3959
|
-
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);
|
|
3960
|
-
ELSIF options->>'policy' = 'exclusive' THEN
|
|
3961
|
-
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);
|
|
3962
|
-
END IF;
|
|
3963
|
-
|
|
3964
|
-
EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
3965
|
-
EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
3966
|
-
END;
|
|
3967
|
-
$$
|
|
3968
|
-
LANGUAGE plpgsql;
|
|
3969
|
-
`
|
|
4282
|
+
createQueueFn[27](schema)
|
|
3970
4283
|
]
|
|
3971
4284
|
},
|
|
3972
4285
|
{
|
|
@@ -3990,172 +4303,10 @@ function getAll(schema) {
|
|
|
3990
4303
|
`ALTER TABLE ${schema}.job ADD COLUMN heartbeat_on timestamp with time zone`,
|
|
3991
4304
|
`ALTER TABLE ${schema}.job ADD COLUMN heartbeat_seconds int`,
|
|
3992
4305
|
`ALTER TABLE ${schema}.queue ADD COLUMN heartbeat_seconds int`,
|
|
3993
|
-
|
|
3994
|
-
CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
3995
|
-
RETURNS VOID AS
|
|
3996
|
-
$$
|
|
3997
|
-
DECLARE
|
|
3998
|
-
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
3999
|
-
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
4000
|
-
ELSE 'job_common'
|
|
4001
|
-
END;
|
|
4002
|
-
queue_created_on timestamptz;
|
|
4003
|
-
BEGIN
|
|
4004
|
-
|
|
4005
|
-
WITH q as (
|
|
4006
|
-
INSERT INTO ${schema}.queue (
|
|
4007
|
-
name,
|
|
4008
|
-
policy,
|
|
4009
|
-
retry_limit,
|
|
4010
|
-
retry_delay,
|
|
4011
|
-
retry_backoff,
|
|
4012
|
-
retry_delay_max,
|
|
4013
|
-
expire_seconds,
|
|
4014
|
-
retention_seconds,
|
|
4015
|
-
deletion_seconds,
|
|
4016
|
-
warning_queued,
|
|
4017
|
-
dead_letter,
|
|
4018
|
-
partition,
|
|
4019
|
-
table_name,
|
|
4020
|
-
heartbeat_seconds
|
|
4021
|
-
)
|
|
4022
|
-
VALUES (
|
|
4023
|
-
queue_name,
|
|
4024
|
-
options->>'policy',
|
|
4025
|
-
COALESCE((options->>'retryLimit')::int, 2),
|
|
4026
|
-
COALESCE((options->>'retryDelay')::int, 0),
|
|
4027
|
-
COALESCE((options->>'retryBackoff')::bool, false),
|
|
4028
|
-
(options->>'retryDelayMax')::int,
|
|
4029
|
-
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
4030
|
-
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
4031
|
-
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
4032
|
-
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
4033
|
-
options->>'deadLetter',
|
|
4034
|
-
COALESCE((options->>'partition')::bool, false),
|
|
4035
|
-
tablename,
|
|
4036
|
-
(options->>'heartbeatSeconds')::int
|
|
4037
|
-
)
|
|
4038
|
-
ON CONFLICT DO NOTHING
|
|
4039
|
-
RETURNING created_on
|
|
4040
|
-
)
|
|
4041
|
-
SELECT created_on into queue_created_on from q;
|
|
4042
|
-
|
|
4043
|
-
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
4044
|
-
RETURN;
|
|
4045
|
-
END IF;
|
|
4046
|
-
|
|
4047
|
-
EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
|
|
4048
|
-
|
|
4049
|
-
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
|
|
4050
|
-
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);
|
|
4051
|
-
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);
|
|
4052
|
-
|
|
4053
|
-
EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active'$cmd$, tablename);
|
|
4054
|
-
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);
|
|
4055
|
-
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);
|
|
4056
|
-
|
|
4057
|
-
IF options->>'policy' = 'short' THEN
|
|
4058
|
-
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);
|
|
4059
|
-
ELSIF options->>'policy' = 'singleton' THEN
|
|
4060
|
-
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);
|
|
4061
|
-
ELSIF options->>'policy' = 'stately' THEN
|
|
4062
|
-
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);
|
|
4063
|
-
ELSIF options->>'policy' = 'exclusive' THEN
|
|
4064
|
-
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);
|
|
4065
|
-
ELSIF options->>'policy' = 'key_strict_fifo' THEN
|
|
4066
|
-
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);
|
|
4067
|
-
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);
|
|
4068
|
-
END IF;
|
|
4069
|
-
|
|
4070
|
-
EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
4071
|
-
EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
4072
|
-
END;
|
|
4073
|
-
$$
|
|
4074
|
-
LANGUAGE plpgsql;
|
|
4075
|
-
`
|
|
4306
|
+
createQueueFn[30](schema)
|
|
4076
4307
|
],
|
|
4077
4308
|
uninstall: [
|
|
4078
|
-
|
|
4079
|
-
CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
4080
|
-
RETURNS VOID AS
|
|
4081
|
-
$$
|
|
4082
|
-
DECLARE
|
|
4083
|
-
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
4084
|
-
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
4085
|
-
ELSE 'job_common'
|
|
4086
|
-
END;
|
|
4087
|
-
queue_created_on timestamptz;
|
|
4088
|
-
BEGIN
|
|
4089
|
-
|
|
4090
|
-
WITH q as (
|
|
4091
|
-
INSERT INTO ${schema}.queue (
|
|
4092
|
-
name,
|
|
4093
|
-
policy,
|
|
4094
|
-
retry_limit,
|
|
4095
|
-
retry_delay,
|
|
4096
|
-
retry_backoff,
|
|
4097
|
-
retry_delay_max,
|
|
4098
|
-
expire_seconds,
|
|
4099
|
-
retention_seconds,
|
|
4100
|
-
deletion_seconds,
|
|
4101
|
-
warning_queued,
|
|
4102
|
-
dead_letter,
|
|
4103
|
-
partition,
|
|
4104
|
-
table_name
|
|
4105
|
-
)
|
|
4106
|
-
VALUES (
|
|
4107
|
-
queue_name,
|
|
4108
|
-
options->>'policy',
|
|
4109
|
-
COALESCE((options->>'retryLimit')::int, 2),
|
|
4110
|
-
COALESCE((options->>'retryDelay')::int, 0),
|
|
4111
|
-
COALESCE((options->>'retryBackoff')::bool, false),
|
|
4112
|
-
(options->>'retryDelayMax')::int,
|
|
4113
|
-
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
4114
|
-
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
4115
|
-
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
4116
|
-
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
4117
|
-
options->>'deadLetter',
|
|
4118
|
-
COALESCE((options->>'partition')::bool, false),
|
|
4119
|
-
tablename
|
|
4120
|
-
)
|
|
4121
|
-
ON CONFLICT DO NOTHING
|
|
4122
|
-
RETURNING created_on
|
|
4123
|
-
)
|
|
4124
|
-
SELECT created_on into queue_created_on from q;
|
|
4125
|
-
|
|
4126
|
-
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
4127
|
-
RETURN;
|
|
4128
|
-
END IF;
|
|
4129
|
-
|
|
4130
|
-
EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
|
|
4131
|
-
|
|
4132
|
-
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
|
|
4133
|
-
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);
|
|
4134
|
-
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);
|
|
4135
|
-
|
|
4136
|
-
EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active'$cmd$, tablename);
|
|
4137
|
-
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);
|
|
4138
|
-
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);
|
|
4139
|
-
|
|
4140
|
-
IF options->>'policy' = 'short' THEN
|
|
4141
|
-
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);
|
|
4142
|
-
ELSIF options->>'policy' = 'singleton' THEN
|
|
4143
|
-
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);
|
|
4144
|
-
ELSIF options->>'policy' = 'stately' THEN
|
|
4145
|
-
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);
|
|
4146
|
-
ELSIF options->>'policy' = 'exclusive' THEN
|
|
4147
|
-
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);
|
|
4148
|
-
ELSIF options->>'policy' = 'key_strict_fifo' THEN
|
|
4149
|
-
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);
|
|
4150
|
-
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);
|
|
4151
|
-
END IF;
|
|
4152
|
-
|
|
4153
|
-
EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
4154
|
-
EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
4155
|
-
END;
|
|
4156
|
-
$$
|
|
4157
|
-
LANGUAGE plpgsql;
|
|
4158
|
-
`,
|
|
4309
|
+
createQueueFn[28](schema),
|
|
4159
4310
|
`ALTER TABLE ${schema}.queue DROP COLUMN heartbeat_seconds`,
|
|
4160
4311
|
`ALTER TABLE ${schema}.job DROP COLUMN heartbeat_seconds`,
|
|
4161
4312
|
`ALTER TABLE ${schema}.job DROP COLUMN heartbeat_on`
|
|
@@ -4181,182 +4332,35 @@ function getAll(schema) {
|
|
|
4181
4332
|
`CREATE INDEX IF NOT EXISTS job_dep_parent_idx ON ${schema}.job_dependency (parent_name, parent_id)`,
|
|
4182
4333
|
`SELECT ${schema}.job_table_run($cmd$DROP INDEX IF EXISTS ${schema}.job_i5$cmd$)`,
|
|
4183
4334
|
`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$)`,
|
|
4184
|
-
|
|
4185
|
-
CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
4186
|
-
RETURNS VOID AS
|
|
4187
|
-
$$
|
|
4188
|
-
DECLARE
|
|
4189
|
-
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
4190
|
-
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
4191
|
-
ELSE 'job_common'
|
|
4192
|
-
END;
|
|
4193
|
-
queue_created_on timestamptz;
|
|
4194
|
-
BEGIN
|
|
4195
|
-
|
|
4196
|
-
WITH q as (
|
|
4197
|
-
INSERT INTO ${schema}.queue (
|
|
4198
|
-
name,
|
|
4199
|
-
policy,
|
|
4200
|
-
retry_limit,
|
|
4201
|
-
retry_delay,
|
|
4202
|
-
retry_backoff,
|
|
4203
|
-
retry_delay_max,
|
|
4204
|
-
expire_seconds,
|
|
4205
|
-
retention_seconds,
|
|
4206
|
-
deletion_seconds,
|
|
4207
|
-
warning_queued,
|
|
4208
|
-
dead_letter,
|
|
4209
|
-
partition,
|
|
4210
|
-
table_name,
|
|
4211
|
-
heartbeat_seconds
|
|
4212
|
-
)
|
|
4213
|
-
VALUES (
|
|
4214
|
-
queue_name,
|
|
4215
|
-
options->>'policy',
|
|
4216
|
-
COALESCE((options->>'retryLimit')::int, 2),
|
|
4217
|
-
COALESCE((options->>'retryDelay')::int, 0),
|
|
4218
|
-
COALESCE((options->>'retryBackoff')::bool, false),
|
|
4219
|
-
(options->>'retryDelayMax')::int,
|
|
4220
|
-
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
4221
|
-
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
4222
|
-
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
4223
|
-
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
4224
|
-
options->>'deadLetter',
|
|
4225
|
-
COALESCE((options->>'partition')::bool, false),
|
|
4226
|
-
tablename,
|
|
4227
|
-
(options->>'heartbeatSeconds')::int
|
|
4228
|
-
)
|
|
4229
|
-
ON CONFLICT DO NOTHING
|
|
4230
|
-
RETURNING created_on
|
|
4231
|
-
)
|
|
4232
|
-
SELECT created_on into queue_created_on from q;
|
|
4233
|
-
|
|
4234
|
-
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
4235
|
-
RETURN;
|
|
4236
|
-
END IF;
|
|
4237
|
-
|
|
4238
|
-
EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
|
|
4239
|
-
|
|
4240
|
-
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
|
|
4241
|
-
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);
|
|
4242
|
-
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);
|
|
4243
|
-
|
|
4244
|
-
EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active' AND NOT blocked$cmd$, tablename);
|
|
4245
|
-
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);
|
|
4246
|
-
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);
|
|
4247
|
-
|
|
4248
|
-
IF options->>'policy' = 'short' THEN
|
|
4249
|
-
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);
|
|
4250
|
-
ELSIF options->>'policy' = 'singleton' THEN
|
|
4251
|
-
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);
|
|
4252
|
-
ELSIF options->>'policy' = 'stately' THEN
|
|
4253
|
-
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);
|
|
4254
|
-
ELSIF options->>'policy' = 'exclusive' THEN
|
|
4255
|
-
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);
|
|
4256
|
-
ELSIF options->>'policy' = 'key_strict_fifo' THEN
|
|
4257
|
-
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);
|
|
4258
|
-
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);
|
|
4259
|
-
END IF;
|
|
4260
|
-
|
|
4261
|
-
EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
4262
|
-
EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
4263
|
-
END;
|
|
4264
|
-
$$
|
|
4265
|
-
LANGUAGE plpgsql;
|
|
4266
|
-
`
|
|
4335
|
+
createQueueFn[31](schema)
|
|
4267
4336
|
],
|
|
4268
4337
|
uninstall: [
|
|
4269
4338
|
`DROP INDEX IF EXISTS ${schema}.job_dep_parent_idx`,
|
|
4270
4339
|
`DROP TABLE IF EXISTS ${schema}.job_dependency`,
|
|
4271
|
-
|
|
4272
|
-
CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
4273
|
-
RETURNS VOID AS
|
|
4274
|
-
$$
|
|
4275
|
-
DECLARE
|
|
4276
|
-
tablename varchar := CASE WHEN options->>'partition' = 'true'
|
|
4277
|
-
THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
|
|
4278
|
-
ELSE 'job_common'
|
|
4279
|
-
END;
|
|
4280
|
-
queue_created_on timestamptz;
|
|
4281
|
-
BEGIN
|
|
4282
|
-
|
|
4283
|
-
WITH q as (
|
|
4284
|
-
INSERT INTO ${schema}.queue (
|
|
4285
|
-
name,
|
|
4286
|
-
policy,
|
|
4287
|
-
retry_limit,
|
|
4288
|
-
retry_delay,
|
|
4289
|
-
retry_backoff,
|
|
4290
|
-
retry_delay_max,
|
|
4291
|
-
expire_seconds,
|
|
4292
|
-
retention_seconds,
|
|
4293
|
-
deletion_seconds,
|
|
4294
|
-
warning_queued,
|
|
4295
|
-
dead_letter,
|
|
4296
|
-
partition,
|
|
4297
|
-
table_name,
|
|
4298
|
-
heartbeat_seconds
|
|
4299
|
-
)
|
|
4300
|
-
VALUES (
|
|
4301
|
-
queue_name,
|
|
4302
|
-
options->>'policy',
|
|
4303
|
-
COALESCE((options->>'retryLimit')::int, 2),
|
|
4304
|
-
COALESCE((options->>'retryDelay')::int, 0),
|
|
4305
|
-
COALESCE((options->>'retryBackoff')::bool, false),
|
|
4306
|
-
(options->>'retryDelayMax')::int,
|
|
4307
|
-
COALESCE((options->>'expireInSeconds')::int, 900),
|
|
4308
|
-
COALESCE((options->>'retentionSeconds')::int, 1209600),
|
|
4309
|
-
COALESCE((options->>'deleteAfterSeconds')::int, 604800),
|
|
4310
|
-
COALESCE((options->>'warningQueueSize')::int, 0),
|
|
4311
|
-
options->>'deadLetter',
|
|
4312
|
-
COALESCE((options->>'partition')::bool, false),
|
|
4313
|
-
tablename,
|
|
4314
|
-
(options->>'heartbeatSeconds')::int
|
|
4315
|
-
)
|
|
4316
|
-
ON CONFLICT DO NOTHING
|
|
4317
|
-
RETURNING created_on
|
|
4318
|
-
)
|
|
4319
|
-
SELECT created_on into queue_created_on from q;
|
|
4320
|
-
|
|
4321
|
-
IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
|
|
4322
|
-
RETURN;
|
|
4323
|
-
END IF;
|
|
4324
|
-
|
|
4325
|
-
EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
|
|
4326
|
-
|
|
4327
|
-
EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
|
|
4328
|
-
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);
|
|
4329
|
-
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);
|
|
4330
|
-
|
|
4331
|
-
EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active'$cmd$, tablename);
|
|
4332
|
-
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);
|
|
4333
|
-
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);
|
|
4334
|
-
|
|
4335
|
-
IF options->>'policy' = 'short' THEN
|
|
4336
|
-
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);
|
|
4337
|
-
ELSIF options->>'policy' = 'singleton' THEN
|
|
4338
|
-
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);
|
|
4339
|
-
ELSIF options->>'policy' = 'stately' THEN
|
|
4340
|
-
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);
|
|
4341
|
-
ELSIF options->>'policy' = 'exclusive' THEN
|
|
4342
|
-
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);
|
|
4343
|
-
ELSIF options->>'policy' = 'key_strict_fifo' THEN
|
|
4344
|
-
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);
|
|
4345
|
-
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);
|
|
4346
|
-
END IF;
|
|
4347
|
-
|
|
4348
|
-
EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
|
|
4349
|
-
EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
|
|
4350
|
-
END;
|
|
4351
|
-
$$
|
|
4352
|
-
LANGUAGE plpgsql;
|
|
4353
|
-
`,
|
|
4340
|
+
createQueueFn[30](schema),
|
|
4354
4341
|
`SELECT ${schema}.job_table_run($cmd$DROP INDEX IF EXISTS ${schema}.job_i5$cmd$)`,
|
|
4355
4342
|
`SELECT ${schema}.job_table_run($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active'$cmd$)`,
|
|
4356
4343
|
`ALTER TABLE ${schema}.job DROP COLUMN pending_dependencies`,
|
|
4357
4344
|
`ALTER TABLE ${schema}.job DROP COLUMN blocking`,
|
|
4358
4345
|
`ALTER TABLE ${schema}.job DROP COLUMN blocked`
|
|
4359
4346
|
]
|
|
4347
|
+
},
|
|
4348
|
+
{
|
|
4349
|
+
release: "12.21.0",
|
|
4350
|
+
version: 32,
|
|
4351
|
+
previous: 31,
|
|
4352
|
+
install: [
|
|
4353
|
+
`ALTER TABLE ${schema}.queue ADD COLUMN notify boolean NOT NULL DEFAULT false`,
|
|
4354
|
+
createQueueFn[32](schema),
|
|
4355
|
+
`ALTER TABLE ${schema}.queue ADD COLUMN failed_count int NOT NULL DEFAULT 0`,
|
|
4356
|
+
`ALTER TABLE ${schema}.queue ADD COLUMN ready_count int NOT NULL DEFAULT 0`
|
|
4357
|
+
],
|
|
4358
|
+
uninstall: [
|
|
4359
|
+
`ALTER TABLE ${schema}.queue DROP COLUMN ready_count`,
|
|
4360
|
+
`ALTER TABLE ${schema}.queue DROP COLUMN failed_count`,
|
|
4361
|
+
createQueueFn[31](schema),
|
|
4362
|
+
`ALTER TABLE ${schema}.queue DROP COLUMN notify`
|
|
4363
|
+
]
|
|
4360
4364
|
}
|
|
4361
4365
|
];
|
|
4362
4366
|
}
|
|
@@ -4364,7 +4368,7 @@ function getAll(schema) {
|
|
|
4364
4368
|
//#region ../../src/contractor.ts
|
|
4365
4369
|
var schemaVersion = {
|
|
4366
4370
|
name: "pg-boss",
|
|
4367
|
-
version: "12.
|
|
4371
|
+
version: "12.21.0",
|
|
4368
4372
|
description: "Queueing jobs in Postgres from Node.js like a boss",
|
|
4369
4373
|
type: "module",
|
|
4370
4374
|
main: "./dist/index.js",
|
|
@@ -4372,18 +4376,18 @@ var schemaVersion = {
|
|
|
4372
4376
|
bin: { "pg-boss": "./dist/cli.js" },
|
|
4373
4377
|
engines: { "node": ">=22.12.0" },
|
|
4374
4378
|
dependencies: {
|
|
4375
|
-
"cron-parser": "^5.
|
|
4376
|
-
"pg": "^8.
|
|
4379
|
+
"cron-parser": "^5.6.0",
|
|
4380
|
+
"pg": "^8.22.0",
|
|
4377
4381
|
"serialize-error": "^13.0.1"
|
|
4378
4382
|
},
|
|
4379
4383
|
devDependencies: {
|
|
4380
|
-
"@electric-sql/pglite": "^0.
|
|
4384
|
+
"@electric-sql/pglite": "^0.5.3",
|
|
4381
4385
|
"@prisma/adapter-pg": "^7.8.0",
|
|
4382
4386
|
"@prisma/client": "^7.8.0",
|
|
4383
4387
|
"@tsconfig/node-ts": "^23.6.4",
|
|
4384
4388
|
"@tsconfig/node22": "^22.0.5",
|
|
4385
|
-
"@types/luxon": "^3.7.
|
|
4386
|
-
"@types/node": "^22.
|
|
4389
|
+
"@types/luxon": "^3.7.2",
|
|
4390
|
+
"@types/node": "^22.20.0",
|
|
4387
4391
|
"@types/pg": "^8.20.0",
|
|
4388
4392
|
"@vitest/coverage-v8": "^4.1.2",
|
|
4389
4393
|
"cli-testlab": "^6.0.1",
|
|
@@ -4403,23 +4407,24 @@ var schemaVersion = {
|
|
|
4403
4407
|
"build": "npm run clean && tsc --project tsconfig.build.json",
|
|
4404
4408
|
"clean": "node -e \"fs.rmSync('dist',{recursive:true,force:true})\"",
|
|
4405
4409
|
"prepublishOnly": "npm install && npm test && npm run build",
|
|
4406
|
-
"pretest": "prisma generate --schema=test/prisma/schema.prisma",
|
|
4410
|
+
"pretest": "prisma generate --schema=test/prisma/schema.prisma && npm run tsc",
|
|
4407
4411
|
"test": "eslint . && vitest run",
|
|
4408
4412
|
"test:distributed": "cross-env DISTRIBUTED=true npm test",
|
|
4413
|
+
"test:ci": "npm run cover && cross-env DISTRIBUTED=true npm run cover && npm run test:pglite",
|
|
4409
4414
|
"test:cockroachdb": "cross-env DB_TYPE=cockroachdb COCKROACH_HOST=localhost npm test -- test/distributedDatabaseTest.ts",
|
|
4410
4415
|
"test:cockroachdb:full": "cross-env DB_TYPE=cockroachdb COCKROACH_HOST=localhost npm test -- --no-file-parallelism",
|
|
4411
4416
|
"test:yugabytedb:full": "cross-env DB_TYPE=yugabytedb YUGABYTE_HOST=localhost npm test -- --no-file-parallelism",
|
|
4412
4417
|
"test:citus:full": "cross-env DB_TYPE=citus CITUS_HOST=localhost npm test",
|
|
4413
4418
|
"test:pglite": "cross-env DB_TYPE=pglite npm test -- --no-file-parallelism",
|
|
4414
4419
|
"lint:fix": "eslint . --fix",
|
|
4415
|
-
"
|
|
4416
|
-
"cover": "vitest run --coverage",
|
|
4420
|
+
"cover": "npm test -- --coverage",
|
|
4417
4421
|
"tsc": "tsc --noEmit",
|
|
4422
|
+
"cli": "node ./dist/cli.js",
|
|
4418
4423
|
"readme": "node ./examples/readme.js",
|
|
4419
|
-
"
|
|
4420
|
-
"
|
|
4424
|
+
"docs": "npm run docs:dev --prefix docs",
|
|
4425
|
+
"docs:readme": "node ./scripts/sync-readme.js"
|
|
4421
4426
|
},
|
|
4422
|
-
pgboss: { "schema":
|
|
4427
|
+
pgboss: { "schema": 32 },
|
|
4423
4428
|
repository: {
|
|
4424
4429
|
"type": "git",
|
|
4425
4430
|
"url": "git+https://github.com/timgit/pg-boss.git"
|
|
@@ -4445,8 +4450,11 @@ var Contractor = class {
|
|
|
4445
4450
|
static constructionPlans(schema = DEFAULT_SCHEMA, options = { createSchema: true }) {
|
|
4446
4451
|
return create(schema, schemaVersion, options);
|
|
4447
4452
|
}
|
|
4448
|
-
static migrationPlans(schema = DEFAULT_SCHEMA, version = schemaVersion - 1) {
|
|
4449
|
-
return migrate(schema, version
|
|
4453
|
+
static migrationPlans(schema = DEFAULT_SCHEMA, version = schemaVersion - 1, options = {}) {
|
|
4454
|
+
return migrate(schema, version, void 0, void 0, {
|
|
4455
|
+
inlineAsync: true,
|
|
4456
|
+
partitionTables: options.partitionTables
|
|
4457
|
+
});
|
|
4450
4458
|
}
|
|
4451
4459
|
static rollbackPlans(schema = DEFAULT_SCHEMA, version = schemaVersion) {
|
|
4452
4460
|
return rollback(schema, version);
|
|
@@ -4741,7 +4749,7 @@ var Worker = class {
|
|
|
4741
4749
|
fetch;
|
|
4742
4750
|
onFetch;
|
|
4743
4751
|
onError;
|
|
4744
|
-
|
|
4752
|
+
resolveInterval;
|
|
4745
4753
|
jobs = [];
|
|
4746
4754
|
createdOn = Date.now();
|
|
4747
4755
|
state = WORKER_STATES.created;
|
|
@@ -4757,7 +4765,7 @@ var Worker = class {
|
|
|
4757
4765
|
loopDelayPromise = null;
|
|
4758
4766
|
beenNotified = false;
|
|
4759
4767
|
runPromise = null;
|
|
4760
|
-
constructor({ id, workId, name, options,
|
|
4768
|
+
constructor({ id, workId, name, options, resolveInterval, fetch, onFetch, onError }) {
|
|
4761
4769
|
this.id = id;
|
|
4762
4770
|
this.workId = workId;
|
|
4763
4771
|
this.name = name;
|
|
@@ -4765,7 +4773,7 @@ var Worker = class {
|
|
|
4765
4773
|
this.fetch = fetch;
|
|
4766
4774
|
this.onFetch = onFetch;
|
|
4767
4775
|
this.onError = onError;
|
|
4768
|
-
this.
|
|
4776
|
+
this.resolveInterval = resolveInterval;
|
|
4769
4777
|
}
|
|
4770
4778
|
start() {
|
|
4771
4779
|
this.runPromise = this.run();
|
|
@@ -4774,11 +4782,13 @@ var Worker = class {
|
|
|
4774
4782
|
this.state = WORKER_STATES.active;
|
|
4775
4783
|
while (!this.stopping) {
|
|
4776
4784
|
const started = Date.now();
|
|
4785
|
+
let fetchedCount = 0;
|
|
4777
4786
|
try {
|
|
4778
4787
|
this.beenNotified = false;
|
|
4779
4788
|
const jobs = await this.fetch();
|
|
4780
4789
|
this.lastFetchedOn = Date.now();
|
|
4781
4790
|
if (jobs) {
|
|
4791
|
+
fetchedCount = jobs.length;
|
|
4782
4792
|
this.jobs = jobs;
|
|
4783
4793
|
this.lastJobStartedOn = this.lastFetchedOn;
|
|
4784
4794
|
await this.onFetch(jobs);
|
|
@@ -4793,8 +4803,9 @@ var Worker = class {
|
|
|
4793
4803
|
}
|
|
4794
4804
|
const duration = Date.now() - started;
|
|
4795
4805
|
this.lastJobDuration = duration;
|
|
4796
|
-
|
|
4797
|
-
|
|
4806
|
+
const interval = this.resolveInterval(fetchedCount);
|
|
4807
|
+
if (!this.stopping && !this.beenNotified && interval - duration > 100) {
|
|
4808
|
+
this.loopDelayPromise = delay(interval - duration);
|
|
4798
4809
|
await this.loopDelayPromise;
|
|
4799
4810
|
this.loopDelayPromise = null;
|
|
4800
4811
|
}
|
|
@@ -4927,7 +4938,7 @@ var NUMERIC_QUEUE_FIELDS = [
|
|
|
4927
4938
|
"activeCount",
|
|
4928
4939
|
"totalCount"
|
|
4929
4940
|
];
|
|
4930
|
-
var events$
|
|
4941
|
+
var events$4 = {
|
|
4931
4942
|
error: "error",
|
|
4932
4943
|
wip: "wip"
|
|
4933
4944
|
};
|
|
@@ -4936,7 +4947,7 @@ function rethrowWriteError(err) {
|
|
|
4936
4947
|
throw err;
|
|
4937
4948
|
}
|
|
4938
4949
|
var Manager = class extends EventEmitter {
|
|
4939
|
-
events = events$
|
|
4950
|
+
events = events$4;
|
|
4940
4951
|
db;
|
|
4941
4952
|
config;
|
|
4942
4953
|
wipTs;
|
|
@@ -4945,6 +4956,7 @@ var Manager = class extends EventEmitter {
|
|
|
4945
4956
|
queueCacheInterval;
|
|
4946
4957
|
wipInterval;
|
|
4947
4958
|
timekeeper;
|
|
4959
|
+
notifier;
|
|
4948
4960
|
queues;
|
|
4949
4961
|
pendingOffWorkCleanups;
|
|
4950
4962
|
#spies;
|
|
@@ -5013,19 +5025,136 @@ var Manager = class extends EventEmitter {
|
|
|
5013
5025
|
const spy = this.config.__test__enableSpies ? this.#spies.get(name) : void 0;
|
|
5014
5026
|
if (spy) for (const job of jobs) spy.addJob(job.id, name, job.data, "active");
|
|
5015
5027
|
}
|
|
5016
|
-
#trackJobsCompleted(name, jobs, result) {
|
|
5028
|
+
async #trackJobsCompleted(name, jobs, result, affected) {
|
|
5017
5029
|
const spy = this.config.__test__enableSpies ? this.#spies.get(name) : void 0;
|
|
5018
|
-
if (spy)
|
|
5030
|
+
if (!spy) return;
|
|
5031
|
+
if (affected === jobs.length) {
|
|
5019
5032
|
const output = jobs.length === 1 ? result : void 0;
|
|
5020
5033
|
for (const job of jobs) spy.addJob(job.id, name, job.data, "completed", output);
|
|
5034
|
+
return;
|
|
5035
|
+
}
|
|
5036
|
+
for (const job of jobs) {
|
|
5037
|
+
const persisted = await this.getJobById(name, job.id);
|
|
5038
|
+
const state = persisted?.state;
|
|
5039
|
+
if (state === "completed" || state === "failed" || state === "active" || state === "created") spy.addJob(job.id, name, job.data, state, persisted?.output);
|
|
5040
|
+
else if (!persisted) spy.addJob(job.id, name, job.data, "completed", void 0);
|
|
5021
5041
|
}
|
|
5022
5042
|
}
|
|
5023
|
-
#trackJobsFailed(name, jobs, err) {
|
|
5043
|
+
async #trackJobsFailed(name, jobs, err) {
|
|
5024
5044
|
const spy = this.config.__test__enableSpies ? this.#spies.get(name) : void 0;
|
|
5025
|
-
if (spy)
|
|
5026
|
-
|
|
5027
|
-
|
|
5045
|
+
if (!spy) return;
|
|
5046
|
+
for (const job of jobs) {
|
|
5047
|
+
const persisted = await this.getJobById(name, job.id);
|
|
5048
|
+
if (persisted?.state === "failed") spy.addJob(job.id, name, job.data, "failed", persisted.output ?? {
|
|
5049
|
+
message: err?.message,
|
|
5050
|
+
stack: err?.stack
|
|
5051
|
+
});
|
|
5052
|
+
}
|
|
5053
|
+
}
|
|
5054
|
+
#trackJobsSettled(name, completed, failed) {
|
|
5055
|
+
const spy = this.config.__test__enableSpies ? this.#spies.get(name) : void 0;
|
|
5056
|
+
if (!spy) return;
|
|
5057
|
+
for (const { job, output } of completed) spy.addJob(job.id, name, job.data, "completed", output);
|
|
5058
|
+
for (const { job, output } of failed) spy.addJob(job.id, name, job.data, "failed", serializeError(output));
|
|
5059
|
+
}
|
|
5060
|
+
async #settlePerJob(name, jobs, result) {
|
|
5061
|
+
if (!Array.isArray(result)) {
|
|
5062
|
+
const err = /* @__PURE__ */ new Error("perJobResults handler must resolve with an array of job results");
|
|
5063
|
+
await this.fail(name, jobs.map((job) => job.id), err);
|
|
5064
|
+
this.#trackJobsFailed(name, jobs, err);
|
|
5065
|
+
return;
|
|
5066
|
+
}
|
|
5067
|
+
const batch = new Map(jobs.map((job) => [job.id, job]));
|
|
5068
|
+
const disposition = /* @__PURE__ */ new Map();
|
|
5069
|
+
for (const item of result) if (item && batch.has(item.id) && (item.status === "completed" || item.status === "failed" || item.status === "deadletter")) disposition.set(item.id, item);
|
|
5070
|
+
const completed = [];
|
|
5071
|
+
const failed = [];
|
|
5072
|
+
const deadLettered = [];
|
|
5073
|
+
for (const job of jobs) {
|
|
5074
|
+
const item = disposition.get(job.id);
|
|
5075
|
+
if (item?.status === "completed") completed.push({
|
|
5076
|
+
job,
|
|
5077
|
+
output: item.output
|
|
5078
|
+
});
|
|
5079
|
+
else if (item?.status === "failed") failed.push({
|
|
5080
|
+
job,
|
|
5081
|
+
output: item.output
|
|
5082
|
+
});
|
|
5083
|
+
else if (item?.status === "deadletter") deadLettered.push({
|
|
5084
|
+
job,
|
|
5085
|
+
output: item.output
|
|
5086
|
+
});
|
|
5087
|
+
else failed.push({
|
|
5088
|
+
job,
|
|
5089
|
+
output: /* @__PURE__ */ new Error("no disposition returned by handler")
|
|
5090
|
+
});
|
|
5091
|
+
}
|
|
5092
|
+
if (completed.length > 0) await this.#completeWithOutputs(name, completed.map((c) => ({
|
|
5093
|
+
id: c.job.id,
|
|
5094
|
+
output: c.output
|
|
5095
|
+
})));
|
|
5096
|
+
if (failed.length > 0) await this.#failWithOutputs(name, failed.map((f) => ({
|
|
5097
|
+
id: f.job.id,
|
|
5098
|
+
output: f.output
|
|
5099
|
+
})));
|
|
5100
|
+
if (deadLettered.length > 0) await this.#failWithOutputs(name, deadLettered.map((d) => ({
|
|
5101
|
+
id: d.job.id,
|
|
5102
|
+
output: d.output
|
|
5103
|
+
})), true);
|
|
5104
|
+
this.#trackJobsSettled(name, completed, [...failed, ...deadLettered]);
|
|
5105
|
+
}
|
|
5106
|
+
async #completeWithOutputs(name, items) {
|
|
5107
|
+
const { table } = await this.getQueueCache(name);
|
|
5108
|
+
const payload = items.map((item) => ({
|
|
5109
|
+
id: item.id,
|
|
5110
|
+
output: this.mapCompletionDataArg(item.output)
|
|
5111
|
+
}));
|
|
5112
|
+
const ids = items.map((item) => item.id);
|
|
5113
|
+
if (this.config.noMultiMutationCte) return this.withDistributedTransaction(this.db, async (tx) => {
|
|
5114
|
+
const sql = completeJobsWithOutputsDistributed(this.config.schema, table);
|
|
5115
|
+
const { rows } = await tx.executeSql(sql, [name, JSON.stringify(payload)]);
|
|
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]);
|
|
5118
|
+
return {
|
|
5119
|
+
jobs: ids,
|
|
5120
|
+
requested: ids.length,
|
|
5121
|
+
affected: rows.length
|
|
5122
|
+
};
|
|
5028
5123
|
});
|
|
5124
|
+
const sql = completeJobsWithOutputs(this.config.schema, table);
|
|
5125
|
+
const result = await this.db.executeSql(sql, [name, JSON.stringify(payload)]);
|
|
5126
|
+
return this.mapCommandResponse(ids, result);
|
|
5127
|
+
}
|
|
5128
|
+
async #failWithOutputs(name, items, forceTerminal = false) {
|
|
5129
|
+
const { table } = await this.getQueueCache(name);
|
|
5130
|
+
const ids = items.map((item) => item.id);
|
|
5131
|
+
if (this.config.noMultiMutationCte) {
|
|
5132
|
+
const outputById = new Map(items.map((item) => [item.id, this.mapCompletionDataArg(item.output)]));
|
|
5133
|
+
return this.withDistributedTransaction(this.db, async (tx) => {
|
|
5134
|
+
const selectQuery = selectJobsToFailById(this.config.schema, table);
|
|
5135
|
+
const { rows: jobs } = await tx.executeSql(selectQuery.text, [name, ids]);
|
|
5136
|
+
if (jobs.length === 0) return {
|
|
5137
|
+
jobs: ids,
|
|
5138
|
+
requested: ids.length,
|
|
5139
|
+
affected: 0
|
|
5140
|
+
};
|
|
5141
|
+
const deleteQuery = deleteJobsToFail(this.config.schema, table);
|
|
5142
|
+
await tx.executeSql(deleteQuery.text, [name, ids]);
|
|
5143
|
+
const count = await this.reinsertFailedJobs(tx, table, jobs, null, outputById, forceTerminal);
|
|
5144
|
+
return {
|
|
5145
|
+
jobs: ids,
|
|
5146
|
+
requested: ids.length,
|
|
5147
|
+
affected: count
|
|
5148
|
+
};
|
|
5149
|
+
});
|
|
5150
|
+
}
|
|
5151
|
+
const payload = items.map((item) => ({
|
|
5152
|
+
id: item.id,
|
|
5153
|
+
output: this.mapCompletionDataArg(item.output)
|
|
5154
|
+
}));
|
|
5155
|
+
const sql = forceTerminal ? deadLetterJobsByIdWithOutputs(this.config.schema, table) : failJobsByIdWithOutputs(this.config.schema, table);
|
|
5156
|
+
const result = await this.db.executeSql(sql, [name, JSON.stringify(payload)]);
|
|
5157
|
+
return this.mapCommandResponse(ids, result);
|
|
5029
5158
|
}
|
|
5030
5159
|
#storeLocalGroupConfig(name, localGroupConcurrency) {
|
|
5031
5160
|
const config = typeof localGroupConcurrency === "number" ? { default: localGroupConcurrency } : localGroupConcurrency;
|
|
@@ -5063,7 +5192,7 @@ var Manager = class extends EventEmitter {
|
|
|
5063
5192
|
#trackLocalGroupEnd(name, groupedJobs) {
|
|
5064
5193
|
for (const job of groupedJobs) if (job.groupId) this.#decrementLocalGroupCount(name, job.groupId);
|
|
5065
5194
|
}
|
|
5066
|
-
async #processJobs(name, jobs, callback, worker, heartbeatRefreshSeconds) {
|
|
5195
|
+
async #processJobs(name, jobs, callback, worker, heartbeatRefreshSeconds, perJobResults = false) {
|
|
5067
5196
|
const jobIds = jobs.map((job) => job.id);
|
|
5068
5197
|
const maxExpiration = jobs.reduce((acc, i) => Math.max(acc, i.expireInSeconds), 0);
|
|
5069
5198
|
const heartbeatSeconds = jobs.reduce((acc, j) => Math.max(acc, j.heartbeatSeconds || 0), 0);
|
|
@@ -5079,21 +5208,34 @@ var Manager = class extends EventEmitter {
|
|
|
5079
5208
|
try {
|
|
5080
5209
|
await this.touch(name, jobIds);
|
|
5081
5210
|
} catch (err) {
|
|
5082
|
-
this.emit(events$
|
|
5211
|
+
this.emit(events$4.error, err);
|
|
5083
5212
|
}
|
|
5084
5213
|
}, intervalMs);
|
|
5085
5214
|
}
|
|
5215
|
+
let completedResult;
|
|
5216
|
+
let completedAffected = 0;
|
|
5217
|
+
let failedError;
|
|
5218
|
+
let didFail = false;
|
|
5086
5219
|
try {
|
|
5087
5220
|
const result = await resolveWithinSeconds(callback(jobs), maxExpiration, `handler execution exceeded ${maxExpiration}s`, ac);
|
|
5088
|
-
await this
|
|
5089
|
-
|
|
5221
|
+
if (perJobResults) await this.#settlePerJob(name, jobs, result);
|
|
5222
|
+
else {
|
|
5223
|
+
const completion = await this.complete(name, jobIds, jobIds.length === 1 ? result : void 0);
|
|
5224
|
+
completedResult = result;
|
|
5225
|
+
completedAffected = completion.affected;
|
|
5226
|
+
}
|
|
5090
5227
|
} catch (err) {
|
|
5091
5228
|
await this.fail(name, jobIds, err);
|
|
5092
|
-
|
|
5229
|
+
failedError = err;
|
|
5230
|
+
didFail = true;
|
|
5093
5231
|
} finally {
|
|
5094
5232
|
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
5095
5233
|
if (worker) worker.abortController = null;
|
|
5096
5234
|
}
|
|
5235
|
+
if (this.config.__test__enableSpies && this.#spies.has(name)) {
|
|
5236
|
+
if (didFail) await this.#trackJobsFailed(name, jobs, failedError);
|
|
5237
|
+
else if (!perJobResults) await this.#trackJobsCompleted(name, jobs, completedResult, completedAffected);
|
|
5238
|
+
}
|
|
5097
5239
|
}
|
|
5098
5240
|
async start() {
|
|
5099
5241
|
this.stopped = false;
|
|
@@ -5103,7 +5245,7 @@ var Manager = class extends EventEmitter {
|
|
|
5103
5245
|
if (now - this.wipTs < 2e3) return;
|
|
5104
5246
|
const wip = this.getWipData();
|
|
5105
5247
|
if (wip.some((w) => w.count > 0)) {
|
|
5106
|
-
this.emit(events$
|
|
5248
|
+
this.emit(events$4.wip, wip);
|
|
5107
5249
|
this.wipTs = now;
|
|
5108
5250
|
}
|
|
5109
5251
|
}, 2e3);
|
|
@@ -5118,7 +5260,7 @@ var Manager = class extends EventEmitter {
|
|
|
5118
5260
|
return acc;
|
|
5119
5261
|
}, {});
|
|
5120
5262
|
} catch (error) {
|
|
5121
|
-
emit && this.emit(events$
|
|
5263
|
+
emit && this.emit(events$4.error, {
|
|
5122
5264
|
...error,
|
|
5123
5265
|
message: error.message,
|
|
5124
5266
|
stack: error.stack
|
|
@@ -5153,9 +5295,15 @@ var Manager = class extends EventEmitter {
|
|
|
5153
5295
|
async work(name, ...args) {
|
|
5154
5296
|
const { options, callback } = checkWorkArgs(name, args);
|
|
5155
5297
|
if (this.stopped) throw new Error("Workers are disabled. pg-boss is stopped");
|
|
5156
|
-
const { pollingInterval: interval, batchSize = 1, includeMetadata = false, priority = true, localConcurrency = 1, localGroupConcurrency, groupConcurrency, orderByCreatedOn = true, heartbeatRefreshSeconds, minPriority, maxPriority } = options;
|
|
5298
|
+
const { pollingInterval: interval, notifyPollingInterval: notifyInterval, burstWhenReadyExceeds, burstWhenBatchFull = false, batchSize = 1, includeMetadata = false, priority = true, localConcurrency = 1, localGroupConcurrency, groupConcurrency, orderByCreatedOn = true, heartbeatRefreshSeconds, minPriority, maxPriority, perJobResults = false } = options;
|
|
5157
5299
|
if (localGroupConcurrency != null) this.#storeLocalGroupConfig(name, localGroupConcurrency);
|
|
5158
5300
|
const firstWorkerId = randomUUID({ disableEntropyCache: true });
|
|
5301
|
+
const isNotifyActive = () => !!(this.notifier?.available && this.queues?.[name]?.notify);
|
|
5302
|
+
const getReadyCount = () => this.queues?.[name]?.readyCount ?? 0;
|
|
5303
|
+
const resolveInterval = (lastFetchCount) => {
|
|
5304
|
+
if (lastFetchCount >= batchSize && (burstWhenReadyExceeds !== void 0 && getReadyCount() > burstWhenReadyExceeds || burstWhenBatchFull && batchSize > 1)) return 0;
|
|
5305
|
+
return isNotifyActive() ? notifyInterval : interval;
|
|
5306
|
+
};
|
|
5159
5307
|
const createWorker = (workerId, workId) => {
|
|
5160
5308
|
const fetch = () => {
|
|
5161
5309
|
const ignoreGroups = localGroupConcurrency != null ? this.#getGroupsAtLocalCapacity(name) : void 0;
|
|
@@ -5176,7 +5324,7 @@ var Manager = class extends EventEmitter {
|
|
|
5176
5324
|
this.emitWip(name);
|
|
5177
5325
|
this.#trackJobsActive(name, jobs);
|
|
5178
5326
|
const worker = this.workers.get(workerId);
|
|
5179
|
-
if (localGroupConcurrency == null) await this.#processJobs(name, jobs, callback, worker, heartbeatRefreshSeconds);
|
|
5327
|
+
if (localGroupConcurrency == null) await this.#processJobs(name, jobs, callback, worker, heartbeatRefreshSeconds, perJobResults);
|
|
5180
5328
|
else {
|
|
5181
5329
|
const { allowed, excess, groupedJobs } = this.#trackLocalGroupStart(name, jobs);
|
|
5182
5330
|
if (excess.length > 0) {
|
|
@@ -5184,7 +5332,7 @@ var Manager = class extends EventEmitter {
|
|
|
5184
5332
|
await this.restore(name, excessIds);
|
|
5185
5333
|
}
|
|
5186
5334
|
if (allowed.length > 0) try {
|
|
5187
|
-
await this.#processJobs(name, allowed, callback, worker, heartbeatRefreshSeconds);
|
|
5335
|
+
await this.#processJobs(name, allowed, callback, worker, heartbeatRefreshSeconds, perJobResults);
|
|
5188
5336
|
} finally {
|
|
5189
5337
|
this.#trackLocalGroupEnd(name, groupedJobs);
|
|
5190
5338
|
}
|
|
@@ -5192,7 +5340,7 @@ var Manager = class extends EventEmitter {
|
|
|
5192
5340
|
this.emitWip(name);
|
|
5193
5341
|
};
|
|
5194
5342
|
const onError = (error) => {
|
|
5195
|
-
this.emit(events$
|
|
5343
|
+
this.emit(events$4.error, {
|
|
5196
5344
|
...error,
|
|
5197
5345
|
message: error.message,
|
|
5198
5346
|
stack: error.stack,
|
|
@@ -5205,7 +5353,7 @@ var Manager = class extends EventEmitter {
|
|
|
5205
5353
|
workId,
|
|
5206
5354
|
name,
|
|
5207
5355
|
options,
|
|
5208
|
-
|
|
5356
|
+
resolveInterval,
|
|
5209
5357
|
fetch,
|
|
5210
5358
|
onFetch,
|
|
5211
5359
|
onError
|
|
@@ -5231,7 +5379,7 @@ var Manager = class extends EventEmitter {
|
|
|
5231
5379
|
if (!INTERNAL_QUEUES[name]) {
|
|
5232
5380
|
const now = Date.now();
|
|
5233
5381
|
if (now - this.wipTs > 2e3) {
|
|
5234
|
-
this.emit(events$
|
|
5382
|
+
this.emit(events$4.wip, this.getWipData());
|
|
5235
5383
|
this.wipTs = now;
|
|
5236
5384
|
}
|
|
5237
5385
|
}
|
|
@@ -5267,6 +5415,15 @@ var Manager = class extends EventEmitter {
|
|
|
5267
5415
|
notifyWorker(workerId) {
|
|
5268
5416
|
this.workers.get(workerId)?.notify();
|
|
5269
5417
|
}
|
|
5418
|
+
#notifyEnabled(queueNotify) {
|
|
5419
|
+
return !!queueNotify && !this.config.noListenNotify;
|
|
5420
|
+
}
|
|
5421
|
+
notifyQueue(name) {
|
|
5422
|
+
for (const worker of this.workers.values()) if (worker.name === name) worker.notify();
|
|
5423
|
+
}
|
|
5424
|
+
forceFetchLnWorkers() {
|
|
5425
|
+
for (const worker of this.workers.values()) if (this.queues?.[worker.name]?.notify) worker.notify();
|
|
5426
|
+
}
|
|
5270
5427
|
async subscribe(event, name) {
|
|
5271
5428
|
assert(event, "Missing required argument");
|
|
5272
5429
|
assert(name, "Missing required argument");
|
|
@@ -5349,12 +5506,13 @@ var Manager = class extends EventEmitter {
|
|
|
5349
5506
|
deadLetter
|
|
5350
5507
|
};
|
|
5351
5508
|
const db = wrapper || this.db;
|
|
5352
|
-
const { table, policy } = await this.getQueueCache(name);
|
|
5509
|
+
const { table, policy, notify } = await this.getQueueCache(name);
|
|
5353
5510
|
if (policy === QUEUE_POLICIES.key_strict_fifo && !singletonKey) throw new Error(`${QUEUE_POLICIES.key_strict_fifo} queues require a singletonKey`);
|
|
5354
5511
|
const sql = insertJobs(this.config.schema, {
|
|
5355
5512
|
table,
|
|
5356
5513
|
name,
|
|
5357
|
-
returnId: true
|
|
5514
|
+
returnId: true,
|
|
5515
|
+
notify: this.#notifyEnabled(notify)
|
|
5358
5516
|
});
|
|
5359
5517
|
const { rows: try1 } = await db.executeSql(sql, [JSON.stringify([job])]);
|
|
5360
5518
|
if (try1.length === 1) {
|
|
@@ -5382,7 +5540,7 @@ var Manager = class extends EventEmitter {
|
|
|
5382
5540
|
}
|
|
5383
5541
|
async insert(name, jobs, options = {}) {
|
|
5384
5542
|
assert(Array.isArray(jobs), "jobs argument should be an array");
|
|
5385
|
-
const { table, policy } = await this.getQueueCache(name);
|
|
5543
|
+
const { table, policy, notify } = await this.getQueueCache(name);
|
|
5386
5544
|
if (policy === QUEUE_POLICIES.key_strict_fifo) {
|
|
5387
5545
|
for (const job of jobs) if (!job.singletonKey) throw new Error(`${QUEUE_POLICIES.key_strict_fifo} queues require a singletonKey`);
|
|
5388
5546
|
}
|
|
@@ -5396,7 +5554,8 @@ var Manager = class extends EventEmitter {
|
|
|
5396
5554
|
const sql = insertJobs(this.config.schema, {
|
|
5397
5555
|
table,
|
|
5398
5556
|
name,
|
|
5399
|
-
returnId
|
|
5557
|
+
returnId,
|
|
5558
|
+
notify: this.#notifyEnabled(notify)
|
|
5400
5559
|
});
|
|
5401
5560
|
const { rows } = await db.executeSql(sql, [JSON.stringify(insertPayload)]);
|
|
5402
5561
|
if (rows.length) {
|
|
@@ -5443,7 +5602,7 @@ var Manager = class extends EventEmitter {
|
|
|
5443
5602
|
}
|
|
5444
5603
|
const statements = [];
|
|
5445
5604
|
for (const [queueName, queueJobs] of byQueue) {
|
|
5446
|
-
const { table } = await this.getQueueCache(queueName);
|
|
5605
|
+
const { table, notify } = await this.getQueueCache(queueName);
|
|
5447
5606
|
const insertPayload = queueJobs.map((j) => {
|
|
5448
5607
|
const dependencyCount = dependencyCountByRef.get(j.ref) ?? 0;
|
|
5449
5608
|
return {
|
|
@@ -5470,19 +5629,13 @@ var Manager = class extends EventEmitter {
|
|
|
5470
5629
|
pendingDependencies: dependencyCount || void 0
|
|
5471
5630
|
};
|
|
5472
5631
|
});
|
|
5473
|
-
|
|
5632
|
+
statements.push(insertFlowJobs(this.config.schema, {
|
|
5474
5633
|
table,
|
|
5475
|
-
name: queueName
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
statements.push(`
|
|
5479
|
-
WITH ins AS (
|
|
5480
|
-
${insertSql}
|
|
5481
|
-
)
|
|
5482
|
-
SELECT 1 / (CASE WHEN (SELECT count(*) FROM ins) = ${insertPayload.length} THEN 1 ELSE 0 END)
|
|
5483
|
-
`);
|
|
5634
|
+
name: queueName
|
|
5635
|
+
}, insertPayload));
|
|
5636
|
+
if (this.#notifyEnabled(notify)) statements.push(notifyQueue(this.config.schema, queueName));
|
|
5484
5637
|
}
|
|
5485
|
-
if (depRows.length > 0) statements.push(insertDependencies(this.config.schema
|
|
5638
|
+
if (depRows.length > 0) statements.push(insertDependencies(this.config.schema, depRows));
|
|
5486
5639
|
const db = options.db ?? this.db;
|
|
5487
5640
|
const sql = options.db ? statements.join(";\n") : transaction(statements);
|
|
5488
5641
|
try {
|
|
@@ -5632,16 +5785,17 @@ var Manager = class extends EventEmitter {
|
|
|
5632
5785
|
return this.reinsertFailedJobs(tx, table, jobs, outputData);
|
|
5633
5786
|
});
|
|
5634
5787
|
}
|
|
5635
|
-
async reinsertFailedJobs(tx, table, jobs, outputData) {
|
|
5788
|
+
async reinsertFailedJobs(tx, table, jobs, outputData, outputById, forceTerminal = false) {
|
|
5636
5789
|
const insertSql = insertRetryJob(this.config.schema, table);
|
|
5637
5790
|
const dlqSql = insertDeadLetterJob(this.config.schema);
|
|
5638
5791
|
let count = 0;
|
|
5639
5792
|
for (const job of jobs) {
|
|
5793
|
+
const jobOutput = outputById ? outputById.get(job.id) ?? null : outputData;
|
|
5640
5794
|
const retryCount = Number(job.retry_count);
|
|
5641
5795
|
const retryLimit = Number(job.retry_limit);
|
|
5642
5796
|
const retryDelay = Number(job.retry_delay);
|
|
5643
5797
|
const retryDelayMax = job.retry_delay_max != null ? Number(job.retry_delay_max) : null;
|
|
5644
|
-
const canRetry = retryCount < retryLimit;
|
|
5798
|
+
const canRetry = !forceTerminal && retryCount < retryLimit;
|
|
5645
5799
|
let retried = false;
|
|
5646
5800
|
if (canRetry) {
|
|
5647
5801
|
let startAfter = job.start_after;
|
|
@@ -5675,7 +5829,7 @@ var Manager = class extends EventEmitter {
|
|
|
5675
5829
|
null,
|
|
5676
5830
|
job.keep_until,
|
|
5677
5831
|
job.policy,
|
|
5678
|
-
|
|
5832
|
+
jobOutput,
|
|
5679
5833
|
job.dead_letter,
|
|
5680
5834
|
null,
|
|
5681
5835
|
job.heartbeat_seconds,
|
|
@@ -5709,7 +5863,7 @@ var Manager = class extends EventEmitter {
|
|
|
5709
5863
|
/* @__PURE__ */ new Date(),
|
|
5710
5864
|
job.keep_until,
|
|
5711
5865
|
job.policy,
|
|
5712
|
-
|
|
5866
|
+
jobOutput,
|
|
5713
5867
|
job.dead_letter,
|
|
5714
5868
|
null,
|
|
5715
5869
|
job.heartbeat_seconds,
|
|
@@ -5720,7 +5874,7 @@ var Manager = class extends EventEmitter {
|
|
|
5720
5874
|
if (job.dead_letter) await tx.executeSql(dlqSql, [
|
|
5721
5875
|
job.dead_letter,
|
|
5722
5876
|
job.data,
|
|
5723
|
-
|
|
5877
|
+
jobOutput
|
|
5724
5878
|
]);
|
|
5725
5879
|
}
|
|
5726
5880
|
count++;
|
|
@@ -5880,7 +6034,9 @@ var Manager = class extends EventEmitter {
|
|
|
5880
6034
|
return Object.assign(queue, stats || {
|
|
5881
6035
|
deferredCount: 0,
|
|
5882
6036
|
queuedCount: 0,
|
|
6037
|
+
readyCount: 0,
|
|
5883
6038
|
activeCount: 0,
|
|
6039
|
+
failedCount: 0,
|
|
5884
6040
|
totalCount: 0
|
|
5885
6041
|
});
|
|
5886
6042
|
}
|
|
@@ -5943,7 +6099,7 @@ var Manager = class extends EventEmitter {
|
|
|
5943
6099
|
};
|
|
5944
6100
|
//#endregion
|
|
5945
6101
|
//#region ../../src/boss.ts
|
|
5946
|
-
var events$
|
|
6102
|
+
var events$3 = {
|
|
5947
6103
|
error: "error",
|
|
5948
6104
|
warning: "warning"
|
|
5949
6105
|
};
|
|
@@ -5969,7 +6125,7 @@ var Boss = class extends EventEmitter {
|
|
|
5969
6125
|
#db;
|
|
5970
6126
|
#config;
|
|
5971
6127
|
#manager;
|
|
5972
|
-
events = events$
|
|
6128
|
+
events = events$3;
|
|
5973
6129
|
constructor(db, manager, config) {
|
|
5974
6130
|
super();
|
|
5975
6131
|
this.#db = db;
|
|
@@ -6004,8 +6160,8 @@ var Boss = class extends EventEmitter {
|
|
|
6004
6160
|
db: this.#db,
|
|
6005
6161
|
schema: this.#config.schema,
|
|
6006
6162
|
persistWarnings: this.#config.persistWarnings,
|
|
6007
|
-
warningEvent: events$
|
|
6008
|
-
errorEvent: events$
|
|
6163
|
+
warningEvent: events$3.warning,
|
|
6164
|
+
errorEvent: events$3.error
|
|
6009
6165
|
};
|
|
6010
6166
|
}
|
|
6011
6167
|
async #executeQuery(query) {
|
|
@@ -6034,7 +6190,7 @@ var Boss = class extends EventEmitter {
|
|
|
6034
6190
|
!this.#stopped && await this.supervise(queues);
|
|
6035
6191
|
!this.#stopped && await this.#maintainWarnings();
|
|
6036
6192
|
} catch (err) {
|
|
6037
|
-
this.emit(events$
|
|
6193
|
+
this.emit(events$3.error, err);
|
|
6038
6194
|
} finally {
|
|
6039
6195
|
this.#maintaining = false;
|
|
6040
6196
|
}
|
|
@@ -6112,7 +6268,7 @@ var Boss = class extends EventEmitter {
|
|
|
6112
6268
|
};
|
|
6113
6269
|
//#endregion
|
|
6114
6270
|
//#region ../../src/bam.ts
|
|
6115
|
-
var events$
|
|
6271
|
+
var events$2 = {
|
|
6116
6272
|
error: "error",
|
|
6117
6273
|
bam: "bam"
|
|
6118
6274
|
};
|
|
@@ -6122,7 +6278,7 @@ var Bam = class extends EventEmitter {
|
|
|
6122
6278
|
#pollInterval;
|
|
6123
6279
|
#db;
|
|
6124
6280
|
#config;
|
|
6125
|
-
events = events$
|
|
6281
|
+
events = events$2;
|
|
6126
6282
|
constructor(db, config) {
|
|
6127
6283
|
super();
|
|
6128
6284
|
this.#db = db;
|
|
@@ -6158,7 +6314,7 @@ var Bam = class extends EventEmitter {
|
|
|
6158
6314
|
const { rows } = await this.#db.executeSql(sql);
|
|
6159
6315
|
if (rows.length === 1) await this.#processCommands();
|
|
6160
6316
|
} catch (err) {
|
|
6161
|
-
this.emit(events$
|
|
6317
|
+
this.emit(events$2.error, err);
|
|
6162
6318
|
} finally {
|
|
6163
6319
|
this.#working = false;
|
|
6164
6320
|
}
|
|
@@ -6167,7 +6323,7 @@ var Bam = class extends EventEmitter {
|
|
|
6167
6323
|
if (this.#stopped) return;
|
|
6168
6324
|
const entry = await this.#getNextCommand();
|
|
6169
6325
|
if (!entry || this.#stopped) return;
|
|
6170
|
-
this.emit(events$
|
|
6326
|
+
this.emit(events$2.bam, {
|
|
6171
6327
|
id: entry.id,
|
|
6172
6328
|
name: entry.name,
|
|
6173
6329
|
status: "in_progress",
|
|
@@ -6178,7 +6334,7 @@ var Bam = class extends EventEmitter {
|
|
|
6178
6334
|
await this.#db.executeSql(entry.command);
|
|
6179
6335
|
if (this.#stopped) return;
|
|
6180
6336
|
await this.#markCompleted(entry.id);
|
|
6181
|
-
this.emit(events$
|
|
6337
|
+
this.emit(events$2.bam, {
|
|
6182
6338
|
id: entry.id,
|
|
6183
6339
|
name: entry.name,
|
|
6184
6340
|
status: "completed",
|
|
@@ -6188,8 +6344,8 @@ var Bam = class extends EventEmitter {
|
|
|
6188
6344
|
} catch (err) {
|
|
6189
6345
|
if (this.#stopped) return;
|
|
6190
6346
|
await this.#markFailed(entry.id, err);
|
|
6191
|
-
this.emit(events$
|
|
6192
|
-
this.emit(events$
|
|
6347
|
+
this.emit(events$2.error, err);
|
|
6348
|
+
this.emit(events$2.bam, {
|
|
6193
6349
|
id: entry.id,
|
|
6194
6350
|
name: entry.name,
|
|
6195
6351
|
status: "failed",
|
|
@@ -6214,6 +6370,76 @@ var Bam = class extends EventEmitter {
|
|
|
6214
6370
|
}
|
|
6215
6371
|
};
|
|
6216
6372
|
//#endregion
|
|
6373
|
+
//#region ../../src/notifier.ts
|
|
6374
|
+
var events$1 = {
|
|
6375
|
+
error: "error",
|
|
6376
|
+
warning: "warning"
|
|
6377
|
+
};
|
|
6378
|
+
var WARNING_TYPE = "listen_notify_unavailable";
|
|
6379
|
+
var Notifier = class extends EventEmitter {
|
|
6380
|
+
events = events$1;
|
|
6381
|
+
#db;
|
|
6382
|
+
#manager;
|
|
6383
|
+
#config;
|
|
6384
|
+
#handle = null;
|
|
6385
|
+
#stopped = true;
|
|
6386
|
+
constructor(db, manager, config) {
|
|
6387
|
+
super();
|
|
6388
|
+
this.#db = db;
|
|
6389
|
+
this.#manager = manager;
|
|
6390
|
+
this.#config = config;
|
|
6391
|
+
}
|
|
6392
|
+
get available() {
|
|
6393
|
+
return this.#handle !== null;
|
|
6394
|
+
}
|
|
6395
|
+
async start() {
|
|
6396
|
+
if (!this.#stopped) return;
|
|
6397
|
+
this.#stopped = false;
|
|
6398
|
+
if (this.#config.noListenNotify) {
|
|
6399
|
+
this.emit(events$1.warning, {
|
|
6400
|
+
message: `useListenNotify is not supported on the ${this.#config.backend} backend. Continuing with polling only.`,
|
|
6401
|
+
data: {
|
|
6402
|
+
type: WARNING_TYPE,
|
|
6403
|
+
backend: this.#config.backend
|
|
6404
|
+
}
|
|
6405
|
+
});
|
|
6406
|
+
return;
|
|
6407
|
+
}
|
|
6408
|
+
if (typeof this.#db.listen !== "function") {
|
|
6409
|
+
this.emit(events$1.warning, {
|
|
6410
|
+
message: "useListenNotify is enabled but the database connection does not support LISTEN/NOTIFY. Continuing with polling only.",
|
|
6411
|
+
data: { type: WARNING_TYPE }
|
|
6412
|
+
});
|
|
6413
|
+
return;
|
|
6414
|
+
}
|
|
6415
|
+
try {
|
|
6416
|
+
const { rows } = await this.#db.executeSql(`SELECT ${notifyChannelSql(this.#config.schema)} AS channel`);
|
|
6417
|
+
const channel = rows[0].channel;
|
|
6418
|
+
this.#handle = await this.#db.listen(channel, (payload) => this.#manager.notifyQueue(payload), () => this.#manager.forceFetchLnWorkers());
|
|
6419
|
+
} catch (err) {
|
|
6420
|
+
this.emit(events$1.warning, {
|
|
6421
|
+
message: "Failed to start LISTEN/NOTIFY listener. Continuing with polling only.",
|
|
6422
|
+
data: {
|
|
6423
|
+
type: WARNING_TYPE,
|
|
6424
|
+
error: err?.message
|
|
6425
|
+
}
|
|
6426
|
+
});
|
|
6427
|
+
}
|
|
6428
|
+
}
|
|
6429
|
+
async stop() {
|
|
6430
|
+
if (this.#stopped) return;
|
|
6431
|
+
this.#stopped = true;
|
|
6432
|
+
if (this.#handle) {
|
|
6433
|
+
try {
|
|
6434
|
+
await this.#handle.close();
|
|
6435
|
+
} catch (err) {
|
|
6436
|
+
this.emit(events$1.error, err);
|
|
6437
|
+
}
|
|
6438
|
+
this.#handle = null;
|
|
6439
|
+
}
|
|
6440
|
+
}
|
|
6441
|
+
};
|
|
6442
|
+
//#endregion
|
|
6217
6443
|
//#region ../../src/db.ts
|
|
6218
6444
|
var Db = class extends EventEmitter {
|
|
6219
6445
|
pool;
|
|
@@ -6245,6 +6471,63 @@ var Db = class extends EventEmitter {
|
|
|
6245
6471
|
assert(this.opened, "Database not opened. Call open() before executing SQL.");
|
|
6246
6472
|
return await this.pool.query(text, values);
|
|
6247
6473
|
}
|
|
6474
|
+
async listen(channel, onNotification, onReconnect) {
|
|
6475
|
+
assert(this.opened, "Database not opened. Call open() before listening.");
|
|
6476
|
+
let closed = false;
|
|
6477
|
+
let client = null;
|
|
6478
|
+
let reconnectTimer = null;
|
|
6479
|
+
let attempt = 0;
|
|
6480
|
+
const scheduleReconnect = () => {
|
|
6481
|
+
if (closed || reconnectTimer) return;
|
|
6482
|
+
const backoff = Math.min(3e4, 1e3 * 2 ** Math.min(attempt, 5));
|
|
6483
|
+
attempt++;
|
|
6484
|
+
reconnectTimer = setTimeout(() => {
|
|
6485
|
+
reconnectTimer = null;
|
|
6486
|
+
connect().catch(() => scheduleReconnect());
|
|
6487
|
+
}, backoff);
|
|
6488
|
+
};
|
|
6489
|
+
const connect = async () => {
|
|
6490
|
+
if (closed) return;
|
|
6491
|
+
const next = new pg.Client(this.config);
|
|
6492
|
+
next.on("error", (error) => {
|
|
6493
|
+
this.emit("error", error);
|
|
6494
|
+
if (!closed) {
|
|
6495
|
+
next.removeAllListeners();
|
|
6496
|
+
next.end().catch(() => {});
|
|
6497
|
+
if (client === next) client = null;
|
|
6498
|
+
scheduleReconnect();
|
|
6499
|
+
}
|
|
6500
|
+
});
|
|
6501
|
+
next.on("notification", (msg) => {
|
|
6502
|
+
if (msg.payload !== void 0) onNotification(msg.payload);
|
|
6503
|
+
});
|
|
6504
|
+
client = next;
|
|
6505
|
+
try {
|
|
6506
|
+
await next.connect();
|
|
6507
|
+
await next.query(`LISTEN "${channel}"`);
|
|
6508
|
+
} catch (err) {
|
|
6509
|
+
next.removeAllListeners();
|
|
6510
|
+
await next.end().catch(() => {});
|
|
6511
|
+
if (client === next) client = null;
|
|
6512
|
+
throw err;
|
|
6513
|
+
}
|
|
6514
|
+
attempt = 0;
|
|
6515
|
+
onReconnect();
|
|
6516
|
+
};
|
|
6517
|
+
await connect();
|
|
6518
|
+
return { close: async () => {
|
|
6519
|
+
closed = true;
|
|
6520
|
+
if (reconnectTimer) {
|
|
6521
|
+
clearTimeout(reconnectTimer);
|
|
6522
|
+
reconnectTimer = null;
|
|
6523
|
+
}
|
|
6524
|
+
if (client) {
|
|
6525
|
+
client.removeAllListeners();
|
|
6526
|
+
await client.end().catch(() => {});
|
|
6527
|
+
client = null;
|
|
6528
|
+
}
|
|
6529
|
+
} };
|
|
6530
|
+
}
|
|
6248
6531
|
async withTransaction(fn) {
|
|
6249
6532
|
assert(this.opened, "Database not opened. Call open() before executing SQL.");
|
|
6250
6533
|
const client = await this.pool.connect();
|
|
@@ -6282,6 +6565,7 @@ var PgBoss = class extends EventEmitter {
|
|
|
6282
6565
|
#manager;
|
|
6283
6566
|
#timekeeper;
|
|
6284
6567
|
#bam;
|
|
6568
|
+
#notifier;
|
|
6285
6569
|
constructor(value) {
|
|
6286
6570
|
super();
|
|
6287
6571
|
this.#stoppingOn = null;
|
|
@@ -6297,15 +6581,19 @@ var PgBoss = class extends EventEmitter {
|
|
|
6297
6581
|
const timekeeper = new Timekeeper(db, manager, config);
|
|
6298
6582
|
manager.timekeeper = timekeeper;
|
|
6299
6583
|
const bam = new Bam(db, config);
|
|
6584
|
+
const notifier = new Notifier(db, manager, config);
|
|
6585
|
+
manager.notifier = notifier;
|
|
6300
6586
|
this.#promoteEvents(manager);
|
|
6301
6587
|
this.#promoteEvents(boss);
|
|
6302
6588
|
this.#promoteEvents(timekeeper);
|
|
6303
6589
|
this.#promoteEvents(bam);
|
|
6590
|
+
this.#promoteEvents(notifier);
|
|
6304
6591
|
this.#boss = boss;
|
|
6305
6592
|
this.#contractor = contractor;
|
|
6306
6593
|
this.#manager = manager;
|
|
6307
6594
|
this.#timekeeper = timekeeper;
|
|
6308
6595
|
this.#bam = bam;
|
|
6596
|
+
this.#notifier = notifier;
|
|
6309
6597
|
}
|
|
6310
6598
|
#promoteEvents(emitter) {
|
|
6311
6599
|
for (const event of Object.values(emitter?.events)) emitter.on(event, (arg) => this.emit(event, arg));
|
|
@@ -6319,6 +6607,7 @@ var PgBoss = class extends EventEmitter {
|
|
|
6319
6607
|
if (this.#config.migrate) await this.#contractor.start();
|
|
6320
6608
|
else await this.#contractor.check();
|
|
6321
6609
|
await this.#manager.start();
|
|
6610
|
+
if (this.#config.useListenNotify) await this.#notifier.start();
|
|
6322
6611
|
if (this.#config.supervise) await this.#boss.start();
|
|
6323
6612
|
if (this.#config.schedule) await this.#timekeeper.start();
|
|
6324
6613
|
if (this.#config.migrate) await this.#bam.start();
|
|
@@ -6348,6 +6637,7 @@ var PgBoss = class extends EventEmitter {
|
|
|
6348
6637
|
let { close = true, graceful = true, timeout = 3e4 } = options;
|
|
6349
6638
|
timeout = Math.max(timeout, 1e3);
|
|
6350
6639
|
this.#stoppingOn = Date.now();
|
|
6640
|
+
await this.#notifier.stop();
|
|
6351
6641
|
await this.#manager.stop();
|
|
6352
6642
|
await this.#timekeeper.stop();
|
|
6353
6643
|
await this.#boss.stop();
|
|
@@ -6514,7 +6804,7 @@ var PgBoss = class extends EventEmitter {
|
|
|
6514
6804
|
return rows;
|
|
6515
6805
|
}
|
|
6516
6806
|
async getBamEntries() {
|
|
6517
|
-
const sql = getBamEntries(this.#config.schema);
|
|
6807
|
+
const sql = getBamEntries$1(this.#config.schema);
|
|
6518
6808
|
const { rows } = await this.#db.executeSql(sql);
|
|
6519
6809
|
return rows;
|
|
6520
6810
|
}
|
|
@@ -6609,7 +6899,9 @@ var QUEUE_COLUMNS = `
|
|
|
6609
6899
|
deletion_seconds as "deleteAfterSeconds",
|
|
6610
6900
|
deferred_count as "deferredCount",
|
|
6611
6901
|
queued_count as "queuedCount",
|
|
6902
|
+
GREATEST(queued_count - deferred_count, 0) as "readyCount",
|
|
6612
6903
|
active_count as "activeCount",
|
|
6904
|
+
failed_count as "failedCount",
|
|
6613
6905
|
total_count as "totalCount",
|
|
6614
6906
|
warning_queued as "warningQueueSize",
|
|
6615
6907
|
singletons_active as "singletonsActive",
|
|
@@ -6862,19 +7154,81 @@ async function getWarningCount(dbUrl, schema, type) {
|
|
|
6862
7154
|
throw err;
|
|
6863
7155
|
}
|
|
6864
7156
|
}
|
|
7157
|
+
async function getBamEntries(dbUrl, schema, options = {}) {
|
|
7158
|
+
const s = validateIdentifier(schema);
|
|
7159
|
+
const { status = null, limit = 200, offset = 0 } = options;
|
|
7160
|
+
const sql = `
|
|
7161
|
+
SELECT
|
|
7162
|
+
id,
|
|
7163
|
+
name,
|
|
7164
|
+
version,
|
|
7165
|
+
status,
|
|
7166
|
+
queue,
|
|
7167
|
+
table_name as "table",
|
|
7168
|
+
command,
|
|
7169
|
+
error,
|
|
7170
|
+
created_on as "createdOn",
|
|
7171
|
+
started_on as "startedOn",
|
|
7172
|
+
completed_on as "completedOn"
|
|
7173
|
+
FROM ${s}.bam
|
|
7174
|
+
WHERE ($1::text IS NULL OR status = $1)
|
|
7175
|
+
ORDER BY version DESC, created_on DESC
|
|
7176
|
+
LIMIT $2 OFFSET $3
|
|
7177
|
+
`;
|
|
7178
|
+
try {
|
|
7179
|
+
return await query(dbUrl, sql, [
|
|
7180
|
+
status,
|
|
7181
|
+
limit,
|
|
7182
|
+
offset
|
|
7183
|
+
]);
|
|
7184
|
+
} catch (err) {
|
|
7185
|
+
if (err && typeof err === "object" && "code" in err && err.code === "42P01") return [];
|
|
7186
|
+
throw err;
|
|
7187
|
+
}
|
|
7188
|
+
}
|
|
7189
|
+
async function getBamCount(dbUrl, schema, status) {
|
|
7190
|
+
const sql = `
|
|
7191
|
+
SELECT COUNT(*)::int as count
|
|
7192
|
+
FROM ${validateIdentifier(schema)}.bam
|
|
7193
|
+
WHERE ($1::text IS NULL OR status = $1)
|
|
7194
|
+
`;
|
|
7195
|
+
try {
|
|
7196
|
+
return (await queryOne(dbUrl, sql, [status ?? null]))?.count ?? 0;
|
|
7197
|
+
} catch (err) {
|
|
7198
|
+
if (err && typeof err === "object" && "code" in err && err.code === "42P01") return 0;
|
|
7199
|
+
throw err;
|
|
7200
|
+
}
|
|
7201
|
+
}
|
|
7202
|
+
async function getBamStatusSummary(dbUrl, schema) {
|
|
7203
|
+
const sql = `
|
|
7204
|
+
SELECT status, count(*)::int as count, max(created_on) as "lastCreatedOn"
|
|
7205
|
+
FROM ${validateIdentifier(schema)}.bam
|
|
7206
|
+
GROUP BY status
|
|
7207
|
+
`;
|
|
7208
|
+
try {
|
|
7209
|
+
return await query(dbUrl, sql);
|
|
7210
|
+
} catch (err) {
|
|
7211
|
+
if (err && typeof err === "object" && "code" in err && err.code === "42P01") return [];
|
|
7212
|
+
throw err;
|
|
7213
|
+
}
|
|
7214
|
+
}
|
|
6865
7215
|
async function getQueueStats(dbUrl, schema) {
|
|
6866
7216
|
return await queryOne(dbUrl, `
|
|
6867
7217
|
SELECT
|
|
6868
7218
|
COALESCE(SUM(deferred_count), 0)::int as "totalDeferred",
|
|
6869
7219
|
COALESCE(SUM(queued_count), 0)::int as "totalQueued",
|
|
7220
|
+
COALESCE(SUM(GREATEST(queued_count - deferred_count, 0)), 0)::int as "totalReady",
|
|
6870
7221
|
COALESCE(SUM(active_count), 0)::int as "totalActive",
|
|
7222
|
+
COALESCE(SUM(failed_count), 0)::int as "totalFailed",
|
|
6871
7223
|
COALESCE(SUM(total_count), 0)::int as "totalJobs",
|
|
6872
7224
|
COUNT(*)::int as "queueCount"
|
|
6873
7225
|
FROM ${validateIdentifier(schema)}.queue
|
|
6874
7226
|
`) ?? {
|
|
6875
7227
|
totalDeferred: 0,
|
|
6876
7228
|
totalQueued: 0,
|
|
7229
|
+
totalReady: 0,
|
|
6877
7230
|
totalActive: 0,
|
|
7231
|
+
totalFailed: 0,
|
|
6878
7232
|
totalJobs: 0,
|
|
6879
7233
|
queueCount: 0
|
|
6880
7234
|
};
|
|
@@ -6975,9 +7329,21 @@ var statCards = [
|
|
|
6975
7329
|
{
|
|
6976
7330
|
name: "Queued Jobs",
|
|
6977
7331
|
key: "totalQueued",
|
|
6978
|
-
hint: "
|
|
7332
|
+
hint: "incl. deferred",
|
|
7333
|
+
accent: "neutral"
|
|
7334
|
+
},
|
|
7335
|
+
{
|
|
7336
|
+
name: "Deferred",
|
|
7337
|
+
key: "totalDeferred",
|
|
7338
|
+
hint: "scheduled for later",
|
|
6979
7339
|
accent: "neutral"
|
|
6980
7340
|
},
|
|
7341
|
+
{
|
|
7342
|
+
name: "Ready",
|
|
7343
|
+
key: "totalReady",
|
|
7344
|
+
hint: "ready to process",
|
|
7345
|
+
accent: "primary"
|
|
7346
|
+
},
|
|
6981
7347
|
{
|
|
6982
7348
|
name: "Active",
|
|
6983
7349
|
key: "totalActive",
|
|
@@ -6985,9 +7351,9 @@ var statCards = [
|
|
|
6985
7351
|
accent: "primary"
|
|
6986
7352
|
},
|
|
6987
7353
|
{
|
|
6988
|
-
name: "
|
|
6989
|
-
key: "
|
|
6990
|
-
hint: "
|
|
7354
|
+
name: "Failed",
|
|
7355
|
+
key: "totalFailed",
|
|
7356
|
+
hint: "recent failures",
|
|
6991
7357
|
accent: "neutral"
|
|
6992
7358
|
},
|
|
6993
7359
|
{
|
|
@@ -7199,34 +7565,45 @@ function ErrorCard({ title, message = "Please check your database connection and
|
|
|
7199
7565
|
//#endregion
|
|
7200
7566
|
//#region app/routes/_index.tsx
|
|
7201
7567
|
var _index_exports = /* @__PURE__ */ __exportAll({
|
|
7202
|
-
ErrorBoundary: () => ErrorBoundary$
|
|
7568
|
+
ErrorBoundary: () => ErrorBoundary$11,
|
|
7203
7569
|
default: () => _index_default,
|
|
7204
|
-
loader: () => loader$
|
|
7570
|
+
loader: () => loader$11
|
|
7205
7571
|
});
|
|
7206
|
-
async function loader$
|
|
7572
|
+
async function loader$11({ context }) {
|
|
7207
7573
|
const { DB_URL, SCHEMA } = context.get(dbContext);
|
|
7208
|
-
const [warnings, stats, topQueues, totalQueues, problemQueuesCount] = await Promise.all([
|
|
7574
|
+
const [warnings, stats, topQueues, totalQueues, problemQueuesCount, bamSummary] = await Promise.all([
|
|
7209
7575
|
getWarnings(DB_URL, SCHEMA, { limit: 5 }),
|
|
7210
7576
|
getQueueStats(DB_URL, SCHEMA),
|
|
7211
7577
|
getTopQueues(DB_URL, SCHEMA, 5),
|
|
7212
7578
|
getQueueCount(DB_URL, SCHEMA),
|
|
7213
|
-
getProblemQueuesCount(DB_URL, SCHEMA)
|
|
7579
|
+
getProblemQueuesCount(DB_URL, SCHEMA),
|
|
7580
|
+
getBamStatusSummary(DB_URL, SCHEMA)
|
|
7214
7581
|
]);
|
|
7215
7582
|
return {
|
|
7216
7583
|
stats,
|
|
7217
7584
|
warnings,
|
|
7218
7585
|
topQueues,
|
|
7586
|
+
migrations: bamSummary.reduce((acc, row) => {
|
|
7587
|
+
if (row.status === "pending") acc.pending += row.count;
|
|
7588
|
+
else if (row.status === "in_progress") acc.inProgress += row.count;
|
|
7589
|
+
else if (row.status === "failed") acc.failed += row.count;
|
|
7590
|
+
return acc;
|
|
7591
|
+
}, {
|
|
7592
|
+
pending: 0,
|
|
7593
|
+
inProgress: 0,
|
|
7594
|
+
failed: 0
|
|
7595
|
+
}),
|
|
7219
7596
|
queueStats: {
|
|
7220
7597
|
totalQueues,
|
|
7221
7598
|
problemQueues: problemQueuesCount
|
|
7222
7599
|
}
|
|
7223
7600
|
};
|
|
7224
7601
|
}
|
|
7225
|
-
var ErrorBoundary$
|
|
7602
|
+
var ErrorBoundary$11 = UNSAFE_withErrorBoundaryProps(function ErrorBoundary() {
|
|
7226
7603
|
return /* @__PURE__ */ jsx(ErrorCard, { title: "Failed to load dashboard" });
|
|
7227
7604
|
});
|
|
7228
7605
|
var _index_default = UNSAFE_withComponentProps(function Overview({ loaderData }) {
|
|
7229
|
-
const { stats, warnings, topQueues } = loaderData;
|
|
7606
|
+
const { stats, warnings, topQueues, migrations } = loaderData;
|
|
7230
7607
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
7231
7608
|
/* @__PURE__ */ jsx(PageHeader, {
|
|
7232
7609
|
title: "Overview",
|
|
@@ -7241,9 +7618,10 @@ var _index_default = UNSAFE_withComponentProps(function Overview({ loaderData })
|
|
|
7241
7618
|
})
|
|
7242
7619
|
}),
|
|
7243
7620
|
/* @__PURE__ */ jsx("div", {
|
|
7244
|
-
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-
|
|
7621
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6 mb-4",
|
|
7245
7622
|
children: /* @__PURE__ */ jsx(StatsCards, { stats })
|
|
7246
7623
|
}),
|
|
7624
|
+
/* @__PURE__ */ jsx(MigrationsBanner, { migrations }),
|
|
7247
7625
|
/* @__PURE__ */ jsxs("div", {
|
|
7248
7626
|
className: "grid grid-cols-1 lg:grid-cols-[1.5fr_1fr] gap-4",
|
|
7249
7627
|
children: [/* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsxs(CardHeader, { children: [/* @__PURE__ */ jsx(CardTitle, { children: "Top Queues" }), /* @__PURE__ */ jsx(DbLink, {
|
|
@@ -7312,6 +7690,37 @@ var _index_default = UNSAFE_withComponentProps(function Overview({ loaderData })
|
|
|
7312
7690
|
})
|
|
7313
7691
|
] });
|
|
7314
7692
|
});
|
|
7693
|
+
function MigrationsBanner({ migrations }) {
|
|
7694
|
+
const { pending, inProgress, failed } = migrations;
|
|
7695
|
+
if (pending === 0 && inProgress === 0 && failed === 0) return null;
|
|
7696
|
+
const parts = [];
|
|
7697
|
+
if (pending > 0) parts.push(`${pending.toLocaleString()} pending`);
|
|
7698
|
+
if (inProgress > 0) parts.push(`${inProgress.toLocaleString()} in progress`);
|
|
7699
|
+
if (failed > 0) parts.push(`${failed.toLocaleString()} failed`);
|
|
7700
|
+
return /* @__PURE__ */ jsx(DbLink, {
|
|
7701
|
+
to: "/migrations",
|
|
7702
|
+
className: "block mb-4",
|
|
7703
|
+
children: /* @__PURE__ */ jsxs(Card, {
|
|
7704
|
+
className: "flex items-center gap-3 px-4 py-3 hover:bg-[var(--surface-hover)]",
|
|
7705
|
+
children: [
|
|
7706
|
+
/* @__PURE__ */ jsx(Badge, {
|
|
7707
|
+
variant: failed > 0 ? "error" : "warning",
|
|
7708
|
+
size: "sm",
|
|
7709
|
+
dot: true,
|
|
7710
|
+
children: "Async migrations"
|
|
7711
|
+
}),
|
|
7712
|
+
/* @__PURE__ */ jsx("span", {
|
|
7713
|
+
className: "text-sm text-[var(--text-secondary)]",
|
|
7714
|
+
children: parts.join(" · ")
|
|
7715
|
+
}),
|
|
7716
|
+
/* @__PURE__ */ jsx("span", {
|
|
7717
|
+
className: "ml-auto text-sm font-medium text-primary-600 dark:text-primary-400",
|
|
7718
|
+
children: "View"
|
|
7719
|
+
})
|
|
7720
|
+
]
|
|
7721
|
+
})
|
|
7722
|
+
});
|
|
7723
|
+
}
|
|
7315
7724
|
function QueueStatusBadge({ queue }) {
|
|
7316
7725
|
if ((queue.warningQueueSize ?? 0) > 0 && queue.queuedCount > (queue.warningQueueSize ?? 0)) return /* @__PURE__ */ jsx(Badge, {
|
|
7317
7726
|
variant: "error",
|
|
@@ -7826,10 +8235,10 @@ function SearchIcon$1({ className }) {
|
|
|
7826
8235
|
//#endregion
|
|
7827
8236
|
//#region app/routes/jobs.tsx
|
|
7828
8237
|
var jobs_exports = /* @__PURE__ */ __exportAll({
|
|
7829
|
-
ErrorBoundary: () => ErrorBoundary$
|
|
8238
|
+
ErrorBoundary: () => ErrorBoundary$10,
|
|
7830
8239
|
buildSearchParams: () => buildSearchParams,
|
|
7831
8240
|
default: () => jobs_default,
|
|
7832
|
-
loader: () => loader$
|
|
8241
|
+
loader: () => loader$10,
|
|
7833
8242
|
parseFiltersFromUrl: () => parseFiltersFromUrl
|
|
7834
8243
|
});
|
|
7835
8244
|
function parseFiltersFromUrl(searchParams) {
|
|
@@ -7866,7 +8275,7 @@ function parseFiltersFromUrl(searchParams) {
|
|
|
7866
8275
|
shouldRunCount
|
|
7867
8276
|
};
|
|
7868
8277
|
}
|
|
7869
|
-
async function loader$
|
|
8278
|
+
async function loader$10({ request, context }) {
|
|
7870
8279
|
const { DB_URL, SCHEMA } = context.get(dbContext);
|
|
7871
8280
|
const url = new URL(request.url);
|
|
7872
8281
|
const parsed = parseFiltersFromUrl(url.searchParams);
|
|
@@ -7914,7 +8323,7 @@ async function loader$9({ request, context }) {
|
|
|
7914
8323
|
hasPrevPage
|
|
7915
8324
|
};
|
|
7916
8325
|
}
|
|
7917
|
-
var ErrorBoundary$
|
|
8326
|
+
var ErrorBoundary$10 = UNSAFE_withErrorBoundaryProps(function ErrorBoundary() {
|
|
7918
8327
|
return /* @__PURE__ */ jsx(ErrorCard, { title: "Failed to load jobs" });
|
|
7919
8328
|
});
|
|
7920
8329
|
function buildSearchParams(filters) {
|
|
@@ -8094,11 +8503,11 @@ function Chip({ label, onRemove }) {
|
|
|
8094
8503
|
//#endregion
|
|
8095
8504
|
//#region app/routes/queues._index.tsx
|
|
8096
8505
|
var queues__index_exports = /* @__PURE__ */ __exportAll({
|
|
8097
|
-
ErrorBoundary: () => ErrorBoundary$
|
|
8506
|
+
ErrorBoundary: () => ErrorBoundary$9,
|
|
8098
8507
|
default: () => queues__index_default,
|
|
8099
|
-
loader: () => loader$
|
|
8508
|
+
loader: () => loader$9
|
|
8100
8509
|
});
|
|
8101
|
-
async function loader$
|
|
8510
|
+
async function loader$9({ request, context }) {
|
|
8102
8511
|
const { DB_URL, SCHEMA } = context.get(dbContext);
|
|
8103
8512
|
const url = new URL(request.url);
|
|
8104
8513
|
const page = parsePageNumber(url.searchParams.get("page"));
|
|
@@ -8131,7 +8540,7 @@ async function loader$8({ request, context }) {
|
|
|
8131
8540
|
search
|
|
8132
8541
|
};
|
|
8133
8542
|
}
|
|
8134
|
-
var ErrorBoundary$
|
|
8543
|
+
var ErrorBoundary$9 = UNSAFE_withErrorBoundaryProps(function ErrorBoundary() {
|
|
8135
8544
|
return /* @__PURE__ */ jsx("div", {
|
|
8136
8545
|
className: "p-6",
|
|
8137
8546
|
children: /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, {
|
|
@@ -8297,13 +8706,21 @@ var queues__index_default = UNSAFE_withComponentProps(function QueuesIndex({ loa
|
|
|
8297
8706
|
className: "text-right",
|
|
8298
8707
|
children: "Queued"
|
|
8299
8708
|
}),
|
|
8709
|
+
/* @__PURE__ */ jsx(TableHead, {
|
|
8710
|
+
className: "text-right",
|
|
8711
|
+
children: "Deferred"
|
|
8712
|
+
}),
|
|
8713
|
+
/* @__PURE__ */ jsx(TableHead, {
|
|
8714
|
+
className: "text-right",
|
|
8715
|
+
children: "Ready"
|
|
8716
|
+
}),
|
|
8300
8717
|
/* @__PURE__ */ jsx(TableHead, {
|
|
8301
8718
|
className: "text-right",
|
|
8302
8719
|
children: "Active"
|
|
8303
8720
|
}),
|
|
8304
8721
|
/* @__PURE__ */ jsx(TableHead, {
|
|
8305
8722
|
className: "text-right",
|
|
8306
|
-
children: "
|
|
8723
|
+
children: "Failed"
|
|
8307
8724
|
}),
|
|
8308
8725
|
/* @__PURE__ */ jsx(TableHead, {
|
|
8309
8726
|
className: "text-right",
|
|
@@ -8313,7 +8730,7 @@ var queues__index_default = UNSAFE_withComponentProps(function QueuesIndex({ loa
|
|
|
8313
8730
|
/* @__PURE__ */ jsx(TableHead, { children: "Status" })
|
|
8314
8731
|
] }) }), /* @__PURE__ */ jsx(TableBody, { children: queues.length === 0 ? /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, {
|
|
8315
8732
|
className: "text-center text-[var(--text-tertiary)] py-8",
|
|
8316
|
-
colSpan:
|
|
8733
|
+
colSpan: 11,
|
|
8317
8734
|
children: "No queues found"
|
|
8318
8735
|
}) }) : queues.map((queue) => {
|
|
8319
8736
|
const hasBacklog = (queue.warningQueueSize ?? 0) > 0 && queue.queuedCount > (queue.warningQueueSize ?? 0);
|
|
@@ -8336,13 +8753,21 @@ var queues__index_default = UNSAFE_withComponentProps(function QueuesIndex({ loa
|
|
|
8336
8753
|
className: "text-right pgb-num text-[var(--text-primary)]",
|
|
8337
8754
|
children: queue.queuedCount.toLocaleString()
|
|
8338
8755
|
}),
|
|
8756
|
+
/* @__PURE__ */ jsx(TableCell, {
|
|
8757
|
+
className: "text-right pgb-num text-[var(--text-primary)]",
|
|
8758
|
+
children: queue.deferredCount.toLocaleString()
|
|
8759
|
+
}),
|
|
8760
|
+
/* @__PURE__ */ jsx(TableCell, {
|
|
8761
|
+
className: "text-right pgb-num text-[var(--text-primary)]",
|
|
8762
|
+
children: queue.readyCount.toLocaleString()
|
|
8763
|
+
}),
|
|
8339
8764
|
/* @__PURE__ */ jsx(TableCell, {
|
|
8340
8765
|
className: "text-right pgb-num text-[var(--text-primary)]",
|
|
8341
8766
|
children: queue.activeCount.toLocaleString()
|
|
8342
8767
|
}),
|
|
8343
8768
|
/* @__PURE__ */ jsx(TableCell, {
|
|
8344
8769
|
className: "text-right pgb-num text-[var(--text-primary)]",
|
|
8345
|
-
children: queue.
|
|
8770
|
+
children: queue.failedCount.toLocaleString()
|
|
8346
8771
|
}),
|
|
8347
8772
|
/* @__PURE__ */ jsx(TableCell, {
|
|
8348
8773
|
className: "text-right pgb-num text-[var(--text-primary)]",
|
|
@@ -8520,12 +8945,12 @@ var SelectContent = ({ children }) => /* @__PURE__ */ jsx(Fragment, { children }
|
|
|
8520
8945
|
//#endregion
|
|
8521
8946
|
//#region app/routes/queues.create.tsx
|
|
8522
8947
|
var queues_create_exports = /* @__PURE__ */ __exportAll({
|
|
8523
|
-
ErrorBoundary: () => ErrorBoundary$
|
|
8948
|
+
ErrorBoundary: () => ErrorBoundary$8,
|
|
8524
8949
|
action: () => action$5,
|
|
8525
8950
|
default: () => queues_create_default,
|
|
8526
|
-
loader: () => loader$
|
|
8951
|
+
loader: () => loader$8
|
|
8527
8952
|
});
|
|
8528
|
-
async function loader$
|
|
8953
|
+
async function loader$8({ context }) {
|
|
8529
8954
|
const { DB_URL, SCHEMA } = context.get(dbContext);
|
|
8530
8955
|
return { queues: await getQueues(DB_URL, SCHEMA) };
|
|
8531
8956
|
}
|
|
@@ -8593,7 +9018,7 @@ async function action$5({ request, context }) {
|
|
|
8593
9018
|
const dbParam = new URL(request.url).searchParams.get("db");
|
|
8594
9019
|
return redirect(dbParam ? `/queues/${encodeURIComponent(queueName.trim())}?db=${encodeURIComponent(dbParam)}` : `/queues/${encodeURIComponent(queueName.trim())}`);
|
|
8595
9020
|
}
|
|
8596
|
-
var ErrorBoundary$
|
|
9021
|
+
var ErrorBoundary$8 = UNSAFE_withErrorBoundaryProps(function ErrorBoundary() {
|
|
8597
9022
|
return /* @__PURE__ */ jsx(ErrorCard, {
|
|
8598
9023
|
title: "Failed to load queue creation page",
|
|
8599
9024
|
backTo: {
|
|
@@ -9041,12 +9466,12 @@ DialogDescription.displayName = "DialogDescription";
|
|
|
9041
9466
|
//#endregion
|
|
9042
9467
|
//#region app/routes/queues.$name.tsx
|
|
9043
9468
|
var queues_$name_exports = /* @__PURE__ */ __exportAll({
|
|
9044
|
-
ErrorBoundary: () => ErrorBoundary$
|
|
9469
|
+
ErrorBoundary: () => ErrorBoundary$7,
|
|
9045
9470
|
action: () => action$4,
|
|
9046
9471
|
default: () => queues_$name_default,
|
|
9047
|
-
loader: () => loader$
|
|
9472
|
+
loader: () => loader$7
|
|
9048
9473
|
});
|
|
9049
|
-
async function loader$
|
|
9474
|
+
async function loader$7({ params, request, context }) {
|
|
9050
9475
|
const { DB_URL, SCHEMA } = context.get(dbContext);
|
|
9051
9476
|
const url = new URL(request.url);
|
|
9052
9477
|
const stateParam = url.searchParams.get("state");
|
|
@@ -9119,7 +9544,7 @@ async function action$4({ params, request, context }) {
|
|
|
9119
9544
|
message
|
|
9120
9545
|
};
|
|
9121
9546
|
}
|
|
9122
|
-
var ErrorBoundary$
|
|
9547
|
+
var ErrorBoundary$7 = UNSAFE_withErrorBoundaryProps(function ErrorBoundary() {
|
|
9123
9548
|
return /* @__PURE__ */ jsx(ErrorCard, {
|
|
9124
9549
|
title: "Failed to load queue",
|
|
9125
9550
|
backTo: {
|
|
@@ -9178,13 +9603,23 @@ var queues_$name_default = UNSAFE_withComponentProps(function QueueDetail({ load
|
|
|
9178
9603
|
]
|
|
9179
9604
|
}),
|
|
9180
9605
|
/* @__PURE__ */ jsxs("div", {
|
|
9181
|
-
className: "grid grid-cols-2 sm:grid-cols-
|
|
9606
|
+
className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-4",
|
|
9182
9607
|
children: [
|
|
9183
9608
|
/* @__PURE__ */ jsx(StatCard, {
|
|
9184
9609
|
label: "Queued",
|
|
9185
9610
|
value: queue.queuedCount.toLocaleString(),
|
|
9186
9611
|
accent: overThreshold ? "error" : "neutral",
|
|
9187
|
-
hint: overThreshold ? "over threshold" : "
|
|
9612
|
+
hint: overThreshold ? "over threshold" : "incl. deferred"
|
|
9613
|
+
}),
|
|
9614
|
+
/* @__PURE__ */ jsx(StatCard, {
|
|
9615
|
+
label: "Deferred",
|
|
9616
|
+
value: queue.deferredCount.toLocaleString()
|
|
9617
|
+
}),
|
|
9618
|
+
/* @__PURE__ */ jsx(StatCard, {
|
|
9619
|
+
label: "Ready",
|
|
9620
|
+
value: queue.readyCount.toLocaleString(),
|
|
9621
|
+
accent: "primary",
|
|
9622
|
+
hint: "ready to process"
|
|
9188
9623
|
}),
|
|
9189
9624
|
/* @__PURE__ */ jsx(StatCard, {
|
|
9190
9625
|
label: "Active",
|
|
@@ -9192,8 +9627,9 @@ var queues_$name_default = UNSAFE_withComponentProps(function QueueDetail({ load
|
|
|
9192
9627
|
accent: "primary"
|
|
9193
9628
|
}),
|
|
9194
9629
|
/* @__PURE__ */ jsx(StatCard, {
|
|
9195
|
-
label: "
|
|
9196
|
-
value: queue.
|
|
9630
|
+
label: "Failed",
|
|
9631
|
+
value: queue.failedCount.toLocaleString(),
|
|
9632
|
+
hint: "recent failures"
|
|
9197
9633
|
}),
|
|
9198
9634
|
/* @__PURE__ */ jsx(StatCard, {
|
|
9199
9635
|
label: "Total",
|
|
@@ -9539,12 +9975,12 @@ function ConfirmDialog({ title, description, confirmLabel, confirmVariant = "pri
|
|
|
9539
9975
|
//#endregion
|
|
9540
9976
|
//#region app/routes/queues.$name.jobs.$jobId.tsx
|
|
9541
9977
|
var queues_$name_jobs_$jobId_exports = /* @__PURE__ */ __exportAll({
|
|
9542
|
-
ErrorBoundary: () => ErrorBoundary$
|
|
9978
|
+
ErrorBoundary: () => ErrorBoundary$6,
|
|
9543
9979
|
action: () => action$3,
|
|
9544
9980
|
default: () => queues_$name_jobs_$jobId_default,
|
|
9545
|
-
loader: () => loader$
|
|
9981
|
+
loader: () => loader$6
|
|
9546
9982
|
});
|
|
9547
|
-
async function loader$
|
|
9983
|
+
async function loader$6({ params, context }) {
|
|
9548
9984
|
const { DB_URL, SCHEMA } = context.get(dbContext);
|
|
9549
9985
|
const job = await getJobById(DB_URL, SCHEMA, params.name, params.jobId);
|
|
9550
9986
|
if (!job) throw new Response("Job not found", { status: 404 });
|
|
@@ -9598,7 +10034,7 @@ async function action$3({ params, request, context }) {
|
|
|
9598
10034
|
message
|
|
9599
10035
|
};
|
|
9600
10036
|
}
|
|
9601
|
-
var ErrorBoundary$
|
|
10037
|
+
var ErrorBoundary$6 = UNSAFE_withErrorBoundaryProps(function ErrorBoundary() {
|
|
9602
10038
|
return /* @__PURE__ */ jsx(ErrorCard, {
|
|
9603
10039
|
title: "Failed to load job",
|
|
9604
10040
|
backTo: {
|
|
@@ -9851,11 +10287,11 @@ function ConfigItem({ label, value, mono = false }) {
|
|
|
9851
10287
|
//#endregion
|
|
9852
10288
|
//#region app/routes/schedules.tsx
|
|
9853
10289
|
var schedules_exports = /* @__PURE__ */ __exportAll({
|
|
9854
|
-
ErrorBoundary: () => ErrorBoundary$
|
|
10290
|
+
ErrorBoundary: () => ErrorBoundary$5,
|
|
9855
10291
|
default: () => schedules_default,
|
|
9856
|
-
loader: () => loader$
|
|
10292
|
+
loader: () => loader$5
|
|
9857
10293
|
});
|
|
9858
|
-
async function loader$
|
|
10294
|
+
async function loader$5({ request, context }) {
|
|
9859
10295
|
const { DB_URL, SCHEMA } = context.get(dbContext);
|
|
9860
10296
|
const page = parsePageNumber(new URL(request.url).searchParams.get("page"));
|
|
9861
10297
|
const limit = 20;
|
|
@@ -9873,7 +10309,7 @@ async function loader$4({ request, context }) {
|
|
|
9873
10309
|
hasPrevPage: page > 1
|
|
9874
10310
|
};
|
|
9875
10311
|
}
|
|
9876
|
-
var ErrorBoundary$
|
|
10312
|
+
var ErrorBoundary$5 = UNSAFE_withErrorBoundaryProps(function ErrorBoundary() {
|
|
9877
10313
|
return /* @__PURE__ */ jsx(ErrorCard, { title: "Failed to load schedules" });
|
|
9878
10314
|
});
|
|
9879
10315
|
function cronHuman(cron) {
|
|
@@ -9983,12 +10419,12 @@ var schedules_default = UNSAFE_withComponentProps(function Schedules({ loaderDat
|
|
|
9983
10419
|
//#endregion
|
|
9984
10420
|
//#region app/routes/schedules.$name.$key.tsx
|
|
9985
10421
|
var schedules_$name_$key_exports = /* @__PURE__ */ __exportAll({
|
|
9986
|
-
ErrorBoundary: () => ErrorBoundary$
|
|
10422
|
+
ErrorBoundary: () => ErrorBoundary$4,
|
|
9987
10423
|
action: () => action$2,
|
|
9988
10424
|
default: () => schedules_$name_$key_default,
|
|
9989
|
-
loader: () => loader$
|
|
10425
|
+
loader: () => loader$4
|
|
9990
10426
|
});
|
|
9991
|
-
async function loader$
|
|
10427
|
+
async function loader$4({ params, context }) {
|
|
9992
10428
|
const { DB_URL, SCHEMA } = context.get(dbContext);
|
|
9993
10429
|
const key = params.key === "__default__" ? "" : params.key;
|
|
9994
10430
|
const schedule = await getSchedule(DB_URL, SCHEMA, params.name, key);
|
|
@@ -10006,7 +10442,7 @@ async function action$2({ params, request, context }) {
|
|
|
10006
10442
|
}
|
|
10007
10443
|
return { error: "Invalid action" };
|
|
10008
10444
|
}
|
|
10009
|
-
var ErrorBoundary$
|
|
10445
|
+
var ErrorBoundary$4 = UNSAFE_withErrorBoundaryProps(function ErrorBoundary() {
|
|
10010
10446
|
return /* @__PURE__ */ jsx(ErrorCard, {
|
|
10011
10447
|
title: "Failed to load schedule",
|
|
10012
10448
|
backTo: {
|
|
@@ -10166,12 +10602,12 @@ var schedules_$name_$key_default = UNSAFE_withComponentProps(function ScheduleDe
|
|
|
10166
10602
|
//#endregion
|
|
10167
10603
|
//#region app/routes/schedules.new.tsx
|
|
10168
10604
|
var schedules_new_exports = /* @__PURE__ */ __exportAll({
|
|
10169
|
-
ErrorBoundary: () => ErrorBoundary$
|
|
10605
|
+
ErrorBoundary: () => ErrorBoundary$3,
|
|
10170
10606
|
action: () => action$1,
|
|
10171
10607
|
default: () => schedules_new_default,
|
|
10172
|
-
loader: () => loader$
|
|
10608
|
+
loader: () => loader$3
|
|
10173
10609
|
});
|
|
10174
|
-
async function loader$
|
|
10610
|
+
async function loader$3({ context }) {
|
|
10175
10611
|
const { DB_URL, SCHEMA } = context.get(dbContext);
|
|
10176
10612
|
return { queues: await getQueues(DB_URL, SCHEMA) };
|
|
10177
10613
|
}
|
|
@@ -10224,7 +10660,7 @@ async function action$1({ request, context }) {
|
|
|
10224
10660
|
const dbParam = new URL(request.url).searchParams.get("db");
|
|
10225
10661
|
return redirect(dbParam ? `/schedules?db=${encodeURIComponent(dbParam)}` : `/schedules`);
|
|
10226
10662
|
}
|
|
10227
|
-
var ErrorBoundary$
|
|
10663
|
+
var ErrorBoundary$3 = UNSAFE_withErrorBoundaryProps(function ErrorBoundary() {
|
|
10228
10664
|
return /* @__PURE__ */ jsx(ErrorCard, {
|
|
10229
10665
|
title: "Failed to load schedule creation",
|
|
10230
10666
|
backTo: {
|
|
@@ -10490,12 +10926,12 @@ var schedules_new_default = UNSAFE_withComponentProps(function CreateSchedule({
|
|
|
10490
10926
|
//#endregion
|
|
10491
10927
|
//#region app/routes/send.tsx
|
|
10492
10928
|
var send_exports = /* @__PURE__ */ __exportAll({
|
|
10493
|
-
ErrorBoundary: () => ErrorBoundary$
|
|
10929
|
+
ErrorBoundary: () => ErrorBoundary$2,
|
|
10494
10930
|
action: () => action,
|
|
10495
10931
|
default: () => send_default,
|
|
10496
|
-
loader: () => loader$
|
|
10932
|
+
loader: () => loader$2
|
|
10497
10933
|
});
|
|
10498
|
-
async function loader$
|
|
10934
|
+
async function loader$2({ context }) {
|
|
10499
10935
|
const { DB_URL, SCHEMA } = context.get(dbContext);
|
|
10500
10936
|
return { queues: await getQueues(DB_URL, SCHEMA) };
|
|
10501
10937
|
}
|
|
@@ -10545,7 +10981,7 @@ async function action({ request, context }) {
|
|
|
10545
10981
|
const dbParam = new URL(request.url).searchParams.get("db");
|
|
10546
10982
|
return redirect(dbParam ? `/queues/${encodeURIComponent(queueName.trim())}?db=${encodeURIComponent(dbParam)}` : `/queues/${encodeURIComponent(queueName.trim())}`);
|
|
10547
10983
|
}
|
|
10548
|
-
var ErrorBoundary$
|
|
10984
|
+
var ErrorBoundary$2 = UNSAFE_withErrorBoundaryProps(function ErrorBoundary() {
|
|
10549
10985
|
return /* @__PURE__ */ jsx(ErrorCard, {
|
|
10550
10986
|
title: "Failed to load job sending page",
|
|
10551
10987
|
backTo: {
|
|
@@ -10777,6 +11213,182 @@ var send_default = UNSAFE_withComponentProps(function SendJob({ loaderData }) {
|
|
|
10777
11213
|
});
|
|
10778
11214
|
});
|
|
10779
11215
|
//#endregion
|
|
11216
|
+
//#region app/routes/migrations.tsx
|
|
11217
|
+
var migrations_exports = /* @__PURE__ */ __exportAll({
|
|
11218
|
+
ErrorBoundary: () => ErrorBoundary$1,
|
|
11219
|
+
default: () => migrations_default,
|
|
11220
|
+
loader: () => loader$1
|
|
11221
|
+
});
|
|
11222
|
+
var PAGE_SIZE = 50;
|
|
11223
|
+
var STATUS_ACCENT = {
|
|
11224
|
+
pending: "warning",
|
|
11225
|
+
in_progress: "primary",
|
|
11226
|
+
completed: "success",
|
|
11227
|
+
failed: "error"
|
|
11228
|
+
};
|
|
11229
|
+
async function loader$1({ request, context }) {
|
|
11230
|
+
const { DB_URL, SCHEMA } = context.get(dbContext);
|
|
11231
|
+
const url = new URL(request.url);
|
|
11232
|
+
const statusParam = url.searchParams.get("status");
|
|
11233
|
+
const statusFilter = isValidBamStatus(statusParam) ? statusParam : null;
|
|
11234
|
+
const page = parsePageNumber(url.searchParams.get("page"));
|
|
11235
|
+
const offset = (page - 1) * PAGE_SIZE;
|
|
11236
|
+
const [entries, totalCount, summary] = await Promise.all([
|
|
11237
|
+
getBamEntries(DB_URL, SCHEMA, {
|
|
11238
|
+
status: statusFilter,
|
|
11239
|
+
limit: PAGE_SIZE,
|
|
11240
|
+
offset
|
|
11241
|
+
}),
|
|
11242
|
+
getBamCount(DB_URL, SCHEMA, statusFilter),
|
|
11243
|
+
getBamStatusSummary(DB_URL, SCHEMA)
|
|
11244
|
+
]);
|
|
11245
|
+
return {
|
|
11246
|
+
entries,
|
|
11247
|
+
summary,
|
|
11248
|
+
statusFilter,
|
|
11249
|
+
page,
|
|
11250
|
+
totalPages: Math.ceil(totalCount / PAGE_SIZE)
|
|
11251
|
+
};
|
|
11252
|
+
}
|
|
11253
|
+
var ErrorBoundary$1 = UNSAFE_withErrorBoundaryProps(function ErrorBoundary() {
|
|
11254
|
+
return /* @__PURE__ */ jsx(ErrorCard, {
|
|
11255
|
+
title: "Failed to load migrations",
|
|
11256
|
+
backTo: {
|
|
11257
|
+
href: "/",
|
|
11258
|
+
label: "Back to Dashboard"
|
|
11259
|
+
}
|
|
11260
|
+
});
|
|
11261
|
+
});
|
|
11262
|
+
var migrations_default = UNSAFE_withComponentProps(function Migrations({ loaderData }) {
|
|
11263
|
+
const { entries, summary, statusFilter, page, totalPages } = loaderData;
|
|
11264
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
11265
|
+
const counts = countByStatus(summary);
|
|
11266
|
+
const handleFilterChange = (key, value) => {
|
|
11267
|
+
const params = new URLSearchParams(searchParams);
|
|
11268
|
+
if (value) params.set(key, value);
|
|
11269
|
+
else params.delete(key);
|
|
11270
|
+
params.delete("page");
|
|
11271
|
+
setSearchParams(params);
|
|
11272
|
+
};
|
|
11273
|
+
const handlePageChange = (newPage) => {
|
|
11274
|
+
const params = new URLSearchParams(searchParams);
|
|
11275
|
+
params.set("page", newPage.toString());
|
|
11276
|
+
setSearchParams(params);
|
|
11277
|
+
};
|
|
11278
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
11279
|
+
className: "space-y-4",
|
|
11280
|
+
children: [
|
|
11281
|
+
/* @__PURE__ */ jsx(PageHeader, {
|
|
11282
|
+
title: "Migrations",
|
|
11283
|
+
subtitle: "Background async migrations (BAM) — schema changes such as concurrent index builds that run outside the install transaction"
|
|
11284
|
+
}),
|
|
11285
|
+
/* @__PURE__ */ jsx("div", {
|
|
11286
|
+
className: "grid grid-cols-2 gap-4 lg:grid-cols-4",
|
|
11287
|
+
children: BAM_STATUSES.map((status) => /* @__PURE__ */ jsx(StatCard, {
|
|
11288
|
+
label: BAM_STATUS_LABELS[status],
|
|
11289
|
+
value: counts[status].toLocaleString(),
|
|
11290
|
+
accent: counts[status] > 0 ? STATUS_ACCENT[status] : "neutral"
|
|
11291
|
+
}, status))
|
|
11292
|
+
}),
|
|
11293
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
11294
|
+
/* @__PURE__ */ jsxs(CardHeader, { children: [/* @__PURE__ */ jsx(CardTitle, { children: "Migration commands" }), /* @__PURE__ */ jsx(FilterSelect, {
|
|
11295
|
+
value: statusFilter,
|
|
11296
|
+
options: BAM_STATUS_OPTIONS,
|
|
11297
|
+
onChange: (value) => handleFilterChange("status", value)
|
|
11298
|
+
})] }),
|
|
11299
|
+
/* @__PURE__ */ jsx(CardContent, {
|
|
11300
|
+
className: "p-0",
|
|
11301
|
+
children: /* @__PURE__ */ jsxs(Table, { children: [/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
11302
|
+
/* @__PURE__ */ jsx(TableHead, { children: "Name" }),
|
|
11303
|
+
/* @__PURE__ */ jsx(TableHead, {
|
|
11304
|
+
className: "text-right",
|
|
11305
|
+
children: "Ver"
|
|
11306
|
+
}),
|
|
11307
|
+
/* @__PURE__ */ jsx(TableHead, { children: "Status" }),
|
|
11308
|
+
/* @__PURE__ */ jsx(TableHead, { children: "Table" }),
|
|
11309
|
+
/* @__PURE__ */ jsx(TableHead, { children: "Created" }),
|
|
11310
|
+
/* @__PURE__ */ jsx(TableHead, { children: "Started" }),
|
|
11311
|
+
/* @__PURE__ */ jsx(TableHead, { children: "Completed" }),
|
|
11312
|
+
/* @__PURE__ */ jsx(TableHead, { children: "Command / Error" })
|
|
11313
|
+
] }) }), /* @__PURE__ */ jsx(TableBody, { children: entries.length === 0 ? /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, {
|
|
11314
|
+
className: "text-center text-[var(--text-tertiary)] py-8",
|
|
11315
|
+
colSpan: 8,
|
|
11316
|
+
children: statusFilter ? `No ${BAM_STATUS_LABELS[statusFilter].toLowerCase()} migrations found` : "No async migrations recorded."
|
|
11317
|
+
}) }) : entries.map((entry) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
11318
|
+
/* @__PURE__ */ jsx(TableCell, {
|
|
11319
|
+
className: "text-[var(--text-primary)] font-medium",
|
|
11320
|
+
children: entry.name
|
|
11321
|
+
}),
|
|
11322
|
+
/* @__PURE__ */ jsx(TableCell, {
|
|
11323
|
+
className: "text-right pgb-num text-[var(--text-tertiary)]",
|
|
11324
|
+
children: entry.version
|
|
11325
|
+
}),
|
|
11326
|
+
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Badge, {
|
|
11327
|
+
variant: BAM_STATUS_VARIANTS[entry.status],
|
|
11328
|
+
size: "sm",
|
|
11329
|
+
dot: true,
|
|
11330
|
+
children: BAM_STATUS_LABELS[entry.status]
|
|
11331
|
+
}) }),
|
|
11332
|
+
/* @__PURE__ */ jsxs(TableCell, {
|
|
11333
|
+
className: "font-mono text-xs text-[var(--text-secondary)] whitespace-nowrap",
|
|
11334
|
+
children: [entry.table, entry.queue ? /* @__PURE__ */ jsxs("span", {
|
|
11335
|
+
className: "text-[var(--text-tertiary)]",
|
|
11336
|
+
children: [" · ", entry.queue]
|
|
11337
|
+
}) : null]
|
|
11338
|
+
}),
|
|
11339
|
+
/* @__PURE__ */ jsx(TableCell, {
|
|
11340
|
+
className: "pgb-num text-[var(--text-tertiary)] whitespace-nowrap",
|
|
11341
|
+
children: formatTimestamp(entry.createdOn)
|
|
11342
|
+
}),
|
|
11343
|
+
/* @__PURE__ */ jsx(TableCell, {
|
|
11344
|
+
className: "pgb-num text-[var(--text-tertiary)] whitespace-nowrap",
|
|
11345
|
+
children: formatTimestamp(entry.startedOn)
|
|
11346
|
+
}),
|
|
11347
|
+
/* @__PURE__ */ jsx(TableCell, {
|
|
11348
|
+
className: "pgb-num text-[var(--text-tertiary)] whitespace-nowrap",
|
|
11349
|
+
children: formatTimestamp(entry.completedOn)
|
|
11350
|
+
}),
|
|
11351
|
+
/* @__PURE__ */ jsxs(TableCell, {
|
|
11352
|
+
className: "max-w-md",
|
|
11353
|
+
children: [entry.error ? /* @__PURE__ */ jsx("p", {
|
|
11354
|
+
className: "mb-1 font-mono text-xs text-[var(--error-600)] break-words whitespace-pre-wrap",
|
|
11355
|
+
children: entry.error
|
|
11356
|
+
}) : null, /* @__PURE__ */ jsxs("details", { children: [/* @__PURE__ */ jsx("summary", {
|
|
11357
|
+
className: "cursor-pointer text-xs text-[var(--text-tertiary)] select-none",
|
|
11358
|
+
children: "View command"
|
|
11359
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
11360
|
+
className: "mt-1 overflow-x-auto rounded bg-[var(--surface-sunken)] p-2 font-mono text-xs text-[var(--text-secondary)] whitespace-pre-wrap",
|
|
11361
|
+
children: entry.command
|
|
11362
|
+
})] })]
|
|
11363
|
+
})
|
|
11364
|
+
] }, entry.id)) })] })
|
|
11365
|
+
}),
|
|
11366
|
+
/* @__PURE__ */ jsx(Pagination, {
|
|
11367
|
+
page,
|
|
11368
|
+
totalPages,
|
|
11369
|
+
hasNextPage: page < totalPages,
|
|
11370
|
+
hasPrevPage: page > 1,
|
|
11371
|
+
onPageChange: handlePageChange
|
|
11372
|
+
})
|
|
11373
|
+
] })
|
|
11374
|
+
]
|
|
11375
|
+
});
|
|
11376
|
+
});
|
|
11377
|
+
function formatTimestamp(value) {
|
|
11378
|
+
if (!value) return "—";
|
|
11379
|
+
return formatDateWithSeconds(new Date(value));
|
|
11380
|
+
}
|
|
11381
|
+
function countByStatus(summary) {
|
|
11382
|
+
const counts = {
|
|
11383
|
+
pending: 0,
|
|
11384
|
+
in_progress: 0,
|
|
11385
|
+
completed: 0,
|
|
11386
|
+
failed: 0
|
|
11387
|
+
};
|
|
11388
|
+
for (const row of summary) if (row.status in counts) counts[row.status] += row.count;
|
|
11389
|
+
return counts;
|
|
11390
|
+
}
|
|
11391
|
+
//#endregion
|
|
10780
11392
|
//#region app/routes/warnings.tsx
|
|
10781
11393
|
var warnings_exports = /* @__PURE__ */ __exportAll({
|
|
10782
11394
|
ErrorBoundary: () => ErrorBoundary,
|
|
@@ -10888,8 +11500,8 @@ function WarningTypeBadge({ type }) {
|
|
|
10888
11500
|
//#region \0virtual:react-router/server-manifest
|
|
10889
11501
|
var server_manifest_default = {
|
|
10890
11502
|
"entry": {
|
|
10891
|
-
"module": "/assets/entry.client-
|
|
10892
|
-
"imports": ["/assets/jsx-runtime-
|
|
11503
|
+
"module": "/assets/entry.client-CqyjuPDB.js",
|
|
11504
|
+
"imports": ["/assets/jsx-runtime-RQyiN6Nr.js", "/assets/react-dom-D_m_Zgd3.js"],
|
|
10893
11505
|
"css": []
|
|
10894
11506
|
},
|
|
10895
11507
|
"routes": {
|
|
@@ -10906,16 +11518,16 @@ var server_manifest_default = {
|
|
|
10906
11518
|
"hasClientMiddleware": false,
|
|
10907
11519
|
"hasDefaultExport": true,
|
|
10908
11520
|
"hasErrorBoundary": true,
|
|
10909
|
-
"module": "/assets/root-
|
|
11521
|
+
"module": "/assets/root-qxoeL6W3.js",
|
|
10910
11522
|
"imports": [
|
|
10911
|
-
"/assets/jsx-runtime-
|
|
10912
|
-
"/assets/react-dom-
|
|
10913
|
-
"/assets/db-link-
|
|
10914
|
-
"/assets/
|
|
10915
|
-
"/assets/
|
|
10916
|
-
"/assets/useOpenInteractionType-
|
|
11523
|
+
"/assets/jsx-runtime-RQyiN6Nr.js",
|
|
11524
|
+
"/assets/react-dom-D_m_Zgd3.js",
|
|
11525
|
+
"/assets/db-link-BajQ1v8I.js",
|
|
11526
|
+
"/assets/createLucideIcon-C-LI4enx.js",
|
|
11527
|
+
"/assets/MenuTrigger-BNvpjhsQ.js",
|
|
11528
|
+
"/assets/useOpenInteractionType-BQ1arb0B.js"
|
|
10917
11529
|
],
|
|
10918
|
-
"css": ["/assets/root-
|
|
11530
|
+
"css": ["/assets/root-B0MB8jZH.css"],
|
|
10919
11531
|
"clientActionModule": void 0,
|
|
10920
11532
|
"clientLoaderModule": void 0,
|
|
10921
11533
|
"clientMiddlewareModule": void 0,
|
|
@@ -10934,15 +11546,15 @@ var server_manifest_default = {
|
|
|
10934
11546
|
"hasClientMiddleware": false,
|
|
10935
11547
|
"hasDefaultExport": true,
|
|
10936
11548
|
"hasErrorBoundary": true,
|
|
10937
|
-
"module": "/assets/_index-
|
|
11549
|
+
"module": "/assets/_index-DqpFaaQw.js",
|
|
10938
11550
|
"imports": [
|
|
10939
|
-
"/assets/jsx-runtime-
|
|
10940
|
-
"/assets/db-link-
|
|
10941
|
-
"/assets/
|
|
10942
|
-
"/assets/
|
|
10943
|
-
"/assets/
|
|
10944
|
-
"/assets/
|
|
10945
|
-
"/assets/
|
|
11551
|
+
"/assets/jsx-runtime-RQyiN6Nr.js",
|
|
11552
|
+
"/assets/db-link-BajQ1v8I.js",
|
|
11553
|
+
"/assets/stat-card-dyg1wY5p.js",
|
|
11554
|
+
"/assets/button-9NpSS9Ow.js",
|
|
11555
|
+
"/assets/badge-CMnQO7Lq.js",
|
|
11556
|
+
"/assets/table-Cz7ujmH_.js",
|
|
11557
|
+
"/assets/error-card-BH7i86fH.js"
|
|
10946
11558
|
],
|
|
10947
11559
|
"css": [],
|
|
10948
11560
|
"clientActionModule": void 0,
|
|
@@ -10963,24 +11575,24 @@ var server_manifest_default = {
|
|
|
10963
11575
|
"hasClientMiddleware": false,
|
|
10964
11576
|
"hasDefaultExport": true,
|
|
10965
11577
|
"hasErrorBoundary": true,
|
|
10966
|
-
"module": "/assets/jobs-
|
|
11578
|
+
"module": "/assets/jobs-CAd_qqLH.js",
|
|
10967
11579
|
"imports": [
|
|
10968
|
-
"/assets/jsx-runtime-
|
|
10969
|
-
"/assets/db-link-
|
|
10970
|
-
"/assets/
|
|
10971
|
-
"/assets/
|
|
10972
|
-
"/assets/
|
|
10973
|
-
"/assets/
|
|
10974
|
-
"/assets/
|
|
10975
|
-
"/assets/
|
|
10976
|
-
"/assets/
|
|
10977
|
-
"/assets/
|
|
10978
|
-
"/assets/
|
|
10979
|
-
"/assets/
|
|
10980
|
-
"/assets/
|
|
10981
|
-
"/assets/
|
|
10982
|
-
"/assets/
|
|
10983
|
-
"/assets/react-dom-
|
|
11580
|
+
"/assets/jsx-runtime-RQyiN6Nr.js",
|
|
11581
|
+
"/assets/db-link-BajQ1v8I.js",
|
|
11582
|
+
"/assets/createLucideIcon-C-LI4enx.js",
|
|
11583
|
+
"/assets/check-7jwc5sb1.js",
|
|
11584
|
+
"/assets/chevron-down-BFFjfYD4.js",
|
|
11585
|
+
"/assets/chevron-right-DGk5QFJF.js",
|
|
11586
|
+
"/assets/x-AhXI_F1j.js",
|
|
11587
|
+
"/assets/MenuTrigger-BNvpjhsQ.js",
|
|
11588
|
+
"/assets/useOpenInteractionType-BQ1arb0B.js",
|
|
11589
|
+
"/assets/button-9NpSS9Ow.js",
|
|
11590
|
+
"/assets/badge-CMnQO7Lq.js",
|
|
11591
|
+
"/assets/table-Cz7ujmH_.js",
|
|
11592
|
+
"/assets/error-card-BH7i86fH.js",
|
|
11593
|
+
"/assets/pagination-C-ohiBmY.js",
|
|
11594
|
+
"/assets/filter-select-Bn_oSiip.js",
|
|
11595
|
+
"/assets/react-dom-D_m_Zgd3.js"
|
|
10984
11596
|
],
|
|
10985
11597
|
"css": [],
|
|
10986
11598
|
"clientActionModule": void 0,
|
|
@@ -11001,14 +11613,14 @@ var server_manifest_default = {
|
|
|
11001
11613
|
"hasClientMiddleware": false,
|
|
11002
11614
|
"hasDefaultExport": true,
|
|
11003
11615
|
"hasErrorBoundary": true,
|
|
11004
|
-
"module": "/assets/queues._index-
|
|
11616
|
+
"module": "/assets/queues._index-8YriSqbQ.js",
|
|
11005
11617
|
"imports": [
|
|
11006
|
-
"/assets/jsx-runtime-
|
|
11007
|
-
"/assets/db-link-
|
|
11008
|
-
"/assets/
|
|
11009
|
-
"/assets/
|
|
11010
|
-
"/assets/
|
|
11011
|
-
"/assets/
|
|
11618
|
+
"/assets/jsx-runtime-RQyiN6Nr.js",
|
|
11619
|
+
"/assets/db-link-BajQ1v8I.js",
|
|
11620
|
+
"/assets/button-9NpSS9Ow.js",
|
|
11621
|
+
"/assets/badge-CMnQO7Lq.js",
|
|
11622
|
+
"/assets/table-Cz7ujmH_.js",
|
|
11623
|
+
"/assets/filter-select-Bn_oSiip.js"
|
|
11012
11624
|
],
|
|
11013
11625
|
"css": [],
|
|
11014
11626
|
"clientActionModule": void 0,
|
|
@@ -11029,14 +11641,14 @@ var server_manifest_default = {
|
|
|
11029
11641
|
"hasClientMiddleware": false,
|
|
11030
11642
|
"hasDefaultExport": true,
|
|
11031
11643
|
"hasErrorBoundary": true,
|
|
11032
|
-
"module": "/assets/queues.create-
|
|
11644
|
+
"module": "/assets/queues.create-DsY0Sc19.js",
|
|
11033
11645
|
"imports": [
|
|
11034
|
-
"/assets/jsx-runtime-
|
|
11035
|
-
"/assets/db-link-
|
|
11036
|
-
"/assets/
|
|
11037
|
-
"/assets/button-
|
|
11038
|
-
"/assets/
|
|
11039
|
-
"/assets/createLucideIcon-
|
|
11646
|
+
"/assets/jsx-runtime-RQyiN6Nr.js",
|
|
11647
|
+
"/assets/db-link-BajQ1v8I.js",
|
|
11648
|
+
"/assets/chevron-down-BFFjfYD4.js",
|
|
11649
|
+
"/assets/button-9NpSS9Ow.js",
|
|
11650
|
+
"/assets/error-card-BH7i86fH.js",
|
|
11651
|
+
"/assets/createLucideIcon-C-LI4enx.js"
|
|
11040
11652
|
],
|
|
11041
11653
|
"css": [],
|
|
11042
11654
|
"clientActionModule": void 0,
|
|
@@ -11057,25 +11669,25 @@ var server_manifest_default = {
|
|
|
11057
11669
|
"hasClientMiddleware": false,
|
|
11058
11670
|
"hasDefaultExport": true,
|
|
11059
11671
|
"hasErrorBoundary": true,
|
|
11060
|
-
"module": "/assets/queues._name-
|
|
11672
|
+
"module": "/assets/queues._name-Cb17IB2u.js",
|
|
11061
11673
|
"imports": [
|
|
11062
|
-
"/assets/jsx-runtime-
|
|
11063
|
-
"/assets/db-link-
|
|
11064
|
-
"/assets/
|
|
11065
|
-
"/assets/
|
|
11066
|
-
"/assets/
|
|
11067
|
-
"/assets/
|
|
11068
|
-
"/assets/
|
|
11069
|
-
"/assets/
|
|
11070
|
-
"/assets/
|
|
11071
|
-
"/assets/
|
|
11072
|
-
"/assets/
|
|
11073
|
-
"/assets/
|
|
11074
|
-
"/assets/
|
|
11075
|
-
"/assets/
|
|
11076
|
-
"/assets/
|
|
11077
|
-
"/assets/
|
|
11078
|
-
"/assets/
|
|
11674
|
+
"/assets/jsx-runtime-RQyiN6Nr.js",
|
|
11675
|
+
"/assets/db-link-BajQ1v8I.js",
|
|
11676
|
+
"/assets/createLucideIcon-C-LI4enx.js",
|
|
11677
|
+
"/assets/chevron-down-BFFjfYD4.js",
|
|
11678
|
+
"/assets/chevron-right-DGk5QFJF.js",
|
|
11679
|
+
"/assets/MenuTrigger-BNvpjhsQ.js",
|
|
11680
|
+
"/assets/dialog-D-oczDM2.js",
|
|
11681
|
+
"/assets/stat-card-dyg1wY5p.js",
|
|
11682
|
+
"/assets/button-9NpSS9Ow.js",
|
|
11683
|
+
"/assets/badge-CMnQO7Lq.js",
|
|
11684
|
+
"/assets/table-Cz7ujmH_.js",
|
|
11685
|
+
"/assets/error-card-BH7i86fH.js",
|
|
11686
|
+
"/assets/pagination-C-ohiBmY.js",
|
|
11687
|
+
"/assets/filter-select-Bn_oSiip.js",
|
|
11688
|
+
"/assets/react-dom-D_m_Zgd3.js",
|
|
11689
|
+
"/assets/useOpenInteractionType-BQ1arb0B.js",
|
|
11690
|
+
"/assets/x-AhXI_F1j.js"
|
|
11079
11691
|
],
|
|
11080
11692
|
"css": [],
|
|
11081
11693
|
"clientActionModule": void 0,
|
|
@@ -11096,19 +11708,19 @@ var server_manifest_default = {
|
|
|
11096
11708
|
"hasClientMiddleware": false,
|
|
11097
11709
|
"hasDefaultExport": true,
|
|
11098
11710
|
"hasErrorBoundary": true,
|
|
11099
|
-
"module": "/assets/queues._name.jobs._jobId-
|
|
11711
|
+
"module": "/assets/queues._name.jobs._jobId-Bkv8POBj.js",
|
|
11100
11712
|
"imports": [
|
|
11101
|
-
"/assets/jsx-runtime-
|
|
11102
|
-
"/assets/db-link-
|
|
11103
|
-
"/assets/
|
|
11104
|
-
"/assets/
|
|
11105
|
-
"/assets/
|
|
11106
|
-
"/assets/
|
|
11107
|
-
"/assets/
|
|
11108
|
-
"/assets/
|
|
11109
|
-
"/assets/
|
|
11110
|
-
"/assets/
|
|
11111
|
-
"/assets/react-dom-
|
|
11713
|
+
"/assets/jsx-runtime-RQyiN6Nr.js",
|
|
11714
|
+
"/assets/db-link-BajQ1v8I.js",
|
|
11715
|
+
"/assets/createLucideIcon-C-LI4enx.js",
|
|
11716
|
+
"/assets/check-7jwc5sb1.js",
|
|
11717
|
+
"/assets/dialog-D-oczDM2.js",
|
|
11718
|
+
"/assets/button-9NpSS9Ow.js",
|
|
11719
|
+
"/assets/badge-CMnQO7Lq.js",
|
|
11720
|
+
"/assets/error-card-BH7i86fH.js",
|
|
11721
|
+
"/assets/x-AhXI_F1j.js",
|
|
11722
|
+
"/assets/useOpenInteractionType-BQ1arb0B.js",
|
|
11723
|
+
"/assets/react-dom-D_m_Zgd3.js"
|
|
11112
11724
|
],
|
|
11113
11725
|
"css": [],
|
|
11114
11726
|
"clientActionModule": void 0,
|
|
@@ -11129,15 +11741,15 @@ var server_manifest_default = {
|
|
|
11129
11741
|
"hasClientMiddleware": false,
|
|
11130
11742
|
"hasDefaultExport": true,
|
|
11131
11743
|
"hasErrorBoundary": true,
|
|
11132
|
-
"module": "/assets/schedules-
|
|
11744
|
+
"module": "/assets/schedules-iYfIJxOD.js",
|
|
11133
11745
|
"imports": [
|
|
11134
|
-
"/assets/jsx-runtime-
|
|
11135
|
-
"/assets/db-link-
|
|
11136
|
-
"/assets/
|
|
11137
|
-
"/assets/badge-
|
|
11138
|
-
"/assets/
|
|
11139
|
-
"/assets/
|
|
11140
|
-
"/assets/
|
|
11746
|
+
"/assets/jsx-runtime-RQyiN6Nr.js",
|
|
11747
|
+
"/assets/db-link-BajQ1v8I.js",
|
|
11748
|
+
"/assets/button-9NpSS9Ow.js",
|
|
11749
|
+
"/assets/badge-CMnQO7Lq.js",
|
|
11750
|
+
"/assets/table-Cz7ujmH_.js",
|
|
11751
|
+
"/assets/error-card-BH7i86fH.js",
|
|
11752
|
+
"/assets/pagination-C-ohiBmY.js"
|
|
11141
11753
|
],
|
|
11142
11754
|
"css": [],
|
|
11143
11755
|
"clientActionModule": void 0,
|
|
@@ -11158,17 +11770,17 @@ var server_manifest_default = {
|
|
|
11158
11770
|
"hasClientMiddleware": false,
|
|
11159
11771
|
"hasDefaultExport": true,
|
|
11160
11772
|
"hasErrorBoundary": true,
|
|
11161
|
-
"module": "/assets/schedules._name._key-
|
|
11773
|
+
"module": "/assets/schedules._name._key-CJVu73XY.js",
|
|
11162
11774
|
"imports": [
|
|
11163
|
-
"/assets/jsx-runtime-
|
|
11164
|
-
"/assets/db-link-
|
|
11165
|
-
"/assets/
|
|
11166
|
-
"/assets/button-
|
|
11167
|
-
"/assets/
|
|
11168
|
-
"/assets/
|
|
11169
|
-
"/assets/
|
|
11170
|
-
"/assets/
|
|
11171
|
-
"/assets/
|
|
11775
|
+
"/assets/jsx-runtime-RQyiN6Nr.js",
|
|
11776
|
+
"/assets/db-link-BajQ1v8I.js",
|
|
11777
|
+
"/assets/dialog-D-oczDM2.js",
|
|
11778
|
+
"/assets/button-9NpSS9Ow.js",
|
|
11779
|
+
"/assets/error-card-BH7i86fH.js",
|
|
11780
|
+
"/assets/x-AhXI_F1j.js",
|
|
11781
|
+
"/assets/useOpenInteractionType-BQ1arb0B.js",
|
|
11782
|
+
"/assets/createLucideIcon-C-LI4enx.js",
|
|
11783
|
+
"/assets/react-dom-D_m_Zgd3.js"
|
|
11172
11784
|
],
|
|
11173
11785
|
"css": [],
|
|
11174
11786
|
"clientActionModule": void 0,
|
|
@@ -11189,12 +11801,12 @@ var server_manifest_default = {
|
|
|
11189
11801
|
"hasClientMiddleware": false,
|
|
11190
11802
|
"hasDefaultExport": true,
|
|
11191
11803
|
"hasErrorBoundary": true,
|
|
11192
|
-
"module": "/assets/schedules.new-
|
|
11804
|
+
"module": "/assets/schedules.new-Cq0Mxa7G.js",
|
|
11193
11805
|
"imports": [
|
|
11194
|
-
"/assets/jsx-runtime-
|
|
11195
|
-
"/assets/db-link-
|
|
11196
|
-
"/assets/
|
|
11197
|
-
"/assets/
|
|
11806
|
+
"/assets/jsx-runtime-RQyiN6Nr.js",
|
|
11807
|
+
"/assets/db-link-BajQ1v8I.js",
|
|
11808
|
+
"/assets/button-9NpSS9Ow.js",
|
|
11809
|
+
"/assets/error-card-BH7i86fH.js"
|
|
11198
11810
|
],
|
|
11199
11811
|
"css": [],
|
|
11200
11812
|
"clientActionModule": void 0,
|
|
@@ -11215,12 +11827,43 @@ var server_manifest_default = {
|
|
|
11215
11827
|
"hasClientMiddleware": false,
|
|
11216
11828
|
"hasDefaultExport": true,
|
|
11217
11829
|
"hasErrorBoundary": true,
|
|
11218
|
-
"module": "/assets/send-
|
|
11830
|
+
"module": "/assets/send-8X9ZisG-.js",
|
|
11831
|
+
"imports": [
|
|
11832
|
+
"/assets/jsx-runtime-RQyiN6Nr.js",
|
|
11833
|
+
"/assets/db-link-BajQ1v8I.js",
|
|
11834
|
+
"/assets/button-9NpSS9Ow.js",
|
|
11835
|
+
"/assets/error-card-BH7i86fH.js"
|
|
11836
|
+
],
|
|
11837
|
+
"css": [],
|
|
11838
|
+
"clientActionModule": void 0,
|
|
11839
|
+
"clientLoaderModule": void 0,
|
|
11840
|
+
"clientMiddlewareModule": void 0,
|
|
11841
|
+
"hydrateFallbackModule": void 0
|
|
11842
|
+
},
|
|
11843
|
+
"routes/migrations": {
|
|
11844
|
+
"id": "routes/migrations",
|
|
11845
|
+
"parentId": "root",
|
|
11846
|
+
"path": "migrations",
|
|
11847
|
+
"index": void 0,
|
|
11848
|
+
"caseSensitive": void 0,
|
|
11849
|
+
"hasAction": false,
|
|
11850
|
+
"hasLoader": true,
|
|
11851
|
+
"hasClientAction": false,
|
|
11852
|
+
"hasClientLoader": false,
|
|
11853
|
+
"hasClientMiddleware": false,
|
|
11854
|
+
"hasDefaultExport": true,
|
|
11855
|
+
"hasErrorBoundary": true,
|
|
11856
|
+
"module": "/assets/migrations-D5l0n4Jn.js",
|
|
11219
11857
|
"imports": [
|
|
11220
|
-
"/assets/jsx-runtime-
|
|
11221
|
-
"/assets/db-link-
|
|
11222
|
-
"/assets/
|
|
11223
|
-
"/assets/button-
|
|
11858
|
+
"/assets/jsx-runtime-RQyiN6Nr.js",
|
|
11859
|
+
"/assets/db-link-BajQ1v8I.js",
|
|
11860
|
+
"/assets/stat-card-dyg1wY5p.js",
|
|
11861
|
+
"/assets/button-9NpSS9Ow.js",
|
|
11862
|
+
"/assets/badge-CMnQO7Lq.js",
|
|
11863
|
+
"/assets/table-Cz7ujmH_.js",
|
|
11864
|
+
"/assets/error-card-BH7i86fH.js",
|
|
11865
|
+
"/assets/pagination-C-ohiBmY.js",
|
|
11866
|
+
"/assets/filter-select-Bn_oSiip.js"
|
|
11224
11867
|
],
|
|
11225
11868
|
"css": [],
|
|
11226
11869
|
"clientActionModule": void 0,
|
|
@@ -11241,16 +11884,16 @@ var server_manifest_default = {
|
|
|
11241
11884
|
"hasClientMiddleware": false,
|
|
11242
11885
|
"hasDefaultExport": true,
|
|
11243
11886
|
"hasErrorBoundary": true,
|
|
11244
|
-
"module": "/assets/warnings-
|
|
11887
|
+
"module": "/assets/warnings-C1R_RzIe.js",
|
|
11245
11888
|
"imports": [
|
|
11246
|
-
"/assets/jsx-runtime-
|
|
11247
|
-
"/assets/db-link-
|
|
11248
|
-
"/assets/
|
|
11249
|
-
"/assets/badge-
|
|
11250
|
-
"/assets/
|
|
11251
|
-
"/assets/
|
|
11252
|
-
"/assets/pagination-
|
|
11253
|
-
"/assets/
|
|
11889
|
+
"/assets/jsx-runtime-RQyiN6Nr.js",
|
|
11890
|
+
"/assets/db-link-BajQ1v8I.js",
|
|
11891
|
+
"/assets/button-9NpSS9Ow.js",
|
|
11892
|
+
"/assets/badge-CMnQO7Lq.js",
|
|
11893
|
+
"/assets/table-Cz7ujmH_.js",
|
|
11894
|
+
"/assets/error-card-BH7i86fH.js",
|
|
11895
|
+
"/assets/pagination-C-ohiBmY.js",
|
|
11896
|
+
"/assets/filter-select-Bn_oSiip.js"
|
|
11254
11897
|
],
|
|
11255
11898
|
"css": [],
|
|
11256
11899
|
"clientActionModule": void 0,
|
|
@@ -11259,8 +11902,8 @@ var server_manifest_default = {
|
|
|
11259
11902
|
"hydrateFallbackModule": void 0
|
|
11260
11903
|
}
|
|
11261
11904
|
},
|
|
11262
|
-
"url": "/assets/manifest-
|
|
11263
|
-
"version": "
|
|
11905
|
+
"url": "/assets/manifest-27e8e133.js",
|
|
11906
|
+
"version": "27e8e133",
|
|
11264
11907
|
"sri": void 0
|
|
11265
11908
|
};
|
|
11266
11909
|
//#endregion
|
|
@@ -11366,6 +12009,14 @@ var routes = {
|
|
|
11366
12009
|
caseSensitive: void 0,
|
|
11367
12010
|
module: send_exports
|
|
11368
12011
|
},
|
|
12012
|
+
"routes/migrations": {
|
|
12013
|
+
id: "routes/migrations",
|
|
12014
|
+
parentId: "root",
|
|
12015
|
+
path: "migrations",
|
|
12016
|
+
index: void 0,
|
|
12017
|
+
caseSensitive: void 0,
|
|
12018
|
+
module: migrations_exports
|
|
12019
|
+
},
|
|
11369
12020
|
"routes/warnings": {
|
|
11370
12021
|
id: "routes/warnings",
|
|
11371
12022
|
parentId: "root",
|