@pg-boss/dashboard 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/build/client/assets/MenuTrigger-BhalG0aG.js +1 -0
- package/build/client/assets/_index-D1-nZ7Th.js +1 -0
- package/build/client/assets/{badge-DFReduIj.js → badge-DCQvSdiR.js} +1 -1
- package/build/client/assets/button-BxLcuaPM.js +1 -0
- package/build/client/assets/check-Ch42cXMT.js +1 -0
- package/build/client/assets/{chevron-down-C8oENez_.js → chevron-down-Byq-CYG9.js} +1 -1
- package/build/client/assets/chevron-right-CKAGD7DJ.js +1 -0
- package/build/client/assets/createLucideIcon-DVP_i62f.js +1 -0
- package/build/client/assets/db-link-BWWnHM0k.js +1 -0
- package/build/client/assets/dialog-Bik519zD.js +1 -0
- package/build/client/assets/entry.client-DL_oPh96.js +9 -0
- package/build/client/assets/{error-card-2aexlkmv.js → error-card-B0ANyjh3.js} +1 -1
- package/build/client/assets/{filter-select-Dln9CAM3.js → filter-select--qLjbs9m.js} +1 -1
- package/build/client/assets/jobs-D0a6Lwq0.js +1 -0
- package/build/client/assets/jsx-runtime-BgbGXvsu.js +16 -0
- package/build/client/assets/manifest-ef81a0f9.js +1 -0
- package/build/client/assets/{pagination-5gEldBFM.js → pagination-Bzx8wbXG.js} +1 -1
- package/build/client/assets/queues._index-D8903DTa.js +1 -0
- package/build/client/assets/queues._name-BVt_4pav.js +1 -0
- package/build/client/assets/queues._name.jobs._jobId-BkG9y75k.js +1 -0
- package/build/client/assets/queues.create-CMqQVLup.js +1 -0
- package/build/client/assets/react-dom-QnGHOQwT.js +1 -0
- package/build/client/assets/root-C0MdPLOa.css +2 -0
- package/build/client/assets/root-Df70GAY3.js +40 -0
- package/build/client/assets/{schedules-CE-hWC9e.js → schedules-DPXQoaEE.js} +1 -1
- package/build/client/assets/schedules._name._key-B_luxy1w.js +1 -0
- package/build/client/assets/schedules.new-BQV7GWzs.js +1 -0
- package/build/client/assets/send-DJBsfnx_.js +1 -0
- package/build/client/assets/{stat-card-y2feeHt0.js → stat-card-DLtQnscf.js} +1 -1
- package/build/client/assets/{table-B5BEGV9S.js → table-DqqzSNik.js} +1 -1
- package/build/client/assets/useOpenInteractionType-D3JsvupP.js +1 -0
- package/build/client/assets/{warnings-lzi34PdC.js → warnings-CHKaRfIW.js} +1 -1
- package/build/client/assets/x-BPKZwOn9.js +1 -0
- package/build/server/index.js +1206 -231
- package/package.json +13 -13
- package/build/client/assets/MenuTrigger-CkqlwCsV.js +0 -1
- package/build/client/assets/_index-CNX0dPQi.js +0 -1
- package/build/client/assets/button-CafwM-5D.js +0 -1
- package/build/client/assets/check-BePyOaOM.js +0 -1
- package/build/client/assets/chevron-right-B0sTJmdc.js +0 -1
- package/build/client/assets/createLucideIcon-oZ0wXCaF.js +0 -1
- package/build/client/assets/db-link-DISwKBYZ.js +0 -1
- package/build/client/assets/dialog-IBPuwpas.js +0 -1
- package/build/client/assets/entry.client-6KjasuHH.js +0 -9
- package/build/client/assets/jobs-BjoGjnb0.js +0 -1
- package/build/client/assets/jsx-runtime-DcdjQ3vN.js +0 -26
- package/build/client/assets/manifest-e6a94de0.js +0 -1
- package/build/client/assets/queues._index-BM3eKnSr.js +0 -1
- package/build/client/assets/queues._name-DSOGXBen.js +0 -1
- package/build/client/assets/queues._name.jobs._jobId-DKGu8NkE.js +0 -1
- package/build/client/assets/queues.create-BVR72i85.js +0 -1
- package/build/client/assets/react-dom-DQHA9Ik4.js +0 -1
- package/build/client/assets/root-Cvarn0sH.js +0 -40
- package/build/client/assets/root-D24FtLBP.css +0 -2
- package/build/client/assets/schedules._name._key-DT8Kg4jU.js +0 -1
- package/build/client/assets/schedules.new-bKRusXXO.js +0 -1
- package/build/client/assets/send-BaWw8x7J.js +0 -1
- package/build/client/assets/useOpenInteractionType-BGObzouY.js +0 -1
- package/build/client/assets/x-EA6eZ1AP.js +0 -1
package/build/server/index.js
CHANGED
|
@@ -1434,6 +1434,7 @@ if (typeof process !== "undefined") {
|
|
|
1434
1434
|
}
|
|
1435
1435
|
//#endregion
|
|
1436
1436
|
//#region ../../src/plans.ts
|
|
1437
|
+
var PG_ERROR = { divisionByZero: "22012" };
|
|
1437
1438
|
var DEFAULT_SCHEMA = "pgboss";
|
|
1438
1439
|
var MIGRATE_RACE_MESSAGE = "division by zero";
|
|
1439
1440
|
var CREATE_RACE_MESSAGE = "already exists";
|
|
@@ -1469,6 +1470,10 @@ var QUEUE_DEFAULTS = {
|
|
|
1469
1470
|
};
|
|
1470
1471
|
var COMMON_JOB_TABLE = "job_common";
|
|
1471
1472
|
function create(schema, version, options) {
|
|
1473
|
+
const noPartitioning = options?.noTablePartitioning ?? false;
|
|
1474
|
+
const noDeferrable = options?.noDeferrableConstraints ?? false;
|
|
1475
|
+
const noLocks = options?.noAdvisoryLocks ?? false;
|
|
1476
|
+
const noCovering = options?.noCoveringIndexes ?? false;
|
|
1472
1477
|
return locked(schema, [
|
|
1473
1478
|
options?.createSchema ? createSchema(schema) : "",
|
|
1474
1479
|
createEnumJobState(schema),
|
|
@@ -1477,18 +1482,20 @@ function create(schema, version, options) {
|
|
|
1477
1482
|
createTableSchedule(schema),
|
|
1478
1483
|
createTableSubscription(schema),
|
|
1479
1484
|
createTableBam(schema),
|
|
1480
|
-
jobTableFormatFunction(schema),
|
|
1481
|
-
jobTableRunFunction(schema),
|
|
1482
|
-
jobTableRunAsyncFunction(schema),
|
|
1483
|
-
createTableJob(schema),
|
|
1485
|
+
noPartitioning ? "" : jobTableFormatFunction(schema),
|
|
1486
|
+
noPartitioning ? "" : jobTableRunFunction(schema),
|
|
1487
|
+
noPartitioning ? "" : jobTableRunAsyncFunction(schema),
|
|
1488
|
+
createTableJob(schema, noPartitioning),
|
|
1484
1489
|
createPrimaryKeyJob(schema),
|
|
1485
|
-
createTableJobCommon(schema),
|
|
1490
|
+
noPartitioning ? createTableJobIndexes(schema, noDeferrable, noCovering) : createTableJobCommon(schema),
|
|
1486
1491
|
createTableWarning(schema),
|
|
1487
1492
|
createIndexWarning(schema),
|
|
1488
|
-
|
|
1489
|
-
|
|
1493
|
+
createTableJobDependency(schema),
|
|
1494
|
+
createIndexJobDependencyParent(schema),
|
|
1495
|
+
createQueueFunction(schema, noPartitioning),
|
|
1496
|
+
deleteQueueFunction(schema, noPartitioning),
|
|
1490
1497
|
insertVersion(schema, version)
|
|
1491
|
-
]);
|
|
1498
|
+
], void 0, noLocks);
|
|
1492
1499
|
}
|
|
1493
1500
|
function createSchema(schema) {
|
|
1494
1501
|
return `CREATE SCHEMA IF NOT EXISTS ${schema}`;
|
|
@@ -1601,6 +1608,20 @@ function createTableWarning(schema) {
|
|
|
1601
1608
|
function createIndexWarning(schema) {
|
|
1602
1609
|
return `CREATE INDEX warning_i1 ON ${schema}.warning (created_on DESC)`;
|
|
1603
1610
|
}
|
|
1611
|
+
function createTableJobDependency(schema) {
|
|
1612
|
+
return `
|
|
1613
|
+
CREATE TABLE ${schema}.job_dependency (
|
|
1614
|
+
child_name text NOT NULL,
|
|
1615
|
+
child_id uuid NOT NULL,
|
|
1616
|
+
parent_name text NOT NULL,
|
|
1617
|
+
parent_id uuid NOT NULL,
|
|
1618
|
+
PRIMARY KEY (child_name, child_id, parent_name, parent_id)
|
|
1619
|
+
)
|
|
1620
|
+
`;
|
|
1621
|
+
}
|
|
1622
|
+
function createIndexJobDependencyParent(schema) {
|
|
1623
|
+
return `CREATE INDEX IF NOT EXISTS job_dep_parent_idx ON ${schema}.job_dependency (parent_name, parent_id)`;
|
|
1624
|
+
}
|
|
1604
1625
|
function jobTableFormatFunction(schema) {
|
|
1605
1626
|
return `
|
|
1606
1627
|
CREATE FUNCTION ${schema}.job_table_format(command text, table_name text)
|
|
@@ -1691,7 +1712,8 @@ function jobTableRunAsyncFunction(schema) {
|
|
|
1691
1712
|
LANGUAGE plpgsql;
|
|
1692
1713
|
`;
|
|
1693
1714
|
}
|
|
1694
|
-
function createTableJob(schema) {
|
|
1715
|
+
function createTableJob(schema, noPartitioning = false) {
|
|
1716
|
+
const partitionClause = noPartitioning ? "" : "PARTITION BY LIST (name)";
|
|
1695
1717
|
return `
|
|
1696
1718
|
CREATE TABLE ${schema}.job (
|
|
1697
1719
|
id uuid not null default gen_random_uuid(),
|
|
@@ -1719,8 +1741,11 @@ function createTableJob(schema) {
|
|
|
1719
1741
|
dead_letter text,
|
|
1720
1742
|
policy text,
|
|
1721
1743
|
heartbeat_on timestamp with time zone,
|
|
1722
|
-
heartbeat_seconds int
|
|
1723
|
-
|
|
1744
|
+
heartbeat_seconds int,
|
|
1745
|
+
blocked boolean not null default false,
|
|
1746
|
+
blocking boolean not null default false,
|
|
1747
|
+
pending_dependencies int not null default 0
|
|
1748
|
+
) ${partitionClause}
|
|
1724
1749
|
`;
|
|
1725
1750
|
}
|
|
1726
1751
|
var JOB_COLUMNS_MIN = "id, name, data, expire_seconds as \"expireInSeconds\", heartbeat_seconds as \"heartbeatSeconds\", group_id as \"groupId\", group_tier as \"groupTier\"";
|
|
@@ -1743,6 +1768,9 @@ var JOB_COLUMNS_ALL = `${JOB_COLUMNS_MIN},
|
|
|
1743
1768
|
completed_on as "completedOn",
|
|
1744
1769
|
keep_until as "keepUntil",
|
|
1745
1770
|
dead_letter as "deadLetter",
|
|
1771
|
+
blocked,
|
|
1772
|
+
blocking,
|
|
1773
|
+
pending_dependencies as "pendingDependencies",
|
|
1746
1774
|
output
|
|
1747
1775
|
`;
|
|
1748
1776
|
function createTableJobCommon(schema) {
|
|
@@ -1765,7 +1793,64 @@ function createTableJobCommon(schema) {
|
|
|
1765
1793
|
ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.${COMMON_JOB_TABLE} DEFAULT;
|
|
1766
1794
|
`;
|
|
1767
1795
|
}
|
|
1768
|
-
function
|
|
1796
|
+
function createTableJobIndexes(schema, noDeferrableConstraints = false, noCoveringIndex = false) {
|
|
1797
|
+
return `
|
|
1798
|
+
${createQueueForeignKeyJob(schema, noDeferrableConstraints)};
|
|
1799
|
+
${createQueueForeignKeyJobDeadLetter(schema, noDeferrableConstraints)};
|
|
1800
|
+
${createIndexJobPolicyShort(schema)};
|
|
1801
|
+
${createIndexJobPolicySingleton(schema)};
|
|
1802
|
+
${createIndexJobPolicyStately(schema)};
|
|
1803
|
+
${createIndexJobPolicyExclusive(schema)};
|
|
1804
|
+
${createIndexJobPolicyKeyStrictFifo(schema)};
|
|
1805
|
+
${createCheckConstraintKeyStrictFifo(schema)};
|
|
1806
|
+
${createIndexJobThrottle(schema)};
|
|
1807
|
+
${createIndexJobFetch(schema, noCoveringIndex)};
|
|
1808
|
+
${createIndexJobGroupConcurrency(schema)};
|
|
1809
|
+
`;
|
|
1810
|
+
}
|
|
1811
|
+
function createQueueFunction(schema, noPartitioning = false) {
|
|
1812
|
+
if (noPartitioning) return `
|
|
1813
|
+
CREATE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
1814
|
+
RETURNS VOID AS
|
|
1815
|
+
$$
|
|
1816
|
+
BEGIN
|
|
1817
|
+
INSERT INTO ${schema}.queue (
|
|
1818
|
+
name,
|
|
1819
|
+
policy,
|
|
1820
|
+
retry_limit,
|
|
1821
|
+
retry_delay,
|
|
1822
|
+
retry_backoff,
|
|
1823
|
+
retry_delay_max,
|
|
1824
|
+
expire_seconds,
|
|
1825
|
+
retention_seconds,
|
|
1826
|
+
deletion_seconds,
|
|
1827
|
+
warning_queued,
|
|
1828
|
+
dead_letter,
|
|
1829
|
+
partition,
|
|
1830
|
+
table_name,
|
|
1831
|
+
heartbeat_seconds
|
|
1832
|
+
)
|
|
1833
|
+
VALUES (
|
|
1834
|
+
queue_name,
|
|
1835
|
+
options->>'policy',
|
|
1836
|
+
COALESCE((options->>'retryLimit')::int, ${QUEUE_DEFAULTS.retry_limit}),
|
|
1837
|
+
COALESCE((options->>'retryDelay')::int, ${QUEUE_DEFAULTS.retry_delay}),
|
|
1838
|
+
COALESCE((options->>'retryBackoff')::bool, ${QUEUE_DEFAULTS.retry_backoff}),
|
|
1839
|
+
(options->>'retryDelayMax')::int,
|
|
1840
|
+
COALESCE((options->>'expireInSeconds')::int, ${QUEUE_DEFAULTS.expire_seconds}),
|
|
1841
|
+
COALESCE((options->>'retentionSeconds')::int, ${QUEUE_DEFAULTS.retention_seconds}),
|
|
1842
|
+
COALESCE((options->>'deleteAfterSeconds')::int, ${QUEUE_DEFAULTS.deletion_seconds}),
|
|
1843
|
+
COALESCE((options->>'warningQueueSize')::int, ${QUEUE_DEFAULTS.warning_queued}),
|
|
1844
|
+
options->>'deadLetter',
|
|
1845
|
+
false,
|
|
1846
|
+
'job',
|
|
1847
|
+
(options->>'heartbeatSeconds')::int
|
|
1848
|
+
)
|
|
1849
|
+
ON CONFLICT DO NOTHING;
|
|
1850
|
+
END;
|
|
1851
|
+
$$
|
|
1852
|
+
LANGUAGE plpgsql;
|
|
1853
|
+
`;
|
|
1769
1854
|
return `
|
|
1770
1855
|
CREATE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
|
|
1771
1856
|
RETURNS VOID AS
|
|
@@ -1850,15 +1935,8 @@ function createQueueFunction(schema) {
|
|
|
1850
1935
|
LANGUAGE plpgsql;
|
|
1851
1936
|
`;
|
|
1852
1937
|
}
|
|
1853
|
-
function deleteQueueFunction(schema) {
|
|
1854
|
-
|
|
1855
|
-
CREATE FUNCTION ${schema}.delete_queue(queue_name text)
|
|
1856
|
-
RETURNS VOID AS
|
|
1857
|
-
$$
|
|
1858
|
-
DECLARE
|
|
1859
|
-
v_table varchar;
|
|
1860
|
-
v_partition bool;
|
|
1861
|
-
BEGIN
|
|
1938
|
+
function deleteQueueFunction(schema, noPartitioning = false) {
|
|
1939
|
+
const deleteJobsSql = noPartitioning ? `DELETE FROM ${schema}.job WHERE name = queue_name;` : `
|
|
1862
1940
|
SELECT table_name, partition
|
|
1863
1941
|
FROM ${schema}.queue
|
|
1864
1942
|
WHERE name = queue_name
|
|
@@ -1869,27 +1947,36 @@ function deleteQueueFunction(schema) {
|
|
|
1869
1947
|
ELSE
|
|
1870
1948
|
EXECUTE format('DELETE FROM ${schema}.%I WHERE name = %L', v_table, queue_name);
|
|
1871
1949
|
END IF;
|
|
1872
|
-
|
|
1950
|
+
`;
|
|
1951
|
+
return `
|
|
1952
|
+
CREATE FUNCTION ${schema}.delete_queue(queue_name text)
|
|
1953
|
+
RETURNS VOID AS
|
|
1954
|
+
$$${noPartitioning ? "" : `
|
|
1955
|
+
DECLARE
|
|
1956
|
+
v_table varchar;
|
|
1957
|
+
v_partition bool;`}
|
|
1958
|
+
BEGIN
|
|
1959
|
+
${deleteJobsSql}
|
|
1873
1960
|
DELETE FROM ${schema}.queue WHERE name = queue_name;
|
|
1874
1961
|
END;
|
|
1875
1962
|
$$
|
|
1876
1963
|
LANGUAGE plpgsql;
|
|
1877
1964
|
`;
|
|
1878
1965
|
}
|
|
1879
|
-
function createQueue$1(schema, name, options) {
|
|
1880
|
-
return locked(schema, `SELECT ${schema}.create_queue('${name}', '${JSON.stringify(options)}'::jsonb)`, "create-queue");
|
|
1966
|
+
function createQueue$1(schema, name, options, noAdvisoryLocks) {
|
|
1967
|
+
return locked(schema, `SELECT ${schema}.create_queue('${name}', '${JSON.stringify(options)}'::jsonb)`, "create-queue", noAdvisoryLocks);
|
|
1881
1968
|
}
|
|
1882
|
-
function deleteQueue(schema, name) {
|
|
1883
|
-
return locked(schema, `SELECT ${schema}.delete_queue('${name}')`, "delete-queue");
|
|
1969
|
+
function deleteQueue(schema, name, noAdvisoryLocks) {
|
|
1970
|
+
return locked(schema, `SELECT ${schema}.delete_queue('${name}')`, "delete-queue", noAdvisoryLocks);
|
|
1884
1971
|
}
|
|
1885
1972
|
function createPrimaryKeyJob(schema) {
|
|
1886
1973
|
return `ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)`;
|
|
1887
1974
|
}
|
|
1888
|
-
function createQueueForeignKeyJob(schema) {
|
|
1889
|
-
return `ALTER TABLE ${schema}.job ADD CONSTRAINT q_fkey FOREIGN KEY (name) REFERENCES ${schema}.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED`;
|
|
1975
|
+
function createQueueForeignKeyJob(schema, noPartitioning = false) {
|
|
1976
|
+
return `ALTER TABLE ${schema}.job ADD CONSTRAINT q_fkey FOREIGN KEY (name) REFERENCES ${schema}.queue (name) ON DELETE RESTRICT${noPartitioning ? "" : " DEFERRABLE INITIALLY DEFERRED"}`;
|
|
1890
1977
|
}
|
|
1891
|
-
function createQueueForeignKeyJobDeadLetter(schema) {
|
|
1892
|
-
return `ALTER TABLE ${schema}.job ADD CONSTRAINT dlq_fkey FOREIGN KEY (dead_letter) REFERENCES ${schema}.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED`;
|
|
1978
|
+
function createQueueForeignKeyJobDeadLetter(schema, noPartitioning = false) {
|
|
1979
|
+
return `ALTER TABLE ${schema}.job ADD CONSTRAINT dlq_fkey FOREIGN KEY (dead_letter) REFERENCES ${schema}.queue (name) ON DELETE RESTRICT${noPartitioning ? "" : " DEFERRABLE INITIALLY DEFERRED"}`;
|
|
1893
1980
|
}
|
|
1894
1981
|
function createIndexJobPolicyShort(schema) {
|
|
1895
1982
|
return `CREATE UNIQUE INDEX job_i1 ON ${schema}.job (name, COALESCE(singleton_key, '')) WHERE state = '${JOB_STATES.created}' AND policy = '${QUEUE_POLICIES.short}'`;
|
|
@@ -1903,8 +1990,8 @@ function createIndexJobPolicyStately(schema) {
|
|
|
1903
1990
|
function createIndexJobThrottle(schema) {
|
|
1904
1991
|
return `CREATE UNIQUE INDEX job_i4 ON ${schema}.job (name, singleton_on, COALESCE(singleton_key, '')) WHERE state <> '${JOB_STATES.cancelled}' AND singleton_on IS NOT NULL`;
|
|
1905
1992
|
}
|
|
1906
|
-
function createIndexJobFetch(schema) {
|
|
1907
|
-
return `CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < '${JOB_STATES.active}'`;
|
|
1993
|
+
function createIndexJobFetch(schema, noCoveringIndex = false) {
|
|
1994
|
+
return `CREATE INDEX job_i5 ON ${schema}.job (name, start_after) ${noCoveringIndex ? "" : "INCLUDE (priority, created_on, id) "}WHERE state < '${JOB_STATES.active}' AND NOT blocked`;
|
|
1908
1995
|
}
|
|
1909
1996
|
function createIndexJobPolicyExclusive(schema) {
|
|
1910
1997
|
return `CREATE UNIQUE INDEX job_i6 ON ${schema}.job (name, COALESCE(singleton_key, '')) WHERE state <= '${JOB_STATES.active}' AND policy = '${QUEUE_POLICIES.exclusive}'`;
|
|
@@ -2159,7 +2246,23 @@ function buildFetchParams(options) {
|
|
|
2159
2246
|
maxPriorityParam
|
|
2160
2247
|
};
|
|
2161
2248
|
}
|
|
2162
|
-
|
|
2249
|
+
/**
|
|
2250
|
+
* Builds the fetch query for claiming jobs from the queue.
|
|
2251
|
+
*
|
|
2252
|
+
* With SKIP LOCKED (noSkipLocked=false, the default), uses SELECT FOR UPDATE SKIP
|
|
2253
|
+
* LOCKED, which lets multiple workers efficiently fetch different jobs simultaneously.
|
|
2254
|
+
*
|
|
2255
|
+
* With noSkipLocked=true, omits FOR UPDATE SKIP LOCKED and adds an additional state
|
|
2256
|
+
* check in the WHERE clause. This pattern works better with distributed databases like
|
|
2257
|
+
* CockroachDB where SKIP LOCKED has performance issues and can unexpectedly skip
|
|
2258
|
+
* unlocked rows.
|
|
2259
|
+
*
|
|
2260
|
+
* Trade-off when noSkipLocked is set: under high contention, workers may receive fewer
|
|
2261
|
+
* jobs per fetch as concurrent updates to the same rows will result in some workers
|
|
2262
|
+
* getting empty results. This is acceptable for job queues where processing time
|
|
2263
|
+
* exceeds fetch time.
|
|
2264
|
+
*/
|
|
2265
|
+
function fetchNextJob(options, noSkipLocked = false) {
|
|
2163
2266
|
const { schema, table, name, policy, limit, includeMetadata, priority = true, orderByCreatedOn = true, ignoreStartAfter = false, groupConcurrency, minPriority, maxPriority } = options;
|
|
2164
2267
|
const singletonFetch = limit > 1 && (policy === QUEUE_POLICIES.singleton || policy === QUEUE_POLICIES.stately);
|
|
2165
2268
|
const hasIgnoreSingletons = options.ignoreSingletons != null && options.ignoreSingletons.length > 0;
|
|
@@ -2180,9 +2283,11 @@ function fetchNextJob(options) {
|
|
|
2180
2283
|
WHERE name = '${name}' AND state = '${JOB_STATES.active}' AND group_id IS NOT NULL
|
|
2181
2284
|
GROUP BY group_id
|
|
2182
2285
|
), ` : "";
|
|
2286
|
+
const lockClause = noSkipLocked ? "" : "FOR UPDATE OF j SKIP LOCKED";
|
|
2183
2287
|
const whereConditions = [
|
|
2184
2288
|
`j.name = '${name}'`,
|
|
2185
2289
|
`j.state < '${JOB_STATES.active}'`,
|
|
2290
|
+
"NOT j.blocked",
|
|
2186
2291
|
!ignoreStartAfter ? "j.start_after < now()" : "",
|
|
2187
2292
|
hasIgnoreSingletons ? `j.singleton_key <> ALL(${params.ignoreSingletonsParam})` : "",
|
|
2188
2293
|
hasIgnoreGroups ? `(j.group_id IS NULL OR j.group_id <> ALL(${params.ignoreGroupsParam}))` : "",
|
|
@@ -2200,7 +2305,7 @@ function fetchNextJob(options) {
|
|
|
2200
2305
|
WHERE ${whereConditions}
|
|
2201
2306
|
ORDER BY ${priority ? "j.priority desc, " : ""}${orderByCreatedOn ? "j.created_on, " : ""}j.id
|
|
2202
2307
|
LIMIT ${limit}
|
|
2203
|
-
|
|
2308
|
+
${lockClause}
|
|
2204
2309
|
)`;
|
|
2205
2310
|
const singletonCte = singletonFetch ? `, singleton_ranking AS (
|
|
2206
2311
|
SELECT id, ${hasGroupConcurrency ? "group_id, group_tier, " : ""}
|
|
@@ -2225,6 +2330,7 @@ function fetchNextJob(options) {
|
|
|
2225
2330
|
OR (active_cnt + group_rn) <= ${hasTiers ? `COALESCE((${params.tiersParam} ->> group_tier)::int, ${params.defaultGroupLimitParam})` : params.defaultGroupLimitParam}
|
|
2226
2331
|
)` : "";
|
|
2227
2332
|
const finalCte = hasGroupConcurrency ? "group_filtered" : singletonFetch ? "singleton_ranking" : "next";
|
|
2333
|
+
const distributedStateCheck = noSkipLocked ? `AND j.state < '${JOB_STATES.active}'` : "";
|
|
2228
2334
|
return {
|
|
2229
2335
|
text: `
|
|
2230
2336
|
WITH
|
|
@@ -2240,25 +2346,62 @@ function fetchNextJob(options) {
|
|
|
2240
2346
|
FROM ${finalCte}
|
|
2241
2347
|
WHERE name = '${name}' AND j.id = ${finalCte}.id
|
|
2242
2348
|
${singletonFetch && !hasGroupConcurrency ? "AND singleton_rn = 1" : ""}
|
|
2349
|
+
${distributedStateCheck}
|
|
2243
2350
|
RETURNING j.${includeMetadata ? JOB_COLUMNS_ALL : JOB_COLUMNS_MIN}
|
|
2244
2351
|
`,
|
|
2245
2352
|
values: params.values
|
|
2246
2353
|
};
|
|
2247
2354
|
}
|
|
2248
|
-
function
|
|
2249
|
-
|
|
2250
|
-
return `
|
|
2251
|
-
WITH results AS (
|
|
2252
|
-
UPDATE ${schema}.${table}
|
|
2355
|
+
function completeJobsUpdate(schema, table, includeQueued) {
|
|
2356
|
+
return `UPDATE ${schema}.${table}
|
|
2253
2357
|
SET completed_on = now(),
|
|
2254
2358
|
state = '${JOB_STATES.completed}',
|
|
2255
|
-
output = $3::jsonb
|
|
2359
|
+
output = $3::jsonb,
|
|
2360
|
+
blocked = ${includeQueued ? "false" : "blocked"},
|
|
2361
|
+
pending_dependencies = ${includeQueued ? "0" : "pending_dependencies"}
|
|
2256
2362
|
WHERE name = $1
|
|
2257
2363
|
AND id IN (SELECT UNNEST($2::uuid[]))
|
|
2258
|
-
AND ${
|
|
2259
|
-
|
|
2364
|
+
AND ${includeQueued ? `state < '${JOB_STATES.completed}'` : `state = '${JOB_STATES.active}'`}`;
|
|
2365
|
+
}
|
|
2366
|
+
function lockedChildrenCte(schema) {
|
|
2367
|
+
return `locked_children AS (
|
|
2368
|
+
SELECT j.name, j.id, d.n
|
|
2369
|
+
FROM ${schema}.job j
|
|
2370
|
+
JOIN decremented d ON d.child_name = j.name
|
|
2371
|
+
AND d.child_id = j.id
|
|
2372
|
+
WHERE j.blocked
|
|
2373
|
+
ORDER BY j.name, j.id
|
|
2374
|
+
FOR UPDATE OF j
|
|
2375
|
+
)`;
|
|
2376
|
+
}
|
|
2377
|
+
function unblockChildrenUpdate(schema) {
|
|
2378
|
+
return `UPDATE ${schema}.job j
|
|
2379
|
+
SET pending_dependencies = GREATEST(j.pending_dependencies - lc.n, 0),
|
|
2380
|
+
blocked = GREATEST(j.pending_dependencies - lc.n, 0) > 0
|
|
2381
|
+
FROM locked_children lc
|
|
2382
|
+
WHERE j.name = lc.name
|
|
2383
|
+
AND j.id = lc.id`;
|
|
2384
|
+
}
|
|
2385
|
+
function completeJobs(schema, table, includeQueued) {
|
|
2386
|
+
return `
|
|
2387
|
+
WITH completed AS (
|
|
2388
|
+
${completeJobsUpdate(schema, table, includeQueued)}
|
|
2389
|
+
RETURNING name, id, blocking
|
|
2390
|
+
),
|
|
2391
|
+
decremented AS (
|
|
2392
|
+
SELECT d.child_name, d.child_id, COUNT(*)::int AS n
|
|
2393
|
+
FROM ${schema}.job_dependency d
|
|
2394
|
+
JOIN completed c ON c.blocking
|
|
2395
|
+
AND d.parent_name = c.name
|
|
2396
|
+
AND d.parent_id = c.id
|
|
2397
|
+
GROUP BY d.child_name, d.child_id
|
|
2398
|
+
),
|
|
2399
|
+
${lockedChildrenCte(schema)},
|
|
2400
|
+
unblocked AS (
|
|
2401
|
+
${unblockChildrenUpdate(schema)}
|
|
2402
|
+
RETURNING 1
|
|
2260
2403
|
)
|
|
2261
|
-
SELECT COUNT(*) FROM
|
|
2404
|
+
SELECT COUNT(*) FROM completed
|
|
2262
2405
|
`;
|
|
2263
2406
|
}
|
|
2264
2407
|
function cancelJobs(schema, table) {
|
|
@@ -2320,7 +2463,10 @@ function insertJobs(schema, { table, name, returnId = true }) {
|
|
|
2320
2463
|
retry_delay_max,
|
|
2321
2464
|
policy,
|
|
2322
2465
|
dead_letter,
|
|
2323
|
-
heartbeat_seconds
|
|
2466
|
+
heartbeat_seconds,
|
|
2467
|
+
blocked,
|
|
2468
|
+
blocking,
|
|
2469
|
+
pending_dependencies
|
|
2324
2470
|
)
|
|
2325
2471
|
SELECT
|
|
2326
2472
|
COALESCE(id, gen_random_uuid()) as id,
|
|
@@ -2330,7 +2476,7 @@ function insertJobs(schema, { table, name, returnId = true }) {
|
|
|
2330
2476
|
j.start_after,
|
|
2331
2477
|
"singletonKey",
|
|
2332
2478
|
CASE
|
|
2333
|
-
WHEN "singletonSeconds" IS NOT NULL THEN 'epoch'::timestamp + '1s'::interval * ("singletonSeconds" * floor(( date_part('epoch', now()) + COALESCE("singletonOffset",0)) / "singletonSeconds" ))
|
|
2479
|
+
WHEN "singletonSeconds" IS NOT NULL THEN 'epoch'::timestamp + '1s'::interval * ("singletonSeconds"::float8 * floor(( date_part('epoch', now()) + COALESCE("singletonOffset",0)::float8) / "singletonSeconds"::float8 ))
|
|
2334
2480
|
ELSE NULL
|
|
2335
2481
|
END as singleton_on,
|
|
2336
2482
|
"groupId" as group_id,
|
|
@@ -2344,7 +2490,10 @@ function insertJobs(schema, { table, name, returnId = true }) {
|
|
|
2344
2490
|
COALESCE("retryDelayMax", q.retry_delay_max) as retry_delay_max,
|
|
2345
2491
|
q.policy,
|
|
2346
2492
|
COALESCE("deadLetter", q.dead_letter) as dead_letter,
|
|
2347
|
-
COALESCE("heartbeatSeconds", q.heartbeat_seconds) as heartbeat_seconds
|
|
2493
|
+
COALESCE("heartbeatSeconds", q.heartbeat_seconds) as heartbeat_seconds,
|
|
2494
|
+
COALESCE(blocked, false) as blocked,
|
|
2495
|
+
COALESCE(blocking, false) as blocking,
|
|
2496
|
+
COALESCE("pendingDependencies", 0) as pending_dependencies
|
|
2348
2497
|
FROM (
|
|
2349
2498
|
SELECT *,
|
|
2350
2499
|
CASE
|
|
@@ -2369,7 +2518,10 @@ function insertJobs(schema, { table, name, returnId = true }) {
|
|
|
2369
2518
|
"deleteAfterSeconds" integer,
|
|
2370
2519
|
"retentionSeconds" integer,
|
|
2371
2520
|
"deadLetter" text,
|
|
2372
|
-
"heartbeatSeconds" integer
|
|
2521
|
+
"heartbeatSeconds" integer,
|
|
2522
|
+
blocked boolean,
|
|
2523
|
+
blocking boolean,
|
|
2524
|
+
"pendingDependencies" integer
|
|
2373
2525
|
)
|
|
2374
2526
|
) j
|
|
2375
2527
|
JOIN ${schema}.queue q ON q.name = '${name}'
|
|
@@ -2380,16 +2532,16 @@ function insertJobs(schema, { table, name, returnId = true }) {
|
|
|
2380
2532
|
function failJobsById(schema, table) {
|
|
2381
2533
|
return failJobs(schema, table, `name = $1 AND id IN (SELECT UNNEST($2::uuid[])) AND state < '${JOB_STATES.completed}'`, "$3::jsonb");
|
|
2382
2534
|
}
|
|
2383
|
-
function failJobsByTimeout(schema, table, queues) {
|
|
2535
|
+
function failJobsByTimeout(schema, table, queues, noAdvisoryLocks) {
|
|
2384
2536
|
return locked(schema, failJobs(schema, table, `state = '${JOB_STATES.active}'
|
|
2385
2537
|
AND (started_on + expire_seconds * interval '1s') < now()
|
|
2386
|
-
AND name = ANY(${serializeArrayParam(queues)})`, "'{ \"value\": { \"message\": \"job timed out\" } }'::jsonb"), table + "failJobsByTimeout");
|
|
2538
|
+
AND name = ANY(${serializeArrayParam(queues)})`, "'{ \"value\": { \"message\": \"job timed out\" } }'::jsonb"), table + "failJobsByTimeout", noAdvisoryLocks);
|
|
2387
2539
|
}
|
|
2388
|
-
function failJobsByHeartbeat(schema, table, queues) {
|
|
2540
|
+
function failJobsByHeartbeat(schema, table, queues, noAdvisoryLocks) {
|
|
2389
2541
|
return locked(schema, failJobs(schema, table, `state = '${JOB_STATES.active}'
|
|
2390
2542
|
AND heartbeat_seconds IS NOT NULL
|
|
2391
2543
|
AND (heartbeat_on + heartbeat_seconds * interval '1s') < now()
|
|
2392
|
-
AND name = ANY(${serializeArrayParam(queues)})`, "'{ \"value\": { \"message\": \"job heartbeat timeout\" } }'::jsonb"), table + "failJobsByHeartbeat");
|
|
2544
|
+
AND name = ANY(${serializeArrayParam(queues)})`, "'{ \"value\": { \"message\": \"job heartbeat timeout\" } }'::jsonb"), table + "failJobsByHeartbeat", noAdvisoryLocks);
|
|
2393
2545
|
}
|
|
2394
2546
|
function touchJobs(schema, table) {
|
|
2395
2547
|
return `
|
|
@@ -2438,7 +2590,10 @@ function failJobs(schema, table, where, output) {
|
|
|
2438
2590
|
output,
|
|
2439
2591
|
dead_letter,
|
|
2440
2592
|
heartbeat_on,
|
|
2441
|
-
heartbeat_seconds
|
|
2593
|
+
heartbeat_seconds,
|
|
2594
|
+
blocked,
|
|
2595
|
+
blocking,
|
|
2596
|
+
pending_dependencies
|
|
2442
2597
|
)
|
|
2443
2598
|
SELECT
|
|
2444
2599
|
id,
|
|
@@ -2478,7 +2633,10 @@ function failJobs(schema, table, where, output) {
|
|
|
2478
2633
|
${output},
|
|
2479
2634
|
dead_letter,
|
|
2480
2635
|
NULL as heartbeat_on,
|
|
2481
|
-
heartbeat_seconds
|
|
2636
|
+
heartbeat_seconds,
|
|
2637
|
+
blocked,
|
|
2638
|
+
blocking,
|
|
2639
|
+
pending_dependencies
|
|
2482
2640
|
FROM deleted_jobs
|
|
2483
2641
|
ON CONFLICT DO NOTHING
|
|
2484
2642
|
RETURNING *
|
|
@@ -2510,7 +2668,10 @@ function failJobs(schema, table, where, output) {
|
|
|
2510
2668
|
output,
|
|
2511
2669
|
dead_letter,
|
|
2512
2670
|
heartbeat_on,
|
|
2513
|
-
heartbeat_seconds
|
|
2671
|
+
heartbeat_seconds,
|
|
2672
|
+
blocked,
|
|
2673
|
+
blocking,
|
|
2674
|
+
pending_dependencies
|
|
2514
2675
|
)
|
|
2515
2676
|
SELECT
|
|
2516
2677
|
id,
|
|
@@ -2538,7 +2699,10 @@ function failJobs(schema, table, where, output) {
|
|
|
2538
2699
|
${output},
|
|
2539
2700
|
dead_letter,
|
|
2540
2701
|
NULL as heartbeat_on,
|
|
2541
|
-
heartbeat_seconds
|
|
2702
|
+
heartbeat_seconds,
|
|
2703
|
+
blocked,
|
|
2704
|
+
blocking,
|
|
2705
|
+
pending_dependencies
|
|
2542
2706
|
FROM deleted_jobs
|
|
2543
2707
|
WHERE id NOT IN (SELECT id from retried_jobs)
|
|
2544
2708
|
RETURNING *
|
|
@@ -2566,7 +2730,85 @@ function failJobs(schema, table, where, output) {
|
|
|
2566
2730
|
SELECT COUNT(*) FROM results
|
|
2567
2731
|
`;
|
|
2568
2732
|
}
|
|
2569
|
-
function
|
|
2733
|
+
function selectJobsToFailById(schema, table) {
|
|
2734
|
+
return {
|
|
2735
|
+
text: `SELECT * FROM ${schema}.${table} WHERE name = $1 AND id IN (SELECT UNNEST($2::uuid[])) AND state < '${JOB_STATES.completed}'`,
|
|
2736
|
+
values: []
|
|
2737
|
+
};
|
|
2738
|
+
}
|
|
2739
|
+
function deleteJobsToFail(schema, table) {
|
|
2740
|
+
return {
|
|
2741
|
+
text: `DELETE FROM ${schema}.${table} WHERE name = $1 AND id IN (SELECT UNNEST($2::uuid[]))`,
|
|
2742
|
+
values: []
|
|
2743
|
+
};
|
|
2744
|
+
}
|
|
2745
|
+
function selectJobsToFailByTimeout(schema, table, queues) {
|
|
2746
|
+
return {
|
|
2747
|
+
text: `SELECT * FROM ${schema}.${table}
|
|
2748
|
+
WHERE state = '${JOB_STATES.active}'
|
|
2749
|
+
AND (started_on + expire_seconds * interval '1s') < now()
|
|
2750
|
+
AND name = ANY(${serializeArrayParam(queues)})`,
|
|
2751
|
+
values: []
|
|
2752
|
+
};
|
|
2753
|
+
}
|
|
2754
|
+
function selectJobsToFailByHeartbeat(schema, table, queues) {
|
|
2755
|
+
return {
|
|
2756
|
+
text: `SELECT * FROM ${schema}.${table}
|
|
2757
|
+
WHERE state = '${JOB_STATES.active}'
|
|
2758
|
+
AND heartbeat_seconds IS NOT NULL
|
|
2759
|
+
AND (heartbeat_on + heartbeat_seconds * interval '1s') < now()
|
|
2760
|
+
AND name = ANY(${serializeArrayParam(queues)})`,
|
|
2761
|
+
values: []
|
|
2762
|
+
};
|
|
2763
|
+
}
|
|
2764
|
+
function deleteJobsByIds(schema, table) {
|
|
2765
|
+
return {
|
|
2766
|
+
text: `DELETE FROM ${schema}.${table} WHERE id IN (SELECT UNNEST($1::uuid[]))`,
|
|
2767
|
+
values: []
|
|
2768
|
+
};
|
|
2769
|
+
}
|
|
2770
|
+
function completeJobsDistributed(schema, table, includeQueued) {
|
|
2771
|
+
return `
|
|
2772
|
+
${completeJobsUpdate(schema, table, includeQueued)}
|
|
2773
|
+
RETURNING id, blocking
|
|
2774
|
+
`;
|
|
2775
|
+
}
|
|
2776
|
+
function decrementDependents(schema) {
|
|
2777
|
+
return `
|
|
2778
|
+
WITH decremented AS (
|
|
2779
|
+
SELECT d.child_name, d.child_id, COUNT(*)::int AS n
|
|
2780
|
+
FROM ${schema}.job_dependency d
|
|
2781
|
+
WHERE d.parent_name = $1
|
|
2782
|
+
AND d.parent_id IN (SELECT UNNEST($2::uuid[]))
|
|
2783
|
+
GROUP BY d.child_name, d.child_id
|
|
2784
|
+
),
|
|
2785
|
+
${lockedChildrenCte(schema)}
|
|
2786
|
+
${unblockChildrenUpdate(schema)}
|
|
2787
|
+
`;
|
|
2788
|
+
}
|
|
2789
|
+
function insertRetryJob(schema, table) {
|
|
2790
|
+
return `
|
|
2791
|
+
INSERT INTO ${schema}.${table} (
|
|
2792
|
+
id, name, priority, data, state, retry_limit, retry_count, retry_delay,
|
|
2793
|
+
retry_backoff, retry_delay_max, start_after, started_on, singleton_key, singleton_on,
|
|
2794
|
+
group_id, group_tier, expire_seconds, deletion_seconds, created_on, completed_on,
|
|
2795
|
+
keep_until, policy, output, dead_letter,
|
|
2796
|
+
heartbeat_on, heartbeat_seconds, blocked, blocking, pending_dependencies
|
|
2797
|
+
) VALUES (
|
|
2798
|
+
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24,
|
|
2799
|
+
$25, $26, $27, $28, $29
|
|
2800
|
+
) ON CONFLICT DO NOTHING
|
|
2801
|
+
RETURNING id
|
|
2802
|
+
`;
|
|
2803
|
+
}
|
|
2804
|
+
function insertDeadLetterJob(schema) {
|
|
2805
|
+
return `
|
|
2806
|
+
INSERT INTO ${schema}.job (name, data, output, retry_limit, retry_backoff, retry_delay, keep_until, deletion_seconds)
|
|
2807
|
+
SELECT $1, $2, $3, q.retry_limit, q.retry_backoff, q.retry_delay, now() + q.retention_seconds * interval '1s', q.deletion_seconds
|
|
2808
|
+
FROM ${schema}.queue q WHERE q.name = $1
|
|
2809
|
+
`;
|
|
2810
|
+
}
|
|
2811
|
+
function deletion(schema, table, queues, noAdvisoryLocks) {
|
|
2570
2812
|
return locked(schema, `
|
|
2571
2813
|
DELETE FROM ${schema}.${table}
|
|
2572
2814
|
WHERE name = ANY(${serializeArrayParam(queues)})
|
|
@@ -2576,7 +2818,7 @@ function deletion(schema, table, queues) {
|
|
|
2576
2818
|
OR
|
|
2577
2819
|
(state < '${JOB_STATES.active}' AND keep_until < now())
|
|
2578
2820
|
)
|
|
2579
|
-
`, table + "deletion");
|
|
2821
|
+
`, table + "deletion", noAdvisoryLocks);
|
|
2580
2822
|
}
|
|
2581
2823
|
function retryJobs(schema, table) {
|
|
2582
2824
|
return `
|
|
@@ -2609,7 +2851,7 @@ function getQueueStats$1(schema, table, queues) {
|
|
|
2609
2851
|
values: [queues]
|
|
2610
2852
|
};
|
|
2611
2853
|
}
|
|
2612
|
-
function cacheQueueStats(schema, table, queues) {
|
|
2854
|
+
function cacheQueueStats(schema, table, queues, noAdvisoryLocks) {
|
|
2613
2855
|
return locked(schema, `
|
|
2614
2856
|
WITH stats AS (${getQueueStats$1(schema, table, queues).text.replace("$1::text[]", serializeArrayParam(queues))})
|
|
2615
2857
|
UPDATE ${schema}.queue SET
|
|
@@ -2628,22 +2870,27 @@ function cacheQueueStats(schema, table, queues) {
|
|
|
2628
2870
|
queue.name,
|
|
2629
2871
|
queue.queued_count as "queuedCount",
|
|
2630
2872
|
queue.warning_queued as "warningQueueSize"
|
|
2631
|
-
`, "queue-stats");
|
|
2873
|
+
`, "queue-stats", noAdvisoryLocks);
|
|
2632
2874
|
}
|
|
2633
2875
|
function serializeArrayParam(values) {
|
|
2634
2876
|
return `ARRAY[${values.map((v) => `'${v.replace(SINGLE_QUOTE_REGEX, "''")}'`).join(",")}]::text[]`;
|
|
2635
2877
|
}
|
|
2636
|
-
function
|
|
2637
|
-
|
|
2878
|
+
function serializeJsonParam(value) {
|
|
2879
|
+
return `'${JSON.stringify(value).replace(SINGLE_QUOTE_REGEX, "''")}'`;
|
|
2880
|
+
}
|
|
2881
|
+
function transaction(query) {
|
|
2638
2882
|
return `
|
|
2639
2883
|
BEGIN;
|
|
2640
2884
|
SET LOCAL lock_timeout = 30000;
|
|
2641
2885
|
SET LOCAL idle_in_transaction_session_timeout = 30000;
|
|
2642
|
-
${
|
|
2643
|
-
${sql};
|
|
2886
|
+
${Array.isArray(query) ? query.join(";\n") : query};
|
|
2644
2887
|
COMMIT;
|
|
2645
2888
|
`;
|
|
2646
2889
|
}
|
|
2890
|
+
function locked(schema, query, key, noAdvisoryLocks) {
|
|
2891
|
+
const statements = Array.isArray(query) ? query : [query];
|
|
2892
|
+
return transaction(noAdvisoryLocks ? statements : [advisoryLock(schema, key), ...statements]);
|
|
2893
|
+
}
|
|
2647
2894
|
function advisoryLock(schema, key) {
|
|
2648
2895
|
return `SELECT pg_advisory_xact_lock(
|
|
2649
2896
|
('x' || encode(sha224((current_database() || '.pgboss.${schema}${key || ""}')::bytea), 'hex'))::bit(64)::bigint
|
|
@@ -2684,6 +2931,48 @@ function getJobById$1(schema, table) {
|
|
|
2684
2931
|
AND id = $2
|
|
2685
2932
|
`;
|
|
2686
2933
|
}
|
|
2934
|
+
function insertDependencies(schema) {
|
|
2935
|
+
return `
|
|
2936
|
+
INSERT INTO ${schema}.job_dependency (child_name, child_id, parent_name, parent_id)
|
|
2937
|
+
SELECT child_name, child_id, parent_name, parent_id
|
|
2938
|
+
FROM json_to_recordset($1::json) AS x (
|
|
2939
|
+
child_name text,
|
|
2940
|
+
child_id uuid,
|
|
2941
|
+
parent_name text,
|
|
2942
|
+
parent_id uuid
|
|
2943
|
+
)
|
|
2944
|
+
ON CONFLICT DO NOTHING
|
|
2945
|
+
`;
|
|
2946
|
+
}
|
|
2947
|
+
function getDependencies(schema) {
|
|
2948
|
+
return `
|
|
2949
|
+
SELECT parent_name as "parentName", parent_id as "parentId"
|
|
2950
|
+
FROM ${schema}.job_dependency
|
|
2951
|
+
WHERE child_name = $1 AND child_id = $2
|
|
2952
|
+
`;
|
|
2953
|
+
}
|
|
2954
|
+
function getDependents(schema) {
|
|
2955
|
+
return `
|
|
2956
|
+
SELECT child_name as "childName", child_id as "childId"
|
|
2957
|
+
FROM ${schema}.job_dependency
|
|
2958
|
+
WHERE parent_name = $1 AND parent_id = $2
|
|
2959
|
+
`;
|
|
2960
|
+
}
|
|
2961
|
+
function cleanupDependencies(schema, table, queues, noAdvisoryLocks) {
|
|
2962
|
+
return locked(schema, `
|
|
2963
|
+
DELETE FROM ${schema}.job_dependency
|
|
2964
|
+
WHERE (child_name = ANY(${serializeArrayParam(queues)})
|
|
2965
|
+
AND NOT EXISTS (
|
|
2966
|
+
SELECT 1 FROM ${schema}.${table} j
|
|
2967
|
+
WHERE j.name = child_name AND j.id = child_id
|
|
2968
|
+
))
|
|
2969
|
+
OR (parent_name = ANY(${serializeArrayParam(queues)})
|
|
2970
|
+
AND NOT EXISTS (
|
|
2971
|
+
SELECT 1 FROM ${schema}.${table} j
|
|
2972
|
+
WHERE j.name = parent_name AND j.id = parent_id
|
|
2973
|
+
))
|
|
2974
|
+
`, table + "cleanupDependencies", noAdvisoryLocks);
|
|
2975
|
+
}
|
|
2687
2976
|
function getBlockedKeys(schema, table) {
|
|
2688
2977
|
return `
|
|
2689
2978
|
SELECT DISTINCT singleton_key as "singletonKey"
|
|
@@ -2744,6 +3033,46 @@ var POLICY = {
|
|
|
2744
3033
|
MIN_POLLING_INTERVAL_MS: 500,
|
|
2745
3034
|
MAX_RETENTION_DAYS: 365
|
|
2746
3035
|
};
|
|
3036
|
+
var COMPATIBILITY_FLAGS = [
|
|
3037
|
+
"noSkipLocked",
|
|
3038
|
+
"noMultiMutationCte",
|
|
3039
|
+
"noTablePartitioning",
|
|
3040
|
+
"noDeferrableConstraints",
|
|
3041
|
+
"noAdvisoryLocks",
|
|
3042
|
+
"noCoveringIndexes"
|
|
3043
|
+
];
|
|
3044
|
+
var BACKEND_PROFILES = {
|
|
3045
|
+
postgres: {
|
|
3046
|
+
kind: "standard",
|
|
3047
|
+
flags: {}
|
|
3048
|
+
},
|
|
3049
|
+
cockroachdb: {
|
|
3050
|
+
kind: "distributed",
|
|
3051
|
+
flags: {
|
|
3052
|
+
noSkipLocked: true,
|
|
3053
|
+
noMultiMutationCte: true,
|
|
3054
|
+
noTablePartitioning: true,
|
|
3055
|
+
noDeferrableConstraints: true,
|
|
3056
|
+
noAdvisoryLocks: true,
|
|
3057
|
+
noCoveringIndexes: true
|
|
3058
|
+
}
|
|
3059
|
+
},
|
|
3060
|
+
yugabytedb: {
|
|
3061
|
+
kind: "distributed",
|
|
3062
|
+
flags: {
|
|
3063
|
+
noAdvisoryLocks: true,
|
|
3064
|
+
noTablePartitioning: true
|
|
3065
|
+
}
|
|
3066
|
+
},
|
|
3067
|
+
citus: {
|
|
3068
|
+
kind: "distributed",
|
|
3069
|
+
flags: {}
|
|
3070
|
+
},
|
|
3071
|
+
pglite: {
|
|
3072
|
+
kind: "embedded",
|
|
3073
|
+
flags: {}
|
|
3074
|
+
}
|
|
3075
|
+
};
|
|
2747
3076
|
function assertObjectName(value, name = "Name") {
|
|
2748
3077
|
assert(/^[\w.\-/]+$/.test(value), `${name} can only contain alphanumeric characters, underscores, hyphens, periods, or forward slashes`);
|
|
2749
3078
|
}
|
|
@@ -2796,6 +3125,82 @@ function validateGroupConfig(config) {
|
|
|
2796
3125
|
assert(typeof config.group.id === "string" && config.group.id.length > 0, "group.id must be a non-empty string");
|
|
2797
3126
|
assert(!("tier" in config.group) || typeof config.group.tier === "string" && config.group.tier.length > 0, "group.tier must be a non-empty string if provided");
|
|
2798
3127
|
}
|
|
3128
|
+
function validateFlowJobs(jobs) {
|
|
3129
|
+
assert(Array.isArray(jobs), "flow requires an array of jobs");
|
|
3130
|
+
assert(jobs.length >= 2, "flow requires at least 2 jobs");
|
|
3131
|
+
const refs = /* @__PURE__ */ new Set();
|
|
3132
|
+
for (const job of jobs) {
|
|
3133
|
+
assert(typeof job.ref === "string" && job.ref.length > 0, "each flow job must have a non-empty ref");
|
|
3134
|
+
assert(!refs.has(job.ref), `duplicate ref: "${job.ref}"`);
|
|
3135
|
+
refs.add(job.ref);
|
|
3136
|
+
assert(typeof job.name === "string" && job.name.length > 0, "each flow job must have a non-empty name");
|
|
3137
|
+
assertObjectName(job.name);
|
|
3138
|
+
}
|
|
3139
|
+
assert(jobs.some((j) => j.dependsOn && j.dependsOn.length > 0), "flow requires at least one job with dependsOn");
|
|
3140
|
+
for (const job of jobs) {
|
|
3141
|
+
if (!job.dependsOn) continue;
|
|
3142
|
+
assert(Array.isArray(job.dependsOn), `dependsOn for ref "${job.ref}" must be an array`);
|
|
3143
|
+
for (const dep of job.dependsOn) {
|
|
3144
|
+
assert(typeof dep === "string" && dep.length > 0, "dependsOn entries must be non-empty strings");
|
|
3145
|
+
assert(dep !== job.ref, `job "${job.ref}" cannot depend on itself`);
|
|
3146
|
+
assert(refs.has(dep), `dependsOn ref "${dep}" not found in flow`);
|
|
3147
|
+
}
|
|
3148
|
+
}
|
|
3149
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
3150
|
+
const edges = /* @__PURE__ */ new Map();
|
|
3151
|
+
for (const job of jobs) {
|
|
3152
|
+
inDegree.set(job.ref, 0);
|
|
3153
|
+
edges.set(job.ref, []);
|
|
3154
|
+
}
|
|
3155
|
+
for (const job of jobs) {
|
|
3156
|
+
if (!job.dependsOn) continue;
|
|
3157
|
+
for (const dep of job.dependsOn) {
|
|
3158
|
+
edges.get(dep).push(job.ref);
|
|
3159
|
+
inDegree.set(job.ref, inDegree.get(job.ref) + 1);
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
const queue = [];
|
|
3163
|
+
for (const [ref, deg] of inDegree) if (deg === 0) queue.push(ref);
|
|
3164
|
+
let visited = 0;
|
|
3165
|
+
while (queue.length > 0) {
|
|
3166
|
+
const current = queue.shift();
|
|
3167
|
+
visited++;
|
|
3168
|
+
for (const child of edges.get(current)) {
|
|
3169
|
+
const newDeg = inDegree.get(child) - 1;
|
|
3170
|
+
inDegree.set(child, newDeg);
|
|
3171
|
+
if (newDeg === 0) queue.push(child);
|
|
3172
|
+
}
|
|
3173
|
+
}
|
|
3174
|
+
if (visited !== jobs.length) assert(false, `flow contains a dependency cycle: ${findDependencyCycle(edges).join(" -> ")}`);
|
|
3175
|
+
}
|
|
3176
|
+
function findDependencyCycle(edges) {
|
|
3177
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
3178
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3179
|
+
const path = [];
|
|
3180
|
+
function visit(ref) {
|
|
3181
|
+
if (visiting.has(ref)) {
|
|
3182
|
+
const start = path.indexOf(ref);
|
|
3183
|
+
return [...path.slice(start), ref];
|
|
3184
|
+
}
|
|
3185
|
+
if (visited.has(ref)) return null;
|
|
3186
|
+
visiting.add(ref);
|
|
3187
|
+
path.push(ref);
|
|
3188
|
+
for (const child of edges.get(ref) || []) {
|
|
3189
|
+
const cycle = visit(child);
|
|
3190
|
+
if (cycle) return cycle;
|
|
3191
|
+
}
|
|
3192
|
+
path.pop();
|
|
3193
|
+
visiting.delete(ref);
|
|
3194
|
+
visited.add(ref);
|
|
3195
|
+
return null;
|
|
3196
|
+
}
|
|
3197
|
+
let cycle = null;
|
|
3198
|
+
for (const ref of edges.keys()) {
|
|
3199
|
+
cycle = visit(ref);
|
|
3200
|
+
if (cycle) break;
|
|
3201
|
+
}
|
|
3202
|
+
return cycle;
|
|
3203
|
+
}
|
|
2799
3204
|
function validateGroupConcurrencyValue(value, optionName) {
|
|
2800
3205
|
if (typeof value === "number") {
|
|
2801
3206
|
assert(Number.isInteger(value) && value >= 1, `${optionName} must be an integer >= 1`);
|
|
@@ -2875,6 +3280,7 @@ function getConfig(value) {
|
|
|
2875
3280
|
config.supervise = "supervise" in config ? config.supervise : true;
|
|
2876
3281
|
config.migrate = "migrate" in config ? config.migrate : true;
|
|
2877
3282
|
config.createSchema = "createSchema" in config ? config.createSchema : true;
|
|
3283
|
+
resolveBackend(config);
|
|
2878
3284
|
applySchemaConfig(config);
|
|
2879
3285
|
applyOpsConfig(config);
|
|
2880
3286
|
applyScheduleConfig(config);
|
|
@@ -2892,6 +3298,17 @@ function validateWarningConfig(config) {
|
|
|
2892
3298
|
assert(!("warningRetentionDays" in config) || Number.isInteger(config.warningRetentionDays) && config.warningRetentionDays >= 1, "configuration assert: warningRetentionDays must be an integer >= 1");
|
|
2893
3299
|
assert(!("warningRetentionDays" in config) || config.warningRetentionDays <= POLICY.MAX_RETENTION_DAYS, `configuration assert: warningRetentionDays cannot exceed ${POLICY.MAX_RETENTION_DAYS} days`);
|
|
2894
3300
|
}
|
|
3301
|
+
function resolveBackend(config) {
|
|
3302
|
+
const backend = "backend" in config ? config.backend : "postgres";
|
|
3303
|
+
assert(backend in BACKEND_PROFILES, `configuration assert: backend must be one of ${Object.keys(BACKEND_PROFILES).join(", ")}`);
|
|
3304
|
+
config.backend = backend;
|
|
3305
|
+
const { flags } = BACKEND_PROFILES[backend];
|
|
3306
|
+
for (const flag of COMPATIBILITY_FLAGS) config[flag] = flags[flag] ?? false;
|
|
3307
|
+
if (config.__test__distributed) {
|
|
3308
|
+
config.noSkipLocked = true;
|
|
3309
|
+
config.noMultiMutationCte = true;
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
2895
3312
|
function assertPostgresObjectName(name) {
|
|
2896
3313
|
assert(typeof name === "string", "Name must be a string");
|
|
2897
3314
|
assert(name.length <= 50, "Name cannot exceed 50 characters");
|
|
@@ -2965,24 +3382,24 @@ function applyBamConfig(config) {
|
|
|
2965
3382
|
}
|
|
2966
3383
|
//#endregion
|
|
2967
3384
|
//#region ../../src/migrationStore.ts
|
|
2968
|
-
function flatten(schema, commands, version) {
|
|
3385
|
+
function flatten(schema, commands, version, noAdvisoryLocks) {
|
|
2969
3386
|
commands.unshift(assertMigration(schema, version));
|
|
2970
3387
|
commands.push(setVersion(schema, version));
|
|
2971
|
-
return locked(schema, commands);
|
|
3388
|
+
return locked(schema, commands, void 0, noAdvisoryLocks);
|
|
2972
3389
|
}
|
|
2973
|
-
function rollback(schema, version, migrations) {
|
|
3390
|
+
function rollback(schema, version, migrations, noAdvisoryLocks) {
|
|
2974
3391
|
migrations = migrations || getAll(schema);
|
|
2975
3392
|
const result = migrations.find((i) => i.version === version);
|
|
2976
3393
|
assert(result, `Version ${version} not found.`);
|
|
2977
|
-
return flatten(schema, result.uninstall || [], result.previous);
|
|
3394
|
+
return flatten(schema, result.uninstall || [], result.previous, noAdvisoryLocks);
|
|
2978
3395
|
}
|
|
2979
|
-
function next(schema, version, migrations) {
|
|
3396
|
+
function next(schema, version, migrations, noAdvisoryLocks) {
|
|
2980
3397
|
migrations = migrations || getAll(schema);
|
|
2981
3398
|
const result = migrations.find((i) => i.previous === version);
|
|
2982
3399
|
assert(result, `Version ${version} not found.`);
|
|
2983
|
-
return flatten(schema, result.install, result.version);
|
|
3400
|
+
return flatten(schema, result.install, result.version, noAdvisoryLocks);
|
|
2984
3401
|
}
|
|
2985
|
-
function migrate(schema, version, migrations) {
|
|
3402
|
+
function migrate(schema, version, migrations, noAdvisoryLocks) {
|
|
2986
3403
|
migrations = migrations || getAll(schema);
|
|
2987
3404
|
const result = migrations.filter((i) => i.previous >= version).sort((a, b) => a.version - b.version).reduce((acc, migration) => {
|
|
2988
3405
|
acc.install = acc.install.concat(migration.install);
|
|
@@ -2997,7 +3414,7 @@ function migrate(schema, version, migrations) {
|
|
|
2997
3414
|
version
|
|
2998
3415
|
});
|
|
2999
3416
|
assert(result.install.length > 0, `Version ${version} not found.`);
|
|
3000
|
-
return flatten(schema, result.install, result.version);
|
|
3417
|
+
return flatten(schema, result.install, result.version, noAdvisoryLocks);
|
|
3001
3418
|
}
|
|
3002
3419
|
function getAll(schema) {
|
|
3003
3420
|
return [
|
|
@@ -3743,6 +4160,203 @@ function getAll(schema) {
|
|
|
3743
4160
|
`ALTER TABLE ${schema}.job DROP COLUMN heartbeat_seconds`,
|
|
3744
4161
|
`ALTER TABLE ${schema}.job DROP COLUMN heartbeat_on`
|
|
3745
4162
|
]
|
|
4163
|
+
},
|
|
4164
|
+
{
|
|
4165
|
+
release: "12.19.0",
|
|
4166
|
+
version: 31,
|
|
4167
|
+
previous: 30,
|
|
4168
|
+
install: [
|
|
4169
|
+
`ALTER TABLE ${schema}.job ADD COLUMN blocked boolean NOT NULL DEFAULT false`,
|
|
4170
|
+
`ALTER TABLE ${schema}.job ADD COLUMN blocking boolean NOT NULL DEFAULT false`,
|
|
4171
|
+
`ALTER TABLE ${schema}.job ADD COLUMN pending_dependencies int NOT NULL DEFAULT 0`,
|
|
4172
|
+
`
|
|
4173
|
+
CREATE TABLE IF NOT EXISTS ${schema}.job_dependency (
|
|
4174
|
+
child_name text NOT NULL,
|
|
4175
|
+
child_id uuid NOT NULL,
|
|
4176
|
+
parent_name text NOT NULL,
|
|
4177
|
+
parent_id uuid NOT NULL,
|
|
4178
|
+
PRIMARY KEY (child_name, child_id, parent_name, parent_id)
|
|
4179
|
+
)
|
|
4180
|
+
`,
|
|
4181
|
+
`CREATE INDEX IF NOT EXISTS job_dep_parent_idx ON ${schema}.job_dependency (parent_name, parent_id)`,
|
|
4182
|
+
`SELECT ${schema}.job_table_run($cmd$DROP INDEX IF EXISTS ${schema}.job_i5$cmd$)`,
|
|
4183
|
+
`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
|
+
`
|
|
4267
|
+
],
|
|
4268
|
+
uninstall: [
|
|
4269
|
+
`DROP INDEX IF EXISTS ${schema}.job_dep_parent_idx`,
|
|
4270
|
+
`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
|
+
`,
|
|
4354
|
+
`SELECT ${schema}.job_table_run($cmd$DROP INDEX IF EXISTS ${schema}.job_i5$cmd$)`,
|
|
4355
|
+
`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
|
+
`ALTER TABLE ${schema}.job DROP COLUMN pending_dependencies`,
|
|
4357
|
+
`ALTER TABLE ${schema}.job DROP COLUMN blocking`,
|
|
4358
|
+
`ALTER TABLE ${schema}.job DROP COLUMN blocked`
|
|
4359
|
+
]
|
|
3746
4360
|
}
|
|
3747
4361
|
];
|
|
3748
4362
|
}
|
|
@@ -3750,7 +4364,7 @@ function getAll(schema) {
|
|
|
3750
4364
|
//#region ../../src/contractor.ts
|
|
3751
4365
|
var schemaVersion = {
|
|
3752
4366
|
name: "pg-boss",
|
|
3753
|
-
version: "12.
|
|
4367
|
+
version: "12.20.0",
|
|
3754
4368
|
description: "Queueing jobs in Postgres from Node.js like a boss",
|
|
3755
4369
|
type: "module",
|
|
3756
4370
|
main: "./dist/index.js",
|
|
@@ -3763,6 +4377,7 @@ var schemaVersion = {
|
|
|
3763
4377
|
"serialize-error": "^13.0.1"
|
|
3764
4378
|
},
|
|
3765
4379
|
devDependencies: {
|
|
4380
|
+
"@electric-sql/pglite": "^0.4.1",
|
|
3766
4381
|
"@prisma/adapter-pg": "^7.8.0",
|
|
3767
4382
|
"@prisma/client": "^7.8.0",
|
|
3768
4383
|
"@tsconfig/node-ts": "^23.6.4",
|
|
@@ -3772,6 +4387,7 @@ var schemaVersion = {
|
|
|
3772
4387
|
"@types/pg": "^8.20.0",
|
|
3773
4388
|
"@vitest/coverage-v8": "^4.1.2",
|
|
3774
4389
|
"cli-testlab": "^6.0.1",
|
|
4390
|
+
"cross-env": "^10.1.0",
|
|
3775
4391
|
"drizzle-orm": "^1.0.0-beta.22",
|
|
3776
4392
|
"eslint": "^9.39.4",
|
|
3777
4393
|
"knex": "^3.2.10",
|
|
@@ -3789,6 +4405,12 @@ var schemaVersion = {
|
|
|
3789
4405
|
"prepublishOnly": "npm install && npm test && npm run build",
|
|
3790
4406
|
"pretest": "prisma generate --schema=test/prisma/schema.prisma",
|
|
3791
4407
|
"test": "eslint . && vitest run",
|
|
4408
|
+
"test:distributed": "cross-env DISTRIBUTED=true npm test",
|
|
4409
|
+
"test:cockroachdb": "cross-env DB_TYPE=cockroachdb COCKROACH_HOST=localhost npm test -- test/distributedDatabaseTest.ts",
|
|
4410
|
+
"test:cockroachdb:full": "cross-env DB_TYPE=cockroachdb COCKROACH_HOST=localhost npm test -- --no-file-parallelism",
|
|
4411
|
+
"test:yugabytedb:full": "cross-env DB_TYPE=yugabytedb YUGABYTE_HOST=localhost npm test -- --no-file-parallelism",
|
|
4412
|
+
"test:citus:full": "cross-env DB_TYPE=citus CITUS_HOST=localhost npm test",
|
|
4413
|
+
"test:pglite": "cross-env DB_TYPE=pglite npm test -- --no-file-parallelism",
|
|
3792
4414
|
"lint:fix": "eslint . --fix",
|
|
3793
4415
|
"precover": "prisma generate --schema=test/prisma/schema.prisma",
|
|
3794
4416
|
"cover": "vitest run --coverage",
|
|
@@ -3797,7 +4419,7 @@ var schemaVersion = {
|
|
|
3797
4419
|
"db:migrate": "node --import=tsx -e 'console.log(require(\"./src\").getMigrationPlans())'",
|
|
3798
4420
|
"db:construct": "node --import=tsx -e 'console.log(require(\"./src\").getConstructionPlans())'"
|
|
3799
4421
|
},
|
|
3800
|
-
pgboss: { "schema":
|
|
4422
|
+
pgboss: { "schema": 31 },
|
|
3801
4423
|
repository: {
|
|
3802
4424
|
"type": "git",
|
|
3803
4425
|
"url": "git+https://github.com/timgit/pg-boss.git"
|
|
@@ -3864,18 +4486,18 @@ var Contractor = class {
|
|
|
3864
4486
|
}
|
|
3865
4487
|
async migrate(version) {
|
|
3866
4488
|
try {
|
|
3867
|
-
const commands = migrate(this.config.schema, version, this.migrations);
|
|
4489
|
+
const commands = migrate(this.config.schema, version, this.migrations, this.config.noAdvisoryLocks);
|
|
3868
4490
|
await this.db.executeSql(commands);
|
|
3869
4491
|
} catch (err) {
|
|
3870
4492
|
assert(err.message.includes(MIGRATE_RACE_MESSAGE), err);
|
|
3871
4493
|
}
|
|
3872
4494
|
}
|
|
3873
4495
|
async next(version) {
|
|
3874
|
-
const commands = next(this.config.schema, version, this.migrations);
|
|
4496
|
+
const commands = next(this.config.schema, version, this.migrations, this.config.noAdvisoryLocks);
|
|
3875
4497
|
await this.db.executeSql(commands);
|
|
3876
4498
|
}
|
|
3877
4499
|
async rollback(version) {
|
|
3878
|
-
const commands = rollback(this.config.schema, version, this.migrations);
|
|
4500
|
+
const commands = rollback(this.config.schema, version, this.migrations, this.config.noAdvisoryLocks);
|
|
3879
4501
|
await this.db.executeSql(commands);
|
|
3880
4502
|
}
|
|
3881
4503
|
};
|
|
@@ -4280,10 +4902,39 @@ var INTERNAL_QUEUES = Object.values(QUEUES).reduce((acc, i) => ({
|
|
|
4280
4902
|
...acc,
|
|
4281
4903
|
[i]: i
|
|
4282
4904
|
}), {});
|
|
4905
|
+
var NUMERIC_METADATA_FIELDS = [
|
|
4906
|
+
"priority",
|
|
4907
|
+
"retryLimit",
|
|
4908
|
+
"retryCount",
|
|
4909
|
+
"retryDelay",
|
|
4910
|
+
"retryDelayMax",
|
|
4911
|
+
"expireInSeconds",
|
|
4912
|
+
"heartbeatSeconds",
|
|
4913
|
+
"deleteAfterSeconds",
|
|
4914
|
+
"pendingDependencies"
|
|
4915
|
+
];
|
|
4916
|
+
var NUMERIC_QUEUE_FIELDS = [
|
|
4917
|
+
"retryLimit",
|
|
4918
|
+
"retryDelay",
|
|
4919
|
+
"retryDelayMax",
|
|
4920
|
+
"expireInSeconds",
|
|
4921
|
+
"retentionSeconds",
|
|
4922
|
+
"deleteAfterSeconds",
|
|
4923
|
+
"heartbeatSeconds",
|
|
4924
|
+
"deferredCount",
|
|
4925
|
+
"warningQueueSize",
|
|
4926
|
+
"queuedCount",
|
|
4927
|
+
"activeCount",
|
|
4928
|
+
"totalCount"
|
|
4929
|
+
];
|
|
4283
4930
|
var events$3 = {
|
|
4284
4931
|
error: "error",
|
|
4285
4932
|
wip: "wip"
|
|
4286
4933
|
};
|
|
4934
|
+
function rethrowWriteError(err) {
|
|
4935
|
+
if (err?.code === PG_ERROR.divisionByZero) throw new Error("one or more jobs could not be created. This usually means a job id was duplicated, collided with an existing job, or was rejected by a queue policy (short, singleton, stately, or exclusive).", { cause: err });
|
|
4936
|
+
throw err;
|
|
4937
|
+
}
|
|
4287
4938
|
var Manager = class extends EventEmitter {
|
|
4288
4939
|
events = events$3;
|
|
4289
4940
|
db;
|
|
@@ -4735,6 +5386,10 @@ var Manager = class extends EventEmitter {
|
|
|
4735
5386
|
if (policy === QUEUE_POLICIES.key_strict_fifo) {
|
|
4736
5387
|
for (const job of jobs) if (!job.singletonKey) throw new Error(`${QUEUE_POLICIES.key_strict_fifo} queues require a singletonKey`);
|
|
4737
5388
|
}
|
|
5389
|
+
const insertPayload = jobs.map((j) => {
|
|
5390
|
+
const { blocked, blocking, pendingDependencies, ...rest } = j;
|
|
5391
|
+
return rest;
|
|
5392
|
+
});
|
|
4738
5393
|
const db = this.assertDb(options);
|
|
4739
5394
|
const spy = this.config.__test__enableSpies ? this.#spies.get(name) : void 0;
|
|
4740
5395
|
const returnId = !!spy || !!options.returnId;
|
|
@@ -4743,13 +5398,100 @@ var Manager = class extends EventEmitter {
|
|
|
4743
5398
|
name,
|
|
4744
5399
|
returnId
|
|
4745
5400
|
});
|
|
4746
|
-
const { rows } = await db.executeSql(sql, [JSON.stringify(
|
|
5401
|
+
const { rows } = await db.executeSql(sql, [JSON.stringify(insertPayload)]);
|
|
4747
5402
|
if (rows.length) {
|
|
4748
5403
|
if (spy) for (let i = 0; i < rows.length; i++) spy.addJob(rows[i].id, name, jobs[i].data || {}, "created");
|
|
4749
5404
|
return rows.map((i) => i.id);
|
|
4750
5405
|
}
|
|
4751
5406
|
return null;
|
|
4752
5407
|
}
|
|
5408
|
+
async flow(jobs, options = {}) {
|
|
5409
|
+
validateFlowJobs(jobs);
|
|
5410
|
+
const flowJobs = jobs.map((job) => ({
|
|
5411
|
+
...job,
|
|
5412
|
+
options: checkSendArgs([{
|
|
5413
|
+
name: job.name,
|
|
5414
|
+
data: job.data,
|
|
5415
|
+
options: job.options
|
|
5416
|
+
}]).options
|
|
5417
|
+
}));
|
|
5418
|
+
const refToId = {};
|
|
5419
|
+
for (const job of flowJobs) refToId[job.ref] = job.options?.id ?? randomUUID();
|
|
5420
|
+
const refToJob = new Map(flowJobs.map((job) => [job.ref, job]));
|
|
5421
|
+
const dependencyCountByRef = /* @__PURE__ */ new Map();
|
|
5422
|
+
const parentRefs = /* @__PURE__ */ new Set();
|
|
5423
|
+
const depRows = [];
|
|
5424
|
+
for (const job of flowJobs) {
|
|
5425
|
+
const dependsOn = [...new Set(job.dependsOn ?? [])];
|
|
5426
|
+
dependencyCountByRef.set(job.ref, dependsOn.length);
|
|
5427
|
+
for (const depRef of dependsOn) {
|
|
5428
|
+
const parentJob = refToJob.get(depRef);
|
|
5429
|
+
parentRefs.add(depRef);
|
|
5430
|
+
depRows.push({
|
|
5431
|
+
child_name: job.name,
|
|
5432
|
+
child_id: refToId[job.ref],
|
|
5433
|
+
parent_name: parentJob.name,
|
|
5434
|
+
parent_id: refToId[depRef]
|
|
5435
|
+
});
|
|
5436
|
+
}
|
|
5437
|
+
}
|
|
5438
|
+
const byQueue = /* @__PURE__ */ new Map();
|
|
5439
|
+
for (const job of flowJobs) {
|
|
5440
|
+
const group = byQueue.get(job.name) || [];
|
|
5441
|
+
group.push(job);
|
|
5442
|
+
byQueue.set(job.name, group);
|
|
5443
|
+
}
|
|
5444
|
+
const statements = [];
|
|
5445
|
+
for (const [queueName, queueJobs] of byQueue) {
|
|
5446
|
+
const { table } = await this.getQueueCache(queueName);
|
|
5447
|
+
const insertPayload = queueJobs.map((j) => {
|
|
5448
|
+
const dependencyCount = dependencyCountByRef.get(j.ref) ?? 0;
|
|
5449
|
+
return {
|
|
5450
|
+
id: refToId[j.ref],
|
|
5451
|
+
name: queueName,
|
|
5452
|
+
data: j.data ?? null,
|
|
5453
|
+
priority: j.options?.priority,
|
|
5454
|
+
startAfter: j.options?.startAfter,
|
|
5455
|
+
singletonKey: j.options?.singletonKey ?? void 0,
|
|
5456
|
+
singletonSeconds: j.options?.singletonSeconds,
|
|
5457
|
+
groupId: j.options?.group?.id ?? void 0,
|
|
5458
|
+
groupTier: j.options?.group?.tier ?? void 0,
|
|
5459
|
+
expireInSeconds: j.options?.expireInSeconds,
|
|
5460
|
+
deleteAfterSeconds: j.options?.deleteAfterSeconds,
|
|
5461
|
+
retentionSeconds: j.options?.retentionSeconds,
|
|
5462
|
+
retryLimit: j.options?.retryLimit,
|
|
5463
|
+
retryDelay: j.options?.retryDelay,
|
|
5464
|
+
retryBackoff: j.options?.retryBackoff,
|
|
5465
|
+
retryDelayMax: j.options?.retryDelayMax,
|
|
5466
|
+
heartbeatSeconds: j.options?.heartbeatSeconds,
|
|
5467
|
+
deadLetter: j.options?.deadLetter ?? void 0,
|
|
5468
|
+
blocked: dependencyCount > 0 || void 0,
|
|
5469
|
+
blocking: parentRefs.has(j.ref) || void 0,
|
|
5470
|
+
pendingDependencies: dependencyCount || void 0
|
|
5471
|
+
};
|
|
5472
|
+
});
|
|
5473
|
+
const insertSql = insertJobs(this.config.schema, {
|
|
5474
|
+
table,
|
|
5475
|
+
name: queueName,
|
|
5476
|
+
returnId: true
|
|
5477
|
+
}).replace("$1", () => serializeJsonParam(insertPayload));
|
|
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
|
+
`);
|
|
5484
|
+
}
|
|
5485
|
+
if (depRows.length > 0) statements.push(insertDependencies(this.config.schema).replace("$1", () => serializeJsonParam(depRows)));
|
|
5486
|
+
const db = options.db ?? this.db;
|
|
5487
|
+
const sql = options.db ? statements.join(";\n") : transaction(statements);
|
|
5488
|
+
try {
|
|
5489
|
+
await db.executeSql(sql);
|
|
5490
|
+
} catch (err) {
|
|
5491
|
+
rethrowWriteError(err);
|
|
5492
|
+
}
|
|
5493
|
+
return refToId;
|
|
5494
|
+
}
|
|
4753
5495
|
getDebounceStartAfter(singletonSeconds, clockOffset) {
|
|
4754
5496
|
const debounceInterval = singletonSeconds * 1e3;
|
|
4755
5497
|
const now = Date.now() + clockOffset;
|
|
@@ -4770,12 +5512,16 @@ var Manager = class extends EventEmitter {
|
|
|
4770
5512
|
policy,
|
|
4771
5513
|
limit: options.batchSize || 1,
|
|
4772
5514
|
ignoreSingletons: singletonsActive
|
|
4773
|
-
});
|
|
5515
|
+
}, this.config.noSkipLocked);
|
|
4774
5516
|
let result;
|
|
4775
5517
|
try {
|
|
4776
5518
|
result = await db.executeSql(query.text, query.values);
|
|
4777
5519
|
} catch (err) {}
|
|
4778
|
-
|
|
5520
|
+
const rows = result?.rows || [];
|
|
5521
|
+
if (this.config.backend === "cockroachdb") {
|
|
5522
|
+
for (const row of rows) for (const field of NUMERIC_METADATA_FIELDS) if (row[field] !== void 0 && row[field] !== null) row[field] = Number(row[field]);
|
|
5523
|
+
}
|
|
5524
|
+
return rows;
|
|
4779
5525
|
}
|
|
4780
5526
|
mapCompletionIdArg(id, funcName) {
|
|
4781
5527
|
const errorMessage = `${funcName}() requires an id`;
|
|
@@ -4800,27 +5546,187 @@ var Manager = class extends EventEmitter {
|
|
|
4800
5546
|
const db = this.assertDb(options);
|
|
4801
5547
|
const ids = this.mapCompletionIdArg(id, "complete");
|
|
4802
5548
|
const { table } = await this.getQueueCache(name);
|
|
5549
|
+
const outputData = this.mapCompletionDataArg(data);
|
|
5550
|
+
if (this.config.noMultiMutationCte) return this.completeDistributed(name, ids, outputData, table, db, options.includeQueued);
|
|
4803
5551
|
const sql = completeJobs(this.config.schema, table, options.includeQueued);
|
|
4804
5552
|
const result = await db.executeSql(sql, [
|
|
4805
5553
|
name,
|
|
4806
5554
|
ids,
|
|
4807
|
-
|
|
5555
|
+
outputData
|
|
4808
5556
|
]);
|
|
4809
5557
|
return this.mapCommandResponse(ids, result);
|
|
4810
5558
|
}
|
|
5559
|
+
async withDistributedTransaction(db, fn) {
|
|
5560
|
+
if (db === this.db && this.db._pgbdb) return this.db.withTransaction(fn);
|
|
5561
|
+
return fn(db);
|
|
5562
|
+
}
|
|
5563
|
+
async completeDistributed(name, ids, outputData, table, db, includeQueued) {
|
|
5564
|
+
return this.withDistributedTransaction(db, async (tx) => {
|
|
5565
|
+
const completeSql = completeJobsDistributed(this.config.schema, table, includeQueued);
|
|
5566
|
+
const { rows } = await tx.executeSql(completeSql, [
|
|
5567
|
+
name,
|
|
5568
|
+
ids,
|
|
5569
|
+
outputData
|
|
5570
|
+
]);
|
|
5571
|
+
const blockingIds = rows.filter((row) => row.blocking).map((row) => row.id);
|
|
5572
|
+
if (blockingIds.length > 0) {
|
|
5573
|
+
const decrementSql = decrementDependents(this.config.schema);
|
|
5574
|
+
await tx.executeSql(decrementSql, [name, blockingIds]);
|
|
5575
|
+
}
|
|
5576
|
+
return {
|
|
5577
|
+
jobs: ids,
|
|
5578
|
+
requested: ids.length,
|
|
5579
|
+
affected: rows.length
|
|
5580
|
+
};
|
|
5581
|
+
});
|
|
5582
|
+
}
|
|
4811
5583
|
async fail(name, id, data, options = {}) {
|
|
4812
5584
|
assertQueueName(name);
|
|
4813
5585
|
const db = this.assertDb(options);
|
|
4814
5586
|
const ids = this.mapCompletionIdArg(id, "fail");
|
|
4815
5587
|
const { table } = await this.getQueueCache(name);
|
|
5588
|
+
const outputData = this.mapCompletionDataArg(data);
|
|
5589
|
+
if (this.config.noMultiMutationCte) return this.failDistributed(name, ids, outputData, table, db);
|
|
4816
5590
|
const sql = failJobsById(this.config.schema, table);
|
|
4817
5591
|
const result = await db.executeSql(sql, [
|
|
4818
5592
|
name,
|
|
4819
5593
|
ids,
|
|
4820
|
-
|
|
5594
|
+
outputData
|
|
4821
5595
|
]);
|
|
4822
5596
|
return this.mapCommandResponse(ids, result);
|
|
4823
5597
|
}
|
|
5598
|
+
async failDistributed(name, ids, outputData, table, db) {
|
|
5599
|
+
return this.withDistributedTransaction(db, async (tx) => {
|
|
5600
|
+
const selectQuery = selectJobsToFailById(this.config.schema, table);
|
|
5601
|
+
const { rows: jobs } = await tx.executeSql(selectQuery.text, [name, ids]);
|
|
5602
|
+
if (jobs.length === 0) return {
|
|
5603
|
+
jobs: ids,
|
|
5604
|
+
requested: ids.length,
|
|
5605
|
+
affected: 0
|
|
5606
|
+
};
|
|
5607
|
+
const deleteQuery = deleteJobsToFail(this.config.schema, table);
|
|
5608
|
+
await tx.executeSql(deleteQuery.text, [name, ids]);
|
|
5609
|
+
const count = await this.reinsertFailedJobs(tx, table, jobs, outputData);
|
|
5610
|
+
return {
|
|
5611
|
+
jobs: ids,
|
|
5612
|
+
requested: ids.length,
|
|
5613
|
+
affected: count
|
|
5614
|
+
};
|
|
5615
|
+
});
|
|
5616
|
+
}
|
|
5617
|
+
async failJobsByTimeoutDistributed(table, queues) {
|
|
5618
|
+
const select = selectJobsToFailByTimeout(this.config.schema, table, queues);
|
|
5619
|
+
return this.expireJobsDistributed(table, select, { value: { message: "job timed out" } });
|
|
5620
|
+
}
|
|
5621
|
+
async failJobsByHeartbeatDistributed(table, queues) {
|
|
5622
|
+
const select = selectJobsToFailByHeartbeat(this.config.schema, table, queues);
|
|
5623
|
+
return this.expireJobsDistributed(table, select, { value: { message: "job heartbeat timeout" } });
|
|
5624
|
+
}
|
|
5625
|
+
async expireJobsDistributed(table, select, outputData) {
|
|
5626
|
+
return this.withDistributedTransaction(this.db, async (tx) => {
|
|
5627
|
+
const { rows: jobs } = await tx.executeSql(select.text, []);
|
|
5628
|
+
if (jobs.length === 0) return 0;
|
|
5629
|
+
const ids = jobs.map((job) => job.id);
|
|
5630
|
+
const deleteSql = deleteJobsByIds(this.config.schema, table);
|
|
5631
|
+
await tx.executeSql(deleteSql.text, [ids]);
|
|
5632
|
+
return this.reinsertFailedJobs(tx, table, jobs, outputData);
|
|
5633
|
+
});
|
|
5634
|
+
}
|
|
5635
|
+
async reinsertFailedJobs(tx, table, jobs, outputData) {
|
|
5636
|
+
const insertSql = insertRetryJob(this.config.schema, table);
|
|
5637
|
+
const dlqSql = insertDeadLetterJob(this.config.schema);
|
|
5638
|
+
let count = 0;
|
|
5639
|
+
for (const job of jobs) {
|
|
5640
|
+
const retryCount = Number(job.retry_count);
|
|
5641
|
+
const retryLimit = Number(job.retry_limit);
|
|
5642
|
+
const retryDelay = Number(job.retry_delay);
|
|
5643
|
+
const retryDelayMax = job.retry_delay_max != null ? Number(job.retry_delay_max) : null;
|
|
5644
|
+
const canRetry = retryCount < retryLimit;
|
|
5645
|
+
let retried = false;
|
|
5646
|
+
if (canRetry) {
|
|
5647
|
+
let startAfter = job.start_after;
|
|
5648
|
+
if (!job.retry_backoff) startAfter = new Date(Date.now() + retryDelay * 1e3);
|
|
5649
|
+
else {
|
|
5650
|
+
const exp = Math.min(16, retryCount + 1);
|
|
5651
|
+
const delay = retryDelay * (Math.pow(2, exp) / 2 + Math.pow(2, exp) / 2 * Math.random());
|
|
5652
|
+
const cappedDelay = retryDelayMax != null ? Math.min(retryDelayMax, delay) : delay;
|
|
5653
|
+
startAfter = new Date(Date.now() + cappedDelay * 1e3);
|
|
5654
|
+
}
|
|
5655
|
+
const { rows } = await tx.executeSql(insertSql, [
|
|
5656
|
+
job.id,
|
|
5657
|
+
job.name,
|
|
5658
|
+
job.priority,
|
|
5659
|
+
job.data,
|
|
5660
|
+
"retry",
|
|
5661
|
+
job.retry_limit,
|
|
5662
|
+
job.retry_count,
|
|
5663
|
+
job.retry_delay,
|
|
5664
|
+
job.retry_backoff,
|
|
5665
|
+
job.retry_delay_max,
|
|
5666
|
+
startAfter,
|
|
5667
|
+
job.started_on,
|
|
5668
|
+
job.singleton_key,
|
|
5669
|
+
job.singleton_on,
|
|
5670
|
+
job.group_id,
|
|
5671
|
+
job.group_tier,
|
|
5672
|
+
job.expire_seconds,
|
|
5673
|
+
job.deletion_seconds,
|
|
5674
|
+
job.created_on,
|
|
5675
|
+
null,
|
|
5676
|
+
job.keep_until,
|
|
5677
|
+
job.policy,
|
|
5678
|
+
outputData,
|
|
5679
|
+
job.dead_letter,
|
|
5680
|
+
null,
|
|
5681
|
+
job.heartbeat_seconds,
|
|
5682
|
+
job.blocked,
|
|
5683
|
+
job.blocking,
|
|
5684
|
+
job.pending_dependencies
|
|
5685
|
+
]);
|
|
5686
|
+
retried = rows.length > 0;
|
|
5687
|
+
}
|
|
5688
|
+
if (!retried) {
|
|
5689
|
+
await tx.executeSql(insertSql, [
|
|
5690
|
+
job.id,
|
|
5691
|
+
job.name,
|
|
5692
|
+
job.priority,
|
|
5693
|
+
job.data,
|
|
5694
|
+
"failed",
|
|
5695
|
+
job.retry_limit,
|
|
5696
|
+
job.retry_count,
|
|
5697
|
+
job.retry_delay,
|
|
5698
|
+
job.retry_backoff,
|
|
5699
|
+
job.retry_delay_max,
|
|
5700
|
+
job.start_after,
|
|
5701
|
+
job.started_on,
|
|
5702
|
+
job.singleton_key,
|
|
5703
|
+
job.singleton_on,
|
|
5704
|
+
job.group_id,
|
|
5705
|
+
job.group_tier,
|
|
5706
|
+
job.expire_seconds,
|
|
5707
|
+
job.deletion_seconds,
|
|
5708
|
+
job.created_on,
|
|
5709
|
+
/* @__PURE__ */ new Date(),
|
|
5710
|
+
job.keep_until,
|
|
5711
|
+
job.policy,
|
|
5712
|
+
outputData,
|
|
5713
|
+
job.dead_letter,
|
|
5714
|
+
null,
|
|
5715
|
+
job.heartbeat_seconds,
|
|
5716
|
+
job.blocked,
|
|
5717
|
+
job.blocking,
|
|
5718
|
+
job.pending_dependencies
|
|
5719
|
+
]);
|
|
5720
|
+
if (job.dead_letter) await tx.executeSql(dlqSql, [
|
|
5721
|
+
job.dead_letter,
|
|
5722
|
+
job.data,
|
|
5723
|
+
outputData
|
|
5724
|
+
]);
|
|
5725
|
+
}
|
|
5726
|
+
count++;
|
|
5727
|
+
}
|
|
5728
|
+
return count;
|
|
5729
|
+
}
|
|
4824
5730
|
async deleteJob(name, id, options = {}) {
|
|
4825
5731
|
assertQueueName(name);
|
|
4826
5732
|
const db = this.assertDb(options);
|
|
@@ -4888,7 +5794,7 @@ var Manager = class extends EventEmitter {
|
|
|
4888
5794
|
const sql = createQueue$1(this.config.schema, name, {
|
|
4889
5795
|
...options,
|
|
4890
5796
|
policy
|
|
4891
|
-
});
|
|
5797
|
+
}, this.config.noAdvisoryLocks);
|
|
4892
5798
|
await this.db.executeSql(sql);
|
|
4893
5799
|
}
|
|
4894
5800
|
async getBlockedKeys(name) {
|
|
@@ -4904,6 +5810,9 @@ var Manager = class extends EventEmitter {
|
|
|
4904
5810
|
if (names) for (const name of names) assertQueueName(name);
|
|
4905
5811
|
const query = getQueues$1(this.config.schema, names);
|
|
4906
5812
|
const { rows } = await this.db.executeSql(query.text, query.values);
|
|
5813
|
+
if (this.config.backend === "cockroachdb") {
|
|
5814
|
+
for (const row of rows) for (const field of NUMERIC_QUEUE_FIELDS) if (row[field] !== void 0 && row[field] !== null) row[field] = Number(row[field]);
|
|
5815
|
+
}
|
|
4907
5816
|
return rows;
|
|
4908
5817
|
}
|
|
4909
5818
|
async updateQueue(name, options = {}) {
|
|
@@ -4921,16 +5830,13 @@ var Manager = class extends EventEmitter {
|
|
|
4921
5830
|
await this.db.executeSql(sql, [name, options]);
|
|
4922
5831
|
}
|
|
4923
5832
|
async getQueue(name) {
|
|
4924
|
-
|
|
4925
|
-
const query = getQueues$1(this.config.schema, [name]);
|
|
4926
|
-
const { rows } = await this.db.executeSql(query.text, query.values);
|
|
4927
|
-
return rows[0] || null;
|
|
5833
|
+
return (await this.getQueues([name]))[0] || null;
|
|
4928
5834
|
}
|
|
4929
5835
|
async deleteQueue(name) {
|
|
4930
5836
|
assertQueueName(name);
|
|
4931
5837
|
try {
|
|
4932
5838
|
await this.getQueueCache(name);
|
|
4933
|
-
const sql = deleteQueue(this.config.schema, name);
|
|
5839
|
+
const sql = deleteQueue(this.config.schema, name, this.config.noAdvisoryLocks);
|
|
4934
5840
|
await this.db.executeSql(sql);
|
|
4935
5841
|
} catch {}
|
|
4936
5842
|
}
|
|
@@ -4967,7 +5873,11 @@ var Manager = class extends EventEmitter {
|
|
|
4967
5873
|
const queue = await this.getQueueCache(name);
|
|
4968
5874
|
const query = getQueueStats$1(this.config.schema, queue.table, [name]);
|
|
4969
5875
|
const { rows } = await this.db.executeSql(query.text, query.values);
|
|
4970
|
-
|
|
5876
|
+
const stats = rows.at(0);
|
|
5877
|
+
if (stats && this.config.backend === "cockroachdb") {
|
|
5878
|
+
for (const field of NUMERIC_QUEUE_FIELDS) if (stats[field] !== void 0 && stats[field] !== null) stats[field] = Number(stats[field]);
|
|
5879
|
+
}
|
|
5880
|
+
return Object.assign(queue, stats || {
|
|
4971
5881
|
deferredCount: 0,
|
|
4972
5882
|
queuedCount: 0,
|
|
4973
5883
|
activeCount: 0,
|
|
@@ -4980,8 +5890,13 @@ var Manager = class extends EventEmitter {
|
|
|
4980
5890
|
const { table } = await this.getQueueCache(name);
|
|
4981
5891
|
const sql = getJobById$1(this.config.schema, table);
|
|
4982
5892
|
const result1 = await db.executeSql(sql, [name, id]);
|
|
4983
|
-
if (result1?.rows?.length === 1)
|
|
4984
|
-
|
|
5893
|
+
if (result1?.rows?.length === 1) {
|
|
5894
|
+
const row = result1.rows[0];
|
|
5895
|
+
if (this.config.backend === "cockroachdb") {
|
|
5896
|
+
for (const field of NUMERIC_METADATA_FIELDS) if (row[field] !== void 0 && row[field] !== null) row[field] = Number(row[field]);
|
|
5897
|
+
}
|
|
5898
|
+
return row;
|
|
5899
|
+
} else return null;
|
|
4985
5900
|
}
|
|
4986
5901
|
async findJobs(name, options = {}) {
|
|
4987
5902
|
assertQueueName(name);
|
|
@@ -5000,6 +5915,26 @@ var Manager = class extends EventEmitter {
|
|
|
5000
5915
|
if (data !== void 0) values.push(JSON.stringify(data));
|
|
5001
5916
|
return (await db.executeSql(sql, values))?.rows || [];
|
|
5002
5917
|
}
|
|
5918
|
+
async getDependencies(name, id, options = {}) {
|
|
5919
|
+
assertQueueName(name);
|
|
5920
|
+
const db = this.assertDb(options);
|
|
5921
|
+
const sql = getDependencies(this.config.schema);
|
|
5922
|
+
const { rows } = await db.executeSql(sql, [name, id]);
|
|
5923
|
+
return rows.map((r) => ({
|
|
5924
|
+
name: r.parentName,
|
|
5925
|
+
id: r.parentId
|
|
5926
|
+
}));
|
|
5927
|
+
}
|
|
5928
|
+
async getDependents(name, id, options = {}) {
|
|
5929
|
+
assertQueueName(name);
|
|
5930
|
+
const db = this.assertDb(options);
|
|
5931
|
+
const sql = getDependents(this.config.schema);
|
|
5932
|
+
const { rows } = await db.executeSql(sql, [name, id]);
|
|
5933
|
+
return rows.map((r) => ({
|
|
5934
|
+
name: r.childName,
|
|
5935
|
+
id: r.childId
|
|
5936
|
+
}));
|
|
5937
|
+
}
|
|
5003
5938
|
assertDb(options) {
|
|
5004
5939
|
if (options.db) return options.db;
|
|
5005
5940
|
if (this.db._pgbdb) assert(this.db.opened, "Database connection is not opened");
|
|
@@ -5142,17 +6077,21 @@ var Boss = class extends EventEmitter {
|
|
|
5142
6077
|
if (this.#stopping) return;
|
|
5143
6078
|
if (rows.length) {
|
|
5144
6079
|
const queues = rows.map((q) => q.name);
|
|
5145
|
-
const cacheStatsSql = cacheQueueStats(this.#config.schema, table, queues);
|
|
6080
|
+
const cacheStatsSql = cacheQueueStats(this.#config.schema, table, queues, this.#config.noAdvisoryLocks);
|
|
5146
6081
|
const { rows: rowsCacheStats } = await this.#executeQuery(cacheStatsSql);
|
|
5147
6082
|
if (this.#stopping) return;
|
|
5148
|
-
const warnings = rowsCacheStats.filter((i) => i.queuedCount > (i.warningQueueSize || WARNINGS.LARGE_QUEUE.size));
|
|
6083
|
+
const warnings = rowsCacheStats.filter((i) => Number(i.queuedCount) > (Number(i.warningQueueSize) || WARNINGS.LARGE_QUEUE.size));
|
|
5149
6084
|
for (const warning of warnings) await emitAndPersistWarning(this.#warningContext, WARNING_TYPES.QUEUE_BACKLOG, WARNINGS.LARGE_QUEUE.message, warning);
|
|
5150
|
-
|
|
5151
|
-
|
|
6085
|
+
if (this.#config.noMultiMutationCte) await this.#manager.failJobsByTimeoutDistributed(table, queues);
|
|
6086
|
+
else {
|
|
6087
|
+
const sql = failJobsByTimeout(this.#config.schema, table, queues, this.#config.noAdvisoryLocks);
|
|
6088
|
+
await this.#executeQuery(sql);
|
|
6089
|
+
}
|
|
5152
6090
|
if (this.#stopping) return;
|
|
5153
6091
|
const heartbeatQueues = queues.filter((q) => heartbeatQueueNames.has(q));
|
|
5154
|
-
if (heartbeatQueues.length)
|
|
5155
|
-
|
|
6092
|
+
if (heartbeatQueues.length) if (this.#config.noMultiMutationCte) await this.#manager.failJobsByHeartbeatDistributed(table, heartbeatQueues);
|
|
6093
|
+
else {
|
|
6094
|
+
const heartbeatSql = failJobsByHeartbeat(this.#config.schema, table, heartbeatQueues, this.#config.noAdvisoryLocks);
|
|
5156
6095
|
await this.#executeQuery(heartbeatSql);
|
|
5157
6096
|
}
|
|
5158
6097
|
}
|
|
@@ -5164,8 +6103,10 @@ var Boss = class extends EventEmitter {
|
|
|
5164
6103
|
if (this.#stopping) return;
|
|
5165
6104
|
if (rows.length) {
|
|
5166
6105
|
const queues = rows.map((q) => q.name);
|
|
5167
|
-
const sql = deletion(this.#config.schema, table, queues);
|
|
6106
|
+
const sql = deletion(this.#config.schema, table, queues, this.#config.noAdvisoryLocks);
|
|
5168
6107
|
await this.#executeQuery(sql);
|
|
6108
|
+
const depSql = cleanupDependencies(this.#config.schema, table, queues, this.#config.noAdvisoryLocks);
|
|
6109
|
+
await this.#executeQuery(depSql);
|
|
5169
6110
|
}
|
|
5170
6111
|
}
|
|
5171
6112
|
};
|
|
@@ -5304,6 +6245,21 @@ var Db = class extends EventEmitter {
|
|
|
5304
6245
|
assert(this.opened, "Database not opened. Call open() before executing SQL.");
|
|
5305
6246
|
return await this.pool.query(text, values);
|
|
5306
6247
|
}
|
|
6248
|
+
async withTransaction(fn) {
|
|
6249
|
+
assert(this.opened, "Database not opened. Call open() before executing SQL.");
|
|
6250
|
+
const client = await this.pool.connect();
|
|
6251
|
+
try {
|
|
6252
|
+
await client.query("BEGIN");
|
|
6253
|
+
const result = await fn({ executeSql: (text, values) => client.query(text, values) });
|
|
6254
|
+
await client.query("COMMIT");
|
|
6255
|
+
return result;
|
|
6256
|
+
} catch (err) {
|
|
6257
|
+
await client.query("ROLLBACK");
|
|
6258
|
+
throw err;
|
|
6259
|
+
} finally {
|
|
6260
|
+
client.release();
|
|
6261
|
+
}
|
|
6262
|
+
}
|
|
5307
6263
|
};
|
|
5308
6264
|
//#endregion
|
|
5309
6265
|
//#region ../../src/index.ts
|
|
@@ -5357,18 +6313,36 @@ var PgBoss = class extends EventEmitter {
|
|
|
5357
6313
|
async start() {
|
|
5358
6314
|
if (this.#starting || this.#started) return this;
|
|
5359
6315
|
this.#starting = true;
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
6316
|
+
try {
|
|
6317
|
+
if (this.#db._pgbdb && !this.#db.opened) await this.#db.open();
|
|
6318
|
+
await this.#warnIfDistributedMisconfigured();
|
|
6319
|
+
if (this.#config.migrate) await this.#contractor.start();
|
|
6320
|
+
else await this.#contractor.check();
|
|
6321
|
+
await this.#manager.start();
|
|
6322
|
+
if (this.#config.supervise) await this.#boss.start();
|
|
6323
|
+
if (this.#config.schedule) await this.#timekeeper.start();
|
|
6324
|
+
if (this.#config.migrate) await this.#bam.start();
|
|
6325
|
+
} catch (err) {
|
|
6326
|
+
this.#starting = false;
|
|
6327
|
+
throw err;
|
|
6328
|
+
}
|
|
5367
6329
|
this.#starting = false;
|
|
5368
6330
|
this.#started = true;
|
|
5369
6331
|
this.#stopped = false;
|
|
5370
6332
|
return this;
|
|
5371
6333
|
}
|
|
6334
|
+
async #warnIfDistributedMisconfigured() {
|
|
6335
|
+
try {
|
|
6336
|
+
const { rows } = await this.#db.executeSql("SELECT version()");
|
|
6337
|
+
const version = rows?.[0]?.version || "";
|
|
6338
|
+
if (/yugabyte|-yb-/i.test(version)) {
|
|
6339
|
+
if (!this.#config.noTablePartitioning || !this.#config.noAdvisoryLocks) this.emit(events.warning, {
|
|
6340
|
+
message: "YugabyteDB detected: set backend: 'yugabytedb' for compatibility. Partitioned queues (partition: true) are not supported on YugabyteDB.",
|
|
6341
|
+
data: { backend: "yugabytedb" }
|
|
6342
|
+
});
|
|
6343
|
+
}
|
|
6344
|
+
} catch {}
|
|
6345
|
+
}
|
|
5372
6346
|
async stop(options = {}) {
|
|
5373
6347
|
if (this.#stoppingOn || this.#stopped) return;
|
|
5374
6348
|
let { close = true, graceful = true, timeout = 3e4 } = options;
|
|
@@ -5408,6 +6382,9 @@ var PgBoss = class extends EventEmitter {
|
|
|
5408
6382
|
insert(name, jobs, options) {
|
|
5409
6383
|
return this.#manager.insert(name, jobs, options);
|
|
5410
6384
|
}
|
|
6385
|
+
flow(jobs, options) {
|
|
6386
|
+
return this.#manager.flow(jobs, options);
|
|
6387
|
+
}
|
|
5411
6388
|
fetch(name, options = {}) {
|
|
5412
6389
|
return this.#manager.fetch(name, options);
|
|
5413
6390
|
}
|
|
@@ -5474,6 +6451,12 @@ var PgBoss = class extends EventEmitter {
|
|
|
5474
6451
|
getBlockedKeys(name) {
|
|
5475
6452
|
return this.#manager.getBlockedKeys(name);
|
|
5476
6453
|
}
|
|
6454
|
+
getDependencies(name, id, options) {
|
|
6455
|
+
return this.#manager.getDependencies(name, id, options);
|
|
6456
|
+
}
|
|
6457
|
+
getDependents(name, id, options) {
|
|
6458
|
+
return this.#manager.getDependents(name, id, options);
|
|
6459
|
+
}
|
|
5477
6460
|
updateQueue(name, options) {
|
|
5478
6461
|
return this.#manager.updateQueue(name, options);
|
|
5479
6462
|
}
|
|
@@ -9905,8 +10888,8 @@ function WarningTypeBadge({ type }) {
|
|
|
9905
10888
|
//#region \0virtual:react-router/server-manifest
|
|
9906
10889
|
var server_manifest_default = {
|
|
9907
10890
|
"entry": {
|
|
9908
|
-
"module": "/assets/entry.client-
|
|
9909
|
-
"imports": ["/assets/jsx-runtime-
|
|
10891
|
+
"module": "/assets/entry.client-DL_oPh96.js",
|
|
10892
|
+
"imports": ["/assets/jsx-runtime-BgbGXvsu.js", "/assets/react-dom-QnGHOQwT.js"],
|
|
9910
10893
|
"css": []
|
|
9911
10894
|
},
|
|
9912
10895
|
"routes": {
|
|
@@ -9923,16 +10906,16 @@ var server_manifest_default = {
|
|
|
9923
10906
|
"hasClientMiddleware": false,
|
|
9924
10907
|
"hasDefaultExport": true,
|
|
9925
10908
|
"hasErrorBoundary": true,
|
|
9926
|
-
"module": "/assets/root-
|
|
10909
|
+
"module": "/assets/root-Df70GAY3.js",
|
|
9927
10910
|
"imports": [
|
|
9928
|
-
"/assets/jsx-runtime-
|
|
9929
|
-
"/assets/react-dom-
|
|
9930
|
-
"/assets/db-link-
|
|
9931
|
-
"/assets/MenuTrigger-
|
|
9932
|
-
"/assets/createLucideIcon-
|
|
9933
|
-
"/assets/useOpenInteractionType-
|
|
10911
|
+
"/assets/jsx-runtime-BgbGXvsu.js",
|
|
10912
|
+
"/assets/react-dom-QnGHOQwT.js",
|
|
10913
|
+
"/assets/db-link-BWWnHM0k.js",
|
|
10914
|
+
"/assets/MenuTrigger-BhalG0aG.js",
|
|
10915
|
+
"/assets/createLucideIcon-DVP_i62f.js",
|
|
10916
|
+
"/assets/useOpenInteractionType-D3JsvupP.js"
|
|
9934
10917
|
],
|
|
9935
|
-
"css": ["/assets/root-
|
|
10918
|
+
"css": ["/assets/root-C0MdPLOa.css"],
|
|
9936
10919
|
"clientActionModule": void 0,
|
|
9937
10920
|
"clientLoaderModule": void 0,
|
|
9938
10921
|
"clientMiddlewareModule": void 0,
|
|
@@ -9951,15 +10934,15 @@ var server_manifest_default = {
|
|
|
9951
10934
|
"hasClientMiddleware": false,
|
|
9952
10935
|
"hasDefaultExport": true,
|
|
9953
10936
|
"hasErrorBoundary": true,
|
|
9954
|
-
"module": "/assets/_index-
|
|
10937
|
+
"module": "/assets/_index-D1-nZ7Th.js",
|
|
9955
10938
|
"imports": [
|
|
9956
|
-
"/assets/jsx-runtime-
|
|
9957
|
-
"/assets/db-link-
|
|
9958
|
-
"/assets/error-card-
|
|
9959
|
-
"/assets/badge-
|
|
9960
|
-
"/assets/button-
|
|
9961
|
-
"/assets/stat-card-
|
|
9962
|
-
"/assets/table-
|
|
10939
|
+
"/assets/jsx-runtime-BgbGXvsu.js",
|
|
10940
|
+
"/assets/db-link-BWWnHM0k.js",
|
|
10941
|
+
"/assets/error-card-B0ANyjh3.js",
|
|
10942
|
+
"/assets/badge-DCQvSdiR.js",
|
|
10943
|
+
"/assets/button-BxLcuaPM.js",
|
|
10944
|
+
"/assets/stat-card-DLtQnscf.js",
|
|
10945
|
+
"/assets/table-DqqzSNik.js"
|
|
9963
10946
|
],
|
|
9964
10947
|
"css": [],
|
|
9965
10948
|
"clientActionModule": void 0,
|
|
@@ -9980,24 +10963,24 @@ var server_manifest_default = {
|
|
|
9980
10963
|
"hasClientMiddleware": false,
|
|
9981
10964
|
"hasDefaultExport": true,
|
|
9982
10965
|
"hasErrorBoundary": true,
|
|
9983
|
-
"module": "/assets/jobs-
|
|
10966
|
+
"module": "/assets/jobs-D0a6Lwq0.js",
|
|
9984
10967
|
"imports": [
|
|
9985
|
-
"/assets/jsx-runtime-
|
|
9986
|
-
"/assets/db-link-
|
|
9987
|
-
"/assets/error-card-
|
|
9988
|
-
"/assets/badge-
|
|
9989
|
-
"/assets/button-
|
|
9990
|
-
"/assets/filter-select
|
|
9991
|
-
"/assets/pagination-
|
|
9992
|
-
"/assets/table-
|
|
9993
|
-
"/assets/MenuTrigger-
|
|
9994
|
-
"/assets/useOpenInteractionType-
|
|
9995
|
-
"/assets/createLucideIcon-
|
|
9996
|
-
"/assets/check-
|
|
9997
|
-
"/assets/chevron-down-
|
|
9998
|
-
"/assets/chevron-right-
|
|
9999
|
-
"/assets/x-
|
|
10000
|
-
"/assets/react-dom-
|
|
10968
|
+
"/assets/jsx-runtime-BgbGXvsu.js",
|
|
10969
|
+
"/assets/db-link-BWWnHM0k.js",
|
|
10970
|
+
"/assets/error-card-B0ANyjh3.js",
|
|
10971
|
+
"/assets/badge-DCQvSdiR.js",
|
|
10972
|
+
"/assets/button-BxLcuaPM.js",
|
|
10973
|
+
"/assets/filter-select--qLjbs9m.js",
|
|
10974
|
+
"/assets/pagination-Bzx8wbXG.js",
|
|
10975
|
+
"/assets/table-DqqzSNik.js",
|
|
10976
|
+
"/assets/MenuTrigger-BhalG0aG.js",
|
|
10977
|
+
"/assets/useOpenInteractionType-D3JsvupP.js",
|
|
10978
|
+
"/assets/createLucideIcon-DVP_i62f.js",
|
|
10979
|
+
"/assets/check-Ch42cXMT.js",
|
|
10980
|
+
"/assets/chevron-down-Byq-CYG9.js",
|
|
10981
|
+
"/assets/chevron-right-CKAGD7DJ.js",
|
|
10982
|
+
"/assets/x-BPKZwOn9.js",
|
|
10983
|
+
"/assets/react-dom-QnGHOQwT.js"
|
|
10001
10984
|
],
|
|
10002
10985
|
"css": [],
|
|
10003
10986
|
"clientActionModule": void 0,
|
|
@@ -10018,14 +11001,14 @@ var server_manifest_default = {
|
|
|
10018
11001
|
"hasClientMiddleware": false,
|
|
10019
11002
|
"hasDefaultExport": true,
|
|
10020
11003
|
"hasErrorBoundary": true,
|
|
10021
|
-
"module": "/assets/queues._index-
|
|
11004
|
+
"module": "/assets/queues._index-D8903DTa.js",
|
|
10022
11005
|
"imports": [
|
|
10023
|
-
"/assets/jsx-runtime-
|
|
10024
|
-
"/assets/db-link-
|
|
10025
|
-
"/assets/badge-
|
|
10026
|
-
"/assets/button-
|
|
10027
|
-
"/assets/filter-select
|
|
10028
|
-
"/assets/table-
|
|
11006
|
+
"/assets/jsx-runtime-BgbGXvsu.js",
|
|
11007
|
+
"/assets/db-link-BWWnHM0k.js",
|
|
11008
|
+
"/assets/badge-DCQvSdiR.js",
|
|
11009
|
+
"/assets/button-BxLcuaPM.js",
|
|
11010
|
+
"/assets/filter-select--qLjbs9m.js",
|
|
11011
|
+
"/assets/table-DqqzSNik.js"
|
|
10029
11012
|
],
|
|
10030
11013
|
"css": [],
|
|
10031
11014
|
"clientActionModule": void 0,
|
|
@@ -10046,14 +11029,14 @@ var server_manifest_default = {
|
|
|
10046
11029
|
"hasClientMiddleware": false,
|
|
10047
11030
|
"hasDefaultExport": true,
|
|
10048
11031
|
"hasErrorBoundary": true,
|
|
10049
|
-
"module": "/assets/queues.create-
|
|
11032
|
+
"module": "/assets/queues.create-CMqQVLup.js",
|
|
10050
11033
|
"imports": [
|
|
10051
|
-
"/assets/jsx-runtime-
|
|
10052
|
-
"/assets/db-link-
|
|
10053
|
-
"/assets/error-card-
|
|
10054
|
-
"/assets/button-
|
|
10055
|
-
"/assets/chevron-down-
|
|
10056
|
-
"/assets/createLucideIcon-
|
|
11034
|
+
"/assets/jsx-runtime-BgbGXvsu.js",
|
|
11035
|
+
"/assets/db-link-BWWnHM0k.js",
|
|
11036
|
+
"/assets/error-card-B0ANyjh3.js",
|
|
11037
|
+
"/assets/button-BxLcuaPM.js",
|
|
11038
|
+
"/assets/chevron-down-Byq-CYG9.js",
|
|
11039
|
+
"/assets/createLucideIcon-DVP_i62f.js"
|
|
10057
11040
|
],
|
|
10058
11041
|
"css": [],
|
|
10059
11042
|
"clientActionModule": void 0,
|
|
@@ -10074,25 +11057,25 @@ var server_manifest_default = {
|
|
|
10074
11057
|
"hasClientMiddleware": false,
|
|
10075
11058
|
"hasDefaultExport": true,
|
|
10076
11059
|
"hasErrorBoundary": true,
|
|
10077
|
-
"module": "/assets/queues._name-
|
|
11060
|
+
"module": "/assets/queues._name-BVt_4pav.js",
|
|
10078
11061
|
"imports": [
|
|
10079
|
-
"/assets/jsx-runtime-
|
|
10080
|
-
"/assets/db-link-
|
|
10081
|
-
"/assets/error-card-
|
|
10082
|
-
"/assets/badge-
|
|
10083
|
-
"/assets/button-
|
|
10084
|
-
"/assets/dialog-
|
|
10085
|
-
"/assets/filter-select
|
|
10086
|
-
"/assets/pagination-
|
|
10087
|
-
"/assets/stat-card-
|
|
10088
|
-
"/assets/table-
|
|
10089
|
-
"/assets/MenuTrigger-
|
|
10090
|
-
"/assets/createLucideIcon-
|
|
10091
|
-
"/assets/chevron-down-
|
|
10092
|
-
"/assets/chevron-right-
|
|
10093
|
-
"/assets/useOpenInteractionType-
|
|
10094
|
-
"/assets/x-
|
|
10095
|
-
"/assets/react-dom-
|
|
11062
|
+
"/assets/jsx-runtime-BgbGXvsu.js",
|
|
11063
|
+
"/assets/db-link-BWWnHM0k.js",
|
|
11064
|
+
"/assets/error-card-B0ANyjh3.js",
|
|
11065
|
+
"/assets/badge-DCQvSdiR.js",
|
|
11066
|
+
"/assets/button-BxLcuaPM.js",
|
|
11067
|
+
"/assets/dialog-Bik519zD.js",
|
|
11068
|
+
"/assets/filter-select--qLjbs9m.js",
|
|
11069
|
+
"/assets/pagination-Bzx8wbXG.js",
|
|
11070
|
+
"/assets/stat-card-DLtQnscf.js",
|
|
11071
|
+
"/assets/table-DqqzSNik.js",
|
|
11072
|
+
"/assets/MenuTrigger-BhalG0aG.js",
|
|
11073
|
+
"/assets/createLucideIcon-DVP_i62f.js",
|
|
11074
|
+
"/assets/chevron-down-Byq-CYG9.js",
|
|
11075
|
+
"/assets/chevron-right-CKAGD7DJ.js",
|
|
11076
|
+
"/assets/useOpenInteractionType-D3JsvupP.js",
|
|
11077
|
+
"/assets/x-BPKZwOn9.js",
|
|
11078
|
+
"/assets/react-dom-QnGHOQwT.js"
|
|
10096
11079
|
],
|
|
10097
11080
|
"css": [],
|
|
10098
11081
|
"clientActionModule": void 0,
|
|
@@ -10113,19 +11096,19 @@ var server_manifest_default = {
|
|
|
10113
11096
|
"hasClientMiddleware": false,
|
|
10114
11097
|
"hasDefaultExport": true,
|
|
10115
11098
|
"hasErrorBoundary": true,
|
|
10116
|
-
"module": "/assets/queues._name.jobs._jobId-
|
|
11099
|
+
"module": "/assets/queues._name.jobs._jobId-BkG9y75k.js",
|
|
10117
11100
|
"imports": [
|
|
10118
|
-
"/assets/jsx-runtime-
|
|
10119
|
-
"/assets/db-link-
|
|
10120
|
-
"/assets/error-card-
|
|
10121
|
-
"/assets/badge-
|
|
10122
|
-
"/assets/button-
|
|
10123
|
-
"/assets/dialog-
|
|
10124
|
-
"/assets/createLucideIcon-
|
|
10125
|
-
"/assets/check-
|
|
10126
|
-
"/assets/useOpenInteractionType-
|
|
10127
|
-
"/assets/x-
|
|
10128
|
-
"/assets/react-dom-
|
|
11101
|
+
"/assets/jsx-runtime-BgbGXvsu.js",
|
|
11102
|
+
"/assets/db-link-BWWnHM0k.js",
|
|
11103
|
+
"/assets/error-card-B0ANyjh3.js",
|
|
11104
|
+
"/assets/badge-DCQvSdiR.js",
|
|
11105
|
+
"/assets/button-BxLcuaPM.js",
|
|
11106
|
+
"/assets/dialog-Bik519zD.js",
|
|
11107
|
+
"/assets/createLucideIcon-DVP_i62f.js",
|
|
11108
|
+
"/assets/check-Ch42cXMT.js",
|
|
11109
|
+
"/assets/useOpenInteractionType-D3JsvupP.js",
|
|
11110
|
+
"/assets/x-BPKZwOn9.js",
|
|
11111
|
+
"/assets/react-dom-QnGHOQwT.js"
|
|
10129
11112
|
],
|
|
10130
11113
|
"css": [],
|
|
10131
11114
|
"clientActionModule": void 0,
|
|
@@ -10146,15 +11129,15 @@ var server_manifest_default = {
|
|
|
10146
11129
|
"hasClientMiddleware": false,
|
|
10147
11130
|
"hasDefaultExport": true,
|
|
10148
11131
|
"hasErrorBoundary": true,
|
|
10149
|
-
"module": "/assets/schedules-
|
|
11132
|
+
"module": "/assets/schedules-DPXQoaEE.js",
|
|
10150
11133
|
"imports": [
|
|
10151
|
-
"/assets/jsx-runtime-
|
|
10152
|
-
"/assets/db-link-
|
|
10153
|
-
"/assets/error-card-
|
|
10154
|
-
"/assets/badge-
|
|
10155
|
-
"/assets/button-
|
|
10156
|
-
"/assets/pagination-
|
|
10157
|
-
"/assets/table-
|
|
11134
|
+
"/assets/jsx-runtime-BgbGXvsu.js",
|
|
11135
|
+
"/assets/db-link-BWWnHM0k.js",
|
|
11136
|
+
"/assets/error-card-B0ANyjh3.js",
|
|
11137
|
+
"/assets/badge-DCQvSdiR.js",
|
|
11138
|
+
"/assets/button-BxLcuaPM.js",
|
|
11139
|
+
"/assets/pagination-Bzx8wbXG.js",
|
|
11140
|
+
"/assets/table-DqqzSNik.js"
|
|
10158
11141
|
],
|
|
10159
11142
|
"css": [],
|
|
10160
11143
|
"clientActionModule": void 0,
|
|
@@ -10175,17 +11158,17 @@ var server_manifest_default = {
|
|
|
10175
11158
|
"hasClientMiddleware": false,
|
|
10176
11159
|
"hasDefaultExport": true,
|
|
10177
11160
|
"hasErrorBoundary": true,
|
|
10178
|
-
"module": "/assets/schedules._name._key-
|
|
11161
|
+
"module": "/assets/schedules._name._key-B_luxy1w.js",
|
|
10179
11162
|
"imports": [
|
|
10180
|
-
"/assets/jsx-runtime-
|
|
10181
|
-
"/assets/db-link-
|
|
10182
|
-
"/assets/error-card-
|
|
10183
|
-
"/assets/button-
|
|
10184
|
-
"/assets/dialog-
|
|
10185
|
-
"/assets/useOpenInteractionType-
|
|
10186
|
-
"/assets/x-
|
|
10187
|
-
"/assets/react-dom-
|
|
10188
|
-
"/assets/createLucideIcon-
|
|
11163
|
+
"/assets/jsx-runtime-BgbGXvsu.js",
|
|
11164
|
+
"/assets/db-link-BWWnHM0k.js",
|
|
11165
|
+
"/assets/error-card-B0ANyjh3.js",
|
|
11166
|
+
"/assets/button-BxLcuaPM.js",
|
|
11167
|
+
"/assets/dialog-Bik519zD.js",
|
|
11168
|
+
"/assets/useOpenInteractionType-D3JsvupP.js",
|
|
11169
|
+
"/assets/x-BPKZwOn9.js",
|
|
11170
|
+
"/assets/react-dom-QnGHOQwT.js",
|
|
11171
|
+
"/assets/createLucideIcon-DVP_i62f.js"
|
|
10189
11172
|
],
|
|
10190
11173
|
"css": [],
|
|
10191
11174
|
"clientActionModule": void 0,
|
|
@@ -10206,12 +11189,12 @@ var server_manifest_default = {
|
|
|
10206
11189
|
"hasClientMiddleware": false,
|
|
10207
11190
|
"hasDefaultExport": true,
|
|
10208
11191
|
"hasErrorBoundary": true,
|
|
10209
|
-
"module": "/assets/schedules.new-
|
|
11192
|
+
"module": "/assets/schedules.new-BQV7GWzs.js",
|
|
10210
11193
|
"imports": [
|
|
10211
|
-
"/assets/jsx-runtime-
|
|
10212
|
-
"/assets/db-link-
|
|
10213
|
-
"/assets/error-card-
|
|
10214
|
-
"/assets/button-
|
|
11194
|
+
"/assets/jsx-runtime-BgbGXvsu.js",
|
|
11195
|
+
"/assets/db-link-BWWnHM0k.js",
|
|
11196
|
+
"/assets/error-card-B0ANyjh3.js",
|
|
11197
|
+
"/assets/button-BxLcuaPM.js"
|
|
10215
11198
|
],
|
|
10216
11199
|
"css": [],
|
|
10217
11200
|
"clientActionModule": void 0,
|
|
@@ -10232,12 +11215,12 @@ var server_manifest_default = {
|
|
|
10232
11215
|
"hasClientMiddleware": false,
|
|
10233
11216
|
"hasDefaultExport": true,
|
|
10234
11217
|
"hasErrorBoundary": true,
|
|
10235
|
-
"module": "/assets/send-
|
|
11218
|
+
"module": "/assets/send-DJBsfnx_.js",
|
|
10236
11219
|
"imports": [
|
|
10237
|
-
"/assets/jsx-runtime-
|
|
10238
|
-
"/assets/db-link-
|
|
10239
|
-
"/assets/error-card-
|
|
10240
|
-
"/assets/button-
|
|
11220
|
+
"/assets/jsx-runtime-BgbGXvsu.js",
|
|
11221
|
+
"/assets/db-link-BWWnHM0k.js",
|
|
11222
|
+
"/assets/error-card-B0ANyjh3.js",
|
|
11223
|
+
"/assets/button-BxLcuaPM.js"
|
|
10241
11224
|
],
|
|
10242
11225
|
"css": [],
|
|
10243
11226
|
"clientActionModule": void 0,
|
|
@@ -10258,16 +11241,16 @@ var server_manifest_default = {
|
|
|
10258
11241
|
"hasClientMiddleware": false,
|
|
10259
11242
|
"hasDefaultExport": true,
|
|
10260
11243
|
"hasErrorBoundary": true,
|
|
10261
|
-
"module": "/assets/warnings-
|
|
11244
|
+
"module": "/assets/warnings-CHKaRfIW.js",
|
|
10262
11245
|
"imports": [
|
|
10263
|
-
"/assets/jsx-runtime-
|
|
10264
|
-
"/assets/db-link-
|
|
10265
|
-
"/assets/error-card-
|
|
10266
|
-
"/assets/badge-
|
|
10267
|
-
"/assets/button-
|
|
10268
|
-
"/assets/filter-select
|
|
10269
|
-
"/assets/pagination-
|
|
10270
|
-
"/assets/table-
|
|
11246
|
+
"/assets/jsx-runtime-BgbGXvsu.js",
|
|
11247
|
+
"/assets/db-link-BWWnHM0k.js",
|
|
11248
|
+
"/assets/error-card-B0ANyjh3.js",
|
|
11249
|
+
"/assets/badge-DCQvSdiR.js",
|
|
11250
|
+
"/assets/button-BxLcuaPM.js",
|
|
11251
|
+
"/assets/filter-select--qLjbs9m.js",
|
|
11252
|
+
"/assets/pagination-Bzx8wbXG.js",
|
|
11253
|
+
"/assets/table-DqqzSNik.js"
|
|
10271
11254
|
],
|
|
10272
11255
|
"css": [],
|
|
10273
11256
|
"clientActionModule": void 0,
|
|
@@ -10276,23 +11259,15 @@ var server_manifest_default = {
|
|
|
10276
11259
|
"hydrateFallbackModule": void 0
|
|
10277
11260
|
}
|
|
10278
11261
|
},
|
|
10279
|
-
"url": "/assets/manifest-
|
|
10280
|
-
"version": "
|
|
11262
|
+
"url": "/assets/manifest-ef81a0f9.js",
|
|
11263
|
+
"version": "ef81a0f9",
|
|
10281
11264
|
"sri": void 0
|
|
10282
11265
|
};
|
|
10283
11266
|
//#endregion
|
|
10284
11267
|
//#region \0virtual:react-router/server-build
|
|
10285
11268
|
var assetsBuildDirectory = "build/client";
|
|
10286
11269
|
var basename = "/";
|
|
10287
|
-
var future = {
|
|
10288
|
-
"unstable_optimizeDeps": false,
|
|
10289
|
-
"v8_passThroughRequests": true,
|
|
10290
|
-
"v8_trailingSlashAwareDataRequests": true,
|
|
10291
|
-
"unstable_previewServerPrerendering": false,
|
|
10292
|
-
"v8_middleware": true,
|
|
10293
|
-
"v8_splitRouteModules": true,
|
|
10294
|
-
"v8_viteEnvironmentApi": true
|
|
10295
|
-
};
|
|
11270
|
+
var future = { "unstable_optimizeDeps": false };
|
|
10296
11271
|
var ssr = true;
|
|
10297
11272
|
var isSpaMode = false;
|
|
10298
11273
|
var prerender = [];
|