@dbos-inc/dbos-sdk 5.4.9-preview → 5.4.11-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/cli/cli.js +56 -13
- package/dist/src/cli/cli.js.map +1 -1
- package/dist/src/client.d.ts +20 -10
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +76 -44
- package/dist/src/client.js.map +1 -1
- package/dist/src/config.d.ts +1 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +1 -0
- package/dist/src/config.js.map +1 -1
- package/dist/src/context.d.ts +6 -0
- package/dist/src/context.d.ts.map +1 -1
- package/dist/src/context.js +15 -1
- package/dist/src/context.js.map +1 -1
- package/dist/src/datasource.d.ts +2 -0
- package/dist/src/datasource.d.ts.map +1 -1
- package/dist/src/datasource.js +7 -1
- package/dist/src/datasource.js.map +1 -1
- package/dist/src/dbos-executor.d.ts +13 -4
- package/dist/src/dbos-executor.d.ts.map +1 -1
- package/dist/src/dbos-executor.js +64 -31
- package/dist/src/dbos-executor.js.map +1 -1
- package/dist/src/dbos.d.ts +60 -11
- package/dist/src/dbos.d.ts.map +1 -1
- package/dist/src/dbos.js +192 -65
- package/dist/src/dbos.js.map +1 -1
- package/dist/src/enqueue_options.d.ts +2 -0
- package/dist/src/enqueue_options.d.ts.map +1 -1
- package/dist/src/enqueue_options.js +3 -0
- 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 -5
- package/dist/src/enqueue_workflow.js.map +1 -1
- package/dist/src/enterprise.d.ts +23 -3
- package/dist/src/enterprise.d.ts.map +1 -1
- package/dist/src/enterprise.js +81 -44
- package/dist/src/enterprise.js.map +1 -1
- package/dist/src/eventreceiver.d.ts +2 -0
- package/dist/src/eventreceiver.d.ts.map +1 -1
- package/dist/src/eventreceiver.js +7 -1
- package/dist/src/eventreceiver.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/scheduler/scheduler.d.ts +5 -0
- package/dist/src/scheduler/scheduler.d.ts.map +1 -1
- package/dist/src/scheduler/scheduler.js +14 -4
- package/dist/src/scheduler/scheduler.js.map +1 -1
- package/dist/src/shard_keys.d.ts +13 -0
- package/dist/src/shard_keys.d.ts.map +1 -0
- package/dist/src/shard_keys.js +55 -0
- package/dist/src/shard_keys.js.map +1 -0
- package/dist/src/streams.d.ts +2 -0
- package/dist/src/streams.d.ts.map +1 -1
- package/dist/src/streams.js +5 -2
- package/dist/src/streams.js.map +1 -1
- package/dist/src/system_database.d.ts +143 -9
- package/dist/src/system_database.d.ts.map +1 -1
- package/dist/src/system_database.js +1095 -503
- package/dist/src/system_database.js.map +1 -1
- package/dist/src/wfqueue.d.ts +1 -2
- package/dist/src/wfqueue.d.ts.map +1 -1
- package/dist/src/wfqueue.js +83 -63
- package/dist/src/wfqueue.js.map +1 -1
- package/dist/src/workflow.d.ts +12 -2
- package/dist/src/workflow.d.ts.map +1 -1
- package/dist/src/workflow.js +14 -9
- package/dist/src/workflow.js.map +1 -1
- package/dist/src/workflow_management.d.ts +5 -3
- package/dist/src/workflow_management.d.ts.map +1 -1
- package/dist/src/workflow_management.js +45 -14
- package/dist/src/workflow_management.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/dist/src/dbos.js
CHANGED
|
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.DBOS = exports.runTransactionalInternalStep = exports.getResultInternal = exports.runInternalStep = exports.getExecutor = exports.resolveDelayEpochMS = exports.resolveStreamTimeoutMS = exports.resolveStreamPollingIntervalMs = exports.resolveStreamOffset = exports.resolvePollingIntervalMs = exports.resolveTimeoutSeconds = void 0;
|
|
29
|
+
exports.DBOS = exports.StepReservation = exports.runTransactionalInternalStep = exports.getResultInternal = exports.runInternalStep = exports.getExecutor = exports.resolveDelayEpochMS = exports.resolveStreamTimeoutMS = exports.resolveStreamPollingIntervalMs = exports.resolveStreamOffset = exports.resolvePollingIntervalMs = exports.resolveTimeoutSeconds = void 0;
|
|
30
30
|
const context_1 = require("./context");
|
|
31
31
|
const dbos_executor_1 = require("./dbos-executor");
|
|
32
32
|
const traces_1 = require("./telemetry/traces");
|
|
@@ -48,6 +48,7 @@ const crontab_1 = require("./scheduler/crontab");
|
|
|
48
48
|
const wfqueue_1 = require("./wfqueue");
|
|
49
49
|
const enqueue_workflow_1 = require("./enqueue_workflow");
|
|
50
50
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
51
|
+
const shard_keys_1 = require("./shard_keys");
|
|
51
52
|
function resolveTimeoutSeconds(options) {
|
|
52
53
|
if (options === undefined)
|
|
53
54
|
return undefined;
|
|
@@ -192,6 +193,46 @@ async function runTransactionalInternalStep(callback, funcName) {
|
|
|
192
193
|
return callback(undefined);
|
|
193
194
|
}
|
|
194
195
|
exports.runTransactionalInternalStep = runTransactionalInternalStep;
|
|
196
|
+
/**
|
|
197
|
+
* A step ID drawn by `DBOS.reserveStep`, run exactly once later with `run`.
|
|
198
|
+
* It must run in the workflow that reserved it; a reservation that never runs leaves a gap in the step IDs.
|
|
199
|
+
*/
|
|
200
|
+
class StepReservation {
|
|
201
|
+
stepID;
|
|
202
|
+
ctx;
|
|
203
|
+
#used = false;
|
|
204
|
+
constructor(
|
|
205
|
+
/** The reserved step ID */
|
|
206
|
+
stepID, ctx) {
|
|
207
|
+
this.stepID = stepID;
|
|
208
|
+
this.ctx = ctx;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Run `func` as a checkpointed step at the reserved step ID
|
|
212
|
+
* @param func - The function to run as a step
|
|
213
|
+
* @param config - Configuration information for the step, particularly the retry policy and name
|
|
214
|
+
* @returns - result (either obtained from invoking function, or retrieved if run before)
|
|
215
|
+
*/
|
|
216
|
+
async run(func, config = {}) {
|
|
217
|
+
(0, decorators_1.ensureDBOSIsLaunched)('steps');
|
|
218
|
+
if (this.#used) {
|
|
219
|
+
throw new error_1.DBOSInvalidWorkflowTransitionError('A step reservation can only be run once');
|
|
220
|
+
}
|
|
221
|
+
const ctx = (0, context_1.getCurrentContextStore)();
|
|
222
|
+
if (ctx === undefined ||
|
|
223
|
+
!(0, context_1.isInWorkflowCtx)(ctx) ||
|
|
224
|
+
ctx.workflowId !== this.ctx.workflowId ||
|
|
225
|
+
ctx.ownerXid !== this.ctx.ownerXid) {
|
|
226
|
+
throw new error_1.DBOSInvalidWorkflowTransitionError(`Step ${this.stepID} was reserved in workflow ${this.ctx.workflowId} and must be run from that workflow, outside any step or transaction`);
|
|
227
|
+
}
|
|
228
|
+
const name = config.name ?? func.name;
|
|
229
|
+
(0, step_1.validateStepConfig)(config, name);
|
|
230
|
+
// Claimed before any await, so a concurrent second run is rejected.
|
|
231
|
+
this.#used = true;
|
|
232
|
+
return await dbos_executor_1.DBOSExecutor.globalInstance.callStepFunctionAt(this.stepID, this.ctx, func, name, config, null);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
exports.StepReservation = StepReservation;
|
|
195
236
|
class DBOS {
|
|
196
237
|
///////
|
|
197
238
|
// Lifecycle
|
|
@@ -219,8 +260,29 @@ class DBOS {
|
|
|
219
260
|
* @param systemDatabaseUrl - The system database to create or migrate
|
|
220
261
|
* @param options.schemaName - The schema holding the DBOS system tables (default: `dbos`)
|
|
221
262
|
* @param options.applicationRole - A role to grant access to the system schema once migrated
|
|
263
|
+
* @param options.sharding - Create or migrate a sharded system database (requires @dbos-inc/dbos-enterprise)
|
|
222
264
|
*/
|
|
223
265
|
static async migrate(systemDatabaseUrl, options = {}) {
|
|
266
|
+
if (options.sharding) {
|
|
267
|
+
// Sharded migrations come from Transact Enterprise; the grant is the same.
|
|
268
|
+
const sysdb = (0, system_database_1.createSystemDatabase)({
|
|
269
|
+
systemDatabaseUrl,
|
|
270
|
+
logger: new logs_1.GlobalLogger(),
|
|
271
|
+
serializer: serialization_1.DBOSJSON,
|
|
272
|
+
schemaName: options.schemaName ?? 'dbos',
|
|
273
|
+
sharding: true,
|
|
274
|
+
});
|
|
275
|
+
try {
|
|
276
|
+
await sysdb.runMigrations();
|
|
277
|
+
}
|
|
278
|
+
finally {
|
|
279
|
+
await sysdb.destroy();
|
|
280
|
+
}
|
|
281
|
+
if (options.applicationRole) {
|
|
282
|
+
await (0, system_database_1.grantDbosSchemaPermissions)(systemDatabaseUrl, options.applicationRole, new logs_1.GlobalLogger(), options.schemaName ?? 'dbos');
|
|
283
|
+
}
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
224
286
|
await (0, system_database_1.migrateSystemDatabase)(systemDatabaseUrl, new logs_1.GlobalLogger(), options.schemaName ?? 'dbos', options.applicationRole);
|
|
225
287
|
}
|
|
226
288
|
/**
|
|
@@ -249,7 +311,11 @@ class DBOS {
|
|
|
249
311
|
}
|
|
250
312
|
(0, decorators_1.finalizeClassRegistrations)();
|
|
251
313
|
// Every enterprise feature requires Conductor to report the org entitled to it, and the package.
|
|
252
|
-
|
|
314
|
+
let features = enterprise.enabledFeatures(config);
|
|
315
|
+
if (internalConfig.sharding) {
|
|
316
|
+
// Auto-upgrade is disabled on a sharded system database, so it needs no entitlement.
|
|
317
|
+
features = features.filter((f) => f !== enterprise.AUTO_UPGRADE);
|
|
318
|
+
}
|
|
253
319
|
const entitlementOptions = {
|
|
254
320
|
enterpriseKey: config.transactEnterpriseKey,
|
|
255
321
|
conductorKey: options?.conductorKey,
|
|
@@ -264,7 +330,7 @@ class DBOS {
|
|
|
264
330
|
: undefined;
|
|
265
331
|
// Workflows can also enroll for auto-upgrade individually, which the config flag's check above does not cover.
|
|
266
332
|
const autoUpgradableWorkflowNames = (0, auto_upgrade_1.autoUpgradableWorkflows)(internalConfig.autoUpgrade);
|
|
267
|
-
if (autoUpgradableWorkflowNames.length > 0 && !internalConfig.autoUpgrade) {
|
|
333
|
+
if (autoUpgradableWorkflowNames.length > 0 && !internalConfig.autoUpgrade && !internalConfig.sharding) {
|
|
268
334
|
await enterprise.requireEnterprise([enterprise.AUTO_UPGRADE], entitlementOptions);
|
|
269
335
|
loadedEnterprise ??= enterprise.load(enterprise.AUTO_UPGRADE.description);
|
|
270
336
|
}
|
|
@@ -355,7 +421,11 @@ class DBOS {
|
|
|
355
421
|
executor.conductor.start();
|
|
356
422
|
}
|
|
357
423
|
// Start the auto-upgrade loop if any workflow takes part.
|
|
358
|
-
if (autoUpgradableWorkflowNames.length > 0) {
|
|
424
|
+
if (autoUpgradableWorkflowNames.length > 0 && executor.systemDatabase.sharded) {
|
|
425
|
+
// Its queries aren't keyed by shard, so it doesn't run on a sharded database.
|
|
426
|
+
executor.logger.warn('Auto-upgrade is disabled: it is not supported on a sharded system database.');
|
|
427
|
+
}
|
|
428
|
+
else if (autoUpgradableWorkflowNames.length > 0) {
|
|
359
429
|
(0, node_assert_1.default)(loadedEnterprise, 'the enterprise package is loaded whenever a workflow enrolls in auto-upgrade');
|
|
360
430
|
const upgradePass = new loadedEnterprise.AutoUpgradePass(executor, {
|
|
361
431
|
graceMs: internalConfig.autoUpgradeGraceMs ?? auto_upgrade_1.DEFAULT_AUTO_UPGRADE_GRACE_MS,
|
|
@@ -419,7 +489,7 @@ class DBOS {
|
|
|
419
489
|
executor.autoUpgrader = undefined;
|
|
420
490
|
}
|
|
421
491
|
await executor.awaitRunningWorkflows(drainTimeoutMS);
|
|
422
|
-
await executor.
|
|
492
|
+
await executor.stopBackgroundJobs();
|
|
423
493
|
}
|
|
424
494
|
// Stop the conductor
|
|
425
495
|
if (executor?.conductor) {
|
|
@@ -501,6 +571,11 @@ class DBOS {
|
|
|
501
571
|
static get workflowID() {
|
|
502
572
|
return (0, context_1.getCurrentContextStore)()?.workflowId;
|
|
503
573
|
}
|
|
574
|
+
/** The shard key of the currently executing workflow; undefined outside a workflow or on an unsharded database. */
|
|
575
|
+
static get shardKey() {
|
|
576
|
+
const ctx = (0, context_1.getCurrentContextStore)();
|
|
577
|
+
return ctx?.workflowId !== undefined ? ctx.shardKey : undefined;
|
|
578
|
+
}
|
|
504
579
|
/** Use portable serialization by default? */
|
|
505
580
|
static get #defaultSerializationType() {
|
|
506
581
|
return (0, context_1.getCurrentContextStore)()?.serializationType;
|
|
@@ -563,21 +638,23 @@ class DBOS {
|
|
|
563
638
|
* @param workflowID - ID of the workflow
|
|
564
639
|
* @returns status of the workflow as `WorkflowStatus`, or `null` if there is no workflow with `workflowID`
|
|
565
640
|
*/
|
|
566
|
-
static getWorkflowStatus(workflowID) {
|
|
641
|
+
static async getWorkflowStatus(workflowID, options) {
|
|
567
642
|
(0, decorators_1.ensureDBOSIsLaunched)('getWorkflowStatus');
|
|
568
|
-
|
|
569
|
-
if (DBOS.
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
643
|
+
return await (0, shard_keys_1.bindShardKeys)([[workflowID, options?.shardKey]], () => {
|
|
644
|
+
if (DBOS.isWithinWorkflow()) {
|
|
645
|
+
if (DBOS.isInStep()) {
|
|
646
|
+
// OK to use directly
|
|
647
|
+
return DBOS.#executor.getWorkflowStatus(workflowID);
|
|
648
|
+
}
|
|
649
|
+
else if (DBOS.isInWorkflow()) {
|
|
650
|
+
return DBOS.#executor.getWorkflowStatus(workflowID, DBOS.workflowID, (0, context_1.functionIDGetIncrement)());
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to `getWorkflowStatus` inside a `transaction`');
|
|
654
|
+
}
|
|
578
655
|
}
|
|
579
|
-
|
|
580
|
-
|
|
656
|
+
return DBOS.#executor.getWorkflowStatus(workflowID);
|
|
657
|
+
});
|
|
581
658
|
}
|
|
582
659
|
/**
|
|
583
660
|
* Get the workflow result, given a workflow ID
|
|
@@ -594,7 +671,8 @@ class DBOS {
|
|
|
594
671
|
// Reserve the function ID synchronously, before any await.
|
|
595
672
|
timerFuncID = (0, context_1.functionIDGetIncrement)();
|
|
596
673
|
}
|
|
597
|
-
|
|
674
|
+
const shardKey = typeof options === 'object' ? options.shardKey : undefined;
|
|
675
|
+
return await (0, shard_keys_1.bindShardKeys)([[workflowID, shardKey]], () => getResultInternal(workflowID, timeoutSeconds, timerFuncID, undefined, pollingIntervalMs));
|
|
598
676
|
}
|
|
599
677
|
/**
|
|
600
678
|
* Wait for any one of the given workflow handles to complete and return it.
|
|
@@ -618,9 +696,9 @@ class DBOS {
|
|
|
618
696
|
handleMap.set(handle.workflowID, handle);
|
|
619
697
|
}
|
|
620
698
|
const workflowIds = [...handleMap.keys()];
|
|
621
|
-
const completedId = await runInternalStep(async () => {
|
|
699
|
+
const completedId = await (0, shard_keys_1.bindShardKeys)(handles.map((h) => [h.workflowID, h.shardKey]), () => runInternalStep(async () => {
|
|
622
700
|
return await dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.awaitFirstWorkflowId(workflowIds, DBOS.workflowID, pollingIntervalMs);
|
|
623
|
-
}, 'DBOS.waitFirst');
|
|
701
|
+
}, 'DBOS.waitFirst'));
|
|
624
702
|
return handleMap.get(completedId);
|
|
625
703
|
}
|
|
626
704
|
/**
|
|
@@ -637,9 +715,9 @@ class DBOS {
|
|
|
637
715
|
}
|
|
638
716
|
const pollingIntervalMs = resolvePollingIntervalMs(options);
|
|
639
717
|
const workflowIds = [...new Set(handles.map((handle) => handle.workflowID))];
|
|
640
|
-
await runInternalStep(async () => {
|
|
718
|
+
await (0, shard_keys_1.bindShardKeys)(handles.map((h) => [h.workflowID, h.shardKey]), () => runInternalStep(async () => {
|
|
641
719
|
await dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.awaitWorkflowIds(workflowIds, DBOS.workflowID, pollingIntervalMs);
|
|
642
|
-
}, 'DBOS.waitAll');
|
|
720
|
+
}, 'DBOS.waitAll'));
|
|
643
721
|
return handles;
|
|
644
722
|
}
|
|
645
723
|
/**
|
|
@@ -649,15 +727,14 @@ class DBOS {
|
|
|
649
727
|
* @param workflowID - ID of the workflow
|
|
650
728
|
* @returns `WorkflowHandle` that can be used to poll for the status or result of any workflow with `workflowID`
|
|
651
729
|
*/
|
|
652
|
-
static retrieveWorkflow(workflowID) {
|
|
730
|
+
static retrieveWorkflow(workflowID, options) {
|
|
653
731
|
(0, decorators_1.ensureDBOSIsLaunched)('retrieveWorkflow');
|
|
654
|
-
if (DBOS.isWithinWorkflow()) {
|
|
655
|
-
|
|
656
|
-
throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to `retrieveWorkflow` inside a `transaction` or `step`');
|
|
657
|
-
}
|
|
658
|
-
return new workflow_1.RetrievedHandle(workflowID);
|
|
732
|
+
if (DBOS.isWithinWorkflow() && !DBOS.isInWorkflow()) {
|
|
733
|
+
throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to `retrieveWorkflow` inside a `transaction` or `step`');
|
|
659
734
|
}
|
|
660
|
-
|
|
735
|
+
// The resolved key, so the handle also works outside this binding.
|
|
736
|
+
const shardKey = (0, shard_keys_1.bindShardKeys)([[workflowID, options?.shardKey]], () => DBOS.#executor.systemDatabase.keyHint(workflowID));
|
|
737
|
+
return new workflow_1.RetrievedHandle(workflowID, shardKey);
|
|
661
738
|
}
|
|
662
739
|
/**
|
|
663
740
|
* Query the system database for all workflows matching the provided predicate
|
|
@@ -687,14 +764,14 @@ class DBOS {
|
|
|
687
764
|
* @param workflowID - ID of the workflow
|
|
688
765
|
* @param priority - Priority value (0 to 2,147,483,647). Lower values are dequeued first.
|
|
689
766
|
*/
|
|
690
|
-
static async setWorkflowPriority(workflowID, priority) {
|
|
767
|
+
static async setWorkflowPriority(workflowID, priority, options) {
|
|
691
768
|
(0, decorators_1.ensureDBOSIsLaunched)('setWorkflowPriority');
|
|
692
769
|
if (priority < dbos_executor_1.DBOS_QUEUE_MIN_PRIORITY || priority > dbos_executor_1.DBOS_QUEUE_MAX_PRIORITY) {
|
|
693
770
|
throw new error_1.DBOSInvalidQueuePriorityError(priority, dbos_executor_1.DBOS_QUEUE_MIN_PRIORITY, dbos_executor_1.DBOS_QUEUE_MAX_PRIORITY);
|
|
694
771
|
}
|
|
695
|
-
return runInternalStep(async () => {
|
|
772
|
+
return (0, shard_keys_1.bindShardKeys)([[workflowID, options?.shardKey]], () => runInternalStep(async () => {
|
|
696
773
|
return dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.setWorkflowPriority(workflowID, priority);
|
|
697
|
-
}, 'DBOS.setWorkflowPriority');
|
|
774
|
+
}, 'DBOS.setWorkflowPriority'));
|
|
698
775
|
}
|
|
699
776
|
/**
|
|
700
777
|
* Update the delay on a DELAYED workflow.
|
|
@@ -706,9 +783,10 @@ class DBOS {
|
|
|
706
783
|
static async setWorkflowDelay(workflowID, options) {
|
|
707
784
|
(0, decorators_1.ensureDBOSIsLaunched)('setWorkflowDelay');
|
|
708
785
|
const delayUntilEpochMS = resolveDelayEpochMS(options);
|
|
709
|
-
|
|
786
|
+
const shardKey = typeof options === 'object' ? options.shardKey : undefined;
|
|
787
|
+
return (0, shard_keys_1.bindShardKeys)([[workflowID, shardKey]], () => runInternalStep(async () => {
|
|
710
788
|
return dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.setWorkflowDelay(workflowID, delayUntilEpochMS);
|
|
711
|
-
}, 'DBOS.setWorkflowDelay');
|
|
789
|
+
}, 'DBOS.setWorkflowDelay'));
|
|
712
790
|
}
|
|
713
791
|
/**
|
|
714
792
|
* Retrieve the steps of a workflow
|
|
@@ -718,9 +796,9 @@ class DBOS {
|
|
|
718
796
|
*/
|
|
719
797
|
static async listWorkflowSteps(workflowID, options) {
|
|
720
798
|
(0, decorators_1.ensureDBOSIsLaunched)('listWorkflowSteps');
|
|
721
|
-
return await runInternalStep(async () => {
|
|
799
|
+
return await (0, shard_keys_1.bindShardKeys)([[workflowID, options?.shardKey]], () => runInternalStep(async () => {
|
|
722
800
|
return await DBOS.#executor.listWorkflowSteps(workflowID, true, options);
|
|
723
|
-
}, 'DBOS.listWorkflowSteps');
|
|
801
|
+
}, 'DBOS.listWorkflowSteps'));
|
|
724
802
|
}
|
|
725
803
|
/**
|
|
726
804
|
* Cancel a workflow given its ID.
|
|
@@ -739,9 +817,9 @@ class DBOS {
|
|
|
739
817
|
*/
|
|
740
818
|
static async cancelWorkflows(workflowIDs, options) {
|
|
741
819
|
(0, decorators_1.ensureDBOSIsLaunched)('cancelWorkflows');
|
|
742
|
-
return runInternalStep(async () => {
|
|
820
|
+
return (0, shard_keys_1.bindShardKeys)(workflowIDs.map((wid) => [wid, options?.shardKey]), () => runInternalStep(async () => {
|
|
743
821
|
return dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.cancelWorkflows(workflowIDs, options?.cancelChildren);
|
|
744
|
-
}, 'DBOS.cancelWorkflow');
|
|
822
|
+
}, 'DBOS.cancelWorkflow'));
|
|
745
823
|
}
|
|
746
824
|
/**
|
|
747
825
|
* Resume a workflow given its ID.
|
|
@@ -749,8 +827,11 @@ class DBOS {
|
|
|
749
827
|
* @throws DBOSNonExistentWorkflowError if the workflow does not exist
|
|
750
828
|
*/
|
|
751
829
|
static async resumeWorkflow(workflowID, options) {
|
|
752
|
-
|
|
753
|
-
|
|
830
|
+
return await (0, shard_keys_1.bindShardKeys)([[workflowID, options?.shardKey]], async () => {
|
|
831
|
+
await this.resumeWorkflows([workflowID], options);
|
|
832
|
+
// The resolved key, so the handle also works outside this binding.
|
|
833
|
+
return new workflow_1.RetrievedHandle(workflowID, DBOS.#executor.systemDatabase.keyHint(workflowID));
|
|
834
|
+
});
|
|
754
835
|
}
|
|
755
836
|
/**
|
|
756
837
|
* Resume multiple workflows given their IDs.
|
|
@@ -760,10 +841,12 @@ class DBOS {
|
|
|
760
841
|
*/
|
|
761
842
|
static async resumeWorkflows(workflowIDs, options) {
|
|
762
843
|
(0, decorators_1.ensureDBOSIsLaunched)('resumeWorkflows');
|
|
763
|
-
await
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
844
|
+
return await (0, shard_keys_1.bindShardKeys)(workflowIDs.map((wid) => [wid, options?.shardKey]), async () => {
|
|
845
|
+
await runInternalStep(async () => {
|
|
846
|
+
return dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.resumeWorkflows(workflowIDs, options?.queueName);
|
|
847
|
+
}, 'DBOS.resumeWorkflow');
|
|
848
|
+
return workflowIDs.map((wfid) => new workflow_1.RetrievedHandle(wfid, options?.shardKey));
|
|
849
|
+
});
|
|
767
850
|
}
|
|
768
851
|
/**
|
|
769
852
|
* Delete a workflow and optionally all its child workflows.
|
|
@@ -774,8 +857,8 @@ class DBOS {
|
|
|
774
857
|
* @param workflowID - ID of the workflow to delete
|
|
775
858
|
* @param deleteChildren - If true, also delete all child workflows recursively (default: false)
|
|
776
859
|
*/
|
|
777
|
-
static async deleteWorkflow(workflowID, deleteChildren = false) {
|
|
778
|
-
return this.deleteWorkflows([workflowID], deleteChildren);
|
|
860
|
+
static async deleteWorkflow(workflowID, deleteChildren = false, options) {
|
|
861
|
+
return this.deleteWorkflows([workflowID], deleteChildren, options);
|
|
779
862
|
}
|
|
780
863
|
/**
|
|
781
864
|
* Delete multiple workflows and optionally all their child workflows.
|
|
@@ -785,11 +868,11 @@ class DBOS {
|
|
|
785
868
|
* @param workflowIDs - IDs of the workflows to delete
|
|
786
869
|
* @param deleteChildren - If true, also delete all child workflows recursively (default: false)
|
|
787
870
|
*/
|
|
788
|
-
static async deleteWorkflows(workflowIDs, deleteChildren = false) {
|
|
871
|
+
static async deleteWorkflows(workflowIDs, deleteChildren = false, options) {
|
|
789
872
|
(0, decorators_1.ensureDBOSIsLaunched)('deleteWorkflows');
|
|
790
|
-
return runInternalStep(async () => {
|
|
873
|
+
return (0, shard_keys_1.bindShardKeys)(workflowIDs.map((wid) => [wid, options?.shardKey]), () => runInternalStep(async () => {
|
|
791
874
|
return dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.deleteWorkflows(workflowIDs, deleteChildren);
|
|
792
|
-
}, 'DBOS.deleteWorkflow');
|
|
875
|
+
}, 'DBOS.deleteWorkflow'));
|
|
793
876
|
}
|
|
794
877
|
/**
|
|
795
878
|
* Fork a workflow given its ID.
|
|
@@ -801,10 +884,14 @@ class DBOS {
|
|
|
801
884
|
*/
|
|
802
885
|
static async forkWorkflow(workflowID, startStep, options) {
|
|
803
886
|
(0, decorators_1.ensureDBOSIsLaunched)('forkWorkflow');
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
887
|
+
return await (0, shard_keys_1.bindShardKeys)([[workflowID, options?.shardKey]], async () => {
|
|
888
|
+
const forkedID = await runInternalStep(async () => {
|
|
889
|
+
return await DBOS.#executor.forkWorkflow(workflowID, startStep, options);
|
|
890
|
+
}, 'DBOS.forkWorkflow');
|
|
891
|
+
// A fork lives under its original's shard key, however the caller supplied it.
|
|
892
|
+
const forkKey = DBOS.#executor.systemDatabase.keyHint(workflowID);
|
|
893
|
+
return new workflow_1.RetrievedHandle(forkedID, forkKey);
|
|
894
|
+
});
|
|
808
895
|
}
|
|
809
896
|
/**
|
|
810
897
|
* Rewind a workflow given its ID: drop its recorded history from `startStep` onwards
|
|
@@ -828,10 +915,13 @@ class DBOS {
|
|
|
828
915
|
static async rewindWorkflow(workflowID, options) {
|
|
829
916
|
(0, decorators_1.ensureDBOSIsLaunched)('rewindWorkflow');
|
|
830
917
|
const startStep = options?.startStep ?? 0;
|
|
831
|
-
await
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
918
|
+
return await (0, shard_keys_1.bindShardKeys)([[workflowID, options?.shardKey]], async () => {
|
|
919
|
+
await runInternalStep(async () => {
|
|
920
|
+
return await DBOS.#executor.rewindWorkflow(workflowID, startStep, options ?? {});
|
|
921
|
+
}, 'DBOS.rewindWorkflow');
|
|
922
|
+
// The resolved key, so the handle also works outside this binding.
|
|
923
|
+
return new workflow_1.RetrievedHandle(workflowID, DBOS.#executor.systemDatabase.keyHint(workflowID));
|
|
924
|
+
});
|
|
835
925
|
}
|
|
836
926
|
/**
|
|
837
927
|
* Sleep for the specified amount of time.
|
|
@@ -946,6 +1036,13 @@ class DBOS {
|
|
|
946
1036
|
(0, decorators_1.ensureDBOSIsLaunched)('workflows');
|
|
947
1037
|
return DBOS.#withTopContext({ workflowTimeoutMS: timeoutMS }, callback);
|
|
948
1038
|
}
|
|
1039
|
+
/** Set the shard key of each workflow started, enqueued, or called in `callback`; their children don't inherit it. */
|
|
1040
|
+
static async withShardKey(shardKey, callback) {
|
|
1041
|
+
if (typeof shardKey !== 'string' || shardKey === '') {
|
|
1042
|
+
throw new error_1.DBOSError('shardKey must be a non-empty string');
|
|
1043
|
+
}
|
|
1044
|
+
return DBOS.#withTopContext({ shardKeyForNextWorkflow: shardKey }, callback);
|
|
1045
|
+
}
|
|
949
1046
|
static async #withTopContext(options, callback) {
|
|
950
1047
|
const pctx = (0, context_1.getCurrentContextStore)();
|
|
951
1048
|
if (pctx) {
|
|
@@ -1033,16 +1130,16 @@ class DBOS {
|
|
|
1033
1130
|
// calls in the same workflow don't race for IDs.
|
|
1034
1131
|
const functionID = (0, context_1.functionIDGetIncrement)();
|
|
1035
1132
|
const sermsg = await (0, serialization_1.serializeValue)(message, DBOS.#executor.serializer, options?.serializationType ?? DBOS.#defaultSerializationType);
|
|
1036
|
-
return await dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.send(DBOS.workflowID, functionID, destinationID, sermsg.serializedValue, topic, sermsg.serialization, idempotencyKey);
|
|
1133
|
+
return await dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.send(DBOS.workflowID, functionID, destinationID, sermsg.serializedValue, topic, sermsg.serialization, idempotencyKey, options?.shardKey);
|
|
1037
1134
|
}
|
|
1038
1135
|
else if (DBOS.isInStep()) {
|
|
1039
1136
|
// Inside a step: not recorded, but fenced on the enclosing workflow's ownership.
|
|
1040
1137
|
const sermsg = await (0, serialization_1.serializeValue)(message, DBOS.#executor.serializer, options?.serializationType ?? DBOS.#defaultSerializationType);
|
|
1041
|
-
return await dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.sendFromStep(DBOS.workflowID, destinationID, sermsg.serializedValue, topic, sermsg.serialization, idempotencyKey);
|
|
1138
|
+
return await dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.sendFromStep(DBOS.workflowID, destinationID, sermsg.serializedValue, topic, sermsg.serialization, idempotencyKey, options?.shardKey);
|
|
1042
1139
|
}
|
|
1043
1140
|
else {
|
|
1044
1141
|
const sermsg = await (0, serialization_1.serializeValue)(message, DBOS.#executor.serializer, options?.serializationType ?? DBOS.#defaultSerializationType);
|
|
1045
|
-
return dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.sendDirect(destinationID, sermsg.serializedValue, topic, sermsg.serialization, idempotencyKey);
|
|
1142
|
+
return dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.sendDirect(destinationID, sermsg.serializedValue, topic, sermsg.serialization, idempotencyKey, undefined, options?.shardKey);
|
|
1046
1143
|
}
|
|
1047
1144
|
}
|
|
1048
1145
|
/**
|
|
@@ -1110,6 +1207,10 @@ class DBOS {
|
|
|
1110
1207
|
*/
|
|
1111
1208
|
static async getEvent(workflowID, key, options) {
|
|
1112
1209
|
(0, decorators_1.ensureDBOSIsLaunched)('getEvent');
|
|
1210
|
+
const shardKey = typeof options === 'object' ? options.shardKey : undefined;
|
|
1211
|
+
return await (0, shard_keys_1.bindShardKeys)([[workflowID, shardKey]], () => DBOS.#getEvent(workflowID, key, options));
|
|
1212
|
+
}
|
|
1213
|
+
static async #getEvent(workflowID, key, options) {
|
|
1113
1214
|
const timeoutSeconds = resolveTimeoutSeconds(options);
|
|
1114
1215
|
const pollingIntervalMs = resolvePollingIntervalMs(options);
|
|
1115
1216
|
if (DBOS.isWithinWorkflow()) {
|
|
@@ -1187,6 +1288,7 @@ class DBOS {
|
|
|
1187
1288
|
timeoutMS: resolveStreamTimeoutMS(options),
|
|
1188
1289
|
functionName: system_database_1.DBOS_FUNCNAME_READSTREAM,
|
|
1189
1290
|
checkpoint: true,
|
|
1291
|
+
shardKey: options.shardKey,
|
|
1190
1292
|
});
|
|
1191
1293
|
}
|
|
1192
1294
|
/**
|
|
@@ -1210,6 +1312,7 @@ class DBOS {
|
|
|
1210
1312
|
timeoutMS: resolveStreamTimeoutMS(options),
|
|
1211
1313
|
functionName: system_database_1.DBOS_FUNCNAME_READSTREAMOFFSET,
|
|
1212
1314
|
checkpoint: true,
|
|
1315
|
+
shardKey: options.shardKey,
|
|
1213
1316
|
});
|
|
1214
1317
|
}
|
|
1215
1318
|
//////
|
|
@@ -1266,6 +1369,8 @@ class DBOS {
|
|
|
1266
1369
|
const ppctx = (0, context_1.getCurrentContextStore)();
|
|
1267
1370
|
const queueName = params.queueName ?? ppctx?.queueAssignedForWorkflows;
|
|
1268
1371
|
const timeoutMS = params.timeoutMS ?? ppctx?.workflowTimeoutMS;
|
|
1372
|
+
// Set by DBOS.withShardKey; a workflow never inherits its caller's own key.
|
|
1373
|
+
const shardKey = (0, context_1.nextWorkflowShardKey)(ppctx);
|
|
1269
1374
|
const instance = $this === undefined || typeof $this === 'function' ? undefined : $this;
|
|
1270
1375
|
if (instance && !(instance instanceof decorators_1.ConfiguredInstance)) {
|
|
1271
1376
|
throw new error_1.DBOSInvalidWorkflowTransitionError('Attempt to call a `workflow` function on an object that is not a `ConfiguredInstance`');
|
|
@@ -1303,7 +1408,7 @@ class DBOS {
|
|
|
1303
1408
|
if (isChild) {
|
|
1304
1409
|
const pwfid = ppctx.workflowId;
|
|
1305
1410
|
const wfParams = {
|
|
1306
|
-
workflowUUID: wfId || pwfid
|
|
1411
|
+
workflowUUID: wfId || (0, context_1.childWorkflowID)(pwfid, funcId, ppctx?.shardKey, shardKey),
|
|
1307
1412
|
configuredInstance: instance,
|
|
1308
1413
|
queueName,
|
|
1309
1414
|
timeoutMS,
|
|
@@ -1315,6 +1420,7 @@ class DBOS {
|
|
|
1315
1420
|
workflowAttributes: params.workflowAttributes,
|
|
1316
1421
|
authenticatedUser: params.authenticatedUser,
|
|
1317
1422
|
authenticatedRoles: params.authenticatedRoles,
|
|
1423
|
+
shardKey,
|
|
1318
1424
|
};
|
|
1319
1425
|
return await invokeRegOp(wfParams, pwfid, funcId);
|
|
1320
1426
|
}
|
|
@@ -1330,6 +1436,7 @@ class DBOS {
|
|
|
1330
1436
|
workflowAttributes: params.workflowAttributes,
|
|
1331
1437
|
authenticatedUser: params.authenticatedUser,
|
|
1332
1438
|
authenticatedRoles: params.authenticatedRoles,
|
|
1439
|
+
shardKey,
|
|
1333
1440
|
};
|
|
1334
1441
|
return await invokeRegOp(wfParams, undefined, undefined);
|
|
1335
1442
|
}
|
|
@@ -1356,6 +1463,8 @@ class DBOS {
|
|
|
1356
1463
|
// Reserve the parent's child-funcID once, before any await. Each loop iteration passes it
|
|
1357
1464
|
// through so the return-existing retry loop consumes a single funcID instead of one per try.
|
|
1358
1465
|
const childFuncId = DBOS.isInWorkflow() ? (0, context_1.functionIDGetIncrement)() : undefined;
|
|
1466
|
+
// The key the attached-to workflow lives under, as the start would have used.
|
|
1467
|
+
const shardKey = (0, context_1.nextWorkflowShardKey)((0, context_1.getCurrentContextStore)());
|
|
1359
1468
|
// Resolve the workflow ID once: if the caller supplied one explicitly or via
|
|
1360
1469
|
// `withNextWorkflowID`, reuse it on every iteration.
|
|
1361
1470
|
const resolvedWorkflowID = (0, context_1.getNextWFID)(params.workflowID);
|
|
@@ -1376,7 +1485,7 @@ class DBOS {
|
|
|
1376
1485
|
const now = Date.now();
|
|
1377
1486
|
await dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.recordOperationResult(DBOS.workflowID, childFuncId, regOP.name, true, now, now, { childWorkflowID: existingID });
|
|
1378
1487
|
}
|
|
1379
|
-
return new workflow_1.RetrievedHandle(existingID);
|
|
1488
|
+
return new workflow_1.RetrievedHandle(existingID, shardKey);
|
|
1380
1489
|
}
|
|
1381
1490
|
// The prior workflow's deduplication_id was cleared between our INSERT and
|
|
1382
1491
|
// the lookup (it completed or was cancelled). Loop and try to claim the slot.
|
|
@@ -1511,6 +1620,21 @@ class DBOS {
|
|
|
1511
1620
|
}
|
|
1512
1621
|
return func();
|
|
1513
1622
|
}
|
|
1623
|
+
/**
|
|
1624
|
+
* Draw the next step ID now, so a step can run at it later (for example, from another task) with `run`.
|
|
1625
|
+
* This fixes the step's position in the workflow's history when it would otherwise depend on timing.
|
|
1626
|
+
* Must be called from workflow code, not from a step or transaction.
|
|
1627
|
+
* @returns - a `StepReservation` that runs a step at the reserved ID
|
|
1628
|
+
*/
|
|
1629
|
+
static reserveStep() {
|
|
1630
|
+
(0, decorators_1.ensureDBOSIsLaunched)('steps');
|
|
1631
|
+
const ctx = (0, context_1.getCurrentContextStore)();
|
|
1632
|
+
if (ctx === undefined || !(0, context_1.isInWorkflowCtx)(ctx)) {
|
|
1633
|
+
throw new error_1.DBOSInvalidWorkflowTransitionError('DBOS.reserveStep must be called from a workflow, outside any step or transaction');
|
|
1634
|
+
}
|
|
1635
|
+
const stepID = (0, context_1.functionIDGetIncrement)();
|
|
1636
|
+
return new StepReservation(stepID, { ...ctx });
|
|
1637
|
+
}
|
|
1514
1638
|
/**
|
|
1515
1639
|
* Register serialization recipe; this is used to save/retrieve objects from the DBOS system
|
|
1516
1640
|
* database. This includes workflow inputs, function return values, messages, and events.
|
|
@@ -1645,6 +1769,7 @@ class DBOS {
|
|
|
1645
1769
|
cronTimezone: options.options?.cronTimezone ?? null,
|
|
1646
1770
|
queueName: options.options?.queueName ?? null,
|
|
1647
1771
|
applicationName: utils_1.globalParams.appName,
|
|
1772
|
+
shardKey: options.options?.shardKey,
|
|
1648
1773
|
};
|
|
1649
1774
|
await runTransactionalInternalStep((client) => dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.createSchedule(schedInternal, client), 'DBOS.createSchedule');
|
|
1650
1775
|
}
|
|
@@ -1730,6 +1855,7 @@ class DBOS {
|
|
|
1730
1855
|
automaticBackfill: sched.automaticBackfill ?? false,
|
|
1731
1856
|
cronTimezone: sched.cronTimezone ?? null,
|
|
1732
1857
|
queueName: sched.queueName ?? null,
|
|
1858
|
+
shardKey: sched.shardKey,
|
|
1733
1859
|
});
|
|
1734
1860
|
}
|
|
1735
1861
|
await dbos_executor_1.DBOSExecutor.globalInstance.systemDatabase.applySchedules(internals);
|
|
@@ -1741,7 +1867,7 @@ class DBOS {
|
|
|
1741
1867
|
}
|
|
1742
1868
|
const executor = dbos_executor_1.DBOSExecutor.globalInstance;
|
|
1743
1869
|
const workflowID = await (0, scheduler_1.triggerSchedule)(executor.systemDatabase, executor.serializer, name);
|
|
1744
|
-
return new workflow_1.RetrievedHandle(workflowID);
|
|
1870
|
+
return new workflow_1.RetrievedHandle(workflowID, await (0, scheduler_1.scheduleShardKey)(executor.systemDatabase, name));
|
|
1745
1871
|
}
|
|
1746
1872
|
static async backfillSchedule(name, start, end) {
|
|
1747
1873
|
(0, decorators_1.ensureDBOSIsLaunched)('backfillSchedule');
|
|
@@ -1750,7 +1876,8 @@ class DBOS {
|
|
|
1750
1876
|
}
|
|
1751
1877
|
const executor = dbos_executor_1.DBOSExecutor.globalInstance;
|
|
1752
1878
|
const workflowIDs = await (0, scheduler_1.backfillSchedule)(executor.systemDatabase, executor.serializer, name, start, end);
|
|
1753
|
-
|
|
1879
|
+
const shardKey = await (0, scheduler_1.scheduleShardKey)(executor.systemDatabase, name);
|
|
1880
|
+
return workflowIDs.map((id) => new workflow_1.RetrievedHandle(id, shardKey));
|
|
1754
1881
|
}
|
|
1755
1882
|
// ==================== Application Versions ====================
|
|
1756
1883
|
/**
|