@dbos-inc/dbos-sdk 2.2.9-preview.g48d872ebd0 → 2.3.7-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/.husky/pre-commit +1 -0
- package/.prettierignore +3 -0
- package/.prettierrc +9 -0
- package/CODE_OF_CONDUCT.md +24 -18
- package/CONTRIBUTING.md +12 -10
- package/README.md +2 -2
- package/compose.yaml +17 -17
- package/dbos-config.schema.json +4 -13
- package/dist/schemas/user_db_schema.d.ts.map +1 -1
- package/dist/schemas/user_db_schema.js.map +1 -1
- package/dist/src/context.d.ts +12 -12
- package/dist/src/context.d.ts.map +1 -1
- package/dist/src/context.js +9 -9
- package/dist/src/context.js.map +1 -1
- package/dist/src/data_validation.d.ts +1 -1
- package/dist/src/data_validation.d.ts.map +1 -1
- package/dist/src/data_validation.js +14 -8
- package/dist/src/data_validation.js.map +1 -1
- package/dist/src/dbos-executor.d.ts +4 -2
- package/dist/src/dbos-executor.d.ts.map +1 -1
- package/dist/src/dbos-executor.js +131 -114
- package/dist/src/dbos-executor.js.map +1 -1
- package/dist/src/dbos-runtime/cli.d.ts +3 -3
- package/dist/src/dbos-runtime/cli.d.ts.map +1 -1
- package/dist/src/dbos-runtime/cli.js +79 -38
- package/dist/src/dbos-runtime/cli.js.map +1 -1
- package/dist/src/dbos-runtime/cloudutils/authentication.d.ts +1 -1
- package/dist/src/dbos-runtime/cloudutils/authentication.d.ts.map +1 -1
- package/dist/src/dbos-runtime/cloudutils/authentication.js +14 -14
- package/dist/src/dbos-runtime/cloudutils/authentication.js.map +1 -1
- package/dist/src/dbos-runtime/cloudutils/cloudutils.d.ts +2 -2
- package/dist/src/dbos-runtime/cloudutils/cloudutils.d.ts.map +1 -1
- package/dist/src/dbos-runtime/cloudutils/cloudutils.js +32 -32
- package/dist/src/dbos-runtime/cloudutils/cloudutils.js.map +1 -1
- package/dist/src/dbos-runtime/cloudutils/databases.d.ts +2 -2
- package/dist/src/dbos-runtime/cloudutils/databases.d.ts.map +1 -1
- package/dist/src/dbos-runtime/cloudutils/databases.js +25 -21
- package/dist/src/dbos-runtime/cloudutils/databases.js.map +1 -1
- package/dist/src/dbos-runtime/commands.d.ts +1 -1
- package/dist/src/dbos-runtime/commands.js +9 -9
- package/dist/src/dbos-runtime/config.d.ts +7 -7
- package/dist/src/dbos-runtime/config.d.ts.map +1 -1
- package/dist/src/dbos-runtime/config.js +30 -23
- package/dist/src/dbos-runtime/config.js.map +1 -1
- package/dist/src/dbos-runtime/configure.d.ts.map +1 -1
- package/dist/src/dbos-runtime/configure.js.map +1 -1
- package/dist/src/dbos-runtime/db_connection.d.ts.map +1 -1
- package/dist/src/dbos-runtime/db_connection.js +2 -2
- package/dist/src/dbos-runtime/db_connection.js.map +1 -1
- package/dist/src/dbos-runtime/db_wizard.d.ts +1 -1
- package/dist/src/dbos-runtime/db_wizard.d.ts.map +1 -1
- package/dist/src/dbos-runtime/db_wizard.js +18 -18
- package/dist/src/dbos-runtime/db_wizard.js.map +1 -1
- package/dist/src/dbos-runtime/debug.d.ts +2 -2
- package/dist/src/dbos-runtime/debug.d.ts.map +1 -1
- package/dist/src/dbos-runtime/debug.js +4 -4
- package/dist/src/dbos-runtime/debug.js.map +1 -1
- package/dist/src/dbos-runtime/migrate.d.ts +2 -2
- package/dist/src/dbos-runtime/migrate.d.ts.map +1 -1
- package/dist/src/dbos-runtime/migrate.js +14 -10
- package/dist/src/dbos-runtime/migrate.js.map +1 -1
- package/dist/src/dbos-runtime/reset.d.ts +2 -2
- package/dist/src/dbos-runtime/reset.js +2 -2
- package/dist/src/dbos-runtime/reset.js.map +1 -1
- package/dist/src/dbos-runtime/runtime.d.ts.map +1 -1
- package/dist/src/dbos-runtime/runtime.js +4 -4
- package/dist/src/dbos-runtime/runtime.js.map +1 -1
- package/dist/src/dbos-runtime/workflow_management.d.ts +5 -4
- package/dist/src/dbos-runtime/workflow_management.d.ts.map +1 -1
- package/dist/src/dbos-runtime/workflow_management.js +34 -14
- package/dist/src/dbos-runtime/workflow_management.js.map +1 -1
- package/dist/src/dbos.d.ts +23 -23
- package/dist/src/dbos.d.ts.map +1 -1
- package/dist/src/dbos.js +59 -59
- package/dist/src/dbos.js.map +1 -1
- package/dist/src/debugpoint.d.ts.map +1 -1
- package/dist/src/debugpoint.js +4 -4
- package/dist/src/debugpoint.js.map +1 -1
- package/dist/src/decorators.d.ts +8 -8
- package/dist/src/decorators.d.ts.map +1 -1
- package/dist/src/decorators.js +36 -33
- package/dist/src/decorators.js.map +1 -1
- package/dist/src/error.d.ts.map +1 -1
- package/dist/src/error.js +6 -5
- package/dist/src/error.js.map +1 -1
- package/dist/src/eventreceiver.d.ts +1 -1
- package/dist/src/eventreceiver.d.ts.map +1 -1
- package/dist/src/httpServer/handler.d.ts +8 -8
- package/dist/src/httpServer/handler.d.ts.map +1 -1
- package/dist/src/httpServer/handler.js.map +1 -1
- package/dist/src/httpServer/handlerTypes.d.ts.map +1 -1
- package/dist/src/httpServer/handlerTypes.js.map +1 -1
- package/dist/src/httpServer/middleware.d.ts +9 -9
- package/dist/src/httpServer/middleware.d.ts.map +1 -1
- package/dist/src/httpServer/middleware.js +6 -6
- package/dist/src/httpServer/middleware.js.map +1 -1
- package/dist/src/httpServer/server.d.ts +2 -2
- package/dist/src/httpServer/server.d.ts.map +1 -1
- package/dist/src/httpServer/server.js +27 -33
- package/dist/src/httpServer/server.js.map +1 -1
- package/dist/src/index.d.ts +16 -16
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/kafka/kafka.d.ts.map +1 -1
- package/dist/src/kafka/kafka.js +2 -2
- package/dist/src/kafka/kafka.js.map +1 -1
- package/dist/src/procedure.d.ts +6 -6
- package/dist/src/procedure.d.ts.map +1 -1
- package/dist/src/procedure.js.map +1 -1
- package/dist/src/scheduler/crontab.d.ts.map +1 -1
- package/dist/src/scheduler/crontab.js +54 -33
- package/dist/src/scheduler/crontab.js.map +1 -1
- package/dist/src/scheduler/scheduler.d.ts +3 -3
- package/dist/src/scheduler/scheduler.d.ts.map +1 -1
- package/dist/src/scheduler/scheduler.js +7 -7
- package/dist/src/scheduler/scheduler.js.map +1 -1
- package/dist/src/step.d.ts +4 -4
- package/dist/src/step.d.ts.map +1 -1
- package/dist/src/step.js.map +1 -1
- package/dist/src/system_database.d.ts +16 -11
- package/dist/src/system_database.d.ts.map +1 -1
- package/dist/src/system_database.js +136 -56
- package/dist/src/system_database.js.map +1 -1
- package/dist/src/telemetry/collector.d.ts +3 -3
- package/dist/src/telemetry/exporters.d.ts +1 -1
- package/dist/src/telemetry/exporters.js.map +1 -1
- package/dist/src/telemetry/index.d.ts +5 -5
- package/dist/src/telemetry/logs.d.ts +4 -4
- package/dist/src/telemetry/logs.d.ts.map +1 -1
- package/dist/src/telemetry/logs.js +18 -18
- package/dist/src/telemetry/logs.js.map +1 -1
- package/dist/src/telemetry/traces.d.ts +3 -3
- package/dist/src/telemetry/traces.js +7 -7
- package/dist/src/testing/testing_runtime.d.ts +11 -11
- package/dist/src/testing/testing_runtime.d.ts.map +1 -1
- package/dist/src/testing/testing_runtime.js +15 -8
- package/dist/src/testing/testing_runtime.js.map +1 -1
- package/dist/src/transaction.d.ts +6 -6
- package/dist/src/transaction.d.ts.map +1 -1
- package/dist/src/transaction.js +4 -4
- package/dist/src/transaction.js.map +1 -1
- package/dist/src/user_database.d.ts +4 -4
- package/dist/src/user_database.d.ts.map +1 -1
- package/dist/src/user_database.js +45 -45
- package/dist/src/user_database.js.map +1 -1
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +6 -12
- package/dist/src/utils.js.map +1 -1
- package/dist/src/wfqueue.d.ts +1 -1
- package/dist/src/wfqueue.d.ts.map +1 -1
- package/dist/src/wfqueue.js +5 -7
- package/dist/src/wfqueue.js.map +1 -1
- package/dist/src/workflow.d.ts +17 -10
- package/dist/src/workflow.d.ts.map +1 -1
- package/dist/src/workflow.js +19 -18
- package/dist/src/workflow.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/eslint.config.js +29 -27
- package/migrations/20240123182943_schema.js +7 -8
- package/migrations/20240123183021_tables.js +52 -48
- package/migrations/20240123183025_indexes.js +11 -14
- package/migrations/20240123183030_triggers.js +7 -8
- package/migrations/20240124015239_status_timestamp.js +12 -18
- package/migrations/20240201213211_replica_identity.js +8 -11
- package/migrations/20240205223925_foreign_keys.js +40 -18
- package/migrations/20240207192338_executor_id_index.js +8 -10
- package/migrations/20240430090000_tables.js +8 -10
- package/migrations/20240516004341_application_version.js +10 -12
- package/migrations/20240517000000_status_class_config.js +10 -14
- package/migrations/20240621000000_workflow_tries.js +8 -12
- package/migrations/20240924000000_workflowqueue.js +32 -23
- package/migrations/20241009150000_event_dispatch_kv.js +12 -14
- package/migrations/20252101000000_workflow_queues_executor_id.js +7 -9
- package/package.json +9 -2
- package/src/dbos-runtime/cloudutils/README.md +1 -1
@@ -29,17 +29,17 @@ const wfqueue_1 = require("./wfqueue");
|
|
29
29
|
const debugpoint_1 = require("./debugpoint");
|
30
30
|
exports.dbosNull = {};
|
31
31
|
exports.OperationType = {
|
32
|
-
HANDLER:
|
33
|
-
WORKFLOW:
|
34
|
-
TRANSACTION:
|
35
|
-
COMMUNICATOR:
|
36
|
-
PROCEDURE:
|
32
|
+
HANDLER: 'handler',
|
33
|
+
WORKFLOW: 'workflow',
|
34
|
+
TRANSACTION: 'transaction',
|
35
|
+
COMMUNICATOR: 'communicator',
|
36
|
+
PROCEDURE: 'procedure',
|
37
37
|
};
|
38
38
|
const TempWorkflowType = {
|
39
|
-
transaction:
|
40
|
-
procedure:
|
41
|
-
external:
|
42
|
-
send:
|
39
|
+
transaction: 'transaction',
|
40
|
+
procedure: 'procedure',
|
41
|
+
external: 'external',
|
42
|
+
send: 'send',
|
43
43
|
};
|
44
44
|
class DBOSExecutor {
|
45
45
|
config;
|
@@ -50,14 +50,14 @@ class DBOSExecutor {
|
|
50
50
|
systemDatabase;
|
51
51
|
procedurePool;
|
52
52
|
// Temporary workflows are created by calling transaction/send/recv directly from the executor class
|
53
|
-
static tempWorkflowName =
|
53
|
+
static tempWorkflowName = 'temp_workflow';
|
54
54
|
workflowInfoMap = new Map([
|
55
55
|
// We initialize the map with an entry for temporary workflows.
|
56
56
|
[
|
57
57
|
DBOSExecutor.tempWorkflowName,
|
58
58
|
{
|
59
59
|
workflow: async () => {
|
60
|
-
this.logger.error(
|
60
|
+
this.logger.error('UNREACHABLE: Indirect invoke of temp workflow');
|
61
61
|
return Promise.resolve();
|
62
62
|
},
|
63
63
|
config: {},
|
@@ -77,7 +77,7 @@ class DBOSExecutor {
|
|
77
77
|
static defaultNotificationTimeoutSec = 60;
|
78
78
|
debugMode;
|
79
79
|
debugProxy;
|
80
|
-
static systemDBSchemaName =
|
80
|
+
static systemDBSchemaName = 'dbos';
|
81
81
|
logger;
|
82
82
|
tracer;
|
83
83
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
@@ -86,7 +86,7 @@ class DBOSExecutor {
|
|
86
86
|
eventReceivers = [];
|
87
87
|
scheduler = undefined;
|
88
88
|
wfqEnded = undefined;
|
89
|
-
executorID = process.env.DBOS__VMID ||
|
89
|
+
executorID = process.env.DBOS__VMID || 'local';
|
90
90
|
static globalInstance = undefined;
|
91
91
|
/* WORKFLOW EXECUTOR LIFE CYCLE MANAGEMENT */
|
92
92
|
constructor(config, systemDatabase) {
|
@@ -96,7 +96,7 @@ class DBOSExecutor {
|
|
96
96
|
// Set configured environment variables
|
97
97
|
if (config.env) {
|
98
98
|
for (const [key, value] of Object.entries(config.env)) {
|
99
|
-
if (typeof value ===
|
99
|
+
if (typeof value === 'string') {
|
100
100
|
process.env[key] = value;
|
101
101
|
}
|
102
102
|
else {
|
@@ -115,7 +115,7 @@ class DBOSExecutor {
|
|
115
115
|
this.logger = new logs_1.GlobalLogger(this.telemetryCollector, this.config.telemetry?.logs);
|
116
116
|
this.tracer = new traces_1.Tracer(this.telemetryCollector);
|
117
117
|
if (this.debugMode) {
|
118
|
-
this.logger.info(
|
118
|
+
this.logger.info('Running in debug mode!');
|
119
119
|
if (this.debugProxy) {
|
120
120
|
try {
|
121
121
|
const url = new URL(this.config.debugProxy);
|
@@ -131,11 +131,11 @@ class DBOSExecutor {
|
|
131
131
|
}
|
132
132
|
this.procedurePool = new pg_1.Pool(this.config.poolConfig);
|
133
133
|
if (systemDatabase) {
|
134
|
-
this.logger.debug(
|
134
|
+
this.logger.debug('Using provided system database'); // XXX print the name or something
|
135
135
|
this.systemDatabase = systemDatabase;
|
136
136
|
}
|
137
137
|
else {
|
138
|
-
this.logger.debug(
|
138
|
+
this.logger.debug('Using Postgres system database');
|
139
139
|
this.systemDatabase = new system_database_1.PostgresSystemDatabase(this.config.poolConfig, this.config.system_database, this.logger);
|
140
140
|
}
|
141
141
|
this.flushBufferID = setInterval(() => {
|
@@ -144,7 +144,7 @@ class DBOSExecutor {
|
|
144
144
|
void this.flushWorkflowBuffers();
|
145
145
|
}
|
146
146
|
}, this.flushBufferIntervalMs);
|
147
|
-
this.logger.debug(
|
147
|
+
this.logger.debug('Started workflow status buffer worker');
|
148
148
|
this.initialized = false;
|
149
149
|
DBOSExecutor.globalInstance = this;
|
150
150
|
}
|
@@ -154,25 +154,26 @@ class DBOSExecutor {
|
|
154
154
|
if (userDbClient === user_database_1.UserDatabaseName.PRISMA) {
|
155
155
|
// TODO: make Prisma work with debugger proxy.
|
156
156
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports
|
157
|
-
const { PrismaClient } = require(node_path_1.default.join(process.cwd(),
|
157
|
+
const { PrismaClient } = require(node_path_1.default.join(process.cwd(), 'node_modules', '@prisma', 'client')); // Find the prisma client in the node_modules of the current project
|
158
|
+
this.userDatabase = new user_database_1.PrismaUserDatabase(
|
158
159
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
|
159
|
-
|
160
|
+
new PrismaClient({
|
160
161
|
datasources: {
|
161
162
|
db: {
|
162
163
|
url: `postgresql://${userDBConfig.user}:${userDBConfig.password}@${userDBConfig.host}:${userDBConfig.port}/${userDBConfig.database}`,
|
163
164
|
},
|
164
|
-
}
|
165
|
+
},
|
165
166
|
}));
|
166
|
-
this.logger.debug(
|
167
|
+
this.logger.debug('Loaded Prisma user database');
|
167
168
|
}
|
168
169
|
else if (userDbClient === user_database_1.UserDatabaseName.TYPEORM) {
|
169
170
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports
|
170
|
-
const DataSourceExports = require(
|
171
|
+
const DataSourceExports = require('typeorm');
|
171
172
|
try {
|
172
173
|
this.userDatabase = new user_database_1.TypeORMDatabase(
|
173
174
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
174
175
|
new DataSourceExports.DataSource({
|
175
|
-
type:
|
176
|
+
type: 'postgres', // perhaps should move to config file
|
176
177
|
host: userDBConfig.host,
|
177
178
|
port: userDBConfig.port,
|
178
179
|
username: userDBConfig.user,
|
@@ -186,11 +187,11 @@ class DBOSExecutor {
|
|
186
187
|
s.message = `Error loading TypeORM user database: ${s.message}`;
|
187
188
|
this.logger.error(s);
|
188
189
|
}
|
189
|
-
this.logger.debug(
|
190
|
+
this.logger.debug('Loaded TypeORM user database');
|
190
191
|
}
|
191
192
|
else if (userDbClient === user_database_1.UserDatabaseName.KNEX) {
|
192
193
|
const knexConfig = {
|
193
|
-
client:
|
194
|
+
client: 'postgres',
|
194
195
|
connection: {
|
195
196
|
host: userDBConfig.host,
|
196
197
|
port: userDBConfig.port,
|
@@ -201,21 +202,21 @@ class DBOSExecutor {
|
|
201
202
|
},
|
202
203
|
};
|
203
204
|
this.userDatabase = new user_database_1.KnexUserDatabase((0, knex_1.default)(knexConfig));
|
204
|
-
this.logger.debug(
|
205
|
+
this.logger.debug('Loaded Knex user database');
|
205
206
|
}
|
206
207
|
else if (userDbClient === user_database_1.UserDatabaseName.DRIZZLE) {
|
207
208
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports
|
208
|
-
const DrizzleExports = require(
|
209
|
+
const DrizzleExports = require('drizzle-orm/node-postgres');
|
209
210
|
const drizzlePool = new pg_1.Pool(userDBConfig);
|
210
211
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
211
212
|
const drizzle = DrizzleExports.drizzle(drizzlePool, { schema: this.drizzleEntities });
|
212
213
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
213
214
|
this.userDatabase = new user_database_1.DrizzleUserDatabase(drizzlePool, drizzle);
|
214
|
-
this.logger.debug(
|
215
|
+
this.logger.debug('Loaded Drizzle user database');
|
215
216
|
}
|
216
217
|
else {
|
217
218
|
this.userDatabase = new user_database_1.PGNodeUserDatabase(userDBConfig);
|
218
|
-
this.logger.debug(
|
219
|
+
this.logger.debug('Loaded Postgres user database');
|
219
220
|
}
|
220
221
|
}
|
221
222
|
#registerClass(cls) {
|
@@ -253,7 +254,7 @@ class DBOSExecutor {
|
|
253
254
|
}
|
254
255
|
async init(classes) {
|
255
256
|
if (this.initialized) {
|
256
|
-
this.logger.error(
|
257
|
+
this.logger.error('Workflow executor already initialized!');
|
257
258
|
return;
|
258
259
|
}
|
259
260
|
if (!classes || !classes.length) {
|
@@ -267,7 +268,7 @@ class DBOSExecutor {
|
|
267
268
|
* With TSORM, we take an array of entities (Function[]) and add them to this.entities:
|
268
269
|
*/
|
269
270
|
if (Array.isArray(reg.ormEntities)) {
|
270
|
-
this.typeormEntities =
|
271
|
+
this.typeormEntities = this.typeormEntities.concat(reg.ormEntities);
|
271
272
|
length = reg.ormEntities.length;
|
272
273
|
}
|
273
274
|
else {
|
@@ -282,8 +283,8 @@ class DBOSExecutor {
|
|
282
283
|
await (0, user_database_1.createDBIfDoesNotExist)(this.config.poolConfig, this.logger);
|
283
284
|
this.configureDbClient();
|
284
285
|
if (!this.userDatabase) {
|
285
|
-
this.logger.error(
|
286
|
-
throw new error_1.DBOSInitializationError(
|
286
|
+
this.logger.error('No user database configured!');
|
287
|
+
throw new error_1.DBOSInitializationError('No user database configured!');
|
287
288
|
}
|
288
289
|
for (const cls of classes) {
|
289
290
|
this.#registerClass(cls);
|
@@ -324,30 +325,30 @@ class DBOSExecutor {
|
|
324
325
|
for (const v of this.registeredOperations) {
|
325
326
|
const m = v;
|
326
327
|
if (m.init === true) {
|
327
|
-
this.logger.debug(
|
328
|
+
this.logger.debug('Executing init method: ' + m.name);
|
328
329
|
await m.origFunction(new context_1.InitContext(this));
|
329
330
|
}
|
330
331
|
}
|
331
332
|
await this.recoverPendingWorkflows();
|
332
333
|
}
|
333
|
-
this.logger.info(
|
334
|
+
this.logger.info('Workflow executor initialized');
|
334
335
|
}
|
335
336
|
#logNotice(msg) {
|
336
337
|
switch (msg.severity) {
|
337
|
-
case
|
338
|
-
case
|
339
|
-
case
|
338
|
+
case 'INFO':
|
339
|
+
case 'LOG':
|
340
|
+
case 'NOTICE':
|
340
341
|
this.logger.info(msg.message);
|
341
342
|
break;
|
342
|
-
case
|
343
|
+
case 'WARNING':
|
343
344
|
this.logger.warn(msg.message);
|
344
345
|
break;
|
345
|
-
case
|
346
|
+
case 'DEBUG':
|
346
347
|
this.logger.debug(msg.message);
|
347
348
|
break;
|
348
|
-
case
|
349
|
-
case
|
350
|
-
case
|
349
|
+
case 'ERROR':
|
350
|
+
case 'FATAL':
|
351
|
+
case 'PANIC':
|
351
352
|
this.logger.error(msg.message);
|
352
353
|
break;
|
353
354
|
default:
|
@@ -356,7 +357,7 @@ class DBOSExecutor {
|
|
356
357
|
}
|
357
358
|
async destroy() {
|
358
359
|
if (this.pendingWorkflowMap.size > 0) {
|
359
|
-
this.logger.info(
|
360
|
+
this.logger.info('Waiting for pending workflows to finish.');
|
360
361
|
await Promise.allSettled(this.pendingWorkflowMap.values());
|
361
362
|
}
|
362
363
|
clearInterval(this.flushBufferID);
|
@@ -365,7 +366,7 @@ class DBOSExecutor {
|
|
365
366
|
await this.flushWorkflowBuffers();
|
366
367
|
}
|
367
368
|
while (this.isFlushingBuffers) {
|
368
|
-
this.logger.info(
|
369
|
+
this.logger.info('Waiting for result buffers to be exported.');
|
369
370
|
await (0, utils_1.sleepms)(1000);
|
370
371
|
}
|
371
372
|
await this.systemDatabase.destroy();
|
@@ -439,9 +440,7 @@ class DBOSExecutor {
|
|
439
440
|
this.logger.debug(`Registered stored proc ${cfn}`);
|
440
441
|
}
|
441
442
|
getWorkflowInfo(wf) {
|
442
|
-
const wfname =
|
443
|
-
? wf.name
|
444
|
-
: (0, decorators_1.getRegisteredMethodClassName)(wf) + '.' + wf.name;
|
443
|
+
const wfname = wf.name === DBOSExecutor.tempWorkflowName ? wf.name : (0, decorators_1.getRegisteredMethodClassName)(wf) + '.' + wf.name;
|
445
444
|
return this.workflowInfoMap.get(wfname);
|
446
445
|
}
|
447
446
|
getWorkflowInfoByStatus(wf) {
|
@@ -498,14 +497,14 @@ class DBOSExecutor {
|
|
498
497
|
const wCtxt = new workflow_1.WorkflowContextImpl(this, params.parentCtx, workflowUUID, wConfig, wf.name, presetUUID, params.tempWfType, params.tempWfName);
|
499
498
|
const internalStatus = {
|
500
499
|
workflowUUID: workflowUUID,
|
501
|
-
status:
|
500
|
+
status: params.queueName !== undefined ? workflow_1.StatusString.ENQUEUED : workflow_1.StatusString.PENDING,
|
502
501
|
name: wf.name,
|
503
|
-
className: wCtxt.isTempWorkflow ?
|
504
|
-
configName: params.configuredInstance?.name ||
|
502
|
+
className: wCtxt.isTempWorkflow ? '' : (0, decorators_1.getRegisteredMethodClassName)(wf),
|
503
|
+
configName: params.configuredInstance?.name || '',
|
505
504
|
queueName: params.queueName,
|
506
505
|
authenticatedUser: wCtxt.authenticatedUser,
|
507
506
|
output: undefined,
|
508
|
-
error:
|
507
|
+
error: '',
|
509
508
|
assumedRole: wCtxt.assumedRole,
|
510
509
|
authenticatedRoles: wCtxt.authenticatedRoles,
|
511
510
|
request: wCtxt.request,
|
@@ -514,18 +513,17 @@ class DBOSExecutor {
|
|
514
513
|
applicationID: wCtxt.applicationID,
|
515
514
|
createdAt: Date.now(), // Remember the start time of this workflow
|
516
515
|
maxRetries: wCtxt.maxRecoveryAttempts,
|
517
|
-
recovery: params.recovery === true,
|
518
516
|
};
|
519
517
|
if (wCtxt.isTempWorkflow) {
|
520
518
|
internalStatus.name = `${DBOSExecutor.tempWorkflowName}-${wCtxt.tempWfOperationType}-${wCtxt.tempWfOperationName}`;
|
521
|
-
internalStatus.className = params.tempWfClass ??
|
519
|
+
internalStatus.className = params.tempWfClass ?? '';
|
522
520
|
}
|
523
521
|
let status = undefined;
|
524
522
|
// Synchronously set the workflow's status to PENDING and record workflow inputs (for non single-transaction workflows).
|
525
523
|
// We have to do it for all types of workflows because operation_outputs table has a foreign key constraint on workflow status table.
|
526
|
-
if ((wCtxt.tempWfOperationType !== TempWorkflowType.transaction
|
527
|
-
|
528
|
-
|
524
|
+
if ((wCtxt.tempWfOperationType !== TempWorkflowType.transaction &&
|
525
|
+
wCtxt.tempWfOperationType !== TempWorkflowType.procedure) ||
|
526
|
+
params.queueName !== undefined) {
|
529
527
|
if (this.debugMode) {
|
530
528
|
const wfStatus = await this.systemDatabase.getWorkflowStatus(workflowUUID);
|
531
529
|
const wfInputs = await this.systemDatabase.getWorkflowInputs(workflowUUID);
|
@@ -590,7 +588,7 @@ class DBOSExecutor {
|
|
590
588
|
// Retrieve the handle and wait for the result.
|
591
589
|
const retrievedHandle = this.retrieveWorkflow(workflowUUID);
|
592
590
|
result = await retrievedHandle.getResult();
|
593
|
-
wCtxt.span.setAttribute(
|
591
|
+
wCtxt.span.setAttribute('cached', true);
|
594
592
|
wCtxt.span.setStatus({ code: api_1.SpanStatusCode.OK });
|
595
593
|
}
|
596
594
|
else {
|
@@ -616,8 +614,8 @@ class DBOSExecutor {
|
|
616
614
|
}
|
617
615
|
finally {
|
618
616
|
this.tracer.endSpan(wCtxt.span);
|
619
|
-
if (wCtxt.tempWfOperationType === TempWorkflowType.transaction
|
620
|
-
|
617
|
+
if (wCtxt.tempWfOperationType === TempWorkflowType.transaction ||
|
618
|
+
wCtxt.tempWfOperationType === TempWorkflowType.procedure) {
|
621
619
|
// For single-transaction workflows, asynchronously record inputs.
|
622
620
|
// We must buffer inputs after workflow status is buffered/flushed because workflow_inputs table has a foreign key reference to the workflow_status table.
|
623
621
|
if (!this.debugMode) {
|
@@ -631,12 +629,13 @@ class DBOSExecutor {
|
|
631
629
|
}
|
632
630
|
return result;
|
633
631
|
};
|
634
|
-
if (this.debugMode ||
|
632
|
+
if (this.debugMode ||
|
633
|
+
(status !== 'SUCCESS' && status !== 'ERROR' && (params.queueName === undefined || params.executeWorkflow))) {
|
635
634
|
const workflowPromise = runWorkflow();
|
636
635
|
// Need to await for the workflow and capture errors.
|
637
636
|
const awaitWorkflowPromise = workflowPromise
|
638
637
|
.catch((error) => {
|
639
|
-
this.logger.debug(
|
638
|
+
this.logger.debug('Captured error in awaitWorkflowPromise: ' + error);
|
640
639
|
})
|
641
640
|
.finally(() => {
|
642
641
|
// Remove itself from pending workflow map.
|
@@ -663,7 +662,7 @@ class DBOSExecutor {
|
|
663
662
|
* Retrieve the transaction snapshot information of the current transaction
|
664
663
|
*/
|
665
664
|
static async #retrieveSnapshot(query) {
|
666
|
-
const rows = await query(
|
665
|
+
const rows = await query('SELECT pg_current_snapshot()::text as txn_snapshot;', []);
|
667
666
|
return rows[0].txn_snapshot;
|
668
667
|
}
|
669
668
|
/**
|
@@ -675,14 +674,14 @@ class DBOSExecutor {
|
|
675
674
|
*/
|
676
675
|
async #checkExecution(query, workflowUUID, funcID) {
|
677
676
|
// Note: we read the current snapshot, not the recorded one!
|
678
|
-
const rows = await query(
|
677
|
+
const rows = await query('(SELECT output, error, txn_snapshot, true as recorded FROM dbos.transaction_outputs WHERE workflow_uuid=$1 AND function_id=$2 UNION ALL SELECT null as output, null as error, pg_current_snapshot()::text as txn_snapshot, false as recorded) ORDER BY recorded', [workflowUUID, funcID]);
|
679
678
|
if (rows.length === 0 || rows.length > 2) {
|
680
|
-
this.logger.error(
|
681
|
-
throw new error_1.DBOSError(
|
679
|
+
this.logger.error('Unexpected! This should never happen. Returned rows: ' + rows.toString());
|
680
|
+
throw new error_1.DBOSError('This should never happen. Returned rows: ' + rows.toString());
|
682
681
|
}
|
683
682
|
const res = {
|
684
683
|
output: exports.dbosNull,
|
685
|
-
txn_snapshot:
|
684
|
+
txn_snapshot: '',
|
686
685
|
};
|
687
686
|
// recorded=false row will be first because we used ORDER BY.
|
688
687
|
res.txn_snapshot = rows[0].txn_snapshot;
|
@@ -701,11 +700,11 @@ class DBOSExecutor {
|
|
701
700
|
*/
|
702
701
|
async #recordOutput(query, workflowUUID, funcID, txnSnapshot, output, isKeyConflict) {
|
703
702
|
if (this.debugMode) {
|
704
|
-
throw new error_1.DBOSDebuggerError(
|
703
|
+
throw new error_1.DBOSDebuggerError('Cannot record output in debug mode.');
|
705
704
|
}
|
706
705
|
try {
|
707
706
|
const serialOutput = utils_1.DBOSJSON.stringify(output);
|
708
|
-
const rows = await query(
|
707
|
+
const rows = await query('INSERT INTO dbos.transaction_outputs (workflow_uuid, function_id, output, txn_id, txn_snapshot, created_at) VALUES ($1, $2, $3, (select pg_current_xact_id_if_assigned()::text), $4, $5) RETURNING txn_id;', [workflowUUID, funcID, serialOutput, txnSnapshot, Date.now()]);
|
709
708
|
return rows[0].txn_id;
|
710
709
|
}
|
711
710
|
catch (error) {
|
@@ -723,11 +722,11 @@ class DBOSExecutor {
|
|
723
722
|
*/
|
724
723
|
async #recordError(query, workflowUUID, funcID, txnSnapshot, err, isKeyConflict) {
|
725
724
|
if (this.debugMode) {
|
726
|
-
throw new error_1.DBOSDebuggerError(
|
725
|
+
throw new error_1.DBOSDebuggerError('Cannot record error in debug mode.');
|
727
726
|
}
|
728
727
|
try {
|
729
728
|
const serialErr = utils_1.DBOSJSON.stringify((0, serialize_error_1.serializeError)(err));
|
730
|
-
await query(
|
729
|
+
await query('INSERT INTO dbos.transaction_outputs (workflow_uuid, function_id, error, txn_id, txn_snapshot, created_at) VALUES ($1, $2, $3, null, $4, $5) RETURNING txn_id;', [workflowUUID, funcID, serialErr, txnSnapshot, Date.now()]);
|
731
730
|
}
|
732
731
|
catch (error) {
|
733
732
|
if (isKeyConflict(error)) {
|
@@ -749,11 +748,11 @@ class DBOSExecutor {
|
|
749
748
|
return;
|
750
749
|
}
|
751
750
|
if (this.debugMode) {
|
752
|
-
throw new error_1.DBOSDebuggerError(
|
751
|
+
throw new error_1.DBOSDebuggerError('Cannot flush result buffer in debug mode.');
|
753
752
|
}
|
754
753
|
funcIDs.sort();
|
755
754
|
try {
|
756
|
-
let sqlStmt =
|
755
|
+
let sqlStmt = 'INSERT INTO dbos.transaction_outputs (workflow_uuid, function_id, output, error, txn_id, txn_snapshot, created_at) VALUES ';
|
757
756
|
let paramCnt = 1;
|
758
757
|
const values = [];
|
759
758
|
for (const funcID of funcIDs) {
|
@@ -764,7 +763,7 @@ class DBOSExecutor {
|
|
764
763
|
const txnSnapshot = recorded.txn_snapshot;
|
765
764
|
const createdAt = recorded.created_at;
|
766
765
|
if (paramCnt > 1) {
|
767
|
-
sqlStmt +=
|
766
|
+
sqlStmt += ', ';
|
768
767
|
}
|
769
768
|
sqlStmt += `($${paramCnt++}, $${paramCnt++}, $${paramCnt++}, $${paramCnt++}, null, $${paramCnt++}, $${paramCnt++})`;
|
770
769
|
values.push(workflowUUID, funcID, utils_1.DBOSJSON.stringify(output), utils_1.DBOSJSON.stringify(null), txnSnapshot, createdAt);
|
@@ -787,7 +786,7 @@ class DBOSExecutor {
|
|
787
786
|
return this.#flushResultBuffer(func, resultBuffer, workflowUUID, (error) => this.userDatabase.isKeyConflictError(error));
|
788
787
|
}
|
789
788
|
#flushResultBufferProc(client, resultBuffer, workflowUUID) {
|
790
|
-
const func = (sql, args) => client.query(sql, args).then(v => v.rows);
|
789
|
+
const func = (sql, args) => client.query(sql, args).then((v) => v.rows);
|
791
790
|
return this.#flushResultBuffer(func, resultBuffer, workflowUUID, user_database_1.pgNodeIsKeyConflictError);
|
792
791
|
}
|
793
792
|
async transaction(txn, params, ...args) {
|
@@ -823,7 +822,7 @@ class DBOSExecutor {
|
|
823
822
|
isolationLevel: txnInfo.config.isolationLevel,
|
824
823
|
}, wfCtx.span);
|
825
824
|
while (true) {
|
826
|
-
let txn_snapshot =
|
825
|
+
let txn_snapshot = 'invalid';
|
827
826
|
const workflowUUID = wfCtx.workflowUUID;
|
828
827
|
const wrappedTransaction = async (client) => {
|
829
828
|
const tCtxt = new transaction_1.TransactionContextImpl(this.userDatabase.getName(), client, wfCtx, span, this.logger, funcId, txn.name);
|
@@ -834,7 +833,7 @@ class DBOSExecutor {
|
|
834
833
|
const check = await this.#checkExecution(func, workflowUUID, funcId);
|
835
834
|
txn_snapshot = check.txn_snapshot;
|
836
835
|
if (check.output !== exports.dbosNull) {
|
837
|
-
tCtxt.span.setAttribute(
|
836
|
+
tCtxt.span.setAttribute('cached', true);
|
838
837
|
tCtxt.span.setStatus({ code: api_1.SpanStatusCode.OK });
|
839
838
|
this.tracer.endSpan(tCtxt.span);
|
840
839
|
return check.output;
|
@@ -881,7 +880,7 @@ class DBOSExecutor {
|
|
881
880
|
// Synchronously record the output of write transactions and obtain the transaction ID.
|
882
881
|
const func = (sql, args) => this.userDatabase.queryWithClient(client, sql, ...args);
|
883
882
|
const pg_txn_id = await this.#recordOutput(func, wfCtx.workflowUUID, funcId, txn_snapshot, result, (error) => this.userDatabase.isKeyConflictError(error));
|
884
|
-
tCtxt.span.setAttribute(
|
883
|
+
tCtxt.span.setAttribute('pg_txn_id', pg_txn_id);
|
885
884
|
wfCtx.resultBuffer.clear();
|
886
885
|
}
|
887
886
|
catch (error) {
|
@@ -908,7 +907,7 @@ class DBOSExecutor {
|
|
908
907
|
}
|
909
908
|
if (this.userDatabase.isRetriableTransactionError(err)) {
|
910
909
|
// serialization_failure in PostgreSQL
|
911
|
-
span.addEvent(
|
910
|
+
span.addEvent('TXN SERIALIZATION FAILURE', { retryWaitMillis: retryWaitMillis }, performance.now());
|
912
911
|
// Retry serialization failures.
|
913
912
|
await (0, utils_1.sleepms)(retryWaitMillis);
|
914
913
|
retryWaitMillis *= backoffFactor;
|
@@ -981,15 +980,15 @@ class DBOSExecutor {
|
|
981
980
|
const maxRetryWaitMs = 2000; // Maximum wait 2 seconds.
|
982
981
|
const readOnly = procInfo.config.readOnly ?? false;
|
983
982
|
while (true) {
|
984
|
-
let txn_snapshot =
|
983
|
+
let txn_snapshot = 'invalid';
|
985
984
|
const wrappedProcedure = async (client) => {
|
986
985
|
const ctxt = new procedure_1.StoredProcedureContextImpl(client, wfCtx, span, this.logger, funcId, proc.name);
|
987
986
|
if (wfCtx.presetUUID) {
|
988
|
-
const func = (sql, args) => this.procedurePool.query(sql, args).then(v => v.rows);
|
987
|
+
const func = (sql, args) => this.procedurePool.query(sql, args).then((v) => v.rows);
|
989
988
|
const check = await this.#checkExecution(func, wfCtx.workflowUUID, funcId);
|
990
989
|
txn_snapshot = check.txn_snapshot;
|
991
990
|
if (check.output !== exports.dbosNull) {
|
992
|
-
ctxt.span.setAttribute(
|
991
|
+
ctxt.span.setAttribute('cached', true);
|
993
992
|
ctxt.span.setStatus({ code: api_1.SpanStatusCode.OK });
|
994
993
|
this.tracer.endSpan(ctxt.span);
|
995
994
|
return check.output;
|
@@ -997,7 +996,7 @@ class DBOSExecutor {
|
|
997
996
|
}
|
998
997
|
else {
|
999
998
|
// Collect snapshot information for read-only transactions and non-preset UUID transactions, if not already collected above
|
1000
|
-
const func = (sql, args) => this.procedurePool.query(sql, args).then(v => v.rows);
|
999
|
+
const func = (sql, args) => this.procedurePool.query(sql, args).then((v) => v.rows);
|
1001
1000
|
txn_snapshot = await DBOSExecutor.#retrieveSnapshot(func);
|
1002
1001
|
}
|
1003
1002
|
if (this.debugMode) {
|
@@ -1031,23 +1030,25 @@ class DBOSExecutor {
|
|
1031
1030
|
}
|
1032
1031
|
else {
|
1033
1032
|
// Synchronously record the output of write transactions and obtain the transaction ID.
|
1034
|
-
const func = (sql, args) => client.query(sql, args).then(v => v.rows);
|
1033
|
+
const func = (sql, args) => client.query(sql, args).then((v) => v.rows);
|
1035
1034
|
const pg_txn_id = await this.#recordOutput(func, wfCtx.workflowUUID, funcId, txn_snapshot, result, user_database_1.pgNodeIsKeyConflictError);
|
1036
1035
|
// const pg_txn_id = await wfCtx.recordOutputProc<R>(client, funcId, txn_snapshot, result);
|
1037
|
-
ctxt.span.setAttribute(
|
1036
|
+
ctxt.span.setAttribute('pg_txn_id', pg_txn_id);
|
1038
1037
|
wfCtx.resultBuffer.clear();
|
1039
1038
|
}
|
1040
1039
|
return result;
|
1041
1040
|
};
|
1042
1041
|
try {
|
1043
|
-
const result = await this.invokeStoredProcFunction(wrappedProcedure, {
|
1042
|
+
const result = await this.invokeStoredProcFunction(wrappedProcedure, {
|
1043
|
+
isolationLevel: procInfo.config.isolationLevel,
|
1044
|
+
});
|
1044
1045
|
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
1045
1046
|
return result;
|
1046
1047
|
}
|
1047
1048
|
catch (err) {
|
1048
1049
|
if (this.userDatabase.isRetriableTransactionError(err)) {
|
1049
1050
|
// serialization_failure in PostgreSQL
|
1050
|
-
span.addEvent(
|
1051
|
+
span.addEvent('TXN SERIALIZATION FAILURE', { retryWaitMillis: retryWaitMillis }, performance.now());
|
1051
1052
|
// Retry serialization failures.
|
1052
1053
|
await (0, utils_1.sleepms)(retryWaitMillis);
|
1053
1054
|
retryWaitMillis *= backoffFactor;
|
@@ -1058,7 +1059,7 @@ class DBOSExecutor {
|
|
1058
1059
|
const e = err;
|
1059
1060
|
await this.invokeStoredProcFunction(async (client) => {
|
1060
1061
|
await this.#flushResultBufferProc(client, wfCtx.resultBuffer, wfCtx.workflowUUID);
|
1061
|
-
const func = (sql, args) => client.query(sql, args).then(v => v.rows);
|
1062
|
+
const func = (sql, args) => client.query(sql, args).then((v) => v.rows);
|
1062
1063
|
await this.#recordError(func, wfCtx.workflowUUID, funcId, txn_snapshot, e, user_database_1.pgNodeIsKeyConflictError);
|
1063
1064
|
}, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
|
1064
1065
|
await this.userDatabase.transaction(async (client) => {
|
@@ -1118,7 +1119,7 @@ class DBOSExecutor {
|
|
1118
1119
|
wfCtx.resultBuffer.clear();
|
1119
1120
|
}
|
1120
1121
|
if (txn_id) {
|
1121
|
-
span.setAttribute(
|
1122
|
+
span.setAttribute('pg_txn_id', txn_id);
|
1122
1123
|
}
|
1123
1124
|
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
1124
1125
|
return output;
|
@@ -1128,13 +1129,11 @@ class DBOSExecutor {
|
|
1128
1129
|
const log = (msg) => this.#logNotice(msg);
|
1129
1130
|
const procClassName = this.getProcedureClassName(proc);
|
1130
1131
|
const plainProcName = `${procClassName}_${proc.name}_p`;
|
1131
|
-
const procName = this.config.appVersion
|
1132
|
-
? `v${this.config.appVersion}_${plainProcName}`
|
1133
|
-
: plainProcName;
|
1132
|
+
const procName = this.config.appVersion ? `v${this.config.appVersion}_${plainProcName}` : plainProcName;
|
1134
1133
|
const sql = `CALL "${procName}"(${args.map((_v, i) => `$${i + 1}`).join()});`;
|
1135
1134
|
try {
|
1136
1135
|
client.on('notice', log);
|
1137
|
-
return await client.query(sql, args).then(value => value.rows);
|
1136
|
+
return await client.query(sql, args).then((value) => value.rows);
|
1138
1137
|
}
|
1139
1138
|
finally {
|
1140
1139
|
client.off('notice', log);
|
@@ -1206,7 +1205,7 @@ class DBOSExecutor {
|
|
1206
1205
|
// Check if this execution previously happened, returning its original result if it did.
|
1207
1206
|
const check = await this.systemDatabase.checkOperationOutput(wfCtx.workflowUUID, ctxt.functionID);
|
1208
1207
|
if (check !== exports.dbosNull) {
|
1209
|
-
ctxt.span.setAttribute(
|
1208
|
+
ctxt.span.setAttribute('cached', true);
|
1210
1209
|
ctxt.span.setStatus({ code: api_1.SpanStatusCode.OK });
|
1211
1210
|
this.tracer.endSpan(ctxt.span);
|
1212
1211
|
return check;
|
@@ -1245,7 +1244,7 @@ class DBOSExecutor {
|
|
1245
1244
|
const e = error;
|
1246
1245
|
errors.push(e);
|
1247
1246
|
this.logger.warn(`Error in step being automatically retried. Attempt ${numAttempts} of ${ctxt.maxAttempts}. ${e.stack}`);
|
1248
|
-
span.addEvent(`Step attempt ${numAttempts + 1} failed`, {
|
1247
|
+
span.addEvent(`Step attempt ${numAttempts + 1} failed`, { retryIntervalSeconds: intervalSeconds, error: error.message }, performance.now());
|
1249
1248
|
if (numAttempts < ctxt.maxAttempts) {
|
1250
1249
|
// Sleep for an interval, then increase the interval by backoffRate.
|
1251
1250
|
// Cap at the maximum allowed retry interval.
|
@@ -1300,7 +1299,9 @@ class DBOSExecutor {
|
|
1300
1299
|
};
|
1301
1300
|
const workflowUUID = idempotencyKey ? destinationUUID + idempotencyKey : undefined;
|
1302
1301
|
return (await this.workflow(temp_workflow, {
|
1303
|
-
workflowUUID: workflowUUID,
|
1302
|
+
workflowUUID: workflowUUID,
|
1303
|
+
tempWfType: TempWorkflowType.send,
|
1304
|
+
configuredInstance: null,
|
1304
1305
|
}, destinationUUID, message, topic)).getResult();
|
1305
1306
|
}
|
1306
1307
|
/**
|
@@ -1337,7 +1338,7 @@ class DBOSExecutor {
|
|
1337
1338
|
for (const nname of channels) {
|
1338
1339
|
await notificationsClient.query(`LISTEN ${nname};`);
|
1339
1340
|
}
|
1340
|
-
notificationsClient.on(
|
1341
|
+
notificationsClient.on('notification', callback);
|
1341
1342
|
return {
|
1342
1343
|
close: async () => {
|
1343
1344
|
for (const nname of channels) {
|
@@ -1349,7 +1350,7 @@ class DBOSExecutor {
|
|
1349
1350
|
}
|
1350
1351
|
notificationsClient.release();
|
1351
1352
|
}
|
1352
|
-
}
|
1353
|
+
},
|
1353
1354
|
};
|
1354
1355
|
}
|
1355
1356
|
/* INTERNAL HELPERS */
|
@@ -1360,13 +1361,13 @@ class DBOSExecutor {
|
|
1360
1361
|
* A recovery process that by default runs during executor init time.
|
1361
1362
|
* It runs to completion all pending workflows that were executing when the previous executor failed.
|
1362
1363
|
*/
|
1363
|
-
async recoverPendingWorkflows(executorIDs = [
|
1364
|
+
async recoverPendingWorkflows(executorIDs = ['local']) {
|
1364
1365
|
if (this.debugMode) {
|
1365
|
-
throw new error_1.DBOSDebuggerError(
|
1366
|
+
throw new error_1.DBOSDebuggerError('Cannot recover pending workflows in debug mode.');
|
1366
1367
|
}
|
1367
1368
|
const pendingWorkflows = [];
|
1368
1369
|
for (const execID of executorIDs) {
|
1369
|
-
if (execID ===
|
1370
|
+
if (execID === 'local' && process.env.DBOS__VMID) {
|
1370
1371
|
this.logger.debug(`Skip local recovery because it's running in a VM: ${process.env.DBOS__VMID}`);
|
1371
1372
|
continue;
|
1372
1373
|
}
|
@@ -1386,7 +1387,7 @@ class DBOSExecutor {
|
|
1386
1387
|
return handlerArray;
|
1387
1388
|
}
|
1388
1389
|
async deactivateEventReceivers() {
|
1389
|
-
this.logger.info(
|
1390
|
+
this.logger.info('Deactivating event receivers');
|
1390
1391
|
for (const evtRcvr of this.eventReceivers || []) {
|
1391
1392
|
try {
|
1392
1393
|
await evtRcvr.destroy();
|
@@ -1425,15 +1426,18 @@ class DBOSExecutor {
|
|
1425
1426
|
const workflowStartUUID = startNewWorkflow ? undefined : workflowUUID;
|
1426
1427
|
if (wfInfo) {
|
1427
1428
|
return this.workflow(wfInfo.workflow, {
|
1428
|
-
workflowUUID: workflowStartUUID,
|
1429
|
-
|
1429
|
+
workflowUUID: workflowStartUUID,
|
1430
|
+
parentCtx: parentCtx,
|
1431
|
+
configuredInstance: configuredInst,
|
1432
|
+
queueName: wfStatus.queueName,
|
1433
|
+
executeWorkflow: true,
|
1430
1434
|
},
|
1431
1435
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
1432
1436
|
...inputs);
|
1433
1437
|
}
|
1434
1438
|
// Should be temporary workflows. Parse the name of the workflow.
|
1435
1439
|
const wfName = wfStatus.workflowName;
|
1436
|
-
const nameArr = wfName.split(
|
1440
|
+
const nameArr = wfName.split('-');
|
1437
1441
|
if (!nameArr[0].startsWith(DBOSExecutor.tempWorkflowName)) {
|
1438
1442
|
// CB - Doesn't this happen if the user changed the function name in their code?
|
1439
1443
|
throw new error_1.DBOSError(`This should never happen! Cannot find workflow info for a non-temporary workflow! UUID ${workflowUUID}, name ${wfName}`);
|
@@ -1485,8 +1489,12 @@ class DBOSExecutor {
|
|
1485
1489
|
throw new error_1.DBOSNotRegisteredError(wfName);
|
1486
1490
|
}
|
1487
1491
|
return this.workflow(temp_workflow, {
|
1488
|
-
workflowUUID: workflowStartUUID,
|
1489
|
-
|
1492
|
+
workflowUUID: workflowStartUUID,
|
1493
|
+
parentCtx: parentCtx ?? undefined,
|
1494
|
+
configuredInstance: clsinst,
|
1495
|
+
tempWfType,
|
1496
|
+
tempWfClass,
|
1497
|
+
tempWfName,
|
1490
1498
|
},
|
1491
1499
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
1492
1500
|
...inputs);
|
@@ -1510,6 +1518,13 @@ class DBOSExecutor {
|
|
1510
1518
|
oc.workflowUUID = workflowUUID;
|
1511
1519
|
return oc;
|
1512
1520
|
}
|
1521
|
+
async cancelWorkflow(workflowID) {
|
1522
|
+
await this.systemDatabase.cancelWorkflow(workflowID);
|
1523
|
+
}
|
1524
|
+
async resumeWorkflow(workflowID) {
|
1525
|
+
await this.systemDatabase.resumeWorkflow(workflowID);
|
1526
|
+
return await this.executeWorkflowUUID(workflowID, false);
|
1527
|
+
}
|
1513
1528
|
/* BACKGROUND PROCESSES */
|
1514
1529
|
/**
|
1515
1530
|
* Periodically flush the workflow output buffer to the system database.
|
@@ -1532,7 +1547,7 @@ class DBOSExecutor {
|
|
1532
1547
|
try {
|
1533
1548
|
let finishedCnt = 0;
|
1534
1549
|
while (finishedCnt < totalSize) {
|
1535
|
-
let sqlStmt =
|
1550
|
+
let sqlStmt = 'INSERT INTO dbos.transaction_outputs (workflow_uuid, function_id, output, error, txn_id, txn_snapshot, created_at) VALUES ';
|
1536
1551
|
let paramCnt = 1;
|
1537
1552
|
const values = [];
|
1538
1553
|
const batchUUIDs = [];
|
@@ -1542,7 +1557,7 @@ class DBOSExecutor {
|
|
1542
1557
|
const txnSnapshot = recorded.txn_snapshot;
|
1543
1558
|
const createdAt = recorded.created_at;
|
1544
1559
|
if (paramCnt > 1) {
|
1545
|
-
sqlStmt +=
|
1560
|
+
sqlStmt += ', ';
|
1546
1561
|
}
|
1547
1562
|
sqlStmt += `($${paramCnt++}, $${paramCnt++}, $${paramCnt++}, $${paramCnt++}, null, $${paramCnt++}, $${paramCnt++})`;
|
1548
1563
|
values.push(workflowUUID, funcID, utils_1.DBOSJSON.stringify(output), utils_1.DBOSJSON.stringify(null), txnSnapshot, createdAt);
|
@@ -1558,7 +1573,9 @@ class DBOSExecutor {
|
|
1558
1573
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
1559
1574
|
await this.userDatabase.query(sqlStmt, ...values);
|
1560
1575
|
// Clean up after each batch succeeds
|
1561
|
-
batchUUIDs.forEach((value) => {
|
1576
|
+
batchUUIDs.forEach((value) => {
|
1577
|
+
localBuffer.delete(value);
|
1578
|
+
});
|
1562
1579
|
}
|
1563
1580
|
}
|
1564
1581
|
catch (error) {
|
@@ -1573,14 +1590,14 @@ class DBOSExecutor {
|
|
1573
1590
|
}
|
1574
1591
|
}
|
1575
1592
|
logRegisteredHTTPUrls() {
|
1576
|
-
this.logger.info(
|
1593
|
+
this.logger.info('HTTP endpoints supported:');
|
1577
1594
|
this.registeredOperations.forEach((registeredOperation) => {
|
1578
1595
|
const ro = registeredOperation;
|
1579
1596
|
if (ro.apiURL) {
|
1580
|
-
this.logger.info(
|
1597
|
+
this.logger.info(' ' + ro.apiType.padEnd(6) + ' : ' + ro.apiURL);
|
1581
1598
|
const roles = ro.getRequiredRoles();
|
1582
1599
|
if (roles.length > 0) {
|
1583
|
-
this.logger.info(
|
1600
|
+
this.logger.info(' Required Roles: ' + utils_1.DBOSJSON.stringify(roles));
|
1584
1601
|
}
|
1585
1602
|
}
|
1586
1603
|
});
|