@dbos-inc/dbos-sdk 1.28.14-preview.g65f6845035 → 1.29.83-preview.g0c5a5d6c8e
Sign up to get free protection for your applications and to get access to all the features.
- 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);
|