@dbos-inc/dbos-sdk 4.19.8 → 4.20.5-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/src/client.d.ts +5 -5
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +12 -9
- package/dist/src/client.js.map +1 -1
- package/dist/src/conductor/conductor.js +1 -1
- package/dist/src/conductor/conductor.js.map +1 -1
- package/dist/src/conductor/protocol.d.ts +1 -0
- package/dist/src/conductor/protocol.d.ts.map +1 -1
- package/dist/src/conductor/protocol.js +1 -0
- package/dist/src/conductor/protocol.js.map +1 -1
- package/dist/src/dbos-executor.d.ts +1 -1
- package/dist/src/dbos-executor.d.ts.map +1 -1
- package/dist/src/dbos-executor.js +2 -2
- package/dist/src/dbos-executor.js.map +1 -1
- package/dist/src/dbos.d.ts +43 -8
- package/dist/src/dbos.d.ts.map +1 -1
- package/dist/src/dbos.js +33 -14
- package/dist/src/dbos.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/system_database.d.ts +5 -5
- package/dist/src/system_database.d.ts.map +1 -1
- package/dist/src/system_database.js +89 -47
- package/dist/src/system_database.js.map +1 -1
- package/dist/src/workflow.d.ts +5 -4
- package/dist/src/workflow.d.ts.map +1 -1
- package/dist/src/workflow.js +7 -3
- package/dist/src/workflow.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -517,9 +517,7 @@ class SystemDatabase {
|
|
|
517
517
|
async recordWorkflowOutput(workflowID, status) {
|
|
518
518
|
const client = await this.pool.connect();
|
|
519
519
|
try {
|
|
520
|
-
await this
|
|
521
|
-
update: { output: status.output, resetDeduplicationID: true, setCompletedAt: true },
|
|
522
|
-
});
|
|
520
|
+
await this.#recordWorkflowOutcome(client, workflowID, workflow_1.StatusString.SUCCESS, { output: status.output });
|
|
523
521
|
}
|
|
524
522
|
finally {
|
|
525
523
|
client.release();
|
|
@@ -528,14 +526,37 @@ class SystemDatabase {
|
|
|
528
526
|
async recordWorkflowError(workflowID, status) {
|
|
529
527
|
const client = await this.pool.connect();
|
|
530
528
|
try {
|
|
531
|
-
await this
|
|
532
|
-
update: { error: status.error, resetDeduplicationID: true, setCompletedAt: true },
|
|
533
|
-
});
|
|
529
|
+
await this.#recordWorkflowOutcome(client, workflowID, workflow_1.StatusString.ERROR, { error: status.error });
|
|
534
530
|
}
|
|
535
531
|
finally {
|
|
536
532
|
client.release();
|
|
537
533
|
}
|
|
538
534
|
}
|
|
535
|
+
// Record a workflow's terminal outcome (SUCCESS or ERROR), but never overwrite
|
|
536
|
+
// the terminal CANCELLED status: a workflow can be cancelled during its final
|
|
537
|
+
// step, and if so it must not be able to subsequently complete. If the
|
|
538
|
+
// workflow is cancelled, abort the function so it does not complete. This
|
|
539
|
+
// mirrors the cancellation check done before each step.
|
|
540
|
+
async #recordWorkflowOutcome(client, workflowID, status, outcome) {
|
|
541
|
+
let cancelled = false;
|
|
542
|
+
try {
|
|
543
|
+
await client.query('BEGIN');
|
|
544
|
+
await this.updateWorkflowStatus(client, workflowID, status, {
|
|
545
|
+
update: { ...outcome, resetDeduplicationID: true, setCompletedAt: true },
|
|
546
|
+
where: { notStatus: workflow_1.StatusString.CANCELLED },
|
|
547
|
+
throwOnFailure: false,
|
|
548
|
+
});
|
|
549
|
+
cancelled = (await this.getWorkflowStatusValue(client, workflowID)) === workflow_1.StatusString.CANCELLED;
|
|
550
|
+
await client.query('COMMIT');
|
|
551
|
+
}
|
|
552
|
+
catch (e) {
|
|
553
|
+
await client.query('ROLLBACK');
|
|
554
|
+
throw e;
|
|
555
|
+
}
|
|
556
|
+
if (cancelled) {
|
|
557
|
+
throw new error_1.DBOSWorkflowCancelledError(workflowID);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
539
560
|
async getPendingWorkflows(executorID, appVersion) {
|
|
540
561
|
const getWorkflows = await this.pool.query(`SELECT workflow_uuid, queue_name
|
|
541
562
|
FROM "${this.schemaName}".workflow_status
|
|
@@ -673,7 +694,24 @@ class SystemDatabase {
|
|
|
673
694
|
return { isPatched: true, hasEntry: true };
|
|
674
695
|
}
|
|
675
696
|
// ==================== Workflow Management ====================
|
|
676
|
-
async cancelWorkflows(workflowIDs) {
|
|
697
|
+
async cancelWorkflows(workflowIDs, cancelChildren = false) {
|
|
698
|
+
if (!cancelChildren) {
|
|
699
|
+
await this.#cancelWorkflows(workflowIDs);
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
// Cascade cancellation to child workflows level by level.
|
|
703
|
+
const visited = new Set(workflowIDs);
|
|
704
|
+
let frontier = workflowIDs;
|
|
705
|
+
while (frontier.length > 0) {
|
|
706
|
+
await this.#cancelWorkflows(frontier);
|
|
707
|
+
const children = await this.#getDirectChildren(frontier);
|
|
708
|
+
frontier = children.filter((id) => !visited.has(id));
|
|
709
|
+
for (const id of frontier) {
|
|
710
|
+
visited.add(id);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
async #cancelWorkflows(workflowIDs) {
|
|
677
715
|
await this.pool.query(`UPDATE "${this.schemaName}".workflow_status
|
|
678
716
|
SET status = $1, queue_name = NULL, deduplication_id = NULL, started_at_epoch_ms = NULL,
|
|
679
717
|
updated_at = (EXTRACT(EPOCH FROM now()) * 1000)::bigint,
|
|
@@ -718,32 +756,28 @@ class SystemDatabase {
|
|
|
718
756
|
WHERE workflow_uuid = $3
|
|
719
757
|
AND status = $4`, [delayUntilEpochMS, Date.now(), workflowID, workflow_1.StatusString.DELAYED]);
|
|
720
758
|
}
|
|
759
|
+
// Get the immediate (one-level) child workflow IDs for a set of workflows.
|
|
760
|
+
async #getDirectChildren(workflowIDs) {
|
|
761
|
+
if (workflowIDs.length === 0) {
|
|
762
|
+
return [];
|
|
763
|
+
}
|
|
764
|
+
const result = await this.pool.query(`SELECT workflow_uuid
|
|
765
|
+
FROM "${this.schemaName}".workflow_status
|
|
766
|
+
WHERE parent_workflow_id = ANY($1)`, [workflowIDs]);
|
|
767
|
+
return result.rows.map((row) => row.workflow_uuid);
|
|
768
|
+
}
|
|
721
769
|
async getWorkflowChildren(workflowID) {
|
|
722
770
|
// BFS to find all descendant workflows
|
|
723
|
-
const
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
const result = await client.query(`SELECT DISTINCT child_workflow_id
|
|
731
|
-
FROM "${this.schemaName}".operation_outputs
|
|
732
|
-
WHERE workflow_uuid = ANY($1)
|
|
733
|
-
AND child_workflow_id IS NOT NULL`, [batch]);
|
|
734
|
-
for (const row of result.rows) {
|
|
735
|
-
if (!visited.has(row.child_workflow_id)) {
|
|
736
|
-
visited.add(row.child_workflow_id);
|
|
737
|
-
queue.push(row.child_workflow_id);
|
|
738
|
-
children.push(row.child_workflow_id);
|
|
739
|
-
}
|
|
740
|
-
}
|
|
771
|
+
const descendants = new Set();
|
|
772
|
+
let frontier = [workflowID];
|
|
773
|
+
while (frontier.length > 0) {
|
|
774
|
+
const children = await this.#getDirectChildren(frontier);
|
|
775
|
+
frontier = children.filter((id) => !descendants.has(id));
|
|
776
|
+
for (const id of frontier) {
|
|
777
|
+
descendants.add(id);
|
|
741
778
|
}
|
|
742
779
|
}
|
|
743
|
-
|
|
744
|
-
client.release();
|
|
745
|
-
}
|
|
746
|
-
return children;
|
|
780
|
+
return [...descendants];
|
|
747
781
|
}
|
|
748
782
|
async deleteWorkflows(workflowIDs, deleteChildren = false) {
|
|
749
783
|
const allIds = [...workflowIDs];
|
|
@@ -1151,9 +1185,10 @@ class SystemDatabase {
|
|
|
1151
1185
|
//throw new Error('Message notification map is not empty - shutdown is not clean.');
|
|
1152
1186
|
}
|
|
1153
1187
|
}
|
|
1154
|
-
async awaitWorkflowResult(workflowID, timeoutSeconds, callerID, timerFuncID) {
|
|
1188
|
+
async awaitWorkflowResult(workflowID, timeoutSeconds, callerID, timerFuncID, pollingIntervalMs) {
|
|
1155
1189
|
const timeoutms = timeoutSeconds !== undefined ? timeoutSeconds * 1000 : undefined;
|
|
1156
1190
|
let finishTime = timeoutms !== undefined ? Date.now() + timeoutms : undefined;
|
|
1191
|
+
const pollIntervalMs = pollingIntervalMs ?? this.dbPollingIntervalResultMs;
|
|
1157
1192
|
while (true) {
|
|
1158
1193
|
let resolveNotification;
|
|
1159
1194
|
const statusPromise = new Promise((resolve) => {
|
|
@@ -1203,14 +1238,14 @@ class SystemDatabase {
|
|
|
1203
1238
|
let timeoutPromise = Promise.resolve();
|
|
1204
1239
|
let timeoutCancel = () => { };
|
|
1205
1240
|
if (timerFuncID !== undefined && callerID !== undefined && timeoutms !== undefined) {
|
|
1206
|
-
const { promise, cancel, endTime } = await this.#durableSleep(callerID, timerFuncID, timeoutms,
|
|
1241
|
+
const { promise, cancel, endTime } = await this.#durableSleep(callerID, timerFuncID, timeoutms, pollIntervalMs);
|
|
1207
1242
|
finishTime = endTime;
|
|
1208
1243
|
timeoutPromise = promise;
|
|
1209
1244
|
timeoutCancel = cancel;
|
|
1210
1245
|
}
|
|
1211
1246
|
else {
|
|
1212
|
-
let poll = finishTime ? finishTime - ct :
|
|
1213
|
-
poll = Math.min(
|
|
1247
|
+
let poll = finishTime ? finishTime - ct : pollIntervalMs;
|
|
1248
|
+
poll = Math.min(pollIntervalMs, poll);
|
|
1214
1249
|
const { promise, cancel } = (0, utils_1.cancellableSleep)(poll);
|
|
1215
1250
|
timeoutPromise = promise;
|
|
1216
1251
|
timeoutCancel = cancel;
|
|
@@ -1229,8 +1264,9 @@ class SystemDatabase {
|
|
|
1229
1264
|
}
|
|
1230
1265
|
}
|
|
1231
1266
|
}
|
|
1232
|
-
async awaitFirstWorkflowId(workflowIds, callerID) {
|
|
1267
|
+
async awaitFirstWorkflowId(workflowIds, callerID, pollingIntervalMs) {
|
|
1233
1268
|
const placeholders = workflowIds.map((_, i) => `$${i + 1}`).join(', ');
|
|
1269
|
+
const pollIntervalMs = pollingIntervalMs ?? this.dbPollingIntervalResultMs;
|
|
1234
1270
|
while (true) {
|
|
1235
1271
|
let resolveNotification;
|
|
1236
1272
|
const wakeupPromise = new Promise((resolve) => {
|
|
@@ -1251,7 +1287,7 @@ class SystemDatabase {
|
|
|
1251
1287
|
if (rows.length > 0) {
|
|
1252
1288
|
return rows[0].workflow_uuid;
|
|
1253
1289
|
}
|
|
1254
|
-
const { promise: sleepPromise, cancel: sleepCancel } = (0, utils_1.cancellableSleep)(
|
|
1290
|
+
const { promise: sleepPromise, cancel: sleepCancel } = (0, utils_1.cancellableSleep)(pollIntervalMs);
|
|
1255
1291
|
try {
|
|
1256
1292
|
await Promise.race([wakeupPromise, sleepPromise]);
|
|
1257
1293
|
}
|
|
@@ -1344,7 +1380,7 @@ class SystemDatabase {
|
|
|
1344
1380
|
throw err;
|
|
1345
1381
|
}
|
|
1346
1382
|
}
|
|
1347
|
-
async recv(workflowID, functionID, timeoutFunctionID, topic, timeoutSeconds = dbos_executor_1.DBOSExecutor.defaultNotificationTimeoutSec) {
|
|
1383
|
+
async recv(workflowID, functionID, timeoutFunctionID, topic, timeoutSeconds = dbos_executor_1.DBOSExecutor.defaultNotificationTimeoutSec, pollingIntervalMs) {
|
|
1348
1384
|
topic = topic ?? this.nullTopic;
|
|
1349
1385
|
const startTime = Date.now();
|
|
1350
1386
|
// First, check for previous executions.
|
|
@@ -1357,6 +1393,7 @@ class SystemDatabase {
|
|
|
1357
1393
|
}
|
|
1358
1394
|
const timeoutms = timeoutSeconds !== undefined ? timeoutSeconds * 1000 : undefined;
|
|
1359
1395
|
let finishTime = timeoutms !== undefined ? Date.now() + timeoutms : undefined;
|
|
1396
|
+
const pollIntervalMs = pollingIntervalMs ?? this.dbPollingIntervalEventMs;
|
|
1360
1397
|
while (true) {
|
|
1361
1398
|
// register the key with the global notifications listener.
|
|
1362
1399
|
let resolveNotification;
|
|
@@ -1380,14 +1417,14 @@ class SystemDatabase {
|
|
|
1380
1417
|
let timeoutPromise = Promise.resolve();
|
|
1381
1418
|
let timeoutCancel = () => { };
|
|
1382
1419
|
if (timeoutms) {
|
|
1383
|
-
const { promise, cancel, endTime } = await this.#durableSleep(workflowID, timeoutFunctionID, timeoutms,
|
|
1420
|
+
const { promise, cancel, endTime } = await this.#durableSleep(workflowID, timeoutFunctionID, timeoutms, pollIntervalMs);
|
|
1384
1421
|
timeoutPromise = promise;
|
|
1385
1422
|
timeoutCancel = cancel;
|
|
1386
1423
|
finishTime = endTime;
|
|
1387
1424
|
}
|
|
1388
1425
|
else {
|
|
1389
|
-
let poll = finishTime ? finishTime - ct :
|
|
1390
|
-
poll = Math.min(
|
|
1426
|
+
let poll = finishTime ? finishTime - ct : pollIntervalMs;
|
|
1427
|
+
poll = Math.min(pollIntervalMs, poll);
|
|
1391
1428
|
const { promise, cancel } = (0, utils_1.cancellableSleep)(poll);
|
|
1392
1429
|
timeoutPromise = promise;
|
|
1393
1430
|
timeoutCancel = cancel;
|
|
@@ -1475,7 +1512,7 @@ class SystemDatabase {
|
|
|
1475
1512
|
client.release();
|
|
1476
1513
|
}
|
|
1477
1514
|
}
|
|
1478
|
-
async getEvent(workflowID, key, timeoutSeconds, callerWorkflow) {
|
|
1515
|
+
async getEvent(workflowID, key, timeoutSeconds, callerWorkflow, pollingIntervalMs) {
|
|
1479
1516
|
const startTime = Date.now();
|
|
1480
1517
|
// Check if the operation has been done before for OAOO (only do this inside a workflow).
|
|
1481
1518
|
if (callerWorkflow) {
|
|
@@ -1493,6 +1530,7 @@ class SystemDatabase {
|
|
|
1493
1530
|
const payloadKey = `${workflowID}::${key}`;
|
|
1494
1531
|
const timeoutms = timeoutSeconds !== undefined ? timeoutSeconds * 1000 : undefined;
|
|
1495
1532
|
let finishTime = timeoutms !== undefined ? Date.now() + timeoutms : undefined;
|
|
1533
|
+
const pollIntervalMs = pollingIntervalMs ?? this.dbPollingIntervalEventMs;
|
|
1496
1534
|
// Register the key with the global notifications listener first... we do not want to look in the DB first
|
|
1497
1535
|
// or that would cause a timing hole.
|
|
1498
1536
|
while (true) {
|
|
@@ -1525,14 +1563,14 @@ class SystemDatabase {
|
|
|
1525
1563
|
let timeoutPromise = Promise.resolve();
|
|
1526
1564
|
let timeoutCancel = () => { };
|
|
1527
1565
|
if (callerWorkflow && timeoutms) {
|
|
1528
|
-
const { promise, cancel, endTime } = await this.#durableSleep(callerWorkflow.workflowID, callerWorkflow.timeoutFunctionID ?? -1, timeoutms,
|
|
1566
|
+
const { promise, cancel, endTime } = await this.#durableSleep(callerWorkflow.workflowID, callerWorkflow.timeoutFunctionID ?? -1, timeoutms, pollIntervalMs);
|
|
1529
1567
|
timeoutPromise = promise;
|
|
1530
1568
|
timeoutCancel = cancel;
|
|
1531
1569
|
finishTime = endTime;
|
|
1532
1570
|
}
|
|
1533
1571
|
else {
|
|
1534
|
-
let poll = finishTime ? finishTime - ct :
|
|
1535
|
-
poll = Math.min(
|
|
1572
|
+
let poll = finishTime ? finishTime - ct : pollIntervalMs;
|
|
1573
|
+
poll = Math.min(pollIntervalMs, poll);
|
|
1536
1574
|
const { promise, cancel } = (0, utils_1.cancellableSleep)(poll);
|
|
1537
1575
|
timeoutPromise = promise;
|
|
1538
1576
|
timeoutCancel = cancel;
|
|
@@ -2745,6 +2783,10 @@ class SystemDatabase {
|
|
|
2745
2783
|
const param = args.push(where.status);
|
|
2746
2784
|
whereClause += ` AND status=$${param}`;
|
|
2747
2785
|
}
|
|
2786
|
+
if (where.notStatus) {
|
|
2787
|
+
const param = args.push(where.notStatus);
|
|
2788
|
+
whereClause += ` AND status!=$${param}`;
|
|
2789
|
+
}
|
|
2748
2790
|
const result = await client.query(`UPDATE "${this.schemaName}".workflow_status ${setClause} ${whereClause}`, args);
|
|
2749
2791
|
const throwOnFailure = options.throwOnFailure ?? true;
|
|
2750
2792
|
if (throwOnFailure && result.rowCount !== 1) {
|
|
@@ -3000,13 +3042,13 @@ __decorate([
|
|
|
3000
3042
|
__decorate([
|
|
3001
3043
|
dbRetry(),
|
|
3002
3044
|
__metadata("design:type", Function),
|
|
3003
|
-
__metadata("design:paramtypes", [String, Number, String, Number]),
|
|
3045
|
+
__metadata("design:paramtypes", [String, Number, String, Number, Number]),
|
|
3004
3046
|
__metadata("design:returntype", Promise)
|
|
3005
3047
|
], SystemDatabase.prototype, "awaitWorkflowResult", null);
|
|
3006
3048
|
__decorate([
|
|
3007
3049
|
dbRetry(),
|
|
3008
3050
|
__metadata("design:type", Function),
|
|
3009
|
-
__metadata("design:paramtypes", [Array, String]),
|
|
3051
|
+
__metadata("design:paramtypes", [Array, String, Number]),
|
|
3010
3052
|
__metadata("design:returntype", Promise)
|
|
3011
3053
|
], SystemDatabase.prototype, "awaitFirstWorkflowId", null);
|
|
3012
3054
|
__decorate([
|
|
@@ -3030,7 +3072,7 @@ __decorate([
|
|
|
3030
3072
|
__decorate([
|
|
3031
3073
|
dbRetry(),
|
|
3032
3074
|
__metadata("design:type", Function),
|
|
3033
|
-
__metadata("design:paramtypes", [String, Number, Number, String, Number]),
|
|
3075
|
+
__metadata("design:paramtypes", [String, Number, Number, String, Number, Number]),
|
|
3034
3076
|
__metadata("design:returntype", Promise)
|
|
3035
3077
|
], SystemDatabase.prototype, "recv", null);
|
|
3036
3078
|
__decorate([
|
|
@@ -3042,7 +3084,7 @@ __decorate([
|
|
|
3042
3084
|
__decorate([
|
|
3043
3085
|
dbRetry(),
|
|
3044
3086
|
__metadata("design:type", Function),
|
|
3045
|
-
__metadata("design:paramtypes", [String, String, Number, Object]),
|
|
3087
|
+
__metadata("design:paramtypes", [String, String, Number, Object, Number]),
|
|
3046
3088
|
__metadata("design:returntype", Promise)
|
|
3047
3089
|
], SystemDatabase.prototype, "getEvent", null);
|
|
3048
3090
|
__decorate([
|