@dbos-inc/dbos-sdk 5.1.5-preview → 5.1.8-preview
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/schemas/system_db_schema.d.ts +1 -0
- package/dist/schemas/system_db_schema.d.ts.map +1 -1
- package/dist/schemas/system_db_schema.js.map +1 -1
- package/dist/src/client.d.ts +17 -0
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +20 -4
- package/dist/src/client.js.map +1 -1
- package/dist/src/conductor/conductor.d.ts.map +1 -1
- package/dist/src/conductor/conductor.js +18 -0
- package/dist/src/conductor/conductor.js.map +1 -1
- package/dist/src/conductor/protocol.d.ts +17 -1
- package/dist/src/conductor/protocol.d.ts.map +1 -1
- package/dist/src/conductor/protocol.js +10 -1
- package/dist/src/conductor/protocol.js.map +1 -1
- package/dist/src/context.d.ts +12 -1
- package/dist/src/context.d.ts.map +1 -1
- package/dist/src/context.js +5 -1
- package/dist/src/context.js.map +1 -1
- package/dist/src/datasource.d.ts +6 -0
- package/dist/src/datasource.d.ts.map +1 -1
- package/dist/src/datasource.js.map +1 -1
- package/dist/src/dbos-executor.d.ts +10 -0
- package/dist/src/dbos-executor.d.ts.map +1 -1
- package/dist/src/dbos-executor.js +87 -55
- package/dist/src/dbos-executor.js.map +1 -1
- package/dist/src/dbos.d.ts +27 -1
- package/dist/src/dbos.d.ts.map +1 -1
- package/dist/src/dbos.js +29 -0
- package/dist/src/dbos.js.map +1 -1
- package/dist/src/debouncer.d.ts.map +1 -1
- package/dist/src/debouncer.js +3 -0
- package/dist/src/debouncer.js.map +1 -1
- package/dist/src/enqueue_options.d.ts +3 -1
- package/dist/src/enqueue_options.d.ts.map +1 -1
- package/dist/src/enqueue_options.js.map +1 -1
- package/dist/src/enqueue_workflow.d.ts.map +1 -1
- package/dist/src/enqueue_workflow.js +11 -6
- package/dist/src/enqueue_workflow.js.map +1 -1
- package/dist/src/error.d.ts +10 -0
- package/dist/src/error.d.ts.map +1 -1
- package/dist/src/error.js +25 -1
- package/dist/src/error.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/step.d.ts +1 -0
- package/dist/src/step.d.ts.map +1 -1
- package/dist/src/step.js.map +1 -1
- package/dist/src/sysdb_migrations/internal/migrations.js +6 -0
- package/dist/src/sysdb_migrations/internal/migrations.js.map +1 -1
- package/dist/src/system_database.d.ts +41 -2
- package/dist/src/system_database.d.ts.map +1 -1
- package/dist/src/system_database.js +231 -17
- package/dist/src/system_database.js.map +1 -1
- package/dist/src/workflow.d.ts +3 -0
- package/dist/src/workflow.d.ts.map +1 -1
- package/dist/src/workflow.js.map +1 -1
- package/dist/src/workflow_management.d.ts +15 -0
- package/dist/src/workflow_management.d.ts.map +1 -1
- package/dist/src/workflow_management.js +45 -1
- package/dist/src/workflow_management.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -15,6 +15,7 @@ const pg_1 = require("pg");
|
|
|
15
15
|
const error_1 = require("./error");
|
|
16
16
|
const workflow_1 = require("./workflow");
|
|
17
17
|
const utils_1 = require("./utils");
|
|
18
|
+
const async_hooks_1 = require("async_hooks");
|
|
18
19
|
const crypto_1 = require("crypto");
|
|
19
20
|
const utils_2 = require("./utils");
|
|
20
21
|
const database_utils_1 = require("./database_utils");
|
|
@@ -564,6 +565,7 @@ class SystemDatabase {
|
|
|
564
565
|
dbPollingIntervalResultMs = 1000;
|
|
565
566
|
dbPollingIntervalEventMs = 10000;
|
|
566
567
|
dbPollingIntervalStreamMs = 1000;
|
|
568
|
+
dbPollingIntervalCancelMs = 1000;
|
|
567
569
|
shouldUseDBNotifications = true;
|
|
568
570
|
notificationsMap = new NotificationMap();
|
|
569
571
|
workflowEventsMap = new NotificationMap();
|
|
@@ -587,6 +589,11 @@ class SystemDatabase {
|
|
|
587
589
|
*/
|
|
588
590
|
pollLimiter;
|
|
589
591
|
runningWorkflowMap = new Map(); // Map from workflowID to workflow promise, queue name and partition key
|
|
592
|
+
#cancelWatchers = new Map();
|
|
593
|
+
#cancelPollerLoop = undefined;
|
|
594
|
+
#cancelPollerWake = null;
|
|
595
|
+
// Runs the lazily started cancel poller outside the async context of the step that happens to start it
|
|
596
|
+
#cancelPollerScope = new async_hooks_1.AsyncResource('DBOSCancelPoller');
|
|
590
597
|
// Per-partition-key created_at cursors: keep per-key queue order monotonic across batches
|
|
591
598
|
#batchCreatedAtCursors = new Map();
|
|
592
599
|
// Set by destroy(), so polling waits end instead of running on against a pool that outlives this handle.
|
|
@@ -773,6 +780,10 @@ class SystemDatabase {
|
|
|
773
780
|
await this.#notifierLoop;
|
|
774
781
|
this.#notifierLoop = undefined;
|
|
775
782
|
}
|
|
783
|
+
this.#cancelPollerWake?.();
|
|
784
|
+
if (this.#cancelPollerLoop) {
|
|
785
|
+
await this.#cancelPollerLoop;
|
|
786
|
+
}
|
|
776
787
|
if (this.notificationsClient) {
|
|
777
788
|
this.#retireNotificationsClient(this.notificationsClient);
|
|
778
789
|
}
|
|
@@ -797,18 +808,18 @@ class SystemDatabase {
|
|
|
797
808
|
}
|
|
798
809
|
// ==================== Workflow Status ====================
|
|
799
810
|
/** Runs on `client` if given, joining its transaction; otherwise in its own retried transaction. */
|
|
800
|
-
async initWorkflowStatus(initStatus, ownerXid, client) {
|
|
811
|
+
async initWorkflowStatus(initStatus, ownerXid, client, reusePolicy = 'return-existing') {
|
|
801
812
|
if (client !== undefined) {
|
|
802
|
-
return await this.#initWorkflowStatusInternal(client, initStatus, ownerXid);
|
|
813
|
+
return await this.#initWorkflowStatusInternal(client, initStatus, ownerXid, reusePolicy);
|
|
803
814
|
}
|
|
804
|
-
return await this.initWorkflowStatusStandalone(initStatus, ownerXid);
|
|
815
|
+
return await this.initWorkflowStatusStandalone(initStatus, ownerXid, reusePolicy);
|
|
805
816
|
}
|
|
806
|
-
async initWorkflowStatusStandalone(initStatus, ownerXid) {
|
|
817
|
+
async initWorkflowStatusStandalone(initStatus, ownerXid, reusePolicy) {
|
|
807
818
|
const client = await this.#connect();
|
|
808
819
|
let shouldCommit = false;
|
|
809
820
|
try {
|
|
810
821
|
await client.query('BEGIN ISOLATION LEVEL READ COMMITTED');
|
|
811
|
-
const result = await this.#initWorkflowStatusInternal(client, initStatus, ownerXid);
|
|
822
|
+
const result = await this.#initWorkflowStatusInternal(client, initStatus, ownerXid, reusePolicy);
|
|
812
823
|
// If there is an existing DB record and we aren't here to recover it, leave it be.
|
|
813
824
|
shouldCommit = result.shouldExecuteOnThisExecutor;
|
|
814
825
|
return result;
|
|
@@ -828,8 +839,37 @@ class SystemDatabase {
|
|
|
828
839
|
}
|
|
829
840
|
}
|
|
830
841
|
}
|
|
831
|
-
|
|
842
|
+
/** Insert a child workflow's row and record it as the parent's step in one transaction, so a crash leaves both or neither. */
|
|
843
|
+
async initChildWorkflowStatus(initStatus, ownerXid, parentWorkflowID, parentFunctionID, startTimeEpochMs, endTimeEpochMs, reusePolicy = 'return-existing') {
|
|
844
|
+
const client = await this.#connect();
|
|
845
|
+
let shouldCommit = false;
|
|
846
|
+
try {
|
|
847
|
+
await client.query('BEGIN ISOLATION LEVEL READ COMMITTED');
|
|
848
|
+
const result = await this.#initWorkflowStatusInternal(client, initStatus, ownerXid, reusePolicy);
|
|
849
|
+
await this.recordOperationResultInternal(client, parentWorkflowID, parentFunctionID, initStatus.workflowName, true, startTimeEpochMs, endTimeEpochMs, { childWorkflowID: initStatus.workflowUUID });
|
|
850
|
+
shouldCommit = true;
|
|
851
|
+
return result;
|
|
852
|
+
}
|
|
853
|
+
finally {
|
|
854
|
+
try {
|
|
855
|
+
if (shouldCommit) {
|
|
856
|
+
await client.query('COMMIT');
|
|
857
|
+
await (0, debugpoint_1.debugTriggerPoint)(debugpoint_1.DEBUG_TRIGGER_INITWF_COMMIT);
|
|
858
|
+
}
|
|
859
|
+
else {
|
|
860
|
+
await client.query('ROLLBACK');
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
finally {
|
|
864
|
+
client.release();
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
async #initWorkflowStatusInternal(client, initStatus, ownerXid, reusePolicy) {
|
|
832
869
|
const resRow = await this.insertWorkflowStatus(client, initStatus, ownerXid);
|
|
870
|
+
if (reusePolicy === 'reject' && (ownerXid === null || resRow.owner_xid !== ownerXid)) {
|
|
871
|
+
throw new error_1.DBOSWorkflowIDInUseError(initStatus.workflowUUID, resRow.status, resRow.name);
|
|
872
|
+
}
|
|
833
873
|
if (resRow.name !== initStatus.workflowName) {
|
|
834
874
|
const msg = `Workflow already exists with a different function name: ${resRow.name}, but the provided function name is: ${initStatus.workflowName}`;
|
|
835
875
|
throw new error_1.DBOSConflictingWorkflowError(initStatus.workflowUUID, msg);
|
|
@@ -1464,6 +1504,106 @@ class SystemDatabase {
|
|
|
1464
1504
|
this.runningWorkflowMap.delete(wfid);
|
|
1465
1505
|
}
|
|
1466
1506
|
}
|
|
1507
|
+
/**
|
|
1508
|
+
* Drop a workflow's history from `startStep` onwards and re-enqueue it under the same
|
|
1509
|
+
* workflow ID, so a replay re-executes everything from that step.
|
|
1510
|
+
*
|
|
1511
|
+
* Unlike forkWorkflow this writes no new workflow: peers keep addressing the same ID,
|
|
1512
|
+
* and the workflow's mailbox, events, and streams are not copied anywhere. Unlike
|
|
1513
|
+
* resumeWorkflows it applies only to workflows in a terminal state.
|
|
1514
|
+
*
|
|
1515
|
+
* Events published at or past the cut are rolled back to the last value published
|
|
1516
|
+
* below it, using workflow_events_history as an undo log; a key that only the
|
|
1517
|
+
* discarded run ever published is unpublished outright.
|
|
1518
|
+
*
|
|
1519
|
+
* Messages the discarded run consumed or received after the rewind point are deleted.
|
|
1520
|
+
*
|
|
1521
|
+
* Stream entries written by the discarded run remain in place, with the exception of
|
|
1522
|
+
* the close sentinel, which has to go so new entries can be appended.
|
|
1523
|
+
*/
|
|
1524
|
+
async rewindWorkflow(workflowID, startStep, options = {}) {
|
|
1525
|
+
// Function IDs start at 0 in this SDK, so 0 is the whole history, not an error.
|
|
1526
|
+
if (startStep < 0) {
|
|
1527
|
+
throw new error_1.DBOSError(`startStep must be >= 0, got ${startStep}`);
|
|
1528
|
+
}
|
|
1529
|
+
const schema = this.schemaName;
|
|
1530
|
+
const client = await this.#connect();
|
|
1531
|
+
try {
|
|
1532
|
+
await client.query('BEGIN ISOLATION LEVEL READ COMMITTED');
|
|
1533
|
+
const { rows: statusRows } = await client.query(`SELECT status FROM "${schema}".workflow_status WHERE workflow_uuid = $1`, [workflowID]);
|
|
1534
|
+
if (statusRows.length === 0) {
|
|
1535
|
+
throw new error_1.DBOSNonExistentWorkflowError(`Workflow ${workflowID} does not exist`);
|
|
1536
|
+
}
|
|
1537
|
+
const status = statusRows[0].status;
|
|
1538
|
+
if (status === workflow_1.StatusString.PENDING || status === workflow_1.StatusString.ENQUEUED || status === workflow_1.StatusString.DELAYED) {
|
|
1539
|
+
throw new error_1.DBOSError(`Cannot rewind ${workflowID} (${status}): only a workflow in a terminal state can be rewound, so cancel it first`);
|
|
1540
|
+
}
|
|
1541
|
+
// Roll workflow_events back to the last value published before the cut, using
|
|
1542
|
+
// workflow_events_history as an undo log, then rewind that history too.
|
|
1543
|
+
// `publishedPastCut` is the correlated EXISTS both halves key off.
|
|
1544
|
+
const publishedPastCut = `EXISTS (
|
|
1545
|
+
SELECT 1 FROM "${schema}".workflow_events_history discarded
|
|
1546
|
+
WHERE discarded.workflow_uuid = $1 AND discarded.key = %KEY% AND discarded.function_id >= $2
|
|
1547
|
+
)`;
|
|
1548
|
+
// First unpublish everything the discarded run touched.
|
|
1549
|
+
await client.query(`DELETE FROM "${schema}".workflow_events
|
|
1550
|
+
WHERE workflow_uuid = $1 AND ${publishedPastCut.replace('%KEY%', '"' + schema + '".workflow_events.key')}`, [workflowID, startStep]);
|
|
1551
|
+
// Then restore those keys, if any, to the value published below the cut.
|
|
1552
|
+
await client.query(`INSERT INTO "${schema}".workflow_events (workflow_uuid, key, value, serialization)
|
|
1553
|
+
SELECT surviving.workflow_uuid, surviving.key, surviving.value, surviving.serialization
|
|
1554
|
+
FROM (
|
|
1555
|
+
SELECT weh.workflow_uuid, weh.key, weh.value, weh.serialization,
|
|
1556
|
+
ROW_NUMBER() OVER (PARTITION BY weh.key ORDER BY weh.function_id DESC) AS rn
|
|
1557
|
+
FROM "${schema}".workflow_events_history weh
|
|
1558
|
+
WHERE weh.workflow_uuid = $1 AND weh.function_id < $2
|
|
1559
|
+
AND ${publishedPastCut.replace('%KEY%', 'weh.key')}
|
|
1560
|
+
) surviving
|
|
1561
|
+
WHERE surviving.rn = 1`, [workflowID, startStep]);
|
|
1562
|
+
// Clear the streams' close sentinels so the replay can append again.
|
|
1563
|
+
await client.query(`DELETE FROM "${schema}".streams
|
|
1564
|
+
WHERE workflow_uuid = $1 AND function_id >= $2 AND value = $3 AND serialization = $4`, [workflowID, startStep, exports.DBOS_STREAM_CLOSED_SENTINEL_SERIALIZED, serialization_1.DBOSPortableJSON.name()]);
|
|
1565
|
+
// Discard the steps and the events history past the cut.
|
|
1566
|
+
for (const table of ['operation_outputs', 'workflow_events_history']) {
|
|
1567
|
+
await client.query(`DELETE FROM "${schema}".${table} WHERE workflow_uuid = $1 AND function_id >= $2`, [
|
|
1568
|
+
workflowID,
|
|
1569
|
+
startStep,
|
|
1570
|
+
]);
|
|
1571
|
+
}
|
|
1572
|
+
// Delete messages consumed or received after the rewind point
|
|
1573
|
+
await client.query(`DELETE FROM "${schema}".notifications WHERE destination_uuid = $1 AND (consumed_by_function_id >= $2 OR consumed = false)`, [workflowID, startStep]);
|
|
1574
|
+
await client.query(`DELETE FROM "${schema}".workflow_output WHERE workflow_uuid = $1`, [workflowID]);
|
|
1575
|
+
// Re-enqueue the workflow. Re-asserting the status we read keeps a workflow that
|
|
1576
|
+
// moved on underneath us from being resurrected.
|
|
1577
|
+
const setVersion = options.applicationVersion !== undefined ? ', application_version = $6' : '';
|
|
1578
|
+
const params = [
|
|
1579
|
+
workflow_1.StatusString.ENQUEUED,
|
|
1580
|
+
options.queueName ?? utils_1.INTERNAL_QUEUE_NAME,
|
|
1581
|
+
options.queuePartitionKey ?? null,
|
|
1582
|
+
workflowID,
|
|
1583
|
+
status,
|
|
1584
|
+
];
|
|
1585
|
+
if (options.applicationVersion !== undefined) {
|
|
1586
|
+
params.push(options.applicationVersion);
|
|
1587
|
+
}
|
|
1588
|
+
const { rowCount } = await client.query(`UPDATE "${schema}".workflow_status
|
|
1589
|
+
SET status = $1, queue_name = $2, queue_partition_key = $3, recovery_attempts = 0,
|
|
1590
|
+
workflow_deadline_epoch_ms = NULL, deduplication_id = NULL, started_at_epoch_ms = NULL,
|
|
1591
|
+
completed_at = NULL, output = NULL, error = NULL,
|
|
1592
|
+
updated_at = (EXTRACT(EPOCH FROM now()) * 1000)::bigint${setVersion}
|
|
1593
|
+
WHERE workflow_uuid = $4 AND status = $5`, params);
|
|
1594
|
+
if (rowCount !== 1) {
|
|
1595
|
+
throw new error_1.DBOSError(`Workflow ${workflowID} changed status while being rewound; retry the rewind`);
|
|
1596
|
+
}
|
|
1597
|
+
await client.query('COMMIT');
|
|
1598
|
+
}
|
|
1599
|
+
catch (e) {
|
|
1600
|
+
await client.query('ROLLBACK').catch(() => undefined);
|
|
1601
|
+
throw e;
|
|
1602
|
+
}
|
|
1603
|
+
finally {
|
|
1604
|
+
client.release();
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1467
1607
|
async forkWorkflow(workflowID, startStep, options = {}) {
|
|
1468
1608
|
const newWorkflowID = options.newWorkflowID ?? (0, crypto_1.randomUUID)();
|
|
1469
1609
|
const result = await this.bulkForkWorkflows([workflowID], [newWorkflowID], [startStep], options);
|
|
@@ -1961,6 +2101,74 @@ class SystemDatabase {
|
|
|
1961
2101
|
async checkIfCanceledLimited(workflowID) {
|
|
1962
2102
|
await this.#pollWithLimiter(() => this.#checkIfCanceled(this.pool, workflowID));
|
|
1963
2103
|
}
|
|
2104
|
+
watchForCancellation(workflowID) {
|
|
2105
|
+
const controller = new AbortController();
|
|
2106
|
+
let state = 'idle';
|
|
2107
|
+
const start = () => {
|
|
2108
|
+
state = 'watching';
|
|
2109
|
+
if (this.#destroyed)
|
|
2110
|
+
return;
|
|
2111
|
+
let watchers = this.#cancelWatchers.get(workflowID);
|
|
2112
|
+
if (watchers === undefined) {
|
|
2113
|
+
watchers = new Set();
|
|
2114
|
+
this.#cancelWatchers.set(workflowID, watchers);
|
|
2115
|
+
}
|
|
2116
|
+
watchers.add(controller);
|
|
2117
|
+
this.#cancelPollerLoop ??= this.#cancelPollerScope.runInAsyncScope(() => this.#runCancelPoller());
|
|
2118
|
+
};
|
|
2119
|
+
const stop = () => {
|
|
2120
|
+
const watchers = this.#cancelWatchers.get(workflowID);
|
|
2121
|
+
if (watchers?.delete(controller) && watchers.size === 0) {
|
|
2122
|
+
this.#cancelWatchers.delete(workflowID);
|
|
2123
|
+
if (this.#cancelWatchers.size === 0)
|
|
2124
|
+
this.#cancelPollerWake?.();
|
|
2125
|
+
}
|
|
2126
|
+
};
|
|
2127
|
+
return {
|
|
2128
|
+
get signal() {
|
|
2129
|
+
if (state === 'idle')
|
|
2130
|
+
start();
|
|
2131
|
+
return controller.signal;
|
|
2132
|
+
},
|
|
2133
|
+
get cancelled() {
|
|
2134
|
+
return controller.signal.aborted;
|
|
2135
|
+
},
|
|
2136
|
+
release: () => {
|
|
2137
|
+
if (state === 'watching')
|
|
2138
|
+
stop();
|
|
2139
|
+
state = 'released';
|
|
2140
|
+
},
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
async #runCancelPoller() {
|
|
2144
|
+
while (this.#cancelWatchers.size > 0 && !this.#destroyed) {
|
|
2145
|
+
const { promise, cancel } = (0, utils_1.cancellableSleep)(this.dbPollingIntervalCancelMs);
|
|
2146
|
+
this.#cancelPollerWake = cancel;
|
|
2147
|
+
await promise;
|
|
2148
|
+
this.#cancelPollerWake = null;
|
|
2149
|
+
if (this.#cancelWatchers.size === 0 || this.#destroyed)
|
|
2150
|
+
break;
|
|
2151
|
+
try {
|
|
2152
|
+
await this.#abortCancelledWatchers();
|
|
2153
|
+
}
|
|
2154
|
+
catch (e) {
|
|
2155
|
+
this.logger.debug(`Workflow cancellation poll failed: ${String(e)}`);
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
this.#cancelPollerLoop = undefined;
|
|
2159
|
+
}
|
|
2160
|
+
async #abortCancelledWatchers() {
|
|
2161
|
+
const { rows } = await this.#pollWithLimiter(() => this.pool.query(`SELECT workflow_uuid FROM "${this.schemaName}".workflow_status WHERE workflow_uuid = ANY($1) AND status = $2`, [Array.from(this.#cancelWatchers.keys()), workflow_1.StatusString.CANCELLED]));
|
|
2162
|
+
for (const { workflow_uuid: workflowID } of rows) {
|
|
2163
|
+
const watchers = this.#cancelWatchers.get(workflowID);
|
|
2164
|
+
if (watchers === undefined)
|
|
2165
|
+
continue;
|
|
2166
|
+
this.#cancelWatchers.delete(workflowID);
|
|
2167
|
+
const reason = new error_1.DBOSWorkflowCancelledError(workflowID);
|
|
2168
|
+
for (const controller of watchers)
|
|
2169
|
+
controller.abort(reason);
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
1964
2172
|
// A missing row normally means the workflow has not been inserted yet, so
|
|
1965
2173
|
// polling for it is correct. Callers that know the row must already exist
|
|
1966
2174
|
// (e.g. a run parking on an outcome it just failed to write) pass
|
|
@@ -2179,7 +2387,7 @@ class SystemDatabase {
|
|
|
2179
2387
|
try {
|
|
2180
2388
|
await client.query(`BEGIN ISOLATION LEVEL READ COMMITTED`);
|
|
2181
2389
|
const finalRecvRows = (await client.query(`UPDATE "${this.schemaName}".notifications
|
|
2182
|
-
SET consumed = true
|
|
2390
|
+
SET consumed = true, consumed_by_function_id = $3
|
|
2183
2391
|
WHERE destination_uuid = $1
|
|
2184
2392
|
AND topic = $2
|
|
2185
2393
|
AND consumed = false
|
|
@@ -2192,7 +2400,7 @@ class SystemDatabase {
|
|
|
2192
2400
|
ORDER BY created_at_epoch_ms ASC
|
|
2193
2401
|
LIMIT 1
|
|
2194
2402
|
)
|
|
2195
|
-
RETURNING notifications.message, notifications.serialization;`, [workflowID, topic])).rows;
|
|
2403
|
+
RETURNING notifications.message, notifications.serialization;`, [workflowID, topic, functionID])).rows;
|
|
2196
2404
|
if (finalRecvRows.length > 0) {
|
|
2197
2405
|
message = finalRecvRows[0].message;
|
|
2198
2406
|
serialization = finalRecvRows[0].serialization;
|
|
@@ -4236,13 +4444,8 @@ class SystemDatabase {
|
|
|
4236
4444
|
application_name
|
|
4237
4445
|
) VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29)
|
|
4238
4446
|
ON CONFLICT (workflow_uuid)
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
executor_id = CASE
|
|
4242
|
-
WHEN EXCLUDED.status != '${workflow_1.StatusString.ENQUEUED}' AND EXCLUDED.status != '${workflow_1.StatusString.DELAYED}'
|
|
4243
|
-
THEN EXCLUDED.executor_id
|
|
4244
|
-
ELSE workflow_status.executor_id
|
|
4245
|
-
END
|
|
4447
|
+
-- A no-op update, so an existing row comes back unchanged for the caller to inspect.
|
|
4448
|
+
DO UPDATE SET owner_xid = workflow_status.owner_xid
|
|
4246
4449
|
RETURNING status, name, class_name, config_name, queue_name, workflow_deadline_epoch_ms, executor_id, owner_xid, serialization`, [
|
|
4247
4450
|
initStatus.workflowUUID,
|
|
4248
4451
|
initStatus.status,
|
|
@@ -4274,7 +4477,6 @@ class SystemDatabase {
|
|
|
4274
4477
|
initStatus.scheduleName ?? null,
|
|
4275
4478
|
initStatus.debounceDeadlineEpochMS ?? null,
|
|
4276
4479
|
initStatus.isDebounced ?? false,
|
|
4277
|
-
// Absent from the conflict update: a re-enqueue must not re-own a claimed row.
|
|
4278
4480
|
initStatus.applicationName ?? null,
|
|
4279
4481
|
]);
|
|
4280
4482
|
if (rows.length === 0) {
|
|
@@ -4599,9 +4801,15 @@ exports.SystemDatabase = SystemDatabase;
|
|
|
4599
4801
|
__decorate([
|
|
4600
4802
|
dbRetry(),
|
|
4601
4803
|
__metadata("design:type", Function),
|
|
4602
|
-
__metadata("design:paramtypes", [Object, Object]),
|
|
4804
|
+
__metadata("design:paramtypes", [Object, Object, String]),
|
|
4603
4805
|
__metadata("design:returntype", Promise)
|
|
4604
4806
|
], SystemDatabase.prototype, "initWorkflowStatusStandalone", null);
|
|
4807
|
+
__decorate([
|
|
4808
|
+
dbRetry(),
|
|
4809
|
+
__metadata("design:type", Function),
|
|
4810
|
+
__metadata("design:paramtypes", [Object, String, String, Number, Number, Number, String]),
|
|
4811
|
+
__metadata("design:returntype", Promise)
|
|
4812
|
+
], SystemDatabase.prototype, "initChildWorkflowStatus", null);
|
|
4605
4813
|
__decorate([
|
|
4606
4814
|
dbRetry(),
|
|
4607
4815
|
__metadata("design:type", Function),
|
|
@@ -4662,6 +4870,12 @@ __decorate([
|
|
|
4662
4870
|
__metadata("design:paramtypes", [Object]),
|
|
4663
4871
|
__metadata("design:returntype", Promise)
|
|
4664
4872
|
], SystemDatabase.prototype, "debounceDelayedWorkflowStandalone", null);
|
|
4873
|
+
__decorate([
|
|
4874
|
+
dbRetry(),
|
|
4875
|
+
__metadata("design:type", Function),
|
|
4876
|
+
__metadata("design:paramtypes", [String, Number, Object]),
|
|
4877
|
+
__metadata("design:returntype", Promise)
|
|
4878
|
+
], SystemDatabase.prototype, "rewindWorkflow", null);
|
|
4665
4879
|
__decorate([
|
|
4666
4880
|
dbRetry()
|
|
4667
4881
|
// A missing row normally means the workflow has not been inserted yet, so
|