@dbos-inc/dbos-sdk 3.0.23-preview → 3.0.29-preview
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/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/client.d.ts.map +1 -1
- package/dist/src/client.js.map +1 -1
- package/dist/src/context.d.ts +10 -44
- package/dist/src/context.d.ts.map +1 -1
- package/dist/src/context.js +34 -114
- 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 +41 -18
- package/dist/src/datasource.js.map +1 -1
- package/dist/src/dbos-executor.d.ts +43 -84
- package/dist/src/dbos-executor.d.ts.map +1 -1
- package/dist/src/dbos-executor.js +313 -500
- 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 +11 -42
- package/dist/src/dbos.d.ts.map +1 -1
- package/dist/src/dbos.js +103 -284
- package/dist/src/dbos.js.map +1 -1
- package/dist/src/decorators.d.ts +33 -60
- package/dist/src/decorators.d.ts.map +1 -1
- package/dist/src/decorators.js +154 -190
- 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/middleware.d.ts +2 -2
- package/dist/src/httpServer/middleware.d.ts.map +1 -1
- package/dist/src/httpServer/server.d.ts +3 -3
- package/dist/src/httpServer/server.d.ts.map +1 -1
- package/dist/src/httpServer/server.js +23 -24
- 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 +9 -10
- 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 +4 -8
- package/dist/src/telemetry/logs.d.ts.map +1 -1
- package/dist/src/telemetry/logs.js +25 -13
- package/dist/src/telemetry/logs.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/user_database.d.ts +2 -2
- package/dist/src/user_database.d.ts.map +1 -1
- package/dist/src/user_database.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,17 +65,11 @@ 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',
|
79
71
|
TRANSACTION: 'transaction',
|
80
|
-
|
72
|
+
STEP: 'step',
|
81
73
|
PROCEDURE: 'procedure',
|
82
74
|
};
|
83
75
|
exports.TempWorkflowType = {
|
@@ -96,51 +88,16 @@ 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;
|
96
|
+
ctxLogger;
|
139
97
|
tracer;
|
140
98
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
141
99
|
typeormEntities = [];
|
142
100
|
drizzleEntities = {};
|
143
|
-
eventReceivers = [];
|
144
101
|
scheduler = undefined;
|
145
102
|
wfqEnded = undefined;
|
146
103
|
executorID = utils_1.globalParams.executorID;
|
@@ -148,7 +105,7 @@ class DBOSExecutor {
|
|
148
105
|
/* WORKFLOW EXECUTOR LIFE CYCLE MANAGEMENT */
|
149
106
|
constructor(config, { systemDatabase, debugMode } = {}) {
|
150
107
|
this.config = config;
|
151
|
-
this.debugMode = debugMode ??
|
108
|
+
this.debugMode = debugMode ?? false;
|
152
109
|
// Set configured environment variables
|
153
110
|
if (config.env) {
|
154
111
|
for (const [key, value] of Object.entries(config.env)) {
|
@@ -169,8 +126,9 @@ class DBOSExecutor {
|
|
169
126
|
this.telemetryCollector = new collector_1.TelemetryCollector();
|
170
127
|
}
|
171
128
|
this.logger = new logs_1.GlobalLogger(this.telemetryCollector, this.config.telemetry.logs);
|
129
|
+
this.ctxLogger = new logs_1.DBOSContextualLogger(this.logger, () => (0, context_1.getCurrentContextStore)().span);
|
172
130
|
this.tracer = new traces_1.Tracer(this.telemetryCollector);
|
173
|
-
if (this.
|
131
|
+
if (this.debugMode) {
|
174
132
|
this.logger.info('Running in debug mode!');
|
175
133
|
}
|
176
134
|
this.procedurePool = new pg_1.Pool(this.config.poolConfig);
|
@@ -253,39 +211,6 @@ class DBOSExecutor {
|
|
253
211
|
this.logger.debug('Loaded Postgres user database');
|
254
212
|
}
|
255
213
|
}
|
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
214
|
async init(classes) {
|
290
215
|
if (this.initialized) {
|
291
216
|
this.logger.error('Workflow executor already initialized!');
|
@@ -318,7 +243,7 @@ class DBOSExecutor {
|
|
318
243
|
}
|
319
244
|
this.logger.debug(`Loaded ${length} ORM entities`);
|
320
245
|
}
|
321
|
-
if (!this.
|
246
|
+
if (!this.debugMode) {
|
322
247
|
await (0, user_database_1.createDBIfDoesNotExist)(this.config.poolConfig, this.logger);
|
323
248
|
}
|
324
249
|
this.configureDbClient();
|
@@ -326,12 +251,9 @@ class DBOSExecutor {
|
|
326
251
|
this.logger.error('No user database configured!');
|
327
252
|
throw new error_1.DBOSInitializationError('No user database configured!');
|
328
253
|
}
|
329
|
-
for (const cls of classnames) {
|
330
|
-
this.#registerClass(cls);
|
331
|
-
}
|
332
254
|
// 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.
|
255
|
+
await this.userDatabase.init(this.debugMode);
|
256
|
+
if (!this.debugMode) {
|
335
257
|
await this.systemDatabase.init();
|
336
258
|
}
|
337
259
|
}
|
@@ -354,7 +276,7 @@ class DBOSExecutor {
|
|
354
276
|
}
|
355
277
|
this.initialized = true;
|
356
278
|
// Only execute init code if under non-debug mode
|
357
|
-
if (!this.
|
279
|
+
if (!this.debugMode) {
|
358
280
|
for (const cls of classnames) {
|
359
281
|
// Init its configurations
|
360
282
|
const creg = (0, decorators_1.getClassRegistrationByName)(cls);
|
@@ -362,7 +284,7 @@ class DBOSExecutor {
|
|
362
284
|
await cfg.initialize(new _1.InitContext());
|
363
285
|
}
|
364
286
|
}
|
365
|
-
for (const v of
|
287
|
+
for (const v of (0, decorators_1.getAllRegisteredFunctions)()) {
|
366
288
|
const m = v;
|
367
289
|
if (m.init === true) {
|
368
290
|
this.logger.debug('Executing init method: ' + m.name);
|
@@ -422,109 +344,11 @@ class DBOSExecutor {
|
|
422
344
|
throw err;
|
423
345
|
}
|
424
346
|
}
|
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}`);
|
347
|
+
// This could return WF, or the function underlying a temp wf
|
348
|
+
#getFunctionInfoFromWFStatus(wf) {
|
349
|
+
const methReg = (0, decorators_1.getFunctionRegistrationByName)(wf.workflowClassName, wf.workflowName);
|
350
|
+
return { methReg, configuredInst: (0, decorators_1.getConfiguredInstance)(wf.workflowClassName, wf.workflowConfigName) };
|
457
351
|
}
|
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
352
|
static reviveResultOrError(r, success) {
|
529
353
|
if (success === true || !r.error) {
|
530
354
|
return utils_1.DBOSJSON.parse(r.output ?? null);
|
@@ -536,7 +360,7 @@ class DBOSExecutor {
|
|
536
360
|
async workflow(wf, params, ...args) {
|
537
361
|
return this.internalWorkflow(wf, params, undefined, undefined, ...args);
|
538
362
|
}
|
539
|
-
// If
|
363
|
+
// If callerWFID and functionID are set, it means the workflow is invoked from within a workflow.
|
540
364
|
async internalWorkflow(wf, params, callerID, callerFunctionID, ...args) {
|
541
365
|
const workflowID = params.workflowUUID ? params.workflowUUID : (0, node_crypto_1.randomUUID)();
|
542
366
|
const presetID = params.workflowUUID ? true : false;
|
@@ -560,29 +384,43 @@ class DBOSExecutor {
|
|
560
384
|
this.logger.warn(`Priority is not enabled for queue ${params.queueName}. Setting priority will not have any effect.`);
|
561
385
|
}
|
562
386
|
}
|
563
|
-
const
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
387
|
+
const pctx = (0, context_1.getCurrentContextStore)();
|
388
|
+
let wConfig = {};
|
389
|
+
const wInfo = (0, decorators_1.getFunctionRegistration)(wf);
|
390
|
+
if (wf.name !== DBOSExecutor.tempWorkflowName) {
|
391
|
+
if (!wInfo || !wInfo.workflowConfig) {
|
392
|
+
throw new error_1.DBOSNotRegisteredError(wf.name);
|
393
|
+
}
|
394
|
+
wConfig = wInfo.workflowConfig;
|
395
|
+
}
|
396
|
+
const maxRecoveryAttempts = wConfig.maxRecoveryAttempts ? wConfig.maxRecoveryAttempts : 50;
|
397
|
+
const wfname = wf.name; // TODO: Should be what was registered in wfInfo...
|
398
|
+
const span = this.tracer.startSpan(wfname, {
|
399
|
+
status: workflow_1.StatusString.PENDING,
|
400
|
+
operationUUID: workflowID,
|
401
|
+
operationType: exports.OperationType.WORKFLOW,
|
402
|
+
operationName: wInfo?.name ?? wf.name,
|
403
|
+
authenticatedUser: pctx?.authenticatedUser ?? '',
|
404
|
+
authenticatedRoles: pctx?.authenticatedRoles ?? [],
|
405
|
+
assumedRole: pctx?.assumedRole ?? '',
|
406
|
+
}, pctx?.span);
|
407
|
+
const isTempWorkflow = DBOSExecutor.tempWorkflowName === wfname;
|
570
408
|
const internalStatus = {
|
571
409
|
workflowUUID: workflowID,
|
572
410
|
status: params.queueName !== undefined ? workflow_1.StatusString.ENQUEUED : workflow_1.StatusString.PENDING,
|
573
|
-
workflowName: wf
|
574
|
-
workflowClassName:
|
411
|
+
workflowName: (0, decorators_1.getRegisteredFunctionName)(wf),
|
412
|
+
workflowClassName: isTempWorkflow ? '' : (0, decorators_1.getRegisteredFunctionClassName)(wf),
|
575
413
|
workflowConfigName: params.configuredInstance?.name || '',
|
576
414
|
queueName: params.queueName,
|
577
|
-
authenticatedUser: wCtxt.authenticatedUser,
|
578
415
|
output: null,
|
579
416
|
error: null,
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
417
|
+
authenticatedUser: pctx?.authenticatedUser || '',
|
418
|
+
assumedRole: pctx?.assumedRole || '',
|
419
|
+
authenticatedRoles: pctx?.authenticatedRoles || [],
|
420
|
+
request: pctx?.request || {},
|
421
|
+
executorId: utils_1.globalParams.executorID,
|
584
422
|
applicationVersion: utils_1.globalParams.appVersion,
|
585
|
-
applicationID:
|
423
|
+
applicationID: utils_1.globalParams.appID,
|
586
424
|
createdAt: Date.now(), // Remember the start time of this workflow,
|
587
425
|
timeoutMS: timeoutMS,
|
588
426
|
deadlineEpochMS: deadlineEpochMS,
|
@@ -590,15 +428,15 @@ class DBOSExecutor {
|
|
590
428
|
deduplicationID: params.enqueueOptions?.deduplicationID,
|
591
429
|
priority: priority ?? 0,
|
592
430
|
};
|
593
|
-
if (
|
594
|
-
internalStatus.workflowName = `${DBOSExecutor.tempWorkflowName}-${
|
431
|
+
if (isTempWorkflow) {
|
432
|
+
internalStatus.workflowName = `${DBOSExecutor.tempWorkflowName}-${params.tempWfType}-${params.tempWfName}`;
|
595
433
|
internalStatus.workflowClassName = params.tempWfClass ?? '';
|
596
434
|
}
|
597
435
|
let status = undefined;
|
598
436
|
let $deadlineEpochMS = undefined;
|
599
437
|
// Synchronously set the workflow's status to PENDING and record workflow inputs.
|
600
438
|
// 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.
|
439
|
+
if (this.debugMode) {
|
602
440
|
const wfStatus = await this.systemDatabase.getWorkflowStatus(workflowID);
|
603
441
|
if (!wfStatus) {
|
604
442
|
throw new error_1.DBOSDebuggerError(`Failed to find inputs for workflow UUID ${workflowID}`);
|
@@ -616,7 +454,7 @@ class DBOSExecutor {
|
|
616
454
|
return new workflow_1.RetrievedHandle(this.systemDatabase, result.childWorkflowID, callerID, callerFunctionID);
|
617
455
|
}
|
618
456
|
}
|
619
|
-
const ires = await this.systemDatabase.initWorkflowStatus(internalStatus,
|
457
|
+
const ires = await this.systemDatabase.initWorkflowStatus(internalStatus, maxRecoveryAttempts);
|
620
458
|
if (callerFunctionID !== undefined && callerID !== undefined) {
|
621
459
|
await this.systemDatabase.recordOperationResult(callerID, callerFunctionID, internalStatus.workflowName, true, {
|
622
460
|
childWorkflowID: workflowID,
|
@@ -654,19 +492,24 @@ class DBOSExecutor {
|
|
654
492
|
e.dbos_already_logged = true;
|
655
493
|
internalStatus.error = utils_1.DBOSJSON.stringify((0, serialize_error_1.serializeError)(e));
|
656
494
|
internalStatus.status = workflow_1.StatusString.ERROR;
|
657
|
-
if (!exec.
|
495
|
+
if (!exec.debugMode) {
|
658
496
|
await exec.systemDatabase.recordWorkflowError(workflowID, internalStatus);
|
659
497
|
}
|
660
|
-
|
498
|
+
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: e.message });
|
661
499
|
}
|
662
500
|
const runWorkflow = async () => {
|
663
501
|
let result;
|
664
502
|
// Execute the workflow.
|
665
503
|
try {
|
666
|
-
const callResult = await (0, context_1.
|
667
|
-
|
668
|
-
|
669
|
-
|
504
|
+
const callResult = await (0, context_1.runWithParentContext)(pctx, {
|
505
|
+
presetID,
|
506
|
+
timeoutMS,
|
507
|
+
deadlineEpochMS,
|
508
|
+
workflowId: workflowID,
|
509
|
+
span,
|
510
|
+
logger: this.ctxLogger,
|
511
|
+
}, () => {
|
512
|
+
const callPromise = wf.call(params.configuredInstance, ...args);
|
670
513
|
if ($deadlineEpochMS === undefined) {
|
671
514
|
return callPromise;
|
672
515
|
}
|
@@ -674,7 +517,7 @@ class DBOSExecutor {
|
|
674
517
|
return callPromiseWithTimeout(callPromise, $deadlineEpochMS, this.systemDatabase);
|
675
518
|
}
|
676
519
|
});
|
677
|
-
if (this.
|
520
|
+
if (this.debugMode) {
|
678
521
|
const recordedResult = DBOSExecutor.reviveResultOrError((await this.systemDatabase.awaitWorkflowResult(workflowID)));
|
679
522
|
if (!resultsMatch(recordedResult, callResult)) {
|
680
523
|
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 +535,21 @@ class DBOSExecutor {
|
|
692
535
|
}
|
693
536
|
internalStatus.output = utils_1.DBOSJSON.stringify(result);
|
694
537
|
internalStatus.status = workflow_1.StatusString.SUCCESS;
|
695
|
-
if (!this.
|
538
|
+
if (!this.debugMode) {
|
696
539
|
await this.systemDatabase.recordWorkflowOutput(workflowID, internalStatus);
|
697
540
|
}
|
698
|
-
|
541
|
+
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
699
542
|
}
|
700
543
|
catch (err) {
|
701
544
|
if (err instanceof error_1.DBOSWorkflowConflictError) {
|
702
545
|
// Retrieve the handle and wait for the result.
|
703
546
|
const retrievedHandle = this.retrieveWorkflow(workflowID);
|
704
547
|
result = await retrievedHandle.getResult();
|
705
|
-
|
706
|
-
|
548
|
+
span.setAttribute('cached', true);
|
549
|
+
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
707
550
|
}
|
708
551
|
else if (err instanceof error_1.DBOSWorkflowCancelledError) {
|
709
|
-
|
552
|
+
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err.message });
|
710
553
|
internalStatus.error = err.message;
|
711
554
|
if (err.workflowID === workflowID) {
|
712
555
|
internalStatus.status = workflow_1.StatusString.CANCELLED;
|
@@ -724,11 +567,11 @@ class DBOSExecutor {
|
|
724
567
|
}
|
725
568
|
}
|
726
569
|
finally {
|
727
|
-
this.tracer.endSpan(
|
570
|
+
this.tracer.endSpan(span);
|
728
571
|
}
|
729
572
|
return result;
|
730
573
|
};
|
731
|
-
if (this.
|
574
|
+
if (this.debugMode ||
|
732
575
|
(status !== 'SUCCESS' && status !== 'ERROR' && (params.queueName === undefined || params.executeWorkflow))) {
|
733
576
|
const workflowPromise = runWorkflow();
|
734
577
|
this.systemDatabase.registerRunningWorkflow(workflowID, workflowPromise);
|
@@ -794,7 +637,7 @@ class DBOSExecutor {
|
|
794
637
|
* Write a operation's output to the database.
|
795
638
|
*/
|
796
639
|
async #recordOutput(query, workflowUUID, funcID, txnSnapshot, output, isKeyConflict, function_name) {
|
797
|
-
if (this.
|
640
|
+
if (this.debugMode) {
|
798
641
|
throw new error_1.DBOSDebuggerError('Cannot record output in debug mode.');
|
799
642
|
}
|
800
643
|
try {
|
@@ -816,7 +659,7 @@ class DBOSExecutor {
|
|
816
659
|
* Record an error in an operation to the database.
|
817
660
|
*/
|
818
661
|
async #recordError(query, workflowUUID, funcID, txnSnapshot, err, isKeyConflict, function_name) {
|
819
|
-
if (this.
|
662
|
+
if (this.debugMode) {
|
820
663
|
throw new error_1.DBOSDebuggerError('Cannot record error in debug mode.');
|
821
664
|
}
|
822
665
|
try {
|
@@ -841,70 +684,64 @@ class DBOSExecutor {
|
|
841
684
|
}
|
842
685
|
return rows;
|
843
686
|
}
|
844
|
-
async
|
687
|
+
async runTransactionTempWF(txn, params, ...args) {
|
845
688
|
return await (await this.startTransactionTempWF(txn, params, undefined, undefined, ...args)).getResult();
|
846
689
|
}
|
847
|
-
async startTransactionTempWF(txn, params,
|
690
|
+
async startTransactionTempWF(txn, params, callerWFID, callerFunctionID, ...args) {
|
848
691
|
// Create a workflow and call transaction.
|
849
|
-
const temp_workflow = async (
|
850
|
-
|
851
|
-
return await this.callTransactionFunction(txn, params.configuredInstance ?? null, ctxtImpl, ...args);
|
692
|
+
const temp_workflow = async (...args) => {
|
693
|
+
return await this.callTransactionFunction(txn, params.configuredInstance ?? null, ...args);
|
852
694
|
};
|
853
695
|
return await this.internalWorkflow(temp_workflow, {
|
854
696
|
...params,
|
855
697
|
tempWfType: exports.TempWorkflowType.transaction,
|
856
|
-
tempWfName: (0, decorators_1.
|
857
|
-
tempWfClass: (0, decorators_1.
|
858
|
-
},
|
698
|
+
tempWfName: (0, decorators_1.getRegisteredFunctionName)(txn),
|
699
|
+
tempWfClass: (0, decorators_1.getRegisteredFunctionClassName)(txn),
|
700
|
+
}, callerWFID, callerFunctionID, ...args);
|
859
701
|
}
|
860
|
-
async callTransactionFunction(txn, clsinst,
|
861
|
-
const
|
862
|
-
if (
|
702
|
+
async callTransactionFunction(txn, clsinst, ...args) {
|
703
|
+
const txnReg = (0, decorators_1.getFunctionRegistration)(txn);
|
704
|
+
if (!txnReg || !txnReg.txnConfig) {
|
863
705
|
throw new error_1.DBOSNotRegisteredError(txn.name);
|
864
706
|
}
|
865
|
-
|
707
|
+
const pctx = (0, context_1.getCurrentContextStore)();
|
708
|
+
const wfid = pctx.workflowId;
|
709
|
+
await this.systemDatabase.checkIfCanceled(wfid);
|
866
710
|
let retryWaitMillis = 1;
|
867
711
|
const backoffFactor = 1.5;
|
868
712
|
const maxRetryWaitMs = 2000; // Maximum wait 2 seconds.
|
869
|
-
const funcId =
|
713
|
+
const funcId = (0, context_1.functionIDGetIncrement)();
|
870
714
|
const span = this.tracer.startSpan(txn.name, {
|
871
|
-
operationUUID:
|
715
|
+
operationUUID: wfid,
|
872
716
|
operationType: exports.OperationType.TRANSACTION,
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
717
|
+
operationName: txn.name,
|
718
|
+
authenticatedUser: pctx.authenticatedUser ?? '',
|
719
|
+
assumedRole: pctx.assumedRole ?? '',
|
720
|
+
authenticatedRoles: pctx.authenticatedRoles ?? [],
|
721
|
+
isolationLevel: txnReg.txnConfig.isolationLevel,
|
722
|
+
}, pctx.span);
|
878
723
|
while (true) {
|
879
|
-
await this.systemDatabase.checkIfCanceled(
|
724
|
+
await this.systemDatabase.checkIfCanceled(wfid);
|
880
725
|
let txn_snapshot = 'invalid';
|
881
726
|
let prevResultFound = false;
|
882
|
-
const workflowUUID = wfCtx.workflowUUID;
|
883
727
|
const wrappedTransaction = async (client) => {
|
884
|
-
const tCtxt = new transaction_1.TransactionContextImpl(this.userDatabase.getName(), client, wfCtx, span, this.logger, funcId, txn.name);
|
885
728
|
// If the UUID is preset, it is possible this execution previously happened. Check, and return its original result if it did.
|
886
729
|
// 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
730
|
let prevResult = exports.dbosNull;
|
888
731
|
const queryFunc = (sql, args) => this.userDatabase.queryWithClient(client, sql, ...args);
|
889
|
-
if (
|
890
|
-
const executionResult = await this.#checkExecution(queryFunc,
|
732
|
+
if (pctx.presetID) {
|
733
|
+
const executionResult = await this.#checkExecution(queryFunc, wfid, funcId, txn.name);
|
891
734
|
prevResult = executionResult.result;
|
892
735
|
txn_snapshot = executionResult.txn_snapshot;
|
893
736
|
if (prevResult !== exports.dbosNull) {
|
894
737
|
prevResultFound = true;
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
738
|
+
span.setAttribute('cached', true);
|
739
|
+
// Return/throw the previous result
|
740
|
+
if (prevResult instanceof Error) {
|
741
|
+
throw prevResult;
|
899
742
|
}
|
900
743
|
else {
|
901
|
-
|
902
|
-
if (prevResult instanceof Error) {
|
903
|
-
throw prevResult;
|
904
|
-
}
|
905
|
-
else {
|
906
|
-
return prevResult;
|
907
|
-
}
|
744
|
+
return prevResult;
|
908
745
|
}
|
909
746
|
}
|
910
747
|
}
|
@@ -912,27 +749,32 @@ class DBOSExecutor {
|
|
912
749
|
// Collect snapshot information for read-only transactions and non-preset UUID transactions, if not already collected above
|
913
750
|
txn_snapshot = await DBOSExecutor.#retrieveSnapshot(queryFunc);
|
914
751
|
}
|
915
|
-
if (this.
|
916
|
-
throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the transaction: workflow UUID ${
|
752
|
+
if (this.debugMode && prevResult === exports.dbosNull) {
|
753
|
+
throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the transaction: workflow UUID ${wfid}, step number ${funcId}`);
|
917
754
|
}
|
918
755
|
// Execute the user's transaction.
|
756
|
+
const ctxlog = this.ctxLogger;
|
919
757
|
const result = await (async function () {
|
920
758
|
try {
|
921
|
-
return await (0, context_1.
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
759
|
+
return await (0, context_1.runWithParentContext)(pctx, {
|
760
|
+
authenticatedRoles: pctx?.authenticatedRoles,
|
761
|
+
authenticatedUser: pctx?.authenticatedUser,
|
762
|
+
workflowId: wfid,
|
763
|
+
curTxFunctionId: funcId,
|
764
|
+
parentCtx: pctx,
|
765
|
+
sqlClient: client,
|
766
|
+
logger: ctxlog,
|
767
|
+
span,
|
768
|
+
}, async () => {
|
769
|
+
const tf = txn;
|
770
|
+
return await tf.call(clsinst, ...args);
|
929
771
|
});
|
930
772
|
}
|
931
773
|
catch (e) {
|
932
774
|
return e instanceof Error ? e : new Error(`${e}`);
|
933
775
|
}
|
934
776
|
})();
|
935
|
-
if (this.
|
777
|
+
if (this.debugMode) {
|
936
778
|
if (prevResult instanceof Error) {
|
937
779
|
throw prevResult;
|
938
780
|
}
|
@@ -949,13 +791,13 @@ class DBOSExecutor {
|
|
949
791
|
// Record the execution, commit, and return.
|
950
792
|
try {
|
951
793
|
// Synchronously record the output of write transactions and obtain the transaction ID.
|
952
|
-
const pg_txn_id = await this.#recordOutput(queryFunc,
|
953
|
-
|
794
|
+
const pg_txn_id = await this.#recordOutput(queryFunc, wfid, funcId, txn_snapshot, result, (error) => this.userDatabase.isKeyConflictError(error), txn.name);
|
795
|
+
span.setAttribute('pg_txn_id', pg_txn_id);
|
954
796
|
}
|
955
797
|
catch (error) {
|
956
798
|
if (this.userDatabase.isFailedSqlTransactionError(error)) {
|
957
|
-
this.logger.error(`Postgres aborted the ${txn.name} @DBOS.transaction of Workflow ${
|
958
|
-
throw new error_1.DBOSFailedSqlTransactionError(
|
799
|
+
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.`);
|
800
|
+
throw new error_1.DBOSFailedSqlTransactionError(wfid, txn.name);
|
959
801
|
}
|
960
802
|
else {
|
961
803
|
throw error;
|
@@ -964,7 +806,7 @@ class DBOSExecutor {
|
|
964
806
|
return result;
|
965
807
|
};
|
966
808
|
try {
|
967
|
-
const result = await this.userDatabase.transaction(wrappedTransaction,
|
809
|
+
const result = await this.userDatabase.transaction(wrappedTransaction, txnReg.txnConfig);
|
968
810
|
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
969
811
|
this.tracer.endSpan(span);
|
970
812
|
return result;
|
@@ -985,7 +827,7 @@ class DBOSExecutor {
|
|
985
827
|
const e = err;
|
986
828
|
await this.userDatabase.transaction(async (client) => {
|
987
829
|
const func = (sql, args) => this.userDatabase.queryWithClient(client, sql, ...args);
|
988
|
-
await this.#recordError(func,
|
830
|
+
await this.#recordError(func, wfid, funcId, txn_snapshot, e, (error) => this.userDatabase.isKeyConflictError(error), txn.name);
|
989
831
|
}, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
|
990
832
|
}
|
991
833
|
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: e.message });
|
@@ -994,40 +836,43 @@ class DBOSExecutor {
|
|
994
836
|
}
|
995
837
|
}
|
996
838
|
}
|
997
|
-
async
|
839
|
+
async runProcedureTempWF(proc, params, ...args) {
|
998
840
|
// Create a workflow and call procedure.
|
999
|
-
const temp_workflow = async (
|
1000
|
-
|
1001
|
-
return this.callProcedureFunction(proc, ctxtImpl, ...args);
|
841
|
+
const temp_workflow = async (...args) => {
|
842
|
+
return this.callProcedureFunction(proc, ...args);
|
1002
843
|
};
|
1003
844
|
return await (await this.workflow(temp_workflow, {
|
1004
845
|
...params,
|
1005
846
|
tempWfType: exports.TempWorkflowType.procedure,
|
1006
|
-
tempWfName: (0, decorators_1.
|
1007
|
-
tempWfClass: (0, decorators_1.
|
847
|
+
tempWfName: (0, decorators_1.getRegisteredFunctionName)(proc),
|
848
|
+
tempWfClass: (0, decorators_1.getRegisteredFunctionClassName)(proc),
|
1008
849
|
}, ...args)).getResult();
|
1009
850
|
}
|
1010
|
-
async callProcedureFunction(proc,
|
1011
|
-
const procInfo =
|
1012
|
-
if (procInfo
|
851
|
+
async callProcedureFunction(proc, ...args) {
|
852
|
+
const procInfo = (0, decorators_1.getFunctionRegistration)(proc);
|
853
|
+
if (!procInfo || !procInfo.procConfig) {
|
1013
854
|
throw new error_1.DBOSNotRegisteredError(proc.name);
|
1014
855
|
}
|
1015
|
-
|
1016
|
-
const
|
1017
|
-
const
|
856
|
+
const procConfig = procInfo.procConfig;
|
857
|
+
const pctx = (0, context_1.getCurrentContextStore)();
|
858
|
+
const wfid = pctx.workflowId;
|
859
|
+
await this.systemDatabase.checkIfCanceled(wfid);
|
860
|
+
const executeLocally = this.debugMode || (procConfig.executeLocally ?? false);
|
861
|
+
const funcId = (0, context_1.functionIDGetIncrement)();
|
1018
862
|
const span = this.tracer.startSpan(proc.name, {
|
1019
|
-
operationUUID:
|
863
|
+
operationUUID: wfid,
|
1020
864
|
operationType: exports.OperationType.PROCEDURE,
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
865
|
+
operationName: proc.name,
|
866
|
+
authenticatedUser: pctx.authenticatedUser ?? '',
|
867
|
+
assumedRole: pctx.assumedRole ?? '',
|
868
|
+
authenticatedRoles: pctx.authenticatedRoles ?? [],
|
869
|
+
isolationLevel: procInfo.procConfig.isolationLevel,
|
1025
870
|
executeLocally,
|
1026
|
-
},
|
871
|
+
}, pctx.span);
|
1027
872
|
try {
|
1028
873
|
const result = executeLocally
|
1029
|
-
? await this.#callProcedureFunctionLocal(proc, args,
|
1030
|
-
: await this.#callProcedureFunctionRemote(proc, args,
|
874
|
+
? await this.#callProcedureFunctionLocal(proc, args, span, procInfo, funcId)
|
875
|
+
: await this.#callProcedureFunctionRemote(proc, args, span, procConfig, funcId);
|
1031
876
|
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
1032
877
|
return result;
|
1033
878
|
}
|
@@ -1040,35 +885,30 @@ class DBOSExecutor {
|
|
1040
885
|
this.tracer.endSpan(span);
|
1041
886
|
}
|
1042
887
|
}
|
1043
|
-
async #callProcedureFunctionLocal(proc, args,
|
888
|
+
async #callProcedureFunctionLocal(proc, args, span, procInfo, funcId) {
|
1044
889
|
let retryWaitMillis = 1;
|
1045
890
|
const backoffFactor = 1.5;
|
1046
891
|
const maxRetryWaitMs = 2000; // Maximum wait 2 seconds.
|
892
|
+
const pctx = (0, context_1.getCurrentContextStore)();
|
893
|
+
const wfid = pctx.workflowId;
|
1047
894
|
while (true) {
|
1048
|
-
await this.systemDatabase.checkIfCanceled(
|
895
|
+
await this.systemDatabase.checkIfCanceled(wfid);
|
1049
896
|
let txn_snapshot = 'invalid';
|
1050
897
|
const wrappedProcedure = async (client) => {
|
1051
|
-
const ctxt = new procedure_1.StoredProcedureContextImpl(client, wfCtx, span, this.logger, funcId, proc.name);
|
1052
898
|
let prevResult = exports.dbosNull;
|
1053
899
|
const queryFunc = (sql, args) => this.procedurePool.query(sql, args).then((v) => v.rows);
|
1054
|
-
if (
|
1055
|
-
const executionResult = await this.#checkExecution(queryFunc,
|
900
|
+
if (pctx.presetID) {
|
901
|
+
const executionResult = await this.#checkExecution(queryFunc, wfid, funcId, proc.name);
|
1056
902
|
prevResult = executionResult.result;
|
1057
903
|
txn_snapshot = executionResult.txn_snapshot;
|
1058
904
|
if (prevResult !== exports.dbosNull) {
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
905
|
+
span.setAttribute('cached', true);
|
906
|
+
// Return/throw the previous result
|
907
|
+
if (prevResult instanceof Error) {
|
908
|
+
throw prevResult;
|
1063
909
|
}
|
1064
910
|
else {
|
1065
|
-
|
1066
|
-
if (prevResult instanceof Error) {
|
1067
|
-
throw prevResult;
|
1068
|
-
}
|
1069
|
-
else {
|
1070
|
-
return prevResult;
|
1071
|
-
}
|
911
|
+
return prevResult;
|
1072
912
|
}
|
1073
913
|
}
|
1074
914
|
}
|
@@ -1076,27 +916,36 @@ class DBOSExecutor {
|
|
1076
916
|
// Collect snapshot information for read-only transactions and non-preset UUID transactions, if not already collected above
|
1077
917
|
txn_snapshot = await DBOSExecutor.#retrieveSnapshot(queryFunc);
|
1078
918
|
}
|
1079
|
-
if (this.
|
1080
|
-
throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the procedure: workflow UUID ${
|
919
|
+
if (this.debugMode && prevResult === exports.dbosNull) {
|
920
|
+
throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the procedure: workflow UUID ${wfid}, step number ${funcId}`);
|
1081
921
|
}
|
1082
922
|
// Execute the user's transaction.
|
923
|
+
const ctxlog = this.ctxLogger;
|
1083
924
|
const result = await (async function () {
|
1084
925
|
try {
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
926
|
+
// Check we are in a workflow context and not in a step / transaction already
|
927
|
+
const pctx = (0, context_1.getCurrentContextStore)();
|
928
|
+
if (!pctx)
|
929
|
+
throw new error_1.DBOSInvalidWorkflowTransitionError();
|
930
|
+
if (!(0, context_1.isInWorkflowCtx)(pctx))
|
931
|
+
throw new error_1.DBOSInvalidWorkflowTransitionError();
|
932
|
+
return await (0, context_1.runWithParentContext)(pctx, {
|
933
|
+
curTxFunctionId: funcId,
|
934
|
+
parentCtx: pctx,
|
935
|
+
isInStoredProc: true,
|
936
|
+
sqlClient: client,
|
937
|
+
logger: ctxlog,
|
938
|
+
span,
|
939
|
+
}, async () => {
|
940
|
+
const pf = proc;
|
941
|
+
return await pf(...args);
|
1093
942
|
});
|
1094
943
|
}
|
1095
944
|
catch (e) {
|
1096
945
|
return e instanceof Error ? e : new Error(`${e}`);
|
1097
946
|
}
|
1098
947
|
})();
|
1099
|
-
if (this.
|
948
|
+
if (this.debugMode) {
|
1100
949
|
if (prevResult instanceof Error) {
|
1101
950
|
throw prevResult;
|
1102
951
|
}
|
@@ -1112,20 +961,20 @@ class DBOSExecutor {
|
|
1112
961
|
}
|
1113
962
|
// Synchronously record the output of write transactions and obtain the transaction ID.
|
1114
963
|
const func = (sql, args) => client.query(sql, args).then((v) => v.rows);
|
1115
|
-
const pg_txn_id = await this.#recordOutput(func,
|
964
|
+
const pg_txn_id = await this.#recordOutput(func, wfid, funcId, txn_snapshot, result, user_database_1.pgNodeIsKeyConflictError, proc.name);
|
1116
965
|
// const pg_txn_id = await wfCtx.recordOutputProc<R>(client, funcId, txn_snapshot, result);
|
1117
|
-
|
966
|
+
span.setAttribute('pg_txn_id', pg_txn_id);
|
1118
967
|
return result;
|
1119
968
|
};
|
1120
969
|
try {
|
1121
970
|
const result = await this.invokeStoredProcFunction(wrappedProcedure, {
|
1122
|
-
isolationLevel: procInfo.
|
971
|
+
isolationLevel: procInfo.procConfig.isolationLevel,
|
1123
972
|
});
|
1124
973
|
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
1125
974
|
return result;
|
1126
975
|
}
|
1127
976
|
catch (err) {
|
1128
|
-
if (!this.
|
977
|
+
if (!this.debugMode) {
|
1129
978
|
if (this.userDatabase.isRetriableTransactionError(err)) {
|
1130
979
|
// serialization_failure in PostgreSQL
|
1131
980
|
span.addEvent('TXN SERIALIZATION FAILURE', { retryWaitMillis: retryWaitMillis }, performance.now());
|
@@ -1139,32 +988,34 @@ class DBOSExecutor {
|
|
1139
988
|
const e = err;
|
1140
989
|
await this.invokeStoredProcFunction(async (client) => {
|
1141
990
|
const func = (sql, args) => client.query(sql, args).then((v) => v.rows);
|
1142
|
-
await this.#recordError(func,
|
991
|
+
await this.#recordError(func, wfid, funcId, txn_snapshot, e, user_database_1.pgNodeIsKeyConflictError, proc.name);
|
1143
992
|
}, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
|
1144
993
|
await this.userDatabase.transaction(async (client) => {
|
1145
994
|
const func = (sql, args) => this.userDatabase.queryWithClient(client, sql, ...args);
|
1146
|
-
await this.#recordError(func,
|
995
|
+
await this.#recordError(func, wfid, funcId, txn_snapshot, e, (error) => this.userDatabase.isKeyConflictError(error), proc.name);
|
1147
996
|
}, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
|
1148
997
|
}
|
1149
998
|
throw err;
|
1150
999
|
}
|
1151
1000
|
}
|
1152
1001
|
}
|
1153
|
-
async #callProcedureFunctionRemote(proc, args,
|
1154
|
-
if (this.
|
1002
|
+
async #callProcedureFunctionRemote(proc, args, span, config, funcId) {
|
1003
|
+
if (this.debugMode) {
|
1155
1004
|
throw new error_1.DBOSDebuggerError("Can't invoke stored procedure in debug mode.");
|
1156
1005
|
}
|
1157
|
-
|
1006
|
+
const pctx = (0, context_1.getCurrentContextStore)();
|
1007
|
+
const wfid = pctx.workflowId;
|
1008
|
+
await this.systemDatabase.checkIfCanceled(wfid);
|
1158
1009
|
const $jsonCtx = {
|
1159
|
-
request:
|
1160
|
-
authenticatedUser:
|
1161
|
-
authenticatedRoles:
|
1162
|
-
assumedRole:
|
1010
|
+
request: pctx.request,
|
1011
|
+
authenticatedUser: pctx.authenticatedUser,
|
1012
|
+
authenticatedRoles: pctx.authenticatedRoles,
|
1013
|
+
assumedRole: pctx.assumedRole,
|
1163
1014
|
};
|
1164
1015
|
// TODO (Qian/Harry): remove this unshift when we remove the resultBuffer argument
|
1165
1016
|
// Note, node-pg converts JS arrays to postgres array literals, so must call JSON.strigify on
|
1166
1017
|
// args and bufferedResults before being passed to #invokeStoredProc
|
1167
|
-
const $args = [
|
1018
|
+
const $args = [wfid, funcId, pctx.presetID, $jsonCtx, null, JSON.stringify(args)];
|
1168
1019
|
const readonly = config.readOnly ?? false;
|
1169
1020
|
if (!readonly) {
|
1170
1021
|
$args.unshift(null);
|
@@ -1185,8 +1036,8 @@ class DBOSExecutor {
|
|
1185
1036
|
async #invokeStoredProc(proc, args) {
|
1186
1037
|
const client = await this.procedurePool.connect();
|
1187
1038
|
const log = (msg) => this.#logNotice(msg);
|
1188
|
-
const
|
1189
|
-
const plainProcName = `${
|
1039
|
+
const procname = (0, decorators_1.getRegisteredFunctionFullName)(proc);
|
1040
|
+
const plainProcName = `${procname.className}_${procname.name}_p`;
|
1190
1041
|
const procName = utils_1.globalParams.wasComputed ? plainProcName : `v${utils_1.globalParams.appVersion}_${plainProcName}`;
|
1191
1042
|
const sql = `CALL "${procName}"(${args.map((_v, i) => `$${i + 1}`).join()});`;
|
1192
1043
|
try {
|
@@ -1219,106 +1070,99 @@ class DBOSExecutor {
|
|
1219
1070
|
client.release();
|
1220
1071
|
}
|
1221
1072
|
}
|
1222
|
-
async
|
1073
|
+
async runStepTempWF(stepFn, params, ...args) {
|
1223
1074
|
return await (await this.startStepTempWF(stepFn, params, undefined, undefined, ...args)).getResult();
|
1224
1075
|
}
|
1225
|
-
async startStepTempWF(stepFn, params,
|
1076
|
+
async startStepTempWF(stepFn, params, callerWFID, callerFunctionID, ...args) {
|
1226
1077
|
// 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);
|
1078
|
+
const temp_workflow = async (...args) => {
|
1079
|
+
return await this.callStepFunction(stepFn, undefined, undefined, params.configuredInstance ?? null, ...args);
|
1230
1080
|
};
|
1231
1081
|
return await this.internalWorkflow(temp_workflow, {
|
1232
1082
|
...params,
|
1233
1083
|
tempWfType: exports.TempWorkflowType.step,
|
1234
|
-
tempWfName: (0, decorators_1.
|
1235
|
-
tempWfClass: (0, decorators_1.
|
1236
|
-
},
|
1084
|
+
tempWfName: (0, decorators_1.getRegisteredFunctionName)(stepFn),
|
1085
|
+
tempWfClass: (0, decorators_1.getRegisteredFunctionClassName)(stepFn),
|
1086
|
+
}, callerWFID, callerFunctionID, ...args);
|
1237
1087
|
}
|
1238
1088
|
/**
|
1239
1089
|
* Execute a step function.
|
1240
1090
|
* 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
1091
|
* The step may execute many times, but once it is complete, it will not re-execute.
|
1242
1092
|
*/
|
1243
|
-
async callStepFunction(stepFn, stepFnName, stepConfig, clsInst,
|
1093
|
+
async callStepFunction(stepFn, stepFnName, stepConfig, clsInst, ...args) {
|
1244
1094
|
stepFnName = stepFnName ?? stepFn.name ?? '<unnamed>';
|
1245
|
-
let passContext = false;
|
1246
1095
|
if (!stepConfig) {
|
1247
|
-
const stepReg =
|
1248
|
-
stepConfig = stepReg?.
|
1249
|
-
passContext = stepReg?.registration.passContext ?? true;
|
1096
|
+
const stepReg = (0, decorators_1.getFunctionRegistration)(stepFn);
|
1097
|
+
stepConfig = stepReg?.stepConfig;
|
1250
1098
|
}
|
1251
1099
|
if (stepConfig === undefined) {
|
1252
1100
|
throw new error_1.DBOSNotRegisteredError(stepFnName);
|
1253
1101
|
}
|
1254
|
-
|
1255
|
-
const
|
1102
|
+
const lctx = (0, context_1.getCurrentContextStore)();
|
1103
|
+
const wfid = lctx.workflowId;
|
1104
|
+
await this.systemDatabase.checkIfCanceled(wfid);
|
1105
|
+
const funcID = (0, context_1.functionIDGetIncrement)();
|
1256
1106
|
const maxRetryIntervalSec = 3600; // Maximum retry interval: 1 hour
|
1257
1107
|
const span = this.tracer.startSpan(stepFnName, {
|
1258
|
-
operationUUID:
|
1259
|
-
operationType: exports.OperationType.
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1108
|
+
operationUUID: wfid,
|
1109
|
+
operationType: exports.OperationType.STEP,
|
1110
|
+
operationName: stepFnName,
|
1111
|
+
authenticatedUser: lctx.authenticatedUser ?? '',
|
1112
|
+
assumedRole: lctx.assumedRole ?? '',
|
1113
|
+
authenticatedRoles: lctx.authenticatedRoles ?? [],
|
1263
1114
|
retriesAllowed: stepConfig.retriesAllowed,
|
1264
1115
|
intervalSeconds: stepConfig.intervalSeconds,
|
1265
1116
|
maxAttempts: stepConfig.maxAttempts,
|
1266
1117
|
backoffRate: stepConfig.backoffRate,
|
1267
|
-
},
|
1268
|
-
const ctxt = new step_1.StepContextImpl(wfCtx, funcID, span, this.logger, stepConfig, stepFnName);
|
1118
|
+
}, lctx.span);
|
1269
1119
|
// Check if this execution previously happened, returning its original result if it did.
|
1270
|
-
const checkr = await this.systemDatabase.getOperationResultAndThrowIfCancelled(
|
1120
|
+
const checkr = await this.systemDatabase.getOperationResultAndThrowIfCancelled(wfid, funcID);
|
1271
1121
|
if (checkr) {
|
1272
|
-
if (checkr.functionName !==
|
1273
|
-
throw new error_1.DBOSUnexpectedStepError(
|
1122
|
+
if (checkr.functionName !== stepFnName) {
|
1123
|
+
throw new error_1.DBOSUnexpectedStepError(wfid, funcID, stepFnName, checkr.functionName ?? '?');
|
1274
1124
|
}
|
1275
1125
|
const check = DBOSExecutor.reviveResultOrError(checkr);
|
1276
|
-
|
1277
|
-
|
1278
|
-
this.tracer.endSpan(
|
1126
|
+
span.setAttribute('cached', true);
|
1127
|
+
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
1128
|
+
this.tracer.endSpan(span);
|
1279
1129
|
return check;
|
1280
1130
|
}
|
1281
|
-
if (this.
|
1282
|
-
throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the step: workflow UUID: ${
|
1131
|
+
if (this.debugMode) {
|
1132
|
+
throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the step: workflow UUID: ${wfid}, step number: ${funcID}`);
|
1283
1133
|
}
|
1134
|
+
const maxAttempts = stepConfig.maxAttempts ?? 3;
|
1284
1135
|
// Execute the step function. If it throws an exception, retry with exponential backoff.
|
1285
1136
|
// After reaching the maximum number of retries, throw an DBOSError.
|
1286
1137
|
let result = exports.dbosNull;
|
1287
1138
|
let err = exports.dbosNull;
|
1288
1139
|
const errors = [];
|
1289
|
-
if (
|
1290
|
-
let
|
1291
|
-
let intervalSeconds =
|
1140
|
+
if (stepConfig.retriesAllowed) {
|
1141
|
+
let attemptNum = 0;
|
1142
|
+
let intervalSeconds = stepConfig.intervalSeconds ?? 1;
|
1292
1143
|
if (intervalSeconds > maxRetryIntervalSec) {
|
1293
1144
|
this.logger.warn(`Step config interval exceeds maximum allowed interval, capped to ${maxRetryIntervalSec} seconds!`);
|
1294
1145
|
}
|
1295
|
-
while (result === exports.dbosNull &&
|
1146
|
+
while (result === exports.dbosNull && attemptNum++ < (maxAttempts ?? 3)) {
|
1296
1147
|
try {
|
1297
|
-
await this.systemDatabase.checkIfCanceled(
|
1148
|
+
await this.systemDatabase.checkIfCanceled(wfid);
|
1298
1149
|
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
|
-
}
|
1150
|
+
await (0, context_1.runInStepContext)(lctx, funcID, span, maxAttempts, attemptNum, async () => {
|
1151
|
+
const sf = stepFn;
|
1152
|
+
cresult = await sf.call(clsInst, ...args);
|
1153
|
+
});
|
1310
1154
|
result = cresult;
|
1311
1155
|
}
|
1312
1156
|
catch (error) {
|
1313
1157
|
const e = error;
|
1314
1158
|
errors.push(e);
|
1315
|
-
this.logger.warn(`Error in step being automatically retried. Attempt ${
|
1316
|
-
span.addEvent(`Step attempt ${
|
1317
|
-
if (
|
1159
|
+
this.logger.warn(`Error in step being automatically retried. Attempt ${attemptNum} of ${maxAttempts}. ${e.stack}`);
|
1160
|
+
span.addEvent(`Step attempt ${attemptNum + 1} failed`, { retryIntervalSeconds: intervalSeconds, error: error.message }, performance.now());
|
1161
|
+
if (attemptNum < maxAttempts) {
|
1318
1162
|
// Sleep for an interval, then increase the interval by backoffRate.
|
1319
1163
|
// Cap at the maximum allowed retry interval.
|
1320
1164
|
await (0, utils_1.sleepms)(intervalSeconds * 1000);
|
1321
|
-
intervalSeconds *=
|
1165
|
+
intervalSeconds *= stepConfig.backoffRate ?? 2;
|
1322
1166
|
intervalSeconds = intervalSeconds < maxRetryIntervalSec ? intervalSeconds : maxRetryIntervalSec;
|
1323
1167
|
}
|
1324
1168
|
}
|
@@ -1327,17 +1171,10 @@ class DBOSExecutor {
|
|
1327
1171
|
else {
|
1328
1172
|
try {
|
1329
1173
|
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
|
-
}
|
1174
|
+
await (0, context_1.runInStepContext)(lctx, funcID, span, maxAttempts, undefined, async () => {
|
1175
|
+
const sf = stepFn;
|
1176
|
+
cresult = await sf.call(clsInst, ...args);
|
1177
|
+
});
|
1341
1178
|
result = cresult;
|
1342
1179
|
}
|
1343
1180
|
catch (error) {
|
@@ -1347,35 +1184,37 @@ class DBOSExecutor {
|
|
1347
1184
|
// `result` can only be dbosNull when the step timed out
|
1348
1185
|
if (result === exports.dbosNull) {
|
1349
1186
|
// Record the error, then throw it.
|
1350
|
-
err = err === exports.dbosNull ? new error_1.DBOSMaxStepRetriesError(stepFnName,
|
1351
|
-
await this.systemDatabase.recordOperationResult(
|
1187
|
+
err = err === exports.dbosNull ? new error_1.DBOSMaxStepRetriesError(stepFnName, maxAttempts, errors) : err;
|
1188
|
+
await this.systemDatabase.recordOperationResult(wfid, funcID, stepFnName, true, {
|
1352
1189
|
error: utils_1.DBOSJSON.stringify((0, serialize_error_1.serializeError)(err)),
|
1353
1190
|
});
|
1354
|
-
|
1355
|
-
this.tracer.endSpan(
|
1191
|
+
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err.message });
|
1192
|
+
this.tracer.endSpan(span);
|
1356
1193
|
throw err;
|
1357
1194
|
}
|
1358
1195
|
else {
|
1359
1196
|
// Record the execution and return.
|
1360
|
-
await this.systemDatabase.recordOperationResult(
|
1197
|
+
await this.systemDatabase.recordOperationResult(wfid, funcID, stepFnName, true, {
|
1361
1198
|
output: utils_1.DBOSJSON.stringify(result),
|
1362
1199
|
});
|
1363
|
-
|
1364
|
-
this.tracer.endSpan(
|
1200
|
+
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
1201
|
+
this.tracer.endSpan(span);
|
1365
1202
|
return result;
|
1366
1203
|
}
|
1367
1204
|
}
|
1368
|
-
async
|
1205
|
+
async runSendTempWF(destinationId, message, topic, idempotencyKey) {
|
1369
1206
|
// Create a workflow and call send.
|
1370
|
-
const temp_workflow = async (
|
1371
|
-
|
1207
|
+
const temp_workflow = async (destinationId, message, topic) => {
|
1208
|
+
const ctx = (0, context_1.getCurrentContextStore)();
|
1209
|
+
const functionID = (0, context_1.functionIDGetIncrement)();
|
1210
|
+
await this.systemDatabase.send(ctx.workflowId, functionID, destinationId, utils_1.DBOSJSON.stringify(message), topic);
|
1372
1211
|
};
|
1373
|
-
const workflowUUID = idempotencyKey ?
|
1212
|
+
const workflowUUID = idempotencyKey ? destinationId + idempotencyKey : undefined;
|
1374
1213
|
return (await this.workflow(temp_workflow, {
|
1375
1214
|
workflowUUID: workflowUUID,
|
1376
1215
|
tempWfType: exports.TempWorkflowType.send,
|
1377
1216
|
configuredInstance: null,
|
1378
|
-
},
|
1217
|
+
}, destinationId, message, topic)).getResult();
|
1379
1218
|
}
|
1380
1219
|
/**
|
1381
1220
|
* Wait for a workflow to emit an event, then return its value.
|
@@ -1443,33 +1282,13 @@ class DBOSExecutor {
|
|
1443
1282
|
return await this.userDatabase.query(sql);
|
1444
1283
|
}
|
1445
1284
|
}
|
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
1285
|
/* INTERNAL HELPERS */
|
1467
1286
|
/**
|
1468
1287
|
* A recovery process that by default runs during executor init time.
|
1469
1288
|
* It runs to completion all pending workflows that were executing when the previous executor failed.
|
1470
1289
|
*/
|
1471
1290
|
async recoverPendingWorkflows(executorIDs = ['local']) {
|
1472
|
-
if (this.
|
1291
|
+
if (this.debugMode) {
|
1473
1292
|
throw new error_1.DBOSDebuggerError('Cannot recover pending workflows in debug mode.');
|
1474
1293
|
}
|
1475
1294
|
const handlerArray = [];
|
@@ -1510,34 +1329,22 @@ class DBOSExecutor {
|
|
1510
1329
|
this.scheduler = new scheduler_1.DBOSScheduler(this);
|
1511
1330
|
this.scheduler.initScheduler();
|
1512
1331
|
this.wfqEnded = wfqueue_1.wfQueueRunner.dispatchLoop(this);
|
1513
|
-
for (const evtRcvr of this.eventReceivers) {
|
1514
|
-
await evtRcvr.initialize(this);
|
1515
|
-
}
|
1516
1332
|
for (const lcl of (0, decorators_1.getLifecycleListeners)()) {
|
1517
|
-
await lcl.initialize();
|
1333
|
+
await lcl.initialize?.();
|
1518
1334
|
}
|
1519
1335
|
}
|
1520
1336
|
async deactivateEventReceivers(stopQueueThread = true) {
|
1521
1337
|
this.logger.debug('Deactivating lifecycle listeners');
|
1522
1338
|
for (const lcl of (0, decorators_1.getLifecycleListeners)()) {
|
1523
1339
|
try {
|
1524
|
-
await lcl.destroy();
|
1340
|
+
await lcl.destroy?.();
|
1525
1341
|
}
|
1526
1342
|
catch (err) {
|
1527
1343
|
const e = err;
|
1528
1344
|
this.logger.warn(`Error destroying lifecycle listener: ${e.message}`);
|
1529
1345
|
}
|
1530
1346
|
}
|
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
|
-
}
|
1347
|
+
this.logger.debug('Deactivating scheduler');
|
1541
1348
|
try {
|
1542
1349
|
await this.scheduler?.destroyScheduler();
|
1543
1350
|
}
|
@@ -1545,6 +1352,7 @@ class DBOSExecutor {
|
|
1545
1352
|
const e = err;
|
1546
1353
|
this.logger.warn(`Error destroying scheduler: ${e.message}`);
|
1547
1354
|
}
|
1355
|
+
this.logger.debug('Deactivating queue runner');
|
1548
1356
|
if (stopQueueThread) {
|
1549
1357
|
try {
|
1550
1358
|
wfqueue_1.wfQueueRunner.stop();
|
@@ -1567,67 +1375,72 @@ class DBOSExecutor {
|
|
1567
1375
|
throw new error_1.DBOSError(`Failed to find inputs for workflow UUID: ${workflowID}`);
|
1568
1376
|
}
|
1569
1377
|
const inputs = utils_1.DBOSJSON.parse(wfStatus.input);
|
1570
|
-
const
|
1571
|
-
const {
|
1378
|
+
const recoverCtx = this.#getRecoveryContext(workflowID, wfStatus);
|
1379
|
+
const { methReg, configuredInst } = this.#getFunctionInfoFromWFStatus(wfStatus);
|
1572
1380
|
// If starting a new workflow, assign a new UUID. Otherwise, use the workflow's original UUID.
|
1573
1381
|
const workflowStartID = startNewWorkflow ? undefined : workflowID;
|
1574
|
-
if (
|
1575
|
-
return
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1382
|
+
if (methReg?.workflowConfig) {
|
1383
|
+
return await (0, context_1.runWithTopContext)(recoverCtx, async () => {
|
1384
|
+
return await this.workflow(methReg.registeredFunction, {
|
1385
|
+
workflowUUID: workflowStartID,
|
1386
|
+
configuredInstance: configuredInst,
|
1387
|
+
queueName: wfStatus.queueName,
|
1388
|
+
executeWorkflow: true,
|
1389
|
+
deadlineEpochMS: wfStatus.deadlineEpochMS,
|
1390
|
+
}, ...inputs);
|
1391
|
+
});
|
1583
1392
|
}
|
1584
1393
|
// Should be temporary workflows. Parse the name of the workflow.
|
1585
1394
|
const wfName = wfStatus.workflowName;
|
1586
1395
|
const nameArr = wfName.split('-');
|
1587
1396
|
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}`);
|
1397
|
+
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
1398
|
}
|
1591
|
-
let temp_workflow;
|
1592
1399
|
if (nameArr[1] === exports.TempWorkflowType.transaction) {
|
1593
|
-
const
|
1594
|
-
if (!
|
1595
|
-
this.logger.error(`Cannot find transaction info for
|
1400
|
+
const txnReg = (0, decorators_1.getFunctionRegistrationByName)(wfStatus.workflowClassName, nameArr[2]);
|
1401
|
+
if (!txnReg?.txnConfig) {
|
1402
|
+
this.logger.error(`Cannot find transaction info for ID ${workflowID}, name ${nameArr[2]}`);
|
1596
1403
|
throw new error_1.DBOSNotRegisteredError(nameArr[2]);
|
1597
1404
|
}
|
1598
|
-
return await
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1604
|
-
|
1405
|
+
return await (0, context_1.runWithTopContext)(recoverCtx, async () => {
|
1406
|
+
return await this.startTransactionTempWF(txnReg.registeredFunction, {
|
1407
|
+
workflowUUID: workflowStartID,
|
1408
|
+
configuredInstance: configuredInst,
|
1409
|
+
queueName: wfStatus.queueName,
|
1410
|
+
executeWorkflow: true,
|
1411
|
+
}, undefined, undefined, ...inputs);
|
1412
|
+
});
|
1605
1413
|
}
|
1606
1414
|
else if (nameArr[1] === exports.TempWorkflowType.step) {
|
1607
|
-
const
|
1608
|
-
if (!
|
1609
|
-
this.logger.error(`Cannot find step info for
|
1415
|
+
const stepReg = (0, decorators_1.getFunctionRegistrationByName)(wfStatus.workflowClassName, nameArr[2]);
|
1416
|
+
if (!stepReg?.stepConfig) {
|
1417
|
+
this.logger.error(`Cannot find step info for ID ${workflowID}, name ${nameArr[2]}`);
|
1610
1418
|
throw new error_1.DBOSNotRegisteredError(nameArr[2]);
|
1611
1419
|
}
|
1612
|
-
return await
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
1616
|
-
|
1617
|
-
|
1618
|
-
|
1420
|
+
return await (0, context_1.runWithTopContext)(recoverCtx, async () => {
|
1421
|
+
return await this.startStepTempWF(stepReg.registeredFunction, {
|
1422
|
+
workflowUUID: workflowStartID,
|
1423
|
+
configuredInstance: configuredInst,
|
1424
|
+
queueName: wfStatus.queueName, // Probably null
|
1425
|
+
executeWorkflow: true,
|
1426
|
+
}, undefined, undefined, ...inputs);
|
1427
|
+
});
|
1619
1428
|
}
|
1620
1429
|
else if (nameArr[1] === exports.TempWorkflowType.send) {
|
1621
|
-
|
1622
|
-
|
1430
|
+
const swf = async (destinationID, message, topic) => {
|
1431
|
+
const ctx = (0, context_1.getCurrentContextStore)();
|
1432
|
+
const functionID = (0, context_1.functionIDGetIncrement)();
|
1433
|
+
await this.systemDatabase.send(ctx.workflowId, functionID, destinationID, utils_1.DBOSJSON.stringify(message), topic);
|
1623
1434
|
};
|
1624
|
-
|
1625
|
-
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
1630
|
-
|
1435
|
+
const temp_workflow = swf;
|
1436
|
+
return await (0, context_1.runWithTopContext)(recoverCtx, async () => {
|
1437
|
+
return this.workflow(temp_workflow, {
|
1438
|
+
workflowUUID: workflowStartID,
|
1439
|
+
tempWfType: exports.TempWorkflowType.send,
|
1440
|
+
queueName: wfStatus.queueName,
|
1441
|
+
executeWorkflow: true,
|
1442
|
+
}, ...inputs);
|
1443
|
+
});
|
1631
1444
|
}
|
1632
1445
|
else {
|
1633
1446
|
this.logger.error(`Unrecognized temporary workflow! UUID ${workflowID}, name ${wfName}`);
|
@@ -1640,14 +1453,13 @@ class DBOSExecutor {
|
|
1640
1453
|
async upsertEventDispatchState(state) {
|
1641
1454
|
return await this.systemDatabase.upsertEventDispatchState(state);
|
1642
1455
|
}
|
1643
|
-
#getRecoveryContext(
|
1456
|
+
#getRecoveryContext(_workflowID, status) {
|
1644
1457
|
// Note: this doesn't inherit the original parent context's span.
|
1645
|
-
const oc =
|
1458
|
+
const oc = {};
|
1646
1459
|
oc.request = status.request;
|
1647
1460
|
oc.authenticatedUser = status.authenticatedUser;
|
1648
1461
|
oc.authenticatedRoles = status.authenticatedRoles;
|
1649
1462
|
oc.assumedRole = status.assumedRole;
|
1650
|
-
oc.workflowUUID = workflowUUID;
|
1651
1463
|
return oc;
|
1652
1464
|
}
|
1653
1465
|
async cancelWorkflow(workflowID) {
|
@@ -1671,7 +1483,7 @@ class DBOSExecutor {
|
|
1671
1483
|
}
|
1672
1484
|
logRegisteredHTTPUrls() {
|
1673
1485
|
this.logger.info('HTTP endpoints supported:');
|
1674
|
-
|
1486
|
+
(0, decorators_1.getAllRegisteredFunctions)().forEach((registeredOperation) => {
|
1675
1487
|
const ro = registeredOperation;
|
1676
1488
|
if (ro.apiURL) {
|
1677
1489
|
this.logger.info(' ' + ro.apiType.padEnd(6) + ' : ' + ro.apiURL);
|
@@ -1699,8 +1511,9 @@ class DBOSExecutor {
|
|
1699
1511
|
*/
|
1700
1512
|
computeAppVersion() {
|
1701
1513
|
const hasher = crypto.createHash('md5');
|
1702
|
-
const sortedWorkflowSource = Array.from(
|
1703
|
-
.
|
1514
|
+
const sortedWorkflowSource = Array.from((0, decorators_1.getAllRegisteredFunctions)())
|
1515
|
+
.filter((e) => e.workflowConfig)
|
1516
|
+
.map((i) => i.origFunction.toString())
|
1704
1517
|
.sort();
|
1705
1518
|
// Different DBOS versions should produce different hashes.
|
1706
1519
|
sortedWorkflowSource.push(utils_1.globalParams.dbosVersion);
|