@dbos-inc/dbos-sdk 1.28.14-preview.g65f6845035 → 1.29.84-preview.gd5bdeb6a8d
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/context.d.ts +36 -0
- package/dist/src/context.d.ts.map +1 -1
- package/dist/src/context.js +104 -1
- package/dist/src/context.js.map +1 -1
- package/dist/src/data_validation.d.ts.map +1 -1
- package/dist/src/data_validation.js +6 -2
- package/dist/src/data_validation.js.map +1 -1
- package/dist/src/dbos-executor.d.ts +36 -20
- package/dist/src/dbos-executor.d.ts.map +1 -1
- package/dist/src/dbos-executor.js +309 -21
- package/dist/src/dbos-executor.js.map +1 -1
- package/dist/src/dbos-runtime/runtime.d.ts +3 -8
- package/dist/src/dbos-runtime/runtime.d.ts.map +1 -1
- package/dist/src/dbos-runtime/runtime.js +7 -11
- package/dist/src/dbos-runtime/runtime.js.map +1 -1
- package/dist/src/dbos.d.ts +109 -0
- package/dist/src/dbos.d.ts.map +1 -0
- package/dist/src/dbos.js +551 -0
- package/dist/src/dbos.js.map +1 -0
- package/dist/src/debugger/debug_workflow.d.ts.map +1 -1
- package/dist/src/debugger/debug_workflow.js +5 -1
- package/dist/src/debugger/debug_workflow.js.map +1 -1
- package/dist/src/decorators.d.ts +11 -2
- package/dist/src/decorators.d.ts.map +1 -1
- package/dist/src/decorators.js +30 -8
- package/dist/src/decorators.js.map +1 -1
- package/dist/src/error.d.ts +6 -0
- package/dist/src/error.d.ts.map +1 -1
- package/dist/src/error.js +20 -2
- package/dist/src/error.js.map +1 -1
- package/dist/src/eventreceiver.d.ts +6 -2
- package/dist/src/eventreceiver.d.ts.map +1 -1
- package/dist/src/httpServer/handler.d.ts +0 -1
- package/dist/src/httpServer/handler.d.ts.map +1 -1
- package/dist/src/httpServer/handler.js +5 -13
- package/dist/src/httpServer/handler.js.map +1 -1
- package/dist/src/httpServer/middleware.d.ts +13 -2
- package/dist/src/httpServer/middleware.d.ts.map +1 -1
- package/dist/src/httpServer/middleware.js +101 -1
- package/dist/src/httpServer/middleware.js.map +1 -1
- package/dist/src/httpServer/server.d.ts +3 -2
- package/dist/src/httpServer/server.d.ts.map +1 -1
- package/dist/src/httpServer/server.js +40 -30
- package/dist/src/httpServer/server.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 +3 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/procedure.d.ts +1 -0
- package/dist/src/procedure.d.ts.map +1 -1
- package/dist/src/procedure.js.map +1 -1
- package/dist/src/scheduler/scheduler.d.ts.map +1 -1
- package/dist/src/scheduler/scheduler.js +3 -1
- package/dist/src/scheduler/scheduler.js.map +1 -1
- package/dist/src/system_database.d.ts.map +1 -1
- package/dist/src/system_database.js.map +1 -1
- package/dist/src/telemetry/logs.d.ts +11 -5
- package/dist/src/telemetry/logs.d.ts.map +1 -1
- package/dist/src/telemetry/logs.js +8 -11
- package/dist/src/telemetry/logs.js.map +1 -1
- package/dist/src/testing/testing_runtime.d.ts.map +1 -1
- package/dist/src/testing/testing_runtime.js +10 -4
- package/dist/src/testing/testing_runtime.js.map +1 -1
- package/dist/src/user_database.d.ts +1 -1
- package/dist/src/user_database.d.ts.map +1 -1
- package/dist/src/wfqueue.d.ts.map +1 -1
- package/dist/src/wfqueue.js +17 -2
- package/dist/src/wfqueue.js.map +1 -1
- package/dist/src/workflow.d.ts +7 -6
- package/dist/src/workflow.d.ts.map +1 -1
- package/dist/src/workflow.js +10 -187
- package/dist/src/workflow.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +5 -1
@@ -4,10 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
6
|
exports.DBOSExecutor = exports.OperationType = exports.dbosNull = void 0;
|
7
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
8
7
|
const error_1 = require("./error");
|
9
8
|
const workflow_1 = require("./workflow");
|
10
9
|
const transaction_1 = require("./transaction");
|
10
|
+
const step_1 = require("./step");
|
11
11
|
const collector_1 = require("./telemetry/collector");
|
12
12
|
const traces_1 = require("./telemetry/traces");
|
13
13
|
const logs_1 = require("./telemetry/logs");
|
@@ -84,7 +84,9 @@ class DBOSExecutor {
|
|
84
84
|
typeormEntities = [];
|
85
85
|
drizzleEntities = {};
|
86
86
|
eventReceivers = [];
|
87
|
-
scheduler =
|
87
|
+
scheduler = undefined;
|
88
|
+
wfqEnded = undefined;
|
89
|
+
static globalInstance = undefined;
|
88
90
|
/* WORKFLOW EXECUTOR LIFE CYCLE MANAGEMENT */
|
89
91
|
constructor(config, systemDatabase) {
|
90
92
|
this.config = config;
|
@@ -143,6 +145,7 @@ class DBOSExecutor {
|
|
143
145
|
}, this.flushBufferIntervalMs);
|
144
146
|
this.logger.debug("Started workflow status buffer worker");
|
145
147
|
this.initialized = false;
|
148
|
+
DBOSExecutor.globalInstance = this;
|
146
149
|
}
|
147
150
|
configureDbClient() {
|
148
151
|
const userDbClient = this.config.userDbclient;
|
@@ -387,6 +390,9 @@ class DBOSExecutor {
|
|
387
390
|
}
|
388
391
|
await this.procedurePool.end();
|
389
392
|
await this.logger.destroy();
|
393
|
+
if (DBOSExecutor.globalInstance === this) {
|
394
|
+
DBOSExecutor.globalInstance = undefined;
|
395
|
+
}
|
390
396
|
}
|
391
397
|
/* WORKFLOW OPERATIONS */
|
392
398
|
#registerWorkflow(ro) {
|
@@ -401,6 +407,7 @@ class DBOSExecutor {
|
|
401
407
|
const workflowInfo = {
|
402
408
|
workflow: wf,
|
403
409
|
config: { ...ro.workflowConfig },
|
410
|
+
registration: ro,
|
404
411
|
};
|
405
412
|
this.workflowInfoMap.set(wfn, workflowInfo);
|
406
413
|
this.logger.debug(`Registered workflow ${wfn}`);
|
@@ -414,6 +421,7 @@ class DBOSExecutor {
|
|
414
421
|
const txnInfo = {
|
415
422
|
transaction: txf,
|
416
423
|
config: { ...ro.txnConfig },
|
424
|
+
registration: ro,
|
417
425
|
};
|
418
426
|
this.transactionInfoMap.set(tfn, txnInfo);
|
419
427
|
this.logger.debug(`Registered transaction ${tfn}`);
|
@@ -424,11 +432,12 @@ class DBOSExecutor {
|
|
424
432
|
if (this.stepInfoMap.has(cfn)) {
|
425
433
|
throw new error_1.DBOSError(`Repeated Commmunicator name: ${cfn}`);
|
426
434
|
}
|
427
|
-
const
|
435
|
+
const stepInfo = {
|
428
436
|
step: comm,
|
429
437
|
config: { ...ro.commConfig },
|
438
|
+
registration: ro,
|
430
439
|
};
|
431
|
-
this.stepInfoMap.set(cfn,
|
440
|
+
this.stepInfoMap.set(cfn, stepInfo);
|
432
441
|
this.logger.debug(`Registered step ${cfn}`);
|
433
442
|
}
|
434
443
|
#registerProcedure(ro) {
|
@@ -440,6 +449,7 @@ class DBOSExecutor {
|
|
440
449
|
const procInfo = {
|
441
450
|
procedure: proc,
|
442
451
|
config: { ...ro.procConfig },
|
452
|
+
registration: ro,
|
443
453
|
};
|
444
454
|
this.procedureInfoMap.set(cfn, procInfo);
|
445
455
|
this.logger.debug(`Registered stored proc ${cfn}`);
|
@@ -532,6 +542,28 @@ class DBOSExecutor {
|
|
532
542
|
throw new error_1.DBOSNotRegisteredError(wf.name);
|
533
543
|
}
|
534
544
|
const wConfig = wInfo.config;
|
545
|
+
// Compatibility with the old way of calling workflows, which would include a parentCtx
|
546
|
+
const pctx = (0, context_1.getCurrentContextStore)();
|
547
|
+
const passContext = wInfo.registration?.passContext ?? true;
|
548
|
+
if (!passContext && pctx) {
|
549
|
+
const span = this.tracer.startSpan(wf.name, {
|
550
|
+
operationUUID: workflowUUID,
|
551
|
+
operationType: exports.OperationType.WORKFLOW,
|
552
|
+
status: workflow_1.StatusString.PENDING,
|
553
|
+
authenticatedUser: pctx.authenticatedUser,
|
554
|
+
assumedRole: pctx.assumedRole,
|
555
|
+
authenticatedRoles: pctx.authenticatedRoles,
|
556
|
+
}
|
557
|
+
// TODO the span should be taken from pctx
|
558
|
+
//pctx.span
|
559
|
+
);
|
560
|
+
params.parentCtx = new context_1.DBOSContextImpl(wf.name, span, this.logger);
|
561
|
+
params.parentCtx.request = pctx.request || {};
|
562
|
+
params.parentCtx.authenticatedUser = pctx.authenticatedUser || "";
|
563
|
+
params.parentCtx.assumedRole = pctx.assumedRole || "";
|
564
|
+
params.parentCtx.authenticatedRoles = pctx.authenticatedRoles || [];
|
565
|
+
params.parentCtx.workflowUUID = workflowUUID;
|
566
|
+
}
|
535
567
|
const wCtxt = new workflow_1.WorkflowContextImpl(this, params.parentCtx, workflowUUID, wConfig, wf.name, presetUUID, params.tempWfType, params.tempWfName);
|
536
568
|
const internalStatus = {
|
537
569
|
workflowUUID: workflowUUID,
|
@@ -570,7 +602,16 @@ class DBOSExecutor {
|
|
570
602
|
let result;
|
571
603
|
// Execute the workflow.
|
572
604
|
try {
|
573
|
-
|
605
|
+
let cresult;
|
606
|
+
await (0, context_1.runWithWorkflowContext)(wCtxt, async () => {
|
607
|
+
if (passContext) {
|
608
|
+
cresult = await wf.call(params.configuredInstance, wCtxt, ...args);
|
609
|
+
}
|
610
|
+
else {
|
611
|
+
cresult = await wf.call(params.configuredInstance, ...args);
|
612
|
+
}
|
613
|
+
});
|
614
|
+
result = cresult;
|
574
615
|
internalStatus.output = result;
|
575
616
|
internalStatus.status = workflow_1.StatusString.SUCCESS;
|
576
617
|
if (internalStatus.queueName) {
|
@@ -675,17 +716,19 @@ class DBOSExecutor {
|
|
675
716
|
if (utils_1.DBOSJSON.stringify(args) !== utils_1.DBOSJSON.stringify(recordedInputs)) {
|
676
717
|
throw new error_1.DBOSDebuggerError(`Detect different input for the workflow UUID ${workflowUUID}!\n Received: ${utils_1.DBOSJSON.stringify(args)}\n Original: ${utils_1.DBOSJSON.stringify(recordedInputs)}`);
|
677
718
|
}
|
678
|
-
const workflowPromise =
|
679
|
-
.
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
719
|
+
const workflowPromise = (0, context_1.runWithWorkflowContext)(wCtxt, async () => {
|
720
|
+
return await wf.call(params.configuredInstance, wCtxt, ...args)
|
721
|
+
.then(async (result) => {
|
722
|
+
// Check if the result is the same.
|
723
|
+
const recordedResult = await this.systemDatabase.getWorkflowResult(workflowUUID);
|
724
|
+
if (result === undefined && !recordedResult) {
|
725
|
+
return result;
|
726
|
+
}
|
727
|
+
if (utils_1.DBOSJSON.stringify(result) !== utils_1.DBOSJSON.stringify(recordedResult)) {
|
728
|
+
this.logger.error(`Detect different output for the workflow UUID ${workflowUUID}!\n Received: ${utils_1.DBOSJSON.stringify(result)}\n Original: ${utils_1.DBOSJSON.stringify(recordedResult)}`);
|
729
|
+
}
|
730
|
+
return recordedResult; // Always return the recorded result.
|
731
|
+
});
|
689
732
|
});
|
690
733
|
return new workflow_1.InvokedHandle(this.systemDatabase, workflowPromise, workflowUUID, wf.name, callerUUID, callerFunctionID);
|
691
734
|
}
|
@@ -702,6 +745,122 @@ class DBOSExecutor {
|
|
702
745
|
tempWfClass: (0, decorators_1.getRegisteredMethodClassName)(txn),
|
703
746
|
}, ...args)).getResult();
|
704
747
|
}
|
748
|
+
async callTransactionFunction(txn, clsinst, wfCtx, ...args) {
|
749
|
+
const txnInfo = this.getTransactionInfo(txn);
|
750
|
+
if (txnInfo === undefined) {
|
751
|
+
throw new error_1.DBOSNotRegisteredError(txn.name);
|
752
|
+
}
|
753
|
+
const readOnly = txnInfo.config.readOnly ?? false;
|
754
|
+
let retryWaitMillis = 1;
|
755
|
+
const backoffFactor = 1.5;
|
756
|
+
const maxRetryWaitMs = 2000; // Maximum wait 2 seconds.
|
757
|
+
const funcId = wfCtx.functionIDGetIncrement();
|
758
|
+
const span = this.tracer.startSpan(txn.name, {
|
759
|
+
operationUUID: wfCtx.workflowUUID,
|
760
|
+
operationType: exports.OperationType.TRANSACTION,
|
761
|
+
authenticatedUser: wfCtx.authenticatedUser,
|
762
|
+
assumedRole: wfCtx.assumedRole,
|
763
|
+
authenticatedRoles: wfCtx.authenticatedRoles,
|
764
|
+
readOnly: readOnly,
|
765
|
+
isolationLevel: txnInfo.config.isolationLevel,
|
766
|
+
}, wfCtx.span);
|
767
|
+
while (true) {
|
768
|
+
let txn_snapshot = "invalid";
|
769
|
+
const workflowUUID = wfCtx.workflowUUID;
|
770
|
+
const wrappedTransaction = async (client) => {
|
771
|
+
const tCtxt = new transaction_1.TransactionContextImpl(this.userDatabase.getName(), client, wfCtx, span, this.logger, funcId, txn.name);
|
772
|
+
// If the UUID is preset, it is possible this execution previously happened. Check, and return its original result if it did.
|
773
|
+
// Note: It is possible to retrieve a generated ID from a workflow handle, run a concurrent execution, and cause trouble for yourself. We recommend against this.
|
774
|
+
if (wfCtx.presetUUID) {
|
775
|
+
const check = await wfCtx.checkTxExecution(client, funcId);
|
776
|
+
txn_snapshot = check.txn_snapshot;
|
777
|
+
if (check.output !== exports.dbosNull) {
|
778
|
+
tCtxt.span.setAttribute("cached", true);
|
779
|
+
tCtxt.span.setStatus({ code: api_1.SpanStatusCode.OK });
|
780
|
+
this.tracer.endSpan(tCtxt.span);
|
781
|
+
return check.output;
|
782
|
+
}
|
783
|
+
}
|
784
|
+
else {
|
785
|
+
// Collect snapshot information for read-only transactions and non-preset UUID transactions, if not already collected above
|
786
|
+
txn_snapshot = await wfCtx.retrieveTxSnapshot(client);
|
787
|
+
}
|
788
|
+
// For non-read-only transactions, flush the result buffer.
|
789
|
+
if (!readOnly) {
|
790
|
+
await wfCtx.flushResultBuffer(client);
|
791
|
+
}
|
792
|
+
// Execute the user's transaction.
|
793
|
+
let cresult;
|
794
|
+
if (txnInfo.registration.passContext) {
|
795
|
+
await (0, context_1.runWithTransactionContext)(tCtxt, async () => {
|
796
|
+
cresult = await txn.call(clsinst, tCtxt, ...args);
|
797
|
+
});
|
798
|
+
}
|
799
|
+
else {
|
800
|
+
await (0, context_1.runWithTransactionContext)(tCtxt, async () => {
|
801
|
+
const tf = txn;
|
802
|
+
cresult = await tf.call(clsinst, ...args);
|
803
|
+
});
|
804
|
+
}
|
805
|
+
const result = cresult;
|
806
|
+
// Record the execution, commit, and return.
|
807
|
+
if (readOnly) {
|
808
|
+
// Buffer the output of read-only transactions instead of synchronously writing it.
|
809
|
+
const readOutput = {
|
810
|
+
output: result,
|
811
|
+
txn_snapshot: txn_snapshot,
|
812
|
+
created_at: Date.now(),
|
813
|
+
};
|
814
|
+
wfCtx.resultBuffer.set(funcId, readOutput);
|
815
|
+
}
|
816
|
+
else {
|
817
|
+
try {
|
818
|
+
// Synchronously record the output of write transactions and obtain the transaction ID.
|
819
|
+
const pg_txn_id = await wfCtx.recordOutputTx(client, funcId, txn_snapshot, result);
|
820
|
+
tCtxt.span.setAttribute("pg_txn_id", pg_txn_id);
|
821
|
+
wfCtx.resultBuffer.clear();
|
822
|
+
}
|
823
|
+
catch (error) {
|
824
|
+
if (this.userDatabase.isFailedSqlTransactionError(error)) {
|
825
|
+
this.logger.error(`Postgres aborted the ${txn.name} @Transaction of Workflow ${workflowUUID}, but the function did not raise an exception. Please ensure that the @Transaction method raises an exception if the database transaction is aborted.`);
|
826
|
+
throw new error_1.DBOSFailedSqlTransactionError(workflowUUID, txn.name);
|
827
|
+
}
|
828
|
+
else {
|
829
|
+
throw error;
|
830
|
+
}
|
831
|
+
}
|
832
|
+
}
|
833
|
+
return result;
|
834
|
+
};
|
835
|
+
try {
|
836
|
+
const result = await this.userDatabase.transaction(wrappedTransaction, txnInfo.config);
|
837
|
+
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
838
|
+
this.tracer.endSpan(span);
|
839
|
+
return result;
|
840
|
+
}
|
841
|
+
catch (err) {
|
842
|
+
if (this.userDatabase.isRetriableTransactionError(err)) {
|
843
|
+
// serialization_failure in PostgreSQL
|
844
|
+
span.addEvent("TXN SERIALIZATION FAILURE", { "retryWaitMillis": retryWaitMillis }, performance.now());
|
845
|
+
// Retry serialization failures.
|
846
|
+
await (0, utils_1.sleepms)(retryWaitMillis);
|
847
|
+
retryWaitMillis *= backoffFactor;
|
848
|
+
retryWaitMillis = retryWaitMillis < maxRetryWaitMs ? retryWaitMillis : maxRetryWaitMs;
|
849
|
+
continue;
|
850
|
+
}
|
851
|
+
// Record and throw other errors.
|
852
|
+
const e = err;
|
853
|
+
await this.userDatabase.transaction(async (client) => {
|
854
|
+
await wfCtx.flushResultBuffer(client);
|
855
|
+
await wfCtx.recordErrorTx(client, funcId, txn_snapshot, e);
|
856
|
+
}, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
|
857
|
+
wfCtx.resultBuffer.clear();
|
858
|
+
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: e.message });
|
859
|
+
this.tracer.endSpan(span);
|
860
|
+
throw err;
|
861
|
+
}
|
862
|
+
}
|
863
|
+
}
|
705
864
|
async procedure(proc, params, ...args) {
|
706
865
|
// Create a workflow and call procedure.
|
707
866
|
const temp_workflow = async (ctxt, ...args) => {
|
@@ -748,13 +907,128 @@ class DBOSExecutor {
|
|
748
907
|
tempWfClass: (0, decorators_1.getRegisteredMethodClassName)(stepFn),
|
749
908
|
}, ...args)).getResult();
|
750
909
|
}
|
910
|
+
/**
|
911
|
+
* Execute a step function.
|
912
|
+
* If it encounters any error, retry according to its configured retry policy until the maximum number of attempts is reached, then throw an DBOSError.
|
913
|
+
* The step may execute many times, but once it is complete, it will not re-execute.
|
914
|
+
*/
|
915
|
+
async callStepFunction(stepFn, clsInst, wfCtx, ...args) {
|
916
|
+
const commInfo = this.getStepInfo(stepFn);
|
917
|
+
if (commInfo === undefined) {
|
918
|
+
throw new error_1.DBOSNotRegisteredError(stepFn.name);
|
919
|
+
}
|
920
|
+
const funcID = wfCtx.functionIDGetIncrement();
|
921
|
+
const maxRetryIntervalSec = 3600; // Maximum retry interval: 1 hour
|
922
|
+
const span = this.tracer.startSpan(stepFn.name, {
|
923
|
+
operationUUID: wfCtx.workflowUUID,
|
924
|
+
operationType: exports.OperationType.COMMUNICATOR,
|
925
|
+
authenticatedUser: wfCtx.authenticatedUser,
|
926
|
+
assumedRole: wfCtx.assumedRole,
|
927
|
+
authenticatedRoles: wfCtx.authenticatedRoles,
|
928
|
+
retriesAllowed: commInfo.config.retriesAllowed,
|
929
|
+
intervalSeconds: commInfo.config.intervalSeconds,
|
930
|
+
maxAttempts: commInfo.config.maxAttempts,
|
931
|
+
backoffRate: commInfo.config.backoffRate,
|
932
|
+
}, wfCtx.span);
|
933
|
+
const ctxt = new step_1.StepContextImpl(wfCtx, funcID, span, this.logger, commInfo.config, stepFn.name);
|
934
|
+
await this.userDatabase.transaction(async (client) => {
|
935
|
+
await wfCtx.flushResultBuffer(client);
|
936
|
+
}, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
|
937
|
+
wfCtx.resultBuffer.clear();
|
938
|
+
// Check if this execution previously happened, returning its original result if it did.
|
939
|
+
const check = await this.systemDatabase.checkOperationOutput(wfCtx.workflowUUID, ctxt.functionID);
|
940
|
+
if (check !== exports.dbosNull) {
|
941
|
+
ctxt.span.setAttribute("cached", true);
|
942
|
+
ctxt.span.setStatus({ code: api_1.SpanStatusCode.OK });
|
943
|
+
this.tracer.endSpan(ctxt.span);
|
944
|
+
return check;
|
945
|
+
}
|
946
|
+
// Execute the step function. If it throws an exception, retry with exponential backoff.
|
947
|
+
// After reaching the maximum number of retries, throw an DBOSError.
|
948
|
+
let result = exports.dbosNull;
|
949
|
+
let err = exports.dbosNull;
|
950
|
+
if (ctxt.retriesAllowed) {
|
951
|
+
let numAttempts = 0;
|
952
|
+
let intervalSeconds = ctxt.intervalSeconds;
|
953
|
+
if (intervalSeconds > maxRetryIntervalSec) {
|
954
|
+
this.logger.warn(`Step config interval exceeds maximum allowed interval, capped to ${maxRetryIntervalSec} seconds!`);
|
955
|
+
}
|
956
|
+
while (result === exports.dbosNull && numAttempts++ < ctxt.maxAttempts) {
|
957
|
+
try {
|
958
|
+
let cresult;
|
959
|
+
if (commInfo.registration.passContext) {
|
960
|
+
await (0, context_1.runWithStepContext)(ctxt, async () => {
|
961
|
+
cresult = await stepFn.call(clsInst, ctxt, ...args);
|
962
|
+
});
|
963
|
+
}
|
964
|
+
else {
|
965
|
+
await (0, context_1.runWithStepContext)(ctxt, async () => {
|
966
|
+
const sf = stepFn;
|
967
|
+
cresult = await sf.call(clsInst, ...args);
|
968
|
+
});
|
969
|
+
}
|
970
|
+
result = cresult;
|
971
|
+
}
|
972
|
+
catch (error) {
|
973
|
+
const e = error;
|
974
|
+
this.logger.warn(`Error in step being automatically retried. Attempt ${numAttempts} of ${ctxt.maxAttempts}. ${e.stack}`);
|
975
|
+
span.addEvent(`Step attempt ${numAttempts + 1} failed`, { "retryIntervalSeconds": intervalSeconds, "error": error.message }, performance.now());
|
976
|
+
if (numAttempts < ctxt.maxAttempts) {
|
977
|
+
// Sleep for an interval, then increase the interval by backoffRate.
|
978
|
+
// Cap at the maximum allowed retry interval.
|
979
|
+
await (0, utils_1.sleepms)(intervalSeconds * 1000);
|
980
|
+
intervalSeconds *= ctxt.backoffRate;
|
981
|
+
intervalSeconds = intervalSeconds < maxRetryIntervalSec ? intervalSeconds : maxRetryIntervalSec;
|
982
|
+
}
|
983
|
+
}
|
984
|
+
}
|
985
|
+
}
|
986
|
+
else {
|
987
|
+
try {
|
988
|
+
let cresult;
|
989
|
+
if (commInfo.registration.passContext) {
|
990
|
+
await (0, context_1.runWithStepContext)(ctxt, async () => {
|
991
|
+
cresult = await stepFn.call(clsInst, ctxt, ...args);
|
992
|
+
});
|
993
|
+
}
|
994
|
+
else {
|
995
|
+
await (0, context_1.runWithStepContext)(ctxt, async () => {
|
996
|
+
const sf = stepFn;
|
997
|
+
cresult = await sf.call(clsInst, ...args);
|
998
|
+
});
|
999
|
+
}
|
1000
|
+
result = cresult;
|
1001
|
+
}
|
1002
|
+
catch (error) {
|
1003
|
+
err = error;
|
1004
|
+
}
|
1005
|
+
}
|
1006
|
+
// `result` can only be dbosNull when the step timed out
|
1007
|
+
if (result === exports.dbosNull) {
|
1008
|
+
// Record the error, then throw it.
|
1009
|
+
err = err === exports.dbosNull ? new error_1.DBOSError("Step reached maximum retries.", 1) : err;
|
1010
|
+
await this.systemDatabase.recordOperationError(wfCtx.workflowUUID, ctxt.functionID, err);
|
1011
|
+
ctxt.span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err.message });
|
1012
|
+
this.tracer.endSpan(ctxt.span);
|
1013
|
+
throw err;
|
1014
|
+
}
|
1015
|
+
else {
|
1016
|
+
// Record the execution and return.
|
1017
|
+
await this.systemDatabase.recordOperationOutput(wfCtx.workflowUUID, ctxt.functionID, result);
|
1018
|
+
ctxt.span.setStatus({ code: api_1.SpanStatusCode.OK });
|
1019
|
+
this.tracer.endSpan(ctxt.span);
|
1020
|
+
return result;
|
1021
|
+
}
|
1022
|
+
}
|
751
1023
|
async send(destinationUUID, message, topic, idempotencyKey) {
|
752
1024
|
// Create a workflow and call send.
|
753
1025
|
const temp_workflow = async (ctxt, destinationUUID, message, topic) => {
|
754
1026
|
return await ctxt.send(destinationUUID, message, topic);
|
755
1027
|
};
|
756
1028
|
const workflowUUID = idempotencyKey ? destinationUUID + idempotencyKey : undefined;
|
757
|
-
return (await this.workflow(temp_workflow, {
|
1029
|
+
return (await this.workflow(temp_workflow, {
|
1030
|
+
workflowUUID: workflowUUID, tempWfType: TempWorkflowType.send, configuredInstance: null,
|
1031
|
+
}, destinationUUID, message, topic)).getResult();
|
758
1032
|
}
|
759
1033
|
/**
|
760
1034
|
* Wait for a workflow to emit an event, then return its value.
|
@@ -765,8 +1039,17 @@ class DBOSExecutor {
|
|
765
1039
|
/**
|
766
1040
|
* Retrieve a handle for a workflow UUID.
|
767
1041
|
*/
|
768
|
-
retrieveWorkflow(
|
769
|
-
return new workflow_1.RetrievedHandle(this.systemDatabase,
|
1042
|
+
retrieveWorkflow(workflowID) {
|
1043
|
+
return new workflow_1.RetrievedHandle(this.systemDatabase, workflowID);
|
1044
|
+
}
|
1045
|
+
getWorkflowStatus(workflowID) {
|
1046
|
+
return this.systemDatabase.getWorkflowStatus(workflowID);
|
1047
|
+
}
|
1048
|
+
getWorkflows(input) {
|
1049
|
+
return this.systemDatabase.getWorkflows(input);
|
1050
|
+
}
|
1051
|
+
getWorkflowQueue(input) {
|
1052
|
+
return this.systemDatabase.getWorkflowQueue(input);
|
770
1053
|
}
|
771
1054
|
async queryUserDB(sql, params) {
|
772
1055
|
if (params !== undefined) {
|
@@ -833,6 +1116,7 @@ class DBOSExecutor {
|
|
833
1116
|
}
|
834
1117
|
await this.scheduler?.destroyScheduler();
|
835
1118
|
wfqueue_1.wfQueueRunner.stop();
|
1119
|
+
await this.wfqEnded;
|
836
1120
|
}
|
837
1121
|
async executeWorkflowUUID(workflowUUID, startNewWorkflow = false) {
|
838
1122
|
const wfStatus = await this.systemDatabase.getWorkflowStatus(workflowUUID);
|
@@ -848,7 +1132,7 @@ class DBOSExecutor {
|
|
848
1132
|
if (wfInfo) {
|
849
1133
|
return this.workflow(wfInfo.workflow, {
|
850
1134
|
workflowUUID: workflowStartUUID, parentCtx: parentCtx, configuredInstance: configuredInst, recovery: true,
|
851
|
-
queueName: wfStatus.queueName, executeWorkflow: true
|
1135
|
+
queueName: wfStatus.queueName, executeWorkflow: true,
|
852
1136
|
},
|
853
1137
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
854
1138
|
...inputs);
|
@@ -906,8 +1190,12 @@ class DBOSExecutor {
|
|
906
1190
|
this.logger.error(`Unrecognized temporary workflow! UUID ${workflowUUID}, name ${wfName}`);
|
907
1191
|
throw new error_1.DBOSNotRegisteredError(wfName);
|
908
1192
|
}
|
1193
|
+
return this.workflow(temp_workflow, {
|
1194
|
+
workflowUUID: workflowStartUUID, parentCtx: parentCtx ?? undefined, configuredInstance: clsinst,
|
1195
|
+
recovery: true, tempWfType, tempWfClass, tempWfName,
|
1196
|
+
},
|
909
1197
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
910
|
-
|
1198
|
+
...inputs);
|
911
1199
|
}
|
912
1200
|
async getEventDispatchState(svc, wfn, key) {
|
913
1201
|
return await this.systemDatabase.getEventDispatchState(svc, wfn, key);
|