@dbos-inc/dbos-sdk 3.0.23-preview → 3.0.37-preview.gfd7eaf193c
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/authdecorators.d.ts +0 -7
- package/dist/src/authdecorators.d.ts.map +1 -1
- package/dist/src/authdecorators.js +1 -29
- package/dist/src/authdecorators.js.map +1 -1
- package/dist/src/context.d.ts +9 -43
- package/dist/src/context.d.ts.map +1 -1
- package/dist/src/context.js +28 -113
- package/dist/src/context.js.map +1 -1
- package/dist/src/datasource.d.ts +14 -3
- package/dist/src/datasource.d.ts.map +1 -1
- package/dist/src/datasource.js +12 -11
- package/dist/src/datasource.js.map +1 -1
- package/dist/src/dbos-executor.d.ts +37 -79
- package/dist/src/dbos-executor.d.ts.map +1 -1
- package/dist/src/dbos-executor.js +297 -498
- package/dist/src/dbos-executor.js.map +1 -1
- package/dist/src/dbos-runtime/cli.d.ts.map +1 -1
- package/dist/src/dbos-runtime/cli.js +1 -3
- package/dist/src/dbos-runtime/cli.js.map +1 -1
- package/dist/src/dbos-runtime/debug.d.ts +1 -1
- package/dist/src/dbos-runtime/debug.d.ts.map +1 -1
- package/dist/src/dbos-runtime/debug.js +2 -3
- package/dist/src/dbos-runtime/debug.js.map +1 -1
- package/dist/src/dbos-runtime/runtime.d.ts.map +1 -1
- package/dist/src/dbos-runtime/runtime.js +0 -9
- package/dist/src/dbos-runtime/runtime.js.map +1 -1
- package/dist/src/dbos.d.ts +9 -34
- package/dist/src/dbos.d.ts.map +1 -1
- package/dist/src/dbos.js +95 -273
- package/dist/src/dbos.js.map +1 -1
- package/dist/src/decorators.d.ts +35 -60
- package/dist/src/decorators.d.ts.map +1 -1
- package/dist/src/decorators.js +157 -184
- package/dist/src/decorators.js.map +1 -1
- package/dist/src/httpServer/handler.d.ts +1 -9
- package/dist/src/httpServer/handler.d.ts.map +1 -1
- package/dist/src/httpServer/handler.js +1 -1
- package/dist/src/httpServer/handler.js.map +1 -1
- package/dist/src/httpServer/server.d.ts.map +1 -1
- package/dist/src/httpServer/server.js +5 -7
- package/dist/src/httpServer/server.js.map +1 -1
- package/dist/src/index.d.ts +5 -8
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -17
- package/dist/src/index.js.map +1 -1
- package/dist/src/paramdecorators.d.ts.map +1 -1
- package/dist/src/paramdecorators.js +0 -6
- package/dist/src/paramdecorators.js.map +1 -1
- package/dist/src/procedure.d.ts +0 -22
- package/dist/src/procedure.d.ts.map +1 -1
- package/dist/src/procedure.js +0 -16
- 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 -2
- package/dist/src/scheduler/scheduler.js.map +1 -1
- package/dist/src/step.d.ts +0 -25
- package/dist/src/step.d.ts.map +1 -1
- package/dist/src/step.js +0 -20
- package/dist/src/step.js.map +1 -1
- package/dist/src/system_database.d.ts +1 -2
- package/dist/src/system_database.d.ts.map +1 -1
- package/dist/src/system_database.js.map +1 -1
- package/dist/src/transaction.d.ts +0 -25
- package/dist/src/transaction.d.ts.map +1 -1
- package/dist/src/transaction.js +1 -15
- package/dist/src/transaction.js.map +1 -1
- package/dist/src/workflow.d.ts +4 -148
- package/dist/src/workflow.d.ts.map +1 -1
- package/dist/src/workflow.js +7 -226
- package/dist/src/workflow.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/src/eventreceiver.d.ts +0 -155
- package/dist/src/eventreceiver.d.ts.map +0 -1
- package/dist/src/eventreceiver.js +0 -3
- package/dist/src/eventreceiver.js.map +0 -1
@@ -26,11 +26,10 @@ 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.DBOSExecutor = exports.TempWorkflowType = exports.OperationType = exports.
|
29
|
+
exports.DBOSExecutor = exports.TempWorkflowType = exports.OperationType = exports.isDeprecatedDBOSConfig = exports.DBOS_QUEUE_MAX_PRIORITY = exports.DBOS_QUEUE_MIN_PRIORITY = exports.dbosNull = void 0;
|
30
30
|
const error_1 = require("./error");
|
31
31
|
const workflow_1 = require("./workflow");
|
32
32
|
const transaction_1 = require("./transaction");
|
33
|
-
const step_1 = require("./step");
|
34
33
|
const collector_1 = require("./telemetry/collector");
|
35
34
|
const traces_1 = require("./telemetry/traces");
|
36
35
|
const logs_1 = require("./telemetry/logs");
|
@@ -46,7 +45,6 @@ const context_1 = require("./context");
|
|
46
45
|
const serialize_error_1 = require("serialize-error");
|
47
46
|
const utils_1 = require("./utils");
|
48
47
|
const node_path_1 = __importDefault(require("node:path"));
|
49
|
-
const procedure_1 = require("./procedure");
|
50
48
|
const _1 = require(".");
|
51
49
|
const lodash_1 = require("lodash");
|
52
50
|
const wfqueue_1 = require("./wfqueue");
|
@@ -67,12 +65,6 @@ function isDeprecatedDBOSConfig(config) {
|
|
67
65
|
return isDeprecated;
|
68
66
|
}
|
69
67
|
exports.isDeprecatedDBOSConfig = isDeprecatedDBOSConfig;
|
70
|
-
var DebugMode;
|
71
|
-
(function (DebugMode) {
|
72
|
-
DebugMode[DebugMode["DISABLED"] = 0] = "DISABLED";
|
73
|
-
DebugMode[DebugMode["ENABLED"] = 1] = "ENABLED";
|
74
|
-
DebugMode[DebugMode["TIME_TRAVEL"] = 2] = "TIME_TRAVEL";
|
75
|
-
})(DebugMode || (exports.DebugMode = DebugMode = {}));
|
76
68
|
exports.OperationType = {
|
77
69
|
HANDLER: 'handler',
|
78
70
|
WORKFLOW: 'workflow',
|
@@ -96,51 +88,15 @@ class DBOSExecutor {
|
|
96
88
|
procedurePool;
|
97
89
|
// Temporary workflows are created by calling transaction/send/recv directly from the executor class
|
98
90
|
static tempWorkflowName = 'temp_workflow';
|
99
|
-
workflowInfoMap = new Map([
|
100
|
-
// We initialize the map with an entry for temporary workflows.
|
101
|
-
[
|
102
|
-
DBOSExecutor.tempWorkflowName,
|
103
|
-
{
|
104
|
-
workflow: async () => {
|
105
|
-
this.logger.error('UNREACHABLE: Indirect invoke of temp workflow');
|
106
|
-
return Promise.resolve();
|
107
|
-
},
|
108
|
-
workflowOrigFunction: async () => {
|
109
|
-
this.logger.error('UNREACHABLE: Indirect invoke of temp workflow');
|
110
|
-
return Promise.resolve();
|
111
|
-
},
|
112
|
-
config: {},
|
113
|
-
},
|
114
|
-
],
|
115
|
-
]);
|
116
|
-
transactionInfoMap = new Map();
|
117
|
-
stepInfoMap = new Map();
|
118
|
-
procedureInfoMap = new Map();
|
119
|
-
registeredOperations = [];
|
120
91
|
telemetryCollector;
|
121
92
|
static defaultNotificationTimeoutSec = 60;
|
122
93
|
debugMode;
|
123
|
-
get isDebugging() {
|
124
|
-
switch (this.debugMode) {
|
125
|
-
case DebugMode.DISABLED:
|
126
|
-
return false;
|
127
|
-
case DebugMode.ENABLED:
|
128
|
-
case DebugMode.TIME_TRAVEL:
|
129
|
-
return true;
|
130
|
-
default: {
|
131
|
-
const _never = this.debugMode;
|
132
|
-
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
133
|
-
throw new Error(`Unexpected DBOS debug mode: ${this.debugMode}`);
|
134
|
-
}
|
135
|
-
}
|
136
|
-
}
|
137
94
|
static systemDBSchemaName = 'dbos';
|
138
95
|
logger;
|
139
96
|
tracer;
|
140
97
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
141
98
|
typeormEntities = [];
|
142
99
|
drizzleEntities = {};
|
143
|
-
eventReceivers = [];
|
144
100
|
scheduler = undefined;
|
145
101
|
wfqEnded = undefined;
|
146
102
|
executorID = utils_1.globalParams.executorID;
|
@@ -148,7 +104,7 @@ class DBOSExecutor {
|
|
148
104
|
/* WORKFLOW EXECUTOR LIFE CYCLE MANAGEMENT */
|
149
105
|
constructor(config, { systemDatabase, debugMode } = {}) {
|
150
106
|
this.config = config;
|
151
|
-
this.debugMode = debugMode ??
|
107
|
+
this.debugMode = debugMode ?? false;
|
152
108
|
// Set configured environment variables
|
153
109
|
if (config.env) {
|
154
110
|
for (const [key, value] of Object.entries(config.env)) {
|
@@ -170,7 +126,7 @@ class DBOSExecutor {
|
|
170
126
|
}
|
171
127
|
this.logger = new logs_1.GlobalLogger(this.telemetryCollector, this.config.telemetry.logs);
|
172
128
|
this.tracer = new traces_1.Tracer(this.telemetryCollector);
|
173
|
-
if (this.
|
129
|
+
if (this.debugMode) {
|
174
130
|
this.logger.info('Running in debug mode!');
|
175
131
|
}
|
176
132
|
this.procedurePool = new pg_1.Pool(this.config.poolConfig);
|
@@ -253,39 +209,6 @@ class DBOSExecutor {
|
|
253
209
|
this.logger.debug('Loaded Postgres user database');
|
254
210
|
}
|
255
211
|
}
|
256
|
-
#registerClass(clsname) {
|
257
|
-
const registeredClassOperations = (0, decorators_1.getRegisteredOperationsByClassname)(clsname);
|
258
|
-
this.registeredOperations.push(...registeredClassOperations);
|
259
|
-
for (const ro of registeredClassOperations) {
|
260
|
-
if (ro.workflowConfig) {
|
261
|
-
this.#registerWorkflow(ro);
|
262
|
-
}
|
263
|
-
else if (ro.txnConfig) {
|
264
|
-
this.#registerTransaction(ro);
|
265
|
-
}
|
266
|
-
else if (ro.stepConfig) {
|
267
|
-
this.#registerStep(ro);
|
268
|
-
}
|
269
|
-
else if (ro.procConfig) {
|
270
|
-
this.#registerProcedure(ro);
|
271
|
-
}
|
272
|
-
for (const [evtRcvr, _cfg] of ro.eventReceiverInfo) {
|
273
|
-
if (!this.eventReceivers.includes(evtRcvr))
|
274
|
-
this.eventReceivers.push(evtRcvr);
|
275
|
-
}
|
276
|
-
}
|
277
|
-
}
|
278
|
-
getRegistrationsFor(obj) {
|
279
|
-
const res = [];
|
280
|
-
for (const r of this.registeredOperations) {
|
281
|
-
if (!r.eventReceiverInfo.has(obj))
|
282
|
-
continue;
|
283
|
-
const methodConfig = r.eventReceiverInfo.get(obj);
|
284
|
-
const classConfig = r.defaults?.eventReceiverInfo.get(obj) ?? {};
|
285
|
-
res.push({ methodReg: r, methodConfig, classConfig });
|
286
|
-
}
|
287
|
-
return res;
|
288
|
-
}
|
289
212
|
async init(classes) {
|
290
213
|
if (this.initialized) {
|
291
214
|
this.logger.error('Workflow executor already initialized!');
|
@@ -318,7 +241,7 @@ class DBOSExecutor {
|
|
318
241
|
}
|
319
242
|
this.logger.debug(`Loaded ${length} ORM entities`);
|
320
243
|
}
|
321
|
-
if (!this.
|
244
|
+
if (!this.debugMode) {
|
322
245
|
await (0, user_database_1.createDBIfDoesNotExist)(this.config.poolConfig, this.logger);
|
323
246
|
}
|
324
247
|
this.configureDbClient();
|
@@ -326,12 +249,9 @@ class DBOSExecutor {
|
|
326
249
|
this.logger.error('No user database configured!');
|
327
250
|
throw new error_1.DBOSInitializationError('No user database configured!');
|
328
251
|
}
|
329
|
-
for (const cls of classnames) {
|
330
|
-
this.#registerClass(cls);
|
331
|
-
}
|
332
252
|
// Debug mode doesn't need to initialize the DBs. Everything should appear to be read-only.
|
333
|
-
await this.userDatabase.init(this.
|
334
|
-
if (!this.
|
253
|
+
await this.userDatabase.init(this.debugMode);
|
254
|
+
if (!this.debugMode) {
|
335
255
|
await this.systemDatabase.init();
|
336
256
|
}
|
337
257
|
}
|
@@ -354,7 +274,7 @@ class DBOSExecutor {
|
|
354
274
|
}
|
355
275
|
this.initialized = true;
|
356
276
|
// Only execute init code if under non-debug mode
|
357
|
-
if (!this.
|
277
|
+
if (!this.debugMode) {
|
358
278
|
for (const cls of classnames) {
|
359
279
|
// Init its configurations
|
360
280
|
const creg = (0, decorators_1.getClassRegistrationByName)(cls);
|
@@ -362,7 +282,7 @@ class DBOSExecutor {
|
|
362
282
|
await cfg.initialize(new _1.InitContext());
|
363
283
|
}
|
364
284
|
}
|
365
|
-
for (const v of
|
285
|
+
for (const v of (0, decorators_1.getAllRegisteredFunctions)()) {
|
366
286
|
const m = v;
|
367
287
|
if (m.init === true) {
|
368
288
|
this.logger.debug('Executing init method: ' + m.name);
|
@@ -422,109 +342,11 @@ class DBOSExecutor {
|
|
422
342
|
throw err;
|
423
343
|
}
|
424
344
|
}
|
425
|
-
|
426
|
-
#
|
427
|
-
const
|
428
|
-
|
429
|
-
throw new error_1.DBOSError(`Unexpected use of reserved workflow name: ${wf.name}`);
|
430
|
-
}
|
431
|
-
const wfn = ro.className + '.' + ro.name;
|
432
|
-
if (this.workflowInfoMap.has(wfn)) {
|
433
|
-
throw new error_1.DBOSError(`Repeated workflow name: ${wfn}`);
|
434
|
-
}
|
435
|
-
const workflowInfo = {
|
436
|
-
workflow: wf,
|
437
|
-
workflowOrigFunction: ro.origFunction,
|
438
|
-
config: { ...ro.workflowConfig },
|
439
|
-
registration: ro,
|
440
|
-
};
|
441
|
-
this.workflowInfoMap.set(wfn, workflowInfo);
|
442
|
-
this.logger.debug(`Registered workflow ${wfn}`);
|
443
|
-
}
|
444
|
-
#registerTransaction(ro) {
|
445
|
-
const txf = ro.registeredFunction;
|
446
|
-
const tfn = ro.className + '.' + ro.name;
|
447
|
-
if (this.transactionInfoMap.has(tfn)) {
|
448
|
-
throw new error_1.DBOSError(`Repeated Transaction name: ${tfn}`);
|
449
|
-
}
|
450
|
-
const txnInfo = {
|
451
|
-
transaction: txf,
|
452
|
-
config: { ...ro.txnConfig },
|
453
|
-
registration: ro,
|
454
|
-
};
|
455
|
-
this.transactionInfoMap.set(tfn, txnInfo);
|
456
|
-
this.logger.debug(`Registered transaction ${tfn}`);
|
345
|
+
// This could return WF, or the function underlying a temp wf
|
346
|
+
#getFunctionInfoFromWFStatus(wf) {
|
347
|
+
const methReg = (0, decorators_1.getFunctionRegistrationByName)(wf.workflowClassName, wf.workflowName);
|
348
|
+
return { methReg, configuredInst: (0, decorators_1.getConfiguredInstance)(wf.workflowClassName, wf.workflowConfigName) };
|
457
349
|
}
|
458
|
-
#registerStep(ro) {
|
459
|
-
const comm = ro.registeredFunction;
|
460
|
-
const cfn = ro.className + '.' + ro.name;
|
461
|
-
if (this.stepInfoMap.has(cfn)) {
|
462
|
-
throw new error_1.DBOSError(`Repeated Commmunicator name: ${cfn}`);
|
463
|
-
}
|
464
|
-
const stepInfo = {
|
465
|
-
step: comm,
|
466
|
-
config: { ...ro.stepConfig },
|
467
|
-
registration: ro,
|
468
|
-
};
|
469
|
-
this.stepInfoMap.set(cfn, stepInfo);
|
470
|
-
this.logger.debug(`Registered step ${cfn}`);
|
471
|
-
}
|
472
|
-
#registerProcedure(ro) {
|
473
|
-
const proc = ro.registeredFunction;
|
474
|
-
const cfn = ro.className + '.' + ro.name;
|
475
|
-
if (this.procedureInfoMap.has(cfn)) {
|
476
|
-
throw new error_1.DBOSError(`Repeated Procedure name: ${cfn}`);
|
477
|
-
}
|
478
|
-
const procInfo = {
|
479
|
-
procedure: proc,
|
480
|
-
config: { ...ro.procConfig },
|
481
|
-
registration: ro,
|
482
|
-
};
|
483
|
-
this.procedureInfoMap.set(cfn, procInfo);
|
484
|
-
this.logger.debug(`Registered stored proc ${cfn}`);
|
485
|
-
}
|
486
|
-
getWorkflowInfo(wf) {
|
487
|
-
const wfname = wf.name === DBOSExecutor.tempWorkflowName ? wf.name : (0, decorators_1.getRegisteredMethodClassName)(wf) + '.' + wf.name;
|
488
|
-
return this.workflowInfoMap.get(wfname);
|
489
|
-
}
|
490
|
-
getWorkflowInfoByStatus(wf) {
|
491
|
-
const wfname = wf.workflowClassName + '.' + wf.workflowName;
|
492
|
-
const wfInfo = this.workflowInfoMap.get(wfname);
|
493
|
-
// wfInfo may be undefined here, if this is a temp workflow
|
494
|
-
return { wfInfo, configuredInst: (0, decorators_1.getConfiguredInstance)(wf.workflowClassName, wf.workflowConfigName) };
|
495
|
-
}
|
496
|
-
getTransactionInfo(tf) {
|
497
|
-
const tfname = (0, decorators_1.getRegisteredMethodClassName)(tf) + '.' + tf.name;
|
498
|
-
return this.transactionInfoMap.get(tfname);
|
499
|
-
}
|
500
|
-
getTransactionInfoByNames(className, functionName, cfgName) {
|
501
|
-
const tfname = className + '.' + functionName;
|
502
|
-
const txnInfo = this.transactionInfoMap.get(tfname);
|
503
|
-
if (!txnInfo) {
|
504
|
-
throw new error_1.DBOSNotRegisteredError(tfname, `Transaction function name '${tfname}' is not registered.`);
|
505
|
-
}
|
506
|
-
return { txnInfo, clsInst: (0, decorators_1.getConfiguredInstance)(className, cfgName) };
|
507
|
-
}
|
508
|
-
getStepInfo(cf) {
|
509
|
-
const cfname = (0, decorators_1.getRegisteredMethodClassName)(cf) + '.' + cf.name;
|
510
|
-
return this.stepInfoMap.get(cfname);
|
511
|
-
}
|
512
|
-
getStepInfoByNames(className, functionName, cfgName) {
|
513
|
-
const cfname = className + '.' + functionName;
|
514
|
-
const stepInfo = this.stepInfoMap.get(cfname);
|
515
|
-
if (!stepInfo) {
|
516
|
-
throw new error_1.DBOSNotRegisteredError(cfname, `Step function name '${cfname}' is not registered.`);
|
517
|
-
}
|
518
|
-
return { commInfo: stepInfo, clsInst: (0, decorators_1.getConfiguredInstance)(className, cfgName) };
|
519
|
-
}
|
520
|
-
getProcedureClassName(pf) {
|
521
|
-
return (0, decorators_1.getRegisteredMethodClassName)(pf);
|
522
|
-
}
|
523
|
-
getProcedureInfo(pf) {
|
524
|
-
const pfName = (0, decorators_1.getRegisteredMethodClassName)(pf) + '.' + pf.name;
|
525
|
-
return this.procedureInfoMap.get(pfName);
|
526
|
-
}
|
527
|
-
// TODO: getProcedureInfoByNames??
|
528
350
|
static reviveResultOrError(r, success) {
|
529
351
|
if (success === true || !r.error) {
|
530
352
|
return utils_1.DBOSJSON.parse(r.output ?? null);
|
@@ -536,7 +358,7 @@ class DBOSExecutor {
|
|
536
358
|
async workflow(wf, params, ...args) {
|
537
359
|
return this.internalWorkflow(wf, params, undefined, undefined, ...args);
|
538
360
|
}
|
539
|
-
// If
|
361
|
+
// If callerWFID and functionID are set, it means the workflow is invoked from within a workflow.
|
540
362
|
async internalWorkflow(wf, params, callerID, callerFunctionID, ...args) {
|
541
363
|
const workflowID = params.workflowUUID ? params.workflowUUID : (0, node_crypto_1.randomUUID)();
|
542
364
|
const presetID = params.workflowUUID ? true : false;
|
@@ -560,29 +382,42 @@ class DBOSExecutor {
|
|
560
382
|
this.logger.warn(`Priority is not enabled for queue ${params.queueName}. Setting priority will not have any effect.`);
|
561
383
|
}
|
562
384
|
}
|
563
|
-
const
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
385
|
+
const pctx = (0, context_1.getCurrentContextStore)();
|
386
|
+
let wConfig = {};
|
387
|
+
if (wf.name !== DBOSExecutor.tempWorkflowName) {
|
388
|
+
const wInfo = (0, decorators_1.getFunctionRegistration)(wf);
|
389
|
+
if (!wInfo || !wInfo.workflowConfig) {
|
390
|
+
throw new error_1.DBOSNotRegisteredError(wf.name);
|
391
|
+
}
|
392
|
+
wConfig = wInfo.workflowConfig;
|
393
|
+
}
|
394
|
+
const maxRecoveryAttempts = wConfig.maxRecoveryAttempts ? wConfig.maxRecoveryAttempts : 50;
|
395
|
+
const wfname = wf.name; // TODO: Should be what was registered in wfInfo...
|
396
|
+
const span = this.tracer.startSpan(wfname, {
|
397
|
+
status: workflow_1.StatusString.PENDING,
|
398
|
+
operationUUID: workflowID,
|
399
|
+
operationType: exports.OperationType.WORKFLOW,
|
400
|
+
authenticatedUser: pctx?.authenticatedUser ?? '',
|
401
|
+
authenticatedRoles: pctx?.authenticatedRoles ?? [],
|
402
|
+
assumedRole: pctx?.assumedRole ?? '',
|
403
|
+
}, pctx?.span);
|
404
|
+
const isTempWorkflow = DBOSExecutor.tempWorkflowName === wfname;
|
570
405
|
const internalStatus = {
|
571
406
|
workflowUUID: workflowID,
|
572
407
|
status: params.queueName !== undefined ? workflow_1.StatusString.ENQUEUED : workflow_1.StatusString.PENDING,
|
573
|
-
workflowName: wf
|
574
|
-
workflowClassName:
|
408
|
+
workflowName: (0, decorators_1.getRegisteredFunctionName)(wf),
|
409
|
+
workflowClassName: isTempWorkflow ? '' : (0, decorators_1.getRegisteredFunctionClassName)(wf),
|
575
410
|
workflowConfigName: params.configuredInstance?.name || '',
|
576
411
|
queueName: params.queueName,
|
577
|
-
authenticatedUser: wCtxt.authenticatedUser,
|
578
412
|
output: null,
|
579
413
|
error: null,
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
414
|
+
authenticatedUser: pctx?.authenticatedUser || '',
|
415
|
+
assumedRole: pctx?.assumedRole || '',
|
416
|
+
authenticatedRoles: pctx?.authenticatedRoles || [],
|
417
|
+
request: pctx?.request || {},
|
418
|
+
executorId: utils_1.globalParams.executorID,
|
584
419
|
applicationVersion: utils_1.globalParams.appVersion,
|
585
|
-
applicationID:
|
420
|
+
applicationID: utils_1.globalParams.appID,
|
586
421
|
createdAt: Date.now(), // Remember the start time of this workflow,
|
587
422
|
timeoutMS: timeoutMS,
|
588
423
|
deadlineEpochMS: deadlineEpochMS,
|
@@ -590,15 +425,15 @@ class DBOSExecutor {
|
|
590
425
|
deduplicationID: params.enqueueOptions?.deduplicationID,
|
591
426
|
priority: priority ?? 0,
|
592
427
|
};
|
593
|
-
if (
|
594
|
-
internalStatus.workflowName = `${DBOSExecutor.tempWorkflowName}-${
|
428
|
+
if (isTempWorkflow) {
|
429
|
+
internalStatus.workflowName = `${DBOSExecutor.tempWorkflowName}-${params.tempWfType}-${params.tempWfName}`;
|
595
430
|
internalStatus.workflowClassName = params.tempWfClass ?? '';
|
596
431
|
}
|
597
432
|
let status = undefined;
|
598
433
|
let $deadlineEpochMS = undefined;
|
599
434
|
// Synchronously set the workflow's status to PENDING and record workflow inputs.
|
600
435
|
// We have to do it for all types of workflows because operation_outputs table has a foreign key constraint on workflow status table.
|
601
|
-
if (this.
|
436
|
+
if (this.debugMode) {
|
602
437
|
const wfStatus = await this.systemDatabase.getWorkflowStatus(workflowID);
|
603
438
|
if (!wfStatus) {
|
604
439
|
throw new error_1.DBOSDebuggerError(`Failed to find inputs for workflow UUID ${workflowID}`);
|
@@ -616,7 +451,7 @@ class DBOSExecutor {
|
|
616
451
|
return new workflow_1.RetrievedHandle(this.systemDatabase, result.childWorkflowID, callerID, callerFunctionID);
|
617
452
|
}
|
618
453
|
}
|
619
|
-
const ires = await this.systemDatabase.initWorkflowStatus(internalStatus,
|
454
|
+
const ires = await this.systemDatabase.initWorkflowStatus(internalStatus, maxRecoveryAttempts);
|
620
455
|
if (callerFunctionID !== undefined && callerID !== undefined) {
|
621
456
|
await this.systemDatabase.recordOperationResult(callerID, callerFunctionID, internalStatus.workflowName, true, {
|
622
457
|
childWorkflowID: workflowID,
|
@@ -654,19 +489,22 @@ class DBOSExecutor {
|
|
654
489
|
e.dbos_already_logged = true;
|
655
490
|
internalStatus.error = utils_1.DBOSJSON.stringify((0, serialize_error_1.serializeError)(e));
|
656
491
|
internalStatus.status = workflow_1.StatusString.ERROR;
|
657
|
-
if (!exec.
|
492
|
+
if (!exec.debugMode) {
|
658
493
|
await exec.systemDatabase.recordWorkflowError(workflowID, internalStatus);
|
659
494
|
}
|
660
|
-
|
495
|
+
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: e.message });
|
661
496
|
}
|
662
497
|
const runWorkflow = async () => {
|
663
498
|
let result;
|
664
499
|
// Execute the workflow.
|
665
500
|
try {
|
666
|
-
const callResult = await (0, context_1.
|
667
|
-
|
668
|
-
|
669
|
-
|
501
|
+
const callResult = await (0, context_1.runWithParentContext)(pctx, {
|
502
|
+
presetID,
|
503
|
+
timeoutMS,
|
504
|
+
deadlineEpochMS,
|
505
|
+
workflowId: workflowID,
|
506
|
+
}, () => {
|
507
|
+
const callPromise = wf.call(params.configuredInstance, ...args);
|
670
508
|
if ($deadlineEpochMS === undefined) {
|
671
509
|
return callPromise;
|
672
510
|
}
|
@@ -674,7 +512,7 @@ class DBOSExecutor {
|
|
674
512
|
return callPromiseWithTimeout(callPromise, $deadlineEpochMS, this.systemDatabase);
|
675
513
|
}
|
676
514
|
});
|
677
|
-
if (this.
|
515
|
+
if (this.debugMode) {
|
678
516
|
const recordedResult = DBOSExecutor.reviveResultOrError((await this.systemDatabase.awaitWorkflowResult(workflowID)));
|
679
517
|
if (!resultsMatch(recordedResult, callResult)) {
|
680
518
|
this.logger.error(`Detect different output for the workflow UUID ${workflowID}!\n Received: ${utils_1.DBOSJSON.stringify(callResult)}\n Original: ${utils_1.DBOSJSON.stringify(recordedResult)}`);
|
@@ -692,21 +530,21 @@ class DBOSExecutor {
|
|
692
530
|
}
|
693
531
|
internalStatus.output = utils_1.DBOSJSON.stringify(result);
|
694
532
|
internalStatus.status = workflow_1.StatusString.SUCCESS;
|
695
|
-
if (!this.
|
533
|
+
if (!this.debugMode) {
|
696
534
|
await this.systemDatabase.recordWorkflowOutput(workflowID, internalStatus);
|
697
535
|
}
|
698
|
-
|
536
|
+
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
699
537
|
}
|
700
538
|
catch (err) {
|
701
539
|
if (err instanceof error_1.DBOSWorkflowConflictError) {
|
702
540
|
// Retrieve the handle and wait for the result.
|
703
541
|
const retrievedHandle = this.retrieveWorkflow(workflowID);
|
704
542
|
result = await retrievedHandle.getResult();
|
705
|
-
|
706
|
-
|
543
|
+
span.setAttribute('cached', true);
|
544
|
+
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
707
545
|
}
|
708
546
|
else if (err instanceof error_1.DBOSWorkflowCancelledError) {
|
709
|
-
|
547
|
+
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err.message });
|
710
548
|
internalStatus.error = err.message;
|
711
549
|
if (err.workflowID === workflowID) {
|
712
550
|
internalStatus.status = workflow_1.StatusString.CANCELLED;
|
@@ -724,11 +562,11 @@ class DBOSExecutor {
|
|
724
562
|
}
|
725
563
|
}
|
726
564
|
finally {
|
727
|
-
this.tracer.endSpan(
|
565
|
+
this.tracer.endSpan(span);
|
728
566
|
}
|
729
567
|
return result;
|
730
568
|
};
|
731
|
-
if (this.
|
569
|
+
if (this.debugMode ||
|
732
570
|
(status !== 'SUCCESS' && status !== 'ERROR' && (params.queueName === undefined || params.executeWorkflow))) {
|
733
571
|
const workflowPromise = runWorkflow();
|
734
572
|
this.systemDatabase.registerRunningWorkflow(workflowID, workflowPromise);
|
@@ -794,7 +632,7 @@ class DBOSExecutor {
|
|
794
632
|
* Write a operation's output to the database.
|
795
633
|
*/
|
796
634
|
async #recordOutput(query, workflowUUID, funcID, txnSnapshot, output, isKeyConflict, function_name) {
|
797
|
-
if (this.
|
635
|
+
if (this.debugMode) {
|
798
636
|
throw new error_1.DBOSDebuggerError('Cannot record output in debug mode.');
|
799
637
|
}
|
800
638
|
try {
|
@@ -816,7 +654,7 @@ class DBOSExecutor {
|
|
816
654
|
* Record an error in an operation to the database.
|
817
655
|
*/
|
818
656
|
async #recordError(query, workflowUUID, funcID, txnSnapshot, err, isKeyConflict, function_name) {
|
819
|
-
if (this.
|
657
|
+
if (this.debugMode) {
|
820
658
|
throw new error_1.DBOSDebuggerError('Cannot record error in debug mode.');
|
821
659
|
}
|
822
660
|
try {
|
@@ -841,70 +679,63 @@ class DBOSExecutor {
|
|
841
679
|
}
|
842
680
|
return rows;
|
843
681
|
}
|
844
|
-
async
|
682
|
+
async runTransactionTempWF(txn, params, ...args) {
|
845
683
|
return await (await this.startTransactionTempWF(txn, params, undefined, undefined, ...args)).getResult();
|
846
684
|
}
|
847
|
-
async startTransactionTempWF(txn, params,
|
685
|
+
async startTransactionTempWF(txn, params, callerWFID, callerFunctionID, ...args) {
|
848
686
|
// Create a workflow and call transaction.
|
849
|
-
const temp_workflow = async (
|
850
|
-
|
851
|
-
return await this.callTransactionFunction(txn, params.configuredInstance ?? null, ctxtImpl, ...args);
|
687
|
+
const temp_workflow = async (...args) => {
|
688
|
+
return await this.callTransactionFunction(txn, params.configuredInstance ?? null, ...args);
|
852
689
|
};
|
853
690
|
return await this.internalWorkflow(temp_workflow, {
|
854
691
|
...params,
|
855
692
|
tempWfType: exports.TempWorkflowType.transaction,
|
856
|
-
tempWfName: (0, decorators_1.
|
857
|
-
tempWfClass: (0, decorators_1.
|
858
|
-
},
|
693
|
+
tempWfName: (0, decorators_1.getRegisteredFunctionName)(txn),
|
694
|
+
tempWfClass: (0, decorators_1.getRegisteredFunctionClassName)(txn),
|
695
|
+
}, callerWFID, callerFunctionID, ...args);
|
859
696
|
}
|
860
|
-
async callTransactionFunction(txn, clsinst,
|
861
|
-
const
|
862
|
-
if (
|
697
|
+
async callTransactionFunction(txn, clsinst, ...args) {
|
698
|
+
const txnReg = (0, decorators_1.getFunctionRegistration)(txn);
|
699
|
+
if (!txnReg || !txnReg.txnConfig) {
|
863
700
|
throw new error_1.DBOSNotRegisteredError(txn.name);
|
864
701
|
}
|
865
|
-
|
702
|
+
const pctx = (0, context_1.getCurrentContextStore)();
|
703
|
+
const wfid = pctx.workflowId;
|
704
|
+
await this.systemDatabase.checkIfCanceled(wfid);
|
866
705
|
let retryWaitMillis = 1;
|
867
706
|
const backoffFactor = 1.5;
|
868
707
|
const maxRetryWaitMs = 2000; // Maximum wait 2 seconds.
|
869
|
-
const funcId =
|
708
|
+
const funcId = (0, context_1.functionIDGetIncrement)();
|
870
709
|
const span = this.tracer.startSpan(txn.name, {
|
871
|
-
operationUUID:
|
710
|
+
operationUUID: wfid,
|
872
711
|
operationType: exports.OperationType.TRANSACTION,
|
873
|
-
authenticatedUser:
|
874
|
-
assumedRole:
|
875
|
-
authenticatedRoles:
|
876
|
-
isolationLevel:
|
877
|
-
},
|
712
|
+
authenticatedUser: pctx.authenticatedUser,
|
713
|
+
assumedRole: pctx.assumedRole,
|
714
|
+
authenticatedRoles: pctx.authenticatedRoles,
|
715
|
+
isolationLevel: txnReg.txnConfig.isolationLevel,
|
716
|
+
}, pctx.span);
|
878
717
|
while (true) {
|
879
|
-
await this.systemDatabase.checkIfCanceled(
|
718
|
+
await this.systemDatabase.checkIfCanceled(wfid);
|
880
719
|
let txn_snapshot = 'invalid';
|
881
720
|
let prevResultFound = false;
|
882
|
-
const workflowUUID = wfCtx.workflowUUID;
|
883
721
|
const wrappedTransaction = async (client) => {
|
884
|
-
const tCtxt = new transaction_1.TransactionContextImpl(this.userDatabase.getName(), client, wfCtx, span, this.logger, funcId, txn.name);
|
885
722
|
// If the UUID is preset, it is possible this execution previously happened. Check, and return its original result if it did.
|
886
723
|
// 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.
|
887
724
|
let prevResult = exports.dbosNull;
|
888
725
|
const queryFunc = (sql, args) => this.userDatabase.queryWithClient(client, sql, ...args);
|
889
|
-
if (
|
890
|
-
const executionResult = await this.#checkExecution(queryFunc,
|
726
|
+
if (pctx.presetID) {
|
727
|
+
const executionResult = await this.#checkExecution(queryFunc, wfid, funcId, txn.name);
|
891
728
|
prevResult = executionResult.result;
|
892
729
|
txn_snapshot = executionResult.txn_snapshot;
|
893
730
|
if (prevResult !== exports.dbosNull) {
|
894
731
|
prevResultFound = true;
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
732
|
+
span.setAttribute('cached', true);
|
733
|
+
// Return/throw the previous result
|
734
|
+
if (prevResult instanceof Error) {
|
735
|
+
throw prevResult;
|
899
736
|
}
|
900
737
|
else {
|
901
|
-
|
902
|
-
if (prevResult instanceof Error) {
|
903
|
-
throw prevResult;
|
904
|
-
}
|
905
|
-
else {
|
906
|
-
return prevResult;
|
907
|
-
}
|
738
|
+
return prevResult;
|
908
739
|
}
|
909
740
|
}
|
910
741
|
}
|
@@ -912,27 +743,29 @@ class DBOSExecutor {
|
|
912
743
|
// Collect snapshot information for read-only transactions and non-preset UUID transactions, if not already collected above
|
913
744
|
txn_snapshot = await DBOSExecutor.#retrieveSnapshot(queryFunc);
|
914
745
|
}
|
915
|
-
if (this.
|
916
|
-
throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the transaction: workflow UUID ${
|
746
|
+
if (this.debugMode && prevResult === exports.dbosNull) {
|
747
|
+
throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the transaction: workflow UUID ${wfid}, step number ${funcId}`);
|
917
748
|
}
|
918
749
|
// Execute the user's transaction.
|
919
750
|
const result = await (async function () {
|
920
751
|
try {
|
921
|
-
return await (0, context_1.
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
752
|
+
return await (0, context_1.runWithParentContext)(pctx, {
|
753
|
+
authenticatedRoles: pctx?.authenticatedRoles,
|
754
|
+
authenticatedUser: pctx?.authenticatedUser,
|
755
|
+
workflowId: wfid,
|
756
|
+
curTxFunctionId: funcId,
|
757
|
+
parentCtx: pctx,
|
758
|
+
sqlClient: client,
|
759
|
+
}, async () => {
|
760
|
+
const tf = txn;
|
761
|
+
return await tf.call(clsinst, ...args);
|
929
762
|
});
|
930
763
|
}
|
931
764
|
catch (e) {
|
932
765
|
return e instanceof Error ? e : new Error(`${e}`);
|
933
766
|
}
|
934
767
|
})();
|
935
|
-
if (this.
|
768
|
+
if (this.debugMode) {
|
936
769
|
if (prevResult instanceof Error) {
|
937
770
|
throw prevResult;
|
938
771
|
}
|
@@ -949,13 +782,13 @@ class DBOSExecutor {
|
|
949
782
|
// Record the execution, commit, and return.
|
950
783
|
try {
|
951
784
|
// Synchronously record the output of write transactions and obtain the transaction ID.
|
952
|
-
const pg_txn_id = await this.#recordOutput(queryFunc,
|
953
|
-
|
785
|
+
const pg_txn_id = await this.#recordOutput(queryFunc, wfid, funcId, txn_snapshot, result, (error) => this.userDatabase.isKeyConflictError(error), txn.name);
|
786
|
+
span.setAttribute('pg_txn_id', pg_txn_id);
|
954
787
|
}
|
955
788
|
catch (error) {
|
956
789
|
if (this.userDatabase.isFailedSqlTransactionError(error)) {
|
957
|
-
this.logger.error(`Postgres aborted the ${txn.name} @DBOS.transaction of Workflow ${
|
958
|
-
throw new error_1.DBOSFailedSqlTransactionError(
|
790
|
+
this.logger.error(`Postgres aborted the ${txn.name} @DBOS.transaction of Workflow ${wfid}, but the function did not raise an exception. Please ensure that the @DBOS.transaction method raises an exception if the database transaction is aborted.`);
|
791
|
+
throw new error_1.DBOSFailedSqlTransactionError(wfid, txn.name);
|
959
792
|
}
|
960
793
|
else {
|
961
794
|
throw error;
|
@@ -964,7 +797,7 @@ class DBOSExecutor {
|
|
964
797
|
return result;
|
965
798
|
};
|
966
799
|
try {
|
967
|
-
const result = await this.userDatabase.transaction(wrappedTransaction,
|
800
|
+
const result = await this.userDatabase.transaction(wrappedTransaction, txnReg.txnConfig);
|
968
801
|
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
969
802
|
this.tracer.endSpan(span);
|
970
803
|
return result;
|
@@ -985,7 +818,7 @@ class DBOSExecutor {
|
|
985
818
|
const e = err;
|
986
819
|
await this.userDatabase.transaction(async (client) => {
|
987
820
|
const func = (sql, args) => this.userDatabase.queryWithClient(client, sql, ...args);
|
988
|
-
await this.#recordError(func,
|
821
|
+
await this.#recordError(func, wfid, funcId, txn_snapshot, e, (error) => this.userDatabase.isKeyConflictError(error), txn.name);
|
989
822
|
}, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
|
990
823
|
}
|
991
824
|
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: e.message });
|
@@ -994,40 +827,42 @@ class DBOSExecutor {
|
|
994
827
|
}
|
995
828
|
}
|
996
829
|
}
|
997
|
-
async
|
830
|
+
async runProcedureTempWF(proc, params, ...args) {
|
998
831
|
// Create a workflow and call procedure.
|
999
|
-
const temp_workflow = async (
|
1000
|
-
|
1001
|
-
return this.callProcedureFunction(proc, ctxtImpl, ...args);
|
832
|
+
const temp_workflow = async (...args) => {
|
833
|
+
return this.callProcedureFunction(proc, ...args);
|
1002
834
|
};
|
1003
835
|
return await (await this.workflow(temp_workflow, {
|
1004
836
|
...params,
|
1005
837
|
tempWfType: exports.TempWorkflowType.procedure,
|
1006
|
-
tempWfName: (0, decorators_1.
|
1007
|
-
tempWfClass: (0, decorators_1.
|
838
|
+
tempWfName: (0, decorators_1.getRegisteredFunctionName)(proc),
|
839
|
+
tempWfClass: (0, decorators_1.getRegisteredFunctionClassName)(proc),
|
1008
840
|
}, ...args)).getResult();
|
1009
841
|
}
|
1010
|
-
async callProcedureFunction(proc,
|
1011
|
-
const procInfo =
|
1012
|
-
if (procInfo
|
842
|
+
async callProcedureFunction(proc, ...args) {
|
843
|
+
const procInfo = (0, decorators_1.getFunctionRegistration)(proc);
|
844
|
+
if (!procInfo || !procInfo.procConfig) {
|
1013
845
|
throw new error_1.DBOSNotRegisteredError(proc.name);
|
1014
846
|
}
|
1015
|
-
|
1016
|
-
const
|
1017
|
-
const
|
847
|
+
const procConfig = procInfo.procConfig;
|
848
|
+
const pctx = (0, context_1.getCurrentContextStore)();
|
849
|
+
const wfid = pctx.workflowId;
|
850
|
+
await this.systemDatabase.checkIfCanceled(wfid);
|
851
|
+
const executeLocally = this.debugMode || (procConfig.executeLocally ?? false);
|
852
|
+
const funcId = (0, context_1.functionIDGetIncrement)();
|
1018
853
|
const span = this.tracer.startSpan(proc.name, {
|
1019
|
-
operationUUID:
|
854
|
+
operationUUID: wfid,
|
1020
855
|
operationType: exports.OperationType.PROCEDURE,
|
1021
|
-
authenticatedUser:
|
1022
|
-
assumedRole:
|
1023
|
-
authenticatedRoles:
|
1024
|
-
isolationLevel:
|
856
|
+
authenticatedUser: pctx.authenticatedUser,
|
857
|
+
assumedRole: pctx.assumedRole,
|
858
|
+
authenticatedRoles: pctx.authenticatedRoles,
|
859
|
+
isolationLevel: procConfig.isolationLevel,
|
1025
860
|
executeLocally,
|
1026
|
-
},
|
861
|
+
}, pctx.span);
|
1027
862
|
try {
|
1028
863
|
const result = executeLocally
|
1029
|
-
? await this.#callProcedureFunctionLocal(proc, args,
|
1030
|
-
: await this.#callProcedureFunctionRemote(proc, args,
|
864
|
+
? await this.#callProcedureFunctionLocal(proc, args, span, procInfo, funcId)
|
865
|
+
: await this.#callProcedureFunctionRemote(proc, args, span, procConfig, funcId);
|
1031
866
|
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
1032
867
|
return result;
|
1033
868
|
}
|
@@ -1040,35 +875,30 @@ class DBOSExecutor {
|
|
1040
875
|
this.tracer.endSpan(span);
|
1041
876
|
}
|
1042
877
|
}
|
1043
|
-
async #callProcedureFunctionLocal(proc, args,
|
878
|
+
async #callProcedureFunctionLocal(proc, args, span, procInfo, funcId) {
|
1044
879
|
let retryWaitMillis = 1;
|
1045
880
|
const backoffFactor = 1.5;
|
1046
881
|
const maxRetryWaitMs = 2000; // Maximum wait 2 seconds.
|
882
|
+
const pctx = (0, context_1.getCurrentContextStore)();
|
883
|
+
const wfid = pctx.workflowId;
|
1047
884
|
while (true) {
|
1048
|
-
await this.systemDatabase.checkIfCanceled(
|
885
|
+
await this.systemDatabase.checkIfCanceled(wfid);
|
1049
886
|
let txn_snapshot = 'invalid';
|
1050
887
|
const wrappedProcedure = async (client) => {
|
1051
|
-
const ctxt = new procedure_1.StoredProcedureContextImpl(client, wfCtx, span, this.logger, funcId, proc.name);
|
1052
888
|
let prevResult = exports.dbosNull;
|
1053
889
|
const queryFunc = (sql, args) => this.procedurePool.query(sql, args).then((v) => v.rows);
|
1054
|
-
if (
|
1055
|
-
const executionResult = await this.#checkExecution(queryFunc,
|
890
|
+
if (pctx.presetID) {
|
891
|
+
const executionResult = await this.#checkExecution(queryFunc, wfid, funcId, proc.name);
|
1056
892
|
prevResult = executionResult.result;
|
1057
893
|
txn_snapshot = executionResult.txn_snapshot;
|
1058
894
|
if (prevResult !== exports.dbosNull) {
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
895
|
+
span.setAttribute('cached', true);
|
896
|
+
// Return/throw the previous result
|
897
|
+
if (prevResult instanceof Error) {
|
898
|
+
throw prevResult;
|
1063
899
|
}
|
1064
900
|
else {
|
1065
|
-
|
1066
|
-
if (prevResult instanceof Error) {
|
1067
|
-
throw prevResult;
|
1068
|
-
}
|
1069
|
-
else {
|
1070
|
-
return prevResult;
|
1071
|
-
}
|
901
|
+
return prevResult;
|
1072
902
|
}
|
1073
903
|
}
|
1074
904
|
}
|
@@ -1076,27 +906,33 @@ class DBOSExecutor {
|
|
1076
906
|
// Collect snapshot information for read-only transactions and non-preset UUID transactions, if not already collected above
|
1077
907
|
txn_snapshot = await DBOSExecutor.#retrieveSnapshot(queryFunc);
|
1078
908
|
}
|
1079
|
-
if (this.
|
1080
|
-
throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the procedure: workflow UUID ${
|
909
|
+
if (this.debugMode && prevResult === exports.dbosNull) {
|
910
|
+
throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the procedure: workflow UUID ${wfid}, step number ${funcId}`);
|
1081
911
|
}
|
1082
912
|
// Execute the user's transaction.
|
1083
913
|
const result = await (async function () {
|
1084
914
|
try {
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
915
|
+
// Check we are in a workflow context and not in a step / transaction already
|
916
|
+
const pctx = (0, context_1.getCurrentContextStore)();
|
917
|
+
if (!pctx)
|
918
|
+
throw new error_1.DBOSInvalidWorkflowTransitionError();
|
919
|
+
if (!(0, context_1.isInWorkflowCtx)(pctx))
|
920
|
+
throw new error_1.DBOSInvalidWorkflowTransitionError();
|
921
|
+
return await (0, context_1.runWithParentContext)(pctx, {
|
922
|
+
curTxFunctionId: funcId,
|
923
|
+
parentCtx: pctx,
|
924
|
+
isInStoredProc: true,
|
925
|
+
sqlClient: client,
|
926
|
+
}, async () => {
|
927
|
+
const pf = proc;
|
928
|
+
return await pf(...args);
|
1093
929
|
});
|
1094
930
|
}
|
1095
931
|
catch (e) {
|
1096
932
|
return e instanceof Error ? e : new Error(`${e}`);
|
1097
933
|
}
|
1098
934
|
})();
|
1099
|
-
if (this.
|
935
|
+
if (this.debugMode) {
|
1100
936
|
if (prevResult instanceof Error) {
|
1101
937
|
throw prevResult;
|
1102
938
|
}
|
@@ -1112,20 +948,20 @@ class DBOSExecutor {
|
|
1112
948
|
}
|
1113
949
|
// Synchronously record the output of write transactions and obtain the transaction ID.
|
1114
950
|
const func = (sql, args) => client.query(sql, args).then((v) => v.rows);
|
1115
|
-
const pg_txn_id = await this.#recordOutput(func,
|
951
|
+
const pg_txn_id = await this.#recordOutput(func, wfid, funcId, txn_snapshot, result, user_database_1.pgNodeIsKeyConflictError, proc.name);
|
1116
952
|
// const pg_txn_id = await wfCtx.recordOutputProc<R>(client, funcId, txn_snapshot, result);
|
1117
|
-
|
953
|
+
span.setAttribute('pg_txn_id', pg_txn_id);
|
1118
954
|
return result;
|
1119
955
|
};
|
1120
956
|
try {
|
1121
957
|
const result = await this.invokeStoredProcFunction(wrappedProcedure, {
|
1122
|
-
isolationLevel: procInfo.
|
958
|
+
isolationLevel: procInfo.procConfig.isolationLevel,
|
1123
959
|
});
|
1124
960
|
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
1125
961
|
return result;
|
1126
962
|
}
|
1127
963
|
catch (err) {
|
1128
|
-
if (!this.
|
964
|
+
if (!this.debugMode) {
|
1129
965
|
if (this.userDatabase.isRetriableTransactionError(err)) {
|
1130
966
|
// serialization_failure in PostgreSQL
|
1131
967
|
span.addEvent('TXN SERIALIZATION FAILURE', { retryWaitMillis: retryWaitMillis }, performance.now());
|
@@ -1139,32 +975,34 @@ class DBOSExecutor {
|
|
1139
975
|
const e = err;
|
1140
976
|
await this.invokeStoredProcFunction(async (client) => {
|
1141
977
|
const func = (sql, args) => client.query(sql, args).then((v) => v.rows);
|
1142
|
-
await this.#recordError(func,
|
978
|
+
await this.#recordError(func, wfid, funcId, txn_snapshot, e, user_database_1.pgNodeIsKeyConflictError, proc.name);
|
1143
979
|
}, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
|
1144
980
|
await this.userDatabase.transaction(async (client) => {
|
1145
981
|
const func = (sql, args) => this.userDatabase.queryWithClient(client, sql, ...args);
|
1146
|
-
await this.#recordError(func,
|
982
|
+
await this.#recordError(func, wfid, funcId, txn_snapshot, e, (error) => this.userDatabase.isKeyConflictError(error), proc.name);
|
1147
983
|
}, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
|
1148
984
|
}
|
1149
985
|
throw err;
|
1150
986
|
}
|
1151
987
|
}
|
1152
988
|
}
|
1153
|
-
async #callProcedureFunctionRemote(proc, args,
|
1154
|
-
if (this.
|
989
|
+
async #callProcedureFunctionRemote(proc, args, span, config, funcId) {
|
990
|
+
if (this.debugMode) {
|
1155
991
|
throw new error_1.DBOSDebuggerError("Can't invoke stored procedure in debug mode.");
|
1156
992
|
}
|
1157
|
-
|
993
|
+
const pctx = (0, context_1.getCurrentContextStore)();
|
994
|
+
const wfid = pctx.workflowId;
|
995
|
+
await this.systemDatabase.checkIfCanceled(wfid);
|
1158
996
|
const $jsonCtx = {
|
1159
|
-
request:
|
1160
|
-
authenticatedUser:
|
1161
|
-
authenticatedRoles:
|
1162
|
-
assumedRole:
|
997
|
+
request: pctx.request,
|
998
|
+
authenticatedUser: pctx.authenticatedUser,
|
999
|
+
authenticatedRoles: pctx.authenticatedRoles,
|
1000
|
+
assumedRole: pctx.assumedRole,
|
1163
1001
|
};
|
1164
1002
|
// TODO (Qian/Harry): remove this unshift when we remove the resultBuffer argument
|
1165
1003
|
// Note, node-pg converts JS arrays to postgres array literals, so must call JSON.strigify on
|
1166
1004
|
// args and bufferedResults before being passed to #invokeStoredProc
|
1167
|
-
const $args = [
|
1005
|
+
const $args = [wfid, funcId, pctx.presetID, $jsonCtx, null, JSON.stringify(args)];
|
1168
1006
|
const readonly = config.readOnly ?? false;
|
1169
1007
|
if (!readonly) {
|
1170
1008
|
$args.unshift(null);
|
@@ -1185,8 +1023,8 @@ class DBOSExecutor {
|
|
1185
1023
|
async #invokeStoredProc(proc, args) {
|
1186
1024
|
const client = await this.procedurePool.connect();
|
1187
1025
|
const log = (msg) => this.#logNotice(msg);
|
1188
|
-
const
|
1189
|
-
const plainProcName = `${
|
1026
|
+
const procname = (0, decorators_1.getRegisteredFunctionFullName)(proc);
|
1027
|
+
const plainProcName = `${procname.className}_${procname.name}_p`;
|
1190
1028
|
const procName = utils_1.globalParams.wasComputed ? plainProcName : `v${utils_1.globalParams.appVersion}_${plainProcName}`;
|
1191
1029
|
const sql = `CALL "${procName}"(${args.map((_v, i) => `$${i + 1}`).join()});`;
|
1192
1030
|
try {
|
@@ -1219,106 +1057,98 @@ class DBOSExecutor {
|
|
1219
1057
|
client.release();
|
1220
1058
|
}
|
1221
1059
|
}
|
1222
|
-
async
|
1060
|
+
async runStepTempWF(stepFn, params, ...args) {
|
1223
1061
|
return await (await this.startStepTempWF(stepFn, params, undefined, undefined, ...args)).getResult();
|
1224
1062
|
}
|
1225
|
-
async startStepTempWF(stepFn, params,
|
1063
|
+
async startStepTempWF(stepFn, params, callerWFID, callerFunctionID, ...args) {
|
1226
1064
|
// Create a workflow and call external.
|
1227
|
-
const temp_workflow = async (
|
1228
|
-
|
1229
|
-
return await this.callStepFunction(stepFn, undefined, undefined, params.configuredInstance ?? null, ctxtImpl, ...args);
|
1065
|
+
const temp_workflow = async (...args) => {
|
1066
|
+
return await this.callStepFunction(stepFn, undefined, undefined, params.configuredInstance ?? null, ...args);
|
1230
1067
|
};
|
1231
1068
|
return await this.internalWorkflow(temp_workflow, {
|
1232
1069
|
...params,
|
1233
1070
|
tempWfType: exports.TempWorkflowType.step,
|
1234
|
-
tempWfName: (0, decorators_1.
|
1235
|
-
tempWfClass: (0, decorators_1.
|
1236
|
-
},
|
1071
|
+
tempWfName: (0, decorators_1.getRegisteredFunctionName)(stepFn),
|
1072
|
+
tempWfClass: (0, decorators_1.getRegisteredFunctionClassName)(stepFn),
|
1073
|
+
}, callerWFID, callerFunctionID, ...args);
|
1237
1074
|
}
|
1238
1075
|
/**
|
1239
1076
|
* Execute a step function.
|
1240
1077
|
* If it encounters any error, retry according to its configured retry policy until the maximum number of attempts is reached, then throw an DBOSError.
|
1241
1078
|
* The step may execute many times, but once it is complete, it will not re-execute.
|
1242
1079
|
*/
|
1243
|
-
async callStepFunction(stepFn, stepFnName, stepConfig, clsInst,
|
1080
|
+
async callStepFunction(stepFn, stepFnName, stepConfig, clsInst, ...args) {
|
1244
1081
|
stepFnName = stepFnName ?? stepFn.name ?? '<unnamed>';
|
1245
|
-
let passContext = false;
|
1246
1082
|
if (!stepConfig) {
|
1247
|
-
const stepReg =
|
1248
|
-
stepConfig = stepReg?.
|
1249
|
-
passContext = stepReg?.registration.passContext ?? true;
|
1083
|
+
const stepReg = (0, decorators_1.getFunctionRegistration)(stepFn);
|
1084
|
+
stepConfig = stepReg?.stepConfig;
|
1250
1085
|
}
|
1251
1086
|
if (stepConfig === undefined) {
|
1252
1087
|
throw new error_1.DBOSNotRegisteredError(stepFnName);
|
1253
1088
|
}
|
1254
|
-
|
1255
|
-
const
|
1089
|
+
const lctx = (0, context_1.getCurrentContextStore)();
|
1090
|
+
const wfid = lctx.workflowId;
|
1091
|
+
await this.systemDatabase.checkIfCanceled(wfid);
|
1092
|
+
const funcID = (0, context_1.functionIDGetIncrement)();
|
1256
1093
|
const maxRetryIntervalSec = 3600; // Maximum retry interval: 1 hour
|
1257
1094
|
const span = this.tracer.startSpan(stepFnName, {
|
1258
|
-
operationUUID:
|
1095
|
+
operationUUID: wfid,
|
1259
1096
|
operationType: exports.OperationType.COMMUNICATOR,
|
1260
|
-
authenticatedUser:
|
1261
|
-
assumedRole:
|
1262
|
-
authenticatedRoles:
|
1097
|
+
authenticatedUser: lctx.authenticatedUser,
|
1098
|
+
assumedRole: lctx.assumedRole,
|
1099
|
+
authenticatedRoles: lctx.authenticatedRoles,
|
1263
1100
|
retriesAllowed: stepConfig.retriesAllowed,
|
1264
1101
|
intervalSeconds: stepConfig.intervalSeconds,
|
1265
1102
|
maxAttempts: stepConfig.maxAttempts,
|
1266
1103
|
backoffRate: stepConfig.backoffRate,
|
1267
|
-
},
|
1268
|
-
const ctxt = new step_1.StepContextImpl(wfCtx, funcID, span, this.logger, stepConfig, stepFnName);
|
1104
|
+
}, lctx.span);
|
1269
1105
|
// Check if this execution previously happened, returning its original result if it did.
|
1270
|
-
const checkr = await this.systemDatabase.getOperationResultAndThrowIfCancelled(
|
1106
|
+
const checkr = await this.systemDatabase.getOperationResultAndThrowIfCancelled(wfid, funcID);
|
1271
1107
|
if (checkr) {
|
1272
|
-
if (checkr.functionName !==
|
1273
|
-
throw new error_1.DBOSUnexpectedStepError(
|
1108
|
+
if (checkr.functionName !== stepFnName) {
|
1109
|
+
throw new error_1.DBOSUnexpectedStepError(wfid, funcID, stepFnName, checkr.functionName ?? '?');
|
1274
1110
|
}
|
1275
1111
|
const check = DBOSExecutor.reviveResultOrError(checkr);
|
1276
|
-
|
1277
|
-
|
1278
|
-
this.tracer.endSpan(
|
1112
|
+
span.setAttribute('cached', true);
|
1113
|
+
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
1114
|
+
this.tracer.endSpan(span);
|
1279
1115
|
return check;
|
1280
1116
|
}
|
1281
|
-
if (this.
|
1282
|
-
throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the step: workflow UUID: ${
|
1117
|
+
if (this.debugMode) {
|
1118
|
+
throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the step: workflow UUID: ${wfid}, step number: ${funcID}`);
|
1283
1119
|
}
|
1120
|
+
const maxAttempts = stepConfig.maxAttempts ?? 3;
|
1284
1121
|
// Execute the step function. If it throws an exception, retry with exponential backoff.
|
1285
1122
|
// After reaching the maximum number of retries, throw an DBOSError.
|
1286
1123
|
let result = exports.dbosNull;
|
1287
1124
|
let err = exports.dbosNull;
|
1288
1125
|
const errors = [];
|
1289
|
-
if (
|
1290
|
-
let
|
1291
|
-
let intervalSeconds =
|
1126
|
+
if (stepConfig.retriesAllowed) {
|
1127
|
+
let attemptNum = 0;
|
1128
|
+
let intervalSeconds = stepConfig.intervalSeconds ?? 1;
|
1292
1129
|
if (intervalSeconds > maxRetryIntervalSec) {
|
1293
1130
|
this.logger.warn(`Step config interval exceeds maximum allowed interval, capped to ${maxRetryIntervalSec} seconds!`);
|
1294
1131
|
}
|
1295
|
-
while (result === exports.dbosNull &&
|
1132
|
+
while (result === exports.dbosNull && attemptNum++ < (maxAttempts ?? 3)) {
|
1296
1133
|
try {
|
1297
|
-
await this.systemDatabase.checkIfCanceled(
|
1134
|
+
await this.systemDatabase.checkIfCanceled(wfid);
|
1298
1135
|
let cresult;
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
}
|
1304
|
-
else {
|
1305
|
-
await (0, context_1.runWithStepContext)(ctxt, numAttempts, async () => {
|
1306
|
-
const sf = stepFn;
|
1307
|
-
cresult = await sf.call(clsInst, ...args);
|
1308
|
-
});
|
1309
|
-
}
|
1136
|
+
await (0, context_1.runInStepContext)(lctx, funcID, maxAttempts, attemptNum, async () => {
|
1137
|
+
const sf = stepFn;
|
1138
|
+
cresult = await sf.call(clsInst, ...args);
|
1139
|
+
});
|
1310
1140
|
result = cresult;
|
1311
1141
|
}
|
1312
1142
|
catch (error) {
|
1313
1143
|
const e = error;
|
1314
1144
|
errors.push(e);
|
1315
|
-
this.logger.warn(`Error in step being automatically retried. Attempt ${
|
1316
|
-
span.addEvent(`Step attempt ${
|
1317
|
-
if (
|
1145
|
+
this.logger.warn(`Error in step being automatically retried. Attempt ${attemptNum} of ${maxAttempts}. ${e.stack}`);
|
1146
|
+
span.addEvent(`Step attempt ${attemptNum + 1} failed`, { retryIntervalSeconds: intervalSeconds, error: error.message }, performance.now());
|
1147
|
+
if (attemptNum < maxAttempts) {
|
1318
1148
|
// Sleep for an interval, then increase the interval by backoffRate.
|
1319
1149
|
// Cap at the maximum allowed retry interval.
|
1320
1150
|
await (0, utils_1.sleepms)(intervalSeconds * 1000);
|
1321
|
-
intervalSeconds *=
|
1151
|
+
intervalSeconds *= stepConfig.backoffRate ?? 2;
|
1322
1152
|
intervalSeconds = intervalSeconds < maxRetryIntervalSec ? intervalSeconds : maxRetryIntervalSec;
|
1323
1153
|
}
|
1324
1154
|
}
|
@@ -1327,17 +1157,10 @@ class DBOSExecutor {
|
|
1327
1157
|
else {
|
1328
1158
|
try {
|
1329
1159
|
let cresult;
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
}
|
1335
|
-
else {
|
1336
|
-
await (0, context_1.runWithStepContext)(ctxt, undefined, async () => {
|
1337
|
-
const sf = stepFn;
|
1338
|
-
cresult = await sf.call(clsInst, ...args);
|
1339
|
-
});
|
1340
|
-
}
|
1160
|
+
await (0, context_1.runInStepContext)(lctx, funcID, maxAttempts, undefined, async () => {
|
1161
|
+
const sf = stepFn;
|
1162
|
+
cresult = await sf.call(clsInst, ...args);
|
1163
|
+
});
|
1341
1164
|
result = cresult;
|
1342
1165
|
}
|
1343
1166
|
catch (error) {
|
@@ -1347,35 +1170,37 @@ class DBOSExecutor {
|
|
1347
1170
|
// `result` can only be dbosNull when the step timed out
|
1348
1171
|
if (result === exports.dbosNull) {
|
1349
1172
|
// Record the error, then throw it.
|
1350
|
-
err = err === exports.dbosNull ? new error_1.DBOSMaxStepRetriesError(stepFnName,
|
1351
|
-
await this.systemDatabase.recordOperationResult(
|
1173
|
+
err = err === exports.dbosNull ? new error_1.DBOSMaxStepRetriesError(stepFnName, maxAttempts, errors) : err;
|
1174
|
+
await this.systemDatabase.recordOperationResult(wfid, funcID, stepFnName, true, {
|
1352
1175
|
error: utils_1.DBOSJSON.stringify((0, serialize_error_1.serializeError)(err)),
|
1353
1176
|
});
|
1354
|
-
|
1355
|
-
this.tracer.endSpan(
|
1177
|
+
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err.message });
|
1178
|
+
this.tracer.endSpan(span);
|
1356
1179
|
throw err;
|
1357
1180
|
}
|
1358
1181
|
else {
|
1359
1182
|
// Record the execution and return.
|
1360
|
-
await this.systemDatabase.recordOperationResult(
|
1183
|
+
await this.systemDatabase.recordOperationResult(wfid, funcID, stepFnName, true, {
|
1361
1184
|
output: utils_1.DBOSJSON.stringify(result),
|
1362
1185
|
});
|
1363
|
-
|
1364
|
-
this.tracer.endSpan(
|
1186
|
+
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
1187
|
+
this.tracer.endSpan(span);
|
1365
1188
|
return result;
|
1366
1189
|
}
|
1367
1190
|
}
|
1368
|
-
async
|
1191
|
+
async runSendTempWF(destinationId, message, topic, idempotencyKey) {
|
1369
1192
|
// Create a workflow and call send.
|
1370
|
-
const temp_workflow = async (
|
1371
|
-
|
1193
|
+
const temp_workflow = async (destinationId, message, topic) => {
|
1194
|
+
const ctx = (0, context_1.getCurrentContextStore)();
|
1195
|
+
const functionID = (0, context_1.functionIDGetIncrement)();
|
1196
|
+
await this.systemDatabase.send(ctx.workflowId, functionID, destinationId, utils_1.DBOSJSON.stringify(message), topic);
|
1372
1197
|
};
|
1373
|
-
const workflowUUID = idempotencyKey ?
|
1198
|
+
const workflowUUID = idempotencyKey ? destinationId + idempotencyKey : undefined;
|
1374
1199
|
return (await this.workflow(temp_workflow, {
|
1375
1200
|
workflowUUID: workflowUUID,
|
1376
1201
|
tempWfType: exports.TempWorkflowType.send,
|
1377
1202
|
configuredInstance: null,
|
1378
|
-
},
|
1203
|
+
}, destinationId, message, topic)).getResult();
|
1379
1204
|
}
|
1380
1205
|
/**
|
1381
1206
|
* Wait for a workflow to emit an event, then return its value.
|
@@ -1443,33 +1268,13 @@ class DBOSExecutor {
|
|
1443
1268
|
return await this.userDatabase.query(sql);
|
1444
1269
|
}
|
1445
1270
|
}
|
1446
|
-
async userDBListen(channels, callback) {
|
1447
|
-
const notificationsClient = await this.procedurePool.connect();
|
1448
|
-
for (const nname of channels) {
|
1449
|
-
await notificationsClient.query(`LISTEN ${nname};`);
|
1450
|
-
}
|
1451
|
-
notificationsClient.on('notification', callback);
|
1452
|
-
return {
|
1453
|
-
close: async () => {
|
1454
|
-
for (const nname of channels) {
|
1455
|
-
try {
|
1456
|
-
await notificationsClient.query(`UNLISTEN ${nname};`);
|
1457
|
-
}
|
1458
|
-
catch (e) {
|
1459
|
-
this.logger.warn(e);
|
1460
|
-
}
|
1461
|
-
notificationsClient.release();
|
1462
|
-
}
|
1463
|
-
},
|
1464
|
-
};
|
1465
|
-
}
|
1466
1271
|
/* INTERNAL HELPERS */
|
1467
1272
|
/**
|
1468
1273
|
* A recovery process that by default runs during executor init time.
|
1469
1274
|
* It runs to completion all pending workflows that were executing when the previous executor failed.
|
1470
1275
|
*/
|
1471
1276
|
async recoverPendingWorkflows(executorIDs = ['local']) {
|
1472
|
-
if (this.
|
1277
|
+
if (this.debugMode) {
|
1473
1278
|
throw new error_1.DBOSDebuggerError('Cannot recover pending workflows in debug mode.');
|
1474
1279
|
}
|
1475
1280
|
const handlerArray = [];
|
@@ -1510,34 +1315,22 @@ class DBOSExecutor {
|
|
1510
1315
|
this.scheduler = new scheduler_1.DBOSScheduler(this);
|
1511
1316
|
this.scheduler.initScheduler();
|
1512
1317
|
this.wfqEnded = wfqueue_1.wfQueueRunner.dispatchLoop(this);
|
1513
|
-
for (const evtRcvr of this.eventReceivers) {
|
1514
|
-
await evtRcvr.initialize(this);
|
1515
|
-
}
|
1516
1318
|
for (const lcl of (0, decorators_1.getLifecycleListeners)()) {
|
1517
|
-
await lcl.initialize();
|
1319
|
+
await lcl.initialize?.();
|
1518
1320
|
}
|
1519
1321
|
}
|
1520
1322
|
async deactivateEventReceivers(stopQueueThread = true) {
|
1521
1323
|
this.logger.debug('Deactivating lifecycle listeners');
|
1522
1324
|
for (const lcl of (0, decorators_1.getLifecycleListeners)()) {
|
1523
1325
|
try {
|
1524
|
-
await lcl.destroy();
|
1326
|
+
await lcl.destroy?.();
|
1525
1327
|
}
|
1526
1328
|
catch (err) {
|
1527
1329
|
const e = err;
|
1528
1330
|
this.logger.warn(`Error destroying lifecycle listener: ${e.message}`);
|
1529
1331
|
}
|
1530
1332
|
}
|
1531
|
-
this.logger.debug('Deactivating
|
1532
|
-
for (const evtRcvr of this.eventReceivers || []) {
|
1533
|
-
try {
|
1534
|
-
await evtRcvr.destroy();
|
1535
|
-
}
|
1536
|
-
catch (err) {
|
1537
|
-
const e = err;
|
1538
|
-
this.logger.warn(`Error destroying event receiver: ${e.message}`);
|
1539
|
-
}
|
1540
|
-
}
|
1333
|
+
this.logger.debug('Deactivating scheduler');
|
1541
1334
|
try {
|
1542
1335
|
await this.scheduler?.destroyScheduler();
|
1543
1336
|
}
|
@@ -1545,6 +1338,7 @@ class DBOSExecutor {
|
|
1545
1338
|
const e = err;
|
1546
1339
|
this.logger.warn(`Error destroying scheduler: ${e.message}`);
|
1547
1340
|
}
|
1341
|
+
this.logger.debug('Deactivating queue runner');
|
1548
1342
|
if (stopQueueThread) {
|
1549
1343
|
try {
|
1550
1344
|
wfqueue_1.wfQueueRunner.stop();
|
@@ -1567,67 +1361,72 @@ class DBOSExecutor {
|
|
1567
1361
|
throw new error_1.DBOSError(`Failed to find inputs for workflow UUID: ${workflowID}`);
|
1568
1362
|
}
|
1569
1363
|
const inputs = utils_1.DBOSJSON.parse(wfStatus.input);
|
1570
|
-
const
|
1571
|
-
const {
|
1364
|
+
const recoverCtx = this.#getRecoveryContext(workflowID, wfStatus);
|
1365
|
+
const { methReg, configuredInst } = this.#getFunctionInfoFromWFStatus(wfStatus);
|
1572
1366
|
// If starting a new workflow, assign a new UUID. Otherwise, use the workflow's original UUID.
|
1573
1367
|
const workflowStartID = startNewWorkflow ? undefined : workflowID;
|
1574
|
-
if (
|
1575
|
-
return
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1368
|
+
if (methReg?.workflowConfig) {
|
1369
|
+
return await (0, context_1.runWithTopContext)(recoverCtx, async () => {
|
1370
|
+
return await this.workflow(methReg.registeredFunction, {
|
1371
|
+
workflowUUID: workflowStartID,
|
1372
|
+
configuredInstance: configuredInst,
|
1373
|
+
queueName: wfStatus.queueName,
|
1374
|
+
executeWorkflow: true,
|
1375
|
+
deadlineEpochMS: wfStatus.deadlineEpochMS,
|
1376
|
+
}, ...inputs);
|
1377
|
+
});
|
1583
1378
|
}
|
1584
1379
|
// Should be temporary workflows. Parse the name of the workflow.
|
1585
1380
|
const wfName = wfStatus.workflowName;
|
1586
1381
|
const nameArr = wfName.split('-');
|
1587
1382
|
if (!nameArr[0].startsWith(DBOSExecutor.tempWorkflowName)) {
|
1588
|
-
|
1589
|
-
throw new error_1.DBOSError(`This should never happen! Cannot find workflow info for a non-temporary workflow! UUID ${workflowID}, name ${wfName}`);
|
1383
|
+
throw new error_1.DBOSError(`Cannot find workflow function for a non-temporary workflow, ID ${workflowID}, class '${wfStatus.workflowClassName}', function '${wfName}'; did you change your code?`);
|
1590
1384
|
}
|
1591
|
-
let temp_workflow;
|
1592
1385
|
if (nameArr[1] === exports.TempWorkflowType.transaction) {
|
1593
|
-
const
|
1594
|
-
if (!
|
1595
|
-
this.logger.error(`Cannot find transaction info for
|
1386
|
+
const txnReg = (0, decorators_1.getFunctionRegistrationByName)(wfStatus.workflowClassName, nameArr[2]);
|
1387
|
+
if (!txnReg?.txnConfig) {
|
1388
|
+
this.logger.error(`Cannot find transaction info for ID ${workflowID}, name ${nameArr[2]}`);
|
1596
1389
|
throw new error_1.DBOSNotRegisteredError(nameArr[2]);
|
1597
1390
|
}
|
1598
|
-
return await
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1604
|
-
|
1391
|
+
return await (0, context_1.runWithTopContext)(recoverCtx, async () => {
|
1392
|
+
return await this.startTransactionTempWF(txnReg.registeredFunction, {
|
1393
|
+
workflowUUID: workflowStartID,
|
1394
|
+
configuredInstance: configuredInst,
|
1395
|
+
queueName: wfStatus.queueName,
|
1396
|
+
executeWorkflow: true,
|
1397
|
+
}, undefined, undefined, ...inputs);
|
1398
|
+
});
|
1605
1399
|
}
|
1606
1400
|
else if (nameArr[1] === exports.TempWorkflowType.step) {
|
1607
|
-
const
|
1608
|
-
if (!
|
1609
|
-
this.logger.error(`Cannot find step info for
|
1401
|
+
const stepReg = (0, decorators_1.getFunctionRegistrationByName)(wfStatus.workflowClassName, nameArr[2]);
|
1402
|
+
if (!stepReg?.stepConfig) {
|
1403
|
+
this.logger.error(`Cannot find step info for ID ${workflowID}, name ${nameArr[2]}`);
|
1610
1404
|
throw new error_1.DBOSNotRegisteredError(nameArr[2]);
|
1611
1405
|
}
|
1612
|
-
return await
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
1616
|
-
|
1617
|
-
|
1618
|
-
|
1406
|
+
return await (0, context_1.runWithTopContext)(recoverCtx, async () => {
|
1407
|
+
return await this.startStepTempWF(stepReg.registeredFunction, {
|
1408
|
+
workflowUUID: workflowStartID,
|
1409
|
+
configuredInstance: configuredInst,
|
1410
|
+
queueName: wfStatus.queueName, // Probably null
|
1411
|
+
executeWorkflow: true,
|
1412
|
+
}, undefined, undefined, ...inputs);
|
1413
|
+
});
|
1619
1414
|
}
|
1620
1415
|
else if (nameArr[1] === exports.TempWorkflowType.send) {
|
1621
|
-
|
1622
|
-
|
1416
|
+
const swf = async (destinationID, message, topic) => {
|
1417
|
+
const ctx = (0, context_1.getCurrentContextStore)();
|
1418
|
+
const functionID = (0, context_1.functionIDGetIncrement)();
|
1419
|
+
await this.systemDatabase.send(ctx.workflowId, functionID, destinationID, utils_1.DBOSJSON.stringify(message), topic);
|
1623
1420
|
};
|
1624
|
-
|
1625
|
-
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
1630
|
-
|
1421
|
+
const temp_workflow = swf;
|
1422
|
+
return await (0, context_1.runWithTopContext)(recoverCtx, async () => {
|
1423
|
+
return this.workflow(temp_workflow, {
|
1424
|
+
workflowUUID: workflowStartID,
|
1425
|
+
tempWfType: exports.TempWorkflowType.send,
|
1426
|
+
queueName: wfStatus.queueName,
|
1427
|
+
executeWorkflow: true,
|
1428
|
+
}, ...inputs);
|
1429
|
+
});
|
1631
1430
|
}
|
1632
1431
|
else {
|
1633
1432
|
this.logger.error(`Unrecognized temporary workflow! UUID ${workflowID}, name ${wfName}`);
|
@@ -1640,14 +1439,13 @@ class DBOSExecutor {
|
|
1640
1439
|
async upsertEventDispatchState(state) {
|
1641
1440
|
return await this.systemDatabase.upsertEventDispatchState(state);
|
1642
1441
|
}
|
1643
|
-
#getRecoveryContext(
|
1442
|
+
#getRecoveryContext(_workflowID, status) {
|
1644
1443
|
// Note: this doesn't inherit the original parent context's span.
|
1645
|
-
const oc =
|
1444
|
+
const oc = {};
|
1646
1445
|
oc.request = status.request;
|
1647
1446
|
oc.authenticatedUser = status.authenticatedUser;
|
1648
1447
|
oc.authenticatedRoles = status.authenticatedRoles;
|
1649
1448
|
oc.assumedRole = status.assumedRole;
|
1650
|
-
oc.workflowUUID = workflowUUID;
|
1651
1449
|
return oc;
|
1652
1450
|
}
|
1653
1451
|
async cancelWorkflow(workflowID) {
|
@@ -1671,7 +1469,7 @@ class DBOSExecutor {
|
|
1671
1469
|
}
|
1672
1470
|
logRegisteredHTTPUrls() {
|
1673
1471
|
this.logger.info('HTTP endpoints supported:');
|
1674
|
-
|
1472
|
+
(0, decorators_1.getAllRegisteredFunctions)().forEach((registeredOperation) => {
|
1675
1473
|
const ro = registeredOperation;
|
1676
1474
|
if (ro.apiURL) {
|
1677
1475
|
this.logger.info(' ' + ro.apiType.padEnd(6) + ' : ' + ro.apiURL);
|
@@ -1699,8 +1497,9 @@ class DBOSExecutor {
|
|
1699
1497
|
*/
|
1700
1498
|
computeAppVersion() {
|
1701
1499
|
const hasher = crypto.createHash('md5');
|
1702
|
-
const sortedWorkflowSource = Array.from(
|
1703
|
-
.
|
1500
|
+
const sortedWorkflowSource = Array.from((0, decorators_1.getAllRegisteredFunctions)())
|
1501
|
+
.filter((e) => e.workflowConfig)
|
1502
|
+
.map((i) => i.origFunction.toString())
|
1704
1503
|
.sort();
|
1705
1504
|
// Different DBOS versions should produce different hashes.
|
1706
1505
|
sortedWorkflowSource.push(utils_1.globalParams.dbosVersion);
|