@dbos-inc/dbos-sdk 2.8.13-preview → 2.8.18-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/dbos-config.schema.json +0 -4
- package/dist/dbos-config.schema.json +0 -4
- package/dist/schemas/system_db_schema.d.ts +1 -0
- package/dist/schemas/system_db_schema.d.ts.map +1 -1
- package/dist/schemas/user_db_schema.d.ts +1 -0
- package/dist/schemas/user_db_schema.d.ts.map +1 -1
- package/dist/schemas/user_db_schema.js.map +1 -1
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +14 -17
- package/dist/src/client.js.map +1 -1
- package/dist/src/dbos-executor.d.ts +7 -4
- package/dist/src/dbos-executor.d.ts.map +1 -1
- package/dist/src/dbos-executor.js +132 -91
- 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 +10 -0
- package/dist/src/dbos-runtime/cli.js.map +1 -1
- package/dist/src/dbos-runtime/config.d.ts +24 -2
- package/dist/src/dbos-runtime/config.d.ts.map +1 -1
- package/dist/src/dbos-runtime/config.js +151 -71
- package/dist/src/dbos-runtime/config.js.map +1 -1
- package/dist/src/dbos-runtime/docker_pg_helper.d.ts +21 -0
- package/dist/src/dbos-runtime/docker_pg_helper.d.ts.map +1 -0
- package/dist/src/dbos-runtime/docker_pg_helper.js +137 -0
- package/dist/src/dbos-runtime/docker_pg_helper.js.map +1 -0
- package/dist/src/dbos-runtime/migrate.d.ts.map +1 -1
- package/dist/src/dbos-runtime/migrate.js +8 -6
- package/dist/src/dbos-runtime/migrate.js.map +1 -1
- package/dist/src/dbos-runtime/runtime.d.ts.map +1 -1
- package/dist/src/dbos-runtime/runtime.js +0 -2
- 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 +14 -16
- package/dist/src/dbos-runtime/workflow_management.js.map +1 -1
- package/dist/src/dbos.d.ts +1 -0
- package/dist/src/dbos.d.ts.map +1 -1
- package/dist/src/dbos.js +41 -22
- package/dist/src/dbos.js.map +1 -1
- package/dist/src/error.d.ts +7 -0
- package/dist/src/error.d.ts.map +1 -1
- package/dist/src/error.js +15 -1
- 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/server.d.ts.map +1 -1
- package/dist/src/httpServer/server.js +8 -19
- package/dist/src/httpServer/server.js.map +1 -1
- package/dist/src/system_database.d.ts +84 -53
- package/dist/src/system_database.d.ts.map +1 -1
- package/dist/src/system_database.js +195 -296
- package/dist/src/system_database.js.map +1 -1
- package/dist/src/user_database.d.ts.map +1 -1
- package/dist/src/user_database.js +8 -2
- package/dist/src/user_database.js.map +1 -1
- package/dist/src/workflow.d.ts +3 -2
- package/dist/src/workflow.d.ts.map +1 -1
- package/dist/src/workflow.js +36 -26
- package/dist/src/workflow.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/src/dbos-runtime/db_connection.d.ts +0 -10
- package/dist/src/dbos-runtime/db_connection.d.ts.map +0 -1
- package/dist/src/dbos-runtime/db_connection.js +0 -59
- package/dist/src/dbos-runtime/db_connection.js.map +0 -1
- package/dist/src/dbos-runtime/db_wizard.d.ts +0 -3
- package/dist/src/dbos-runtime/db_wizard.d.ts.map +0 -1
- package/dist/src/dbos-runtime/db_wizard.js +0 -170
- package/dist/src/dbos-runtime/db_wizard.js.map +0 -1
@@ -4,17 +4,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
5
5
|
};
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
7
|
-
exports.PostgresSystemDatabase = exports.migrateSystemDatabase = void 0;
|
8
|
-
const serialize_error_1 = require("serialize-error");
|
7
|
+
exports.PostgresSystemDatabase = exports.migrateSystemDatabase = exports.DBOS_FUNCNAME_GETSTATUS = exports.DBOS_FUNCNAME_SLEEP = exports.DBOS_FUNCNAME_GETEVENT = exports.DBOS_FUNCNAME_SETEVENT = exports.DBOS_FUNCNAME_RECV = exports.DBOS_FUNCNAME_SEND = void 0;
|
9
8
|
const dbos_executor_1 = require("./dbos-executor");
|
10
9
|
const pg_1 = require("pg");
|
11
10
|
const error_1 = require("./error");
|
12
11
|
const workflow_1 = require("./workflow");
|
13
12
|
const utils_1 = require("./utils");
|
14
|
-
const context_1 = require("./context");
|
15
13
|
const knex_1 = __importDefault(require("knex"));
|
16
14
|
const path_1 = __importDefault(require("path"));
|
17
|
-
|
15
|
+
exports.DBOS_FUNCNAME_SEND = 'DBOS.send';
|
16
|
+
exports.DBOS_FUNCNAME_RECV = 'DBOS.recv';
|
17
|
+
exports.DBOS_FUNCNAME_SETEVENT = 'DBOS.setEvent';
|
18
|
+
exports.DBOS_FUNCNAME_GETEVENT = 'DBOS.getEvent';
|
19
|
+
exports.DBOS_FUNCNAME_SLEEP = 'DBOS.sleep';
|
20
|
+
exports.DBOS_FUNCNAME_GETSTATUS = 'getStatus';
|
18
21
|
async function migrateSystemDatabase(systemPoolConfig, logger) {
|
19
22
|
const migrationsDirectory = path_1.default.join((0, utils_1.findPackageRoot)(__dirname), 'migrations');
|
20
23
|
const knexConfig = {
|
@@ -48,17 +51,19 @@ class PostgresSystemDatabase {
|
|
48
51
|
notificationsClient = null;
|
49
52
|
notificationsMap = {};
|
50
53
|
workflowEventsMap = {};
|
51
|
-
static connectionTimeoutMillis = 10000; // 10 second timeout
|
52
54
|
constructor(pgPoolConfig, systemDatabaseName, logger, sysDbPoolSize) {
|
53
55
|
this.pgPoolConfig = pgPoolConfig;
|
54
56
|
this.systemDatabaseName = systemDatabaseName;
|
55
57
|
this.logger = logger;
|
56
58
|
this.sysDbPoolSize = sysDbPoolSize;
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
59
|
+
// Craft a db string from the app db string, replacing the database name:
|
60
|
+
const systemDbConnectionString = new URL(pgPoolConfig.connectionString);
|
61
|
+
systemDbConnectionString.pathname = `/${systemDatabaseName}`;
|
62
|
+
this.systemPoolConfig = {
|
63
|
+
connectionString: systemDbConnectionString.toString(),
|
64
|
+
// This sets the application_name column in pg_stat_activity
|
65
|
+
application_name: `dbos_transact_${utils_1.globalParams.executorID}_${utils_1.globalParams.appVersion}`,
|
66
|
+
};
|
62
67
|
this.pool = new pg_1.Pool(this.systemPoolConfig);
|
63
68
|
const knexConfig = {
|
64
69
|
client: 'pg',
|
@@ -201,83 +206,25 @@ class PostgresSystemDatabase {
|
|
201
206
|
}
|
202
207
|
return { args: utils_1.DBOSJSON.parse(rows[0].inputs), status };
|
203
208
|
}
|
204
|
-
async
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
executor_id,
|
218
|
-
application_id,
|
219
|
-
application_version,
|
220
|
-
created_at,
|
221
|
-
updated_at
|
222
|
-
) VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
223
|
-
ON CONFLICT (workflow_uuid)
|
224
|
-
DO UPDATE SET status=EXCLUDED.status, output=EXCLUDED.output, updated_at=EXCLUDED.updated_at;`, [
|
225
|
-
workflowUUID,
|
226
|
-
workflow_1.StatusString.SUCCESS,
|
227
|
-
status.workflowName,
|
228
|
-
status.workflowClassName,
|
229
|
-
status.workflowConfigName,
|
230
|
-
status.queueName,
|
231
|
-
status.authenticatedUser,
|
232
|
-
status.assumedRole,
|
233
|
-
utils_1.DBOSJSON.stringify(status.authenticatedRoles),
|
234
|
-
utils_1.DBOSJSON.stringify(status.request),
|
235
|
-
utils_1.DBOSJSON.stringify(status.output),
|
236
|
-
status.executorId,
|
237
|
-
status.applicationID,
|
238
|
-
status.applicationVersion,
|
239
|
-
status.createdAt,
|
240
|
-
Date.now(),
|
241
|
-
]);
|
209
|
+
async recordWorkflowStatusChange(workflowID, status, update, client) {
|
210
|
+
let rec = '';
|
211
|
+
if (update.resetRecoveryAttempts) {
|
212
|
+
rec = ' recovery_attempts = 0, ';
|
213
|
+
}
|
214
|
+
if (update.incrementRecoveryAttempts) {
|
215
|
+
rec = ' recovery_attempts = recovery_attempts + 1';
|
216
|
+
}
|
217
|
+
const wRes = await (client ?? this.pool).query(`UPDATE ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_status
|
218
|
+
SET ${rec} status=$2, output=$3, error=$4, updated_at=$5 WHERE workflow_uuid=$1`, [workflowID, status, update.output, update.error, Date.now()]);
|
219
|
+
if (wRes.rowCount !== 1) {
|
220
|
+
throw new error_1.DBOSWorkflowConflictUUIDError(`Attempt to record transition of nonexistent workflow ${workflowID}`);
|
221
|
+
}
|
242
222
|
}
|
243
|
-
async
|
244
|
-
await this.
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
class_name,
|
249
|
-
config_name,
|
250
|
-
queue_name,
|
251
|
-
authenticated_user,
|
252
|
-
assumed_role,
|
253
|
-
authenticated_roles,
|
254
|
-
request,
|
255
|
-
error,
|
256
|
-
executor_id,
|
257
|
-
application_id,
|
258
|
-
application_version,
|
259
|
-
created_at,
|
260
|
-
updated_at
|
261
|
-
) VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
262
|
-
ON CONFLICT (workflow_uuid)
|
263
|
-
DO UPDATE SET status=EXCLUDED.status, error=EXCLUDED.error, updated_at=EXCLUDED.updated_at;`, [
|
264
|
-
workflowUUID,
|
265
|
-
workflow_1.StatusString.ERROR,
|
266
|
-
status.workflowName,
|
267
|
-
status.workflowClassName,
|
268
|
-
status.workflowConfigName,
|
269
|
-
status.queueName,
|
270
|
-
status.authenticatedUser,
|
271
|
-
status.assumedRole,
|
272
|
-
utils_1.DBOSJSON.stringify(status.authenticatedRoles),
|
273
|
-
utils_1.DBOSJSON.stringify(status.request),
|
274
|
-
status.error,
|
275
|
-
status.executorId,
|
276
|
-
status.applicationID,
|
277
|
-
status.applicationVersion,
|
278
|
-
status.createdAt,
|
279
|
-
Date.now(),
|
280
|
-
]);
|
223
|
+
async recordWorkflowOutput(workflowID, status) {
|
224
|
+
await this.recordWorkflowStatusChange(workflowID, workflow_1.StatusString.SUCCESS, { output: status.output });
|
225
|
+
}
|
226
|
+
async recordWorkflowError(workflowID, status) {
|
227
|
+
await this.recordWorkflowStatusChange(workflowID, workflow_1.StatusString.ERROR, { error: status.error });
|
281
228
|
}
|
282
229
|
async getPendingWorkflows(executorID, appVersion) {
|
283
230
|
const getWorkflows = await this.pool.query(`SELECT workflow_uuid, queue_name FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_status WHERE status=$1 AND executor_id=$2 AND application_version=$3`, [workflow_1.StatusString.PENDING, executorID, appVersion]);
|
@@ -286,152 +233,111 @@ class PostgresSystemDatabase {
|
|
286
233
|
queueName: i.queue_name,
|
287
234
|
}));
|
288
235
|
}
|
289
|
-
async getWorkflowInputs(
|
290
|
-
const { rows } = await this.pool.query(`SELECT inputs FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_inputs WHERE workflow_uuid=$1`, [
|
236
|
+
async getWorkflowInputs(workflowID) {
|
237
|
+
const { rows } = await this.pool.query(`SELECT inputs FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_inputs WHERE workflow_uuid=$1`, [workflowID]);
|
291
238
|
if (rows.length === 0) {
|
292
239
|
return null;
|
293
240
|
}
|
294
241
|
return utils_1.DBOSJSON.parse(rows[0].inputs);
|
295
242
|
}
|
296
|
-
async
|
297
|
-
const { rows } = await this.pool.query(`SELECT output, error
|
243
|
+
async getOperationResult(workflowID, functionID, client) {
|
244
|
+
const { rows } = await (client ?? this.pool).query(`SELECT output, error, child_workflow_id, function_name
|
245
|
+
FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs
|
246
|
+
WHERE workflow_uuid=$1 AND function_id=$2`, [workflowID, functionID]);
|
298
247
|
if (rows.length === 0) {
|
299
|
-
return
|
300
|
-
}
|
301
|
-
else if (utils_1.DBOSJSON.parse(rows[0].error) !== null) {
|
302
|
-
throw (0, serialize_error_1.deserializeError)(utils_1.DBOSJSON.parse(rows[0].error));
|
303
|
-
}
|
304
|
-
else {
|
305
|
-
return utils_1.DBOSJSON.parse(rows[0].output);
|
306
|
-
}
|
307
|
-
}
|
308
|
-
async checkChildWorkflow(workflowUUID, functionID) {
|
309
|
-
const { rows } = await this.pool.query(`SELECT child_workflow_id FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs WHERE workflow_uuid=$1 AND function_id=$2`, [workflowUUID, functionID]);
|
310
|
-
if (rows.length > 0) {
|
311
|
-
return rows[0].child_workflow_id;
|
248
|
+
return {};
|
312
249
|
}
|
313
250
|
else {
|
314
|
-
return
|
251
|
+
return {
|
252
|
+
res: {
|
253
|
+
res: rows[0].output,
|
254
|
+
err: rows[0].error,
|
255
|
+
child: rows[0].child_workflow_id,
|
256
|
+
functionName: rows[0].function_name,
|
257
|
+
},
|
258
|
+
};
|
315
259
|
}
|
316
260
|
}
|
317
|
-
async
|
318
|
-
const { rows } = await this.pool.query(`SELECT
|
319
|
-
for (const row of rows) {
|
320
|
-
row.output = row.output !== null ? utils_1.DBOSJSON.parse(row.output) : null;
|
321
|
-
row.error = row.error !== null ? (0, serialize_error_1.deserializeError)(utils_1.DBOSJSON.parse(row.error)) : null;
|
322
|
-
}
|
261
|
+
async getAllOperationResults(workflowID) {
|
262
|
+
const { rows } = await this.pool.query(`SELECT * FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs WHERE workflow_uuid=$1`, [workflowID]);
|
323
263
|
return rows;
|
324
264
|
}
|
325
|
-
async
|
326
|
-
const serialOutput = utils_1.DBOSJSON.stringify(output);
|
265
|
+
async recordOperationResult(workflowID, functionID, rec, checkConflict, client) {
|
327
266
|
try {
|
328
|
-
await this.pool.query(`INSERT INTO ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs
|
267
|
+
await (client ?? this.pool).query(`INSERT INTO ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs
|
268
|
+
(workflow_uuid, function_id, output, error, function_name, child_workflow_id)
|
269
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
270
|
+
${checkConflict ? '' : ' ON CONFLICT DO NOTHING'}
|
271
|
+
;`, [
|
272
|
+
workflowID,
|
273
|
+
functionID,
|
274
|
+
rec.serialOutput ?? null,
|
275
|
+
rec.serialError ?? null,
|
276
|
+
rec.functionName,
|
277
|
+
rec.childWfId ?? null,
|
278
|
+
]);
|
329
279
|
}
|
330
280
|
catch (error) {
|
331
281
|
const err = error;
|
332
282
|
if (err.code === '40001' || err.code === '23505') {
|
333
283
|
// Serialization and primary key conflict (Postgres).
|
334
|
-
throw new error_1.DBOSWorkflowConflictUUIDError(
|
284
|
+
throw new error_1.DBOSWorkflowConflictUUIDError(workflowID);
|
335
285
|
}
|
336
286
|
else {
|
337
287
|
throw err;
|
338
288
|
}
|
339
289
|
}
|
340
290
|
}
|
341
|
-
async
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
// and do not raise a DBOSWorkflowConflictUUIDError
|
351
|
-
await this.pool.query(`INSERT INTO ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs (workflow_uuid, function_id, output, error, child_workflow_id, function_name) VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT DO NOTHING;`, [ctx.workflowId, functionID, output, error, resultWorkflowID, 'DBOS.getResult']);
|
352
|
-
}
|
353
|
-
async recordOperationError(workflowUUID, functionID, error, functionName) {
|
354
|
-
const serialErr = utils_1.DBOSJSON.stringify((0, serialize_error_1.serializeError)(error));
|
355
|
-
try {
|
356
|
-
await this.pool.query(`INSERT INTO ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs (workflow_uuid, function_id, error, function_name) VALUES ($1, $2, $3, $4);`, [workflowUUID, functionID, serialErr, functionName]);
|
357
|
-
}
|
358
|
-
catch (error) {
|
359
|
-
const err = error;
|
360
|
-
if (err.code === '40001' || err.code === '23505') {
|
361
|
-
// Serialization and primary key conflict (Postgres).
|
362
|
-
throw new error_1.DBOSWorkflowConflictUUIDError(workflowUUID);
|
363
|
-
}
|
364
|
-
else {
|
365
|
-
throw err;
|
291
|
+
async runAsStep(callback, functionName, workflowID, functionID, client) {
|
292
|
+
if (workflowID !== undefined && functionID !== undefined) {
|
293
|
+
const res = await this.getOperationResult(workflowID, functionID, client);
|
294
|
+
if (res.res !== undefined) {
|
295
|
+
if (res.res.functionName !== functionName) {
|
296
|
+
throw new error_1.DBOSUnexpectedStepError(workflowID, functionID, functionName, res.res.functionName);
|
297
|
+
}
|
298
|
+
await client?.query('ROLLBACK');
|
299
|
+
return res.res.res;
|
366
300
|
}
|
367
301
|
}
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
await this.pool.query(`INSERT INTO ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs (workflow_uuid, function_id, function_name, child_workflow_id) VALUES ($1, $2, $3, $4);`, [parentUUID, functionID, functionName, childUUID]);
|
372
|
-
}
|
373
|
-
catch (error) {
|
374
|
-
const err = error;
|
375
|
-
if (err.code === '40001' || err.code === '23505') {
|
376
|
-
// Serialization and primary key conflict (Postgres).
|
377
|
-
throw new error_1.DBOSWorkflowConflictUUIDError(parentUUID);
|
378
|
-
}
|
379
|
-
else {
|
380
|
-
throw err;
|
381
|
-
}
|
302
|
+
const serialOutput = await callback();
|
303
|
+
if (workflowID !== undefined && functionID !== undefined) {
|
304
|
+
await this.recordOperationResult(workflowID, functionID, { serialOutput, functionName }, true, client);
|
382
305
|
}
|
306
|
+
return serialOutput;
|
383
307
|
}
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
catch (error) {
|
392
|
-
const err = error;
|
393
|
-
if (err.code === '40001' || err.code === '23505') {
|
394
|
-
// Serialization and primary key conflict (Postgres).
|
395
|
-
throw new error_1.DBOSWorkflowConflictUUIDError(workflowUUID);
|
396
|
-
}
|
397
|
-
else {
|
398
|
-
throw err;
|
308
|
+
async durableSleepms(workflowID, functionID, durationMS) {
|
309
|
+
const curTime = Date.now();
|
310
|
+
let endTimeMs = curTime + durationMS;
|
311
|
+
const res = await this.getOperationResult(workflowID, functionID);
|
312
|
+
if (res.res !== undefined) {
|
313
|
+
if (res.res.functionName !== exports.DBOS_FUNCNAME_SLEEP) {
|
314
|
+
throw new error_1.DBOSUnexpectedStepError(workflowID, functionID, exports.DBOS_FUNCNAME_SLEEP, res.res.functionName);
|
399
315
|
}
|
400
|
-
|
401
|
-
}
|
402
|
-
async durableSleepms(workflowUUID, functionID, durationMS) {
|
403
|
-
const { rows } = await this.pool.query(`SELECT output FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs WHERE workflow_uuid=$1 AND function_id=$2`, [workflowUUID, functionID]);
|
404
|
-
if (rows.length > 0) {
|
405
|
-
const endTimeMs = utils_1.DBOSJSON.parse(rows[0].output);
|
406
|
-
return (0, utils_1.cancellableSleep)(Math.max(endTimeMs - Date.now(), 0));
|
316
|
+
endTimeMs = JSON.parse(res.res.res);
|
407
317
|
}
|
408
318
|
else {
|
409
|
-
|
410
|
-
await this.pool.query(`INSERT INTO ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs (workflow_uuid, function_id, output, function_name) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING;`, [workflowUUID, functionID, utils_1.DBOSJSON.stringify(endTimeMs), 'DBOS.sleep']);
|
411
|
-
return (0, utils_1.cancellableSleep)(Math.max(endTimeMs - Date.now(), 0));
|
319
|
+
await this.recordOperationResult(workflowID, functionID, { serialOutput: JSON.stringify(endTimeMs), functionName: exports.DBOS_FUNCNAME_SLEEP }, false);
|
412
320
|
}
|
321
|
+
return (0, utils_1.cancellableSleep)(Math.max(endTimeMs - curTime, 0));
|
413
322
|
}
|
414
323
|
nullTopic = '__null__topic__';
|
415
|
-
async send(
|
324
|
+
async send(workflowID, functionID, destinationID, message, topic) {
|
416
325
|
topic = topic ?? this.nullTopic;
|
417
326
|
const client = await this.pool.connect();
|
418
327
|
await client.query('BEGIN ISOLATION LEVEL READ COMMITTED');
|
419
328
|
try {
|
420
|
-
|
421
|
-
|
422
|
-
await client.query('
|
423
|
-
return;
|
424
|
-
}
|
425
|
-
await client.query(`INSERT INTO ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.notifications (destination_uuid, topic, message) VALUES ($1, $2, $3);`, [destinationUUID, topic, utils_1.DBOSJSON.stringify(message)]);
|
426
|
-
await this.recordNotificationOutput(client, workflowUUID, functionID, undefined, 'DBOS.send');
|
427
|
-
await client.query('COMMIT');
|
329
|
+
await this.runAsStep(async () => {
|
330
|
+
await client.query(`INSERT INTO ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.notifications (destination_uuid, topic, message) VALUES ($1, $2, $3);`, [destinationID, topic, message]);
|
331
|
+
await client.query('COMMIT');
|
332
|
+
return undefined;
|
333
|
+
}, exports.DBOS_FUNCNAME_SEND, workflowID, functionID, client);
|
428
334
|
}
|
429
335
|
catch (error) {
|
430
336
|
await client.query('ROLLBACK');
|
431
337
|
const err = error;
|
432
338
|
if (err.code === '23503') {
|
433
339
|
// Foreign key constraint violation (only expected for the INSERT query)
|
434
|
-
throw new error_1.DBOSNonExistentWorkflowError(`Sent to non-existent destination workflow UUID: ${
|
340
|
+
throw new error_1.DBOSNonExistentWorkflowError(`Sent to non-existent destination workflow UUID: ${destinationID}`);
|
435
341
|
}
|
436
342
|
else {
|
437
343
|
throw err;
|
@@ -441,27 +347,30 @@ class PostgresSystemDatabase {
|
|
441
347
|
client.release();
|
442
348
|
}
|
443
349
|
}
|
444
|
-
async recv(
|
350
|
+
async recv(workflowID, functionID, timeoutFunctionID, topic, timeoutSeconds = dbos_executor_1.DBOSExecutor.defaultNotificationTimeoutSec) {
|
445
351
|
topic = topic ?? this.nullTopic;
|
446
352
|
// First, check for previous executions.
|
447
|
-
const
|
448
|
-
if (
|
449
|
-
|
353
|
+
const res = await this.getOperationResult(workflowID, functionID);
|
354
|
+
if (res.res) {
|
355
|
+
if (res.res.functionName !== exports.DBOS_FUNCNAME_RECV) {
|
356
|
+
throw new error_1.DBOSUnexpectedStepError(workflowID, functionID, exports.DBOS_FUNCNAME_RECV, res.res.functionName);
|
357
|
+
}
|
358
|
+
return res.res.res;
|
450
359
|
}
|
451
360
|
// Check if the key is already in the DB, then wait for the notification if it isn't.
|
452
|
-
const initRecvRows = (await this.pool.query(`SELECT topic FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.notifications WHERE destination_uuid=$1 AND topic=$2;`, [
|
361
|
+
const initRecvRows = (await this.pool.query(`SELECT topic FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.notifications WHERE destination_uuid=$1 AND topic=$2;`, [workflowID, topic])).rows;
|
453
362
|
if (initRecvRows.length === 0) {
|
454
363
|
// Then, register the key with the global notifications listener.
|
455
364
|
let resolveNotification;
|
456
365
|
const messagePromise = new Promise((resolve) => {
|
457
366
|
resolveNotification = resolve;
|
458
367
|
});
|
459
|
-
const payload = `${
|
368
|
+
const payload = `${workflowID}::${topic}`;
|
460
369
|
this.notificationsMap[payload] = resolveNotification; // The resolver assignment in the Promise definition runs synchronously.
|
461
370
|
let timeoutPromise = Promise.resolve();
|
462
371
|
let timeoutCancel = () => { };
|
463
372
|
try {
|
464
|
-
const { promise, cancel } = await this.durableSleepms(
|
373
|
+
const { promise, cancel } = await this.durableSleepms(workflowID, timeoutFunctionID, timeoutSeconds * 1000);
|
465
374
|
timeoutPromise = promise;
|
466
375
|
timeoutCancel = cancel;
|
467
376
|
}
|
@@ -498,11 +407,11 @@ class PostgresSystemDatabase {
|
|
498
407
|
WHERE notifications.destination_uuid = oldest_entry.destination_uuid
|
499
408
|
AND notifications.topic = oldest_entry.topic
|
500
409
|
AND notifications.created_at_epoch_ms = oldest_entry.created_at_epoch_ms
|
501
|
-
RETURNING notifications.*;`, [
|
410
|
+
RETURNING notifications.*;`, [workflowID, topic])).rows;
|
502
411
|
if (finalRecvRows.length > 0) {
|
503
|
-
message =
|
412
|
+
message = finalRecvRows[0].message;
|
504
413
|
}
|
505
|
-
await this.
|
414
|
+
await this.recordOperationResult(workflowID, functionID, { serialOutput: message, functionName: exports.DBOS_FUNCNAME_RECV }, true, client);
|
506
415
|
await client.query(`COMMIT`);
|
507
416
|
}
|
508
417
|
catch (e) {
|
@@ -515,22 +424,19 @@ class PostgresSystemDatabase {
|
|
515
424
|
}
|
516
425
|
return message;
|
517
426
|
}
|
518
|
-
async setEvent(
|
427
|
+
async setEvent(workflowID, functionID, key, message) {
|
519
428
|
const client = await this.pool.connect();
|
520
429
|
try {
|
521
430
|
await client.query('BEGIN ISOLATION LEVEL READ COMMITTED');
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
RETURNING workflow_uuid;`, [workflowUUID, key, utils_1.DBOSJSON.stringify(message)]));
|
532
|
-
await this.recordNotificationOutput(client, workflowUUID, functionID, undefined, 'DBOS.setEvent');
|
533
|
-
await client.query('COMMIT');
|
431
|
+
await this.runAsStep(async () => {
|
432
|
+
await client.query(`INSERT INTO ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_events (workflow_uuid, key, value)
|
433
|
+
VALUES ($1, $2, $3)
|
434
|
+
ON CONFLICT (workflow_uuid, key)
|
435
|
+
DO UPDATE SET value = $3
|
436
|
+
RETURNING workflow_uuid;`, [workflowID, key, message]);
|
437
|
+
await client.query('COMMIT');
|
438
|
+
return undefined;
|
439
|
+
}, exports.DBOS_FUNCNAME_SETEVENT, workflowID, functionID, client);
|
534
440
|
}
|
535
441
|
catch (e) {
|
536
442
|
this.logger.error(e);
|
@@ -541,20 +447,20 @@ class PostgresSystemDatabase {
|
|
541
447
|
client.release();
|
542
448
|
}
|
543
449
|
}
|
544
|
-
async getEvent(
|
450
|
+
async getEvent(workflowID, key, timeoutSeconds, callerWorkflow) {
|
545
451
|
// Check if the operation has been done before for OAOO (only do this inside a workflow).
|
546
452
|
if (callerWorkflow) {
|
547
|
-
const
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
return
|
453
|
+
const res = await this.getOperationResult(callerWorkflow.workflowID, callerWorkflow.functionID);
|
454
|
+
if (res.res !== undefined) {
|
455
|
+
if (res.res.functionName !== exports.DBOS_FUNCNAME_GETEVENT) {
|
456
|
+
throw new error_1.DBOSUnexpectedStepError(callerWorkflow.workflowID, callerWorkflow.functionID, exports.DBOS_FUNCNAME_GETEVENT, res.res.functionName);
|
457
|
+
}
|
458
|
+
return res.res.res;
|
553
459
|
}
|
554
460
|
}
|
555
461
|
// Get the return the value. if it's in the DB, otherwise return null.
|
556
462
|
let value = null;
|
557
|
-
const payloadKey = `${
|
463
|
+
const payloadKey = `${workflowID}::${key}`;
|
558
464
|
// Register the key with the global notifications listener first... we do not want to look in the DB first
|
559
465
|
// or that would cause a timing hole.
|
560
466
|
let resolveNotification;
|
@@ -567,9 +473,9 @@ class PostgresSystemDatabase {
|
|
567
473
|
const initRecvRows = (await this.pool.query(`
|
568
474
|
SELECT key, value
|
569
475
|
FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_events
|
570
|
-
WHERE workflow_uuid=$1 AND key=$2;`, [
|
476
|
+
WHERE workflow_uuid=$1 AND key=$2;`, [workflowID, key])).rows;
|
571
477
|
if (initRecvRows.length > 0) {
|
572
|
-
value =
|
478
|
+
value = initRecvRows[0].value;
|
573
479
|
}
|
574
480
|
else {
|
575
481
|
// If we have a callerWorkflow, we want a durable sleep, otherwise, not
|
@@ -577,7 +483,7 @@ class PostgresSystemDatabase {
|
|
577
483
|
let timeoutCancel = () => { };
|
578
484
|
if (callerWorkflow) {
|
579
485
|
try {
|
580
|
-
const { promise, cancel } = await this.durableSleepms(callerWorkflow.
|
486
|
+
const { promise, cancel } = await this.durableSleepms(callerWorkflow.workflowID, callerWorkflow.timeoutFunctionID ?? -1, timeoutSeconds * 1000);
|
581
487
|
timeoutPromise = promise;
|
582
488
|
timeoutCancel = cancel;
|
583
489
|
}
|
@@ -601,9 +507,9 @@ class PostgresSystemDatabase {
|
|
601
507
|
const finalRecvRows = (await this.pool.query(`
|
602
508
|
SELECT value
|
603
509
|
FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_events
|
604
|
-
WHERE workflow_uuid=$1 AND key=$2;`, [
|
510
|
+
WHERE workflow_uuid=$1 AND key=$2;`, [workflowID, key])).rows;
|
605
511
|
if (finalRecvRows.length > 0) {
|
606
|
-
value =
|
512
|
+
value = finalRecvRows[0].value;
|
607
513
|
}
|
608
514
|
}
|
609
515
|
}
|
@@ -612,26 +518,25 @@ class PostgresSystemDatabase {
|
|
612
518
|
}
|
613
519
|
// Record the output if it is inside a workflow.
|
614
520
|
if (callerWorkflow) {
|
615
|
-
await this.
|
521
|
+
await this.recordOperationResult(callerWorkflow.workflowID, callerWorkflow.functionID, {
|
522
|
+
serialOutput: value,
|
523
|
+
functionName: exports.DBOS_FUNCNAME_GETEVENT,
|
524
|
+
}, true);
|
616
525
|
}
|
617
526
|
return value;
|
618
527
|
}
|
619
|
-
async setWorkflowStatus(
|
620
|
-
await this.
|
621
|
-
if (resetRecoveryAttempts) {
|
622
|
-
await this.pool.query(`UPDATE ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_status SET recovery_attempts=0 WHERE workflow_uuid=$1`, [workflowUUID]);
|
623
|
-
}
|
528
|
+
async setWorkflowStatus(workflowID, status, resetRecoveryAttempts) {
|
529
|
+
await this.recordWorkflowStatusChange(workflowID, status, { resetRecoveryAttempts });
|
624
530
|
}
|
625
|
-
async cancelWorkflow(
|
531
|
+
async cancelWorkflow(workflowID) {
|
626
532
|
const client = await this.pool.connect();
|
627
533
|
try {
|
628
534
|
await client.query('BEGIN');
|
629
535
|
// Remove workflow from queues table
|
630
536
|
await client.query(`DELETE FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_queue
|
631
|
-
WHERE workflow_uuid = $1`, [
|
632
|
-
|
633
|
-
|
634
|
-
WHERE workflow_uuid = $2`, [workflow_1.StatusString.CANCELLED, workflowUUID]);
|
537
|
+
WHERE workflow_uuid = $1`, [workflowID]);
|
538
|
+
// Should we check if it is incomplete first?
|
539
|
+
await this.recordWorkflowStatusChange(workflowID, workflow_1.StatusString.CANCELLED, {}, client);
|
635
540
|
await client.query('COMMIT');
|
636
541
|
}
|
637
542
|
catch (error) {
|
@@ -642,13 +547,13 @@ class PostgresSystemDatabase {
|
|
642
547
|
client.release();
|
643
548
|
}
|
644
549
|
}
|
645
|
-
async resumeWorkflow(
|
550
|
+
async resumeWorkflow(workflowID) {
|
646
551
|
const client = await this.pool.connect();
|
647
552
|
try {
|
648
553
|
await client.query('BEGIN');
|
649
554
|
// Check workflow status. If it is complete, do nothing.
|
650
555
|
const statusResult = await client.query(`SELECT status FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_status
|
651
|
-
WHERE workflow_uuid = $1`, [
|
556
|
+
WHERE workflow_uuid = $1`, [workflowID]);
|
652
557
|
if (statusResult.rows.length === 0 ||
|
653
558
|
statusResult.rows[0].status === workflow_1.StatusString.SUCCESS ||
|
654
559
|
statusResult.rows[0].status === workflow_1.StatusString.ERROR) {
|
@@ -657,11 +562,9 @@ class PostgresSystemDatabase {
|
|
657
562
|
}
|
658
563
|
// Remove the workflow from the queues table so resume can safely be called on an ENQUEUED workflow
|
659
564
|
await client.query(`DELETE FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_queue
|
660
|
-
WHERE workflow_uuid = $1`, [
|
565
|
+
WHERE workflow_uuid = $1`, [workflowID]);
|
661
566
|
// Update status to pending and reset recovery attempts
|
662
|
-
await
|
663
|
-
SET status = $1, recovery_attempts = 0
|
664
|
-
WHERE workflow_uuid = $2`, [workflow_1.StatusString.PENDING, workflowUUID]);
|
567
|
+
await this.recordWorkflowStatusChange(workflowID, workflow_1.StatusString.PENDING, { resetRecoveryAttempts: true }, client);
|
665
568
|
await client.query('COMMIT');
|
666
569
|
}
|
667
570
|
catch (error) {
|
@@ -672,8 +575,8 @@ class PostgresSystemDatabase {
|
|
672
575
|
client.release();
|
673
576
|
}
|
674
577
|
}
|
675
|
-
async getWorkflowStatus(
|
676
|
-
const internalStatus = await this.getWorkflowStatusInternal(
|
578
|
+
async getWorkflowStatus(workflowID, callerID, callerFN) {
|
579
|
+
const internalStatus = await this.getWorkflowStatusInternal(workflowID, callerID, callerFN);
|
677
580
|
if (internalStatus === null) {
|
678
581
|
return null;
|
679
582
|
}
|
@@ -690,63 +593,66 @@ class PostgresSystemDatabase {
|
|
690
593
|
executorId: internalStatus.executorId,
|
691
594
|
};
|
692
595
|
}
|
693
|
-
async getWorkflowStatusInternal(
|
596
|
+
async getWorkflowStatusInternal(workflowID, callerID, callerFN) {
|
694
597
|
// Check if the operation has been done before for OAOO (only do this inside a workflow).
|
695
|
-
const
|
696
|
-
|
697
|
-
|
698
|
-
newfunctionId = wfctx.functionIDGetIncrement();
|
699
|
-
const { rows } = await this.pool.query(`SELECT output FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs WHERE workflow_uuid=$1 AND function_id=$2 AND function_name=$3`, [callerUUID, newfunctionId, 'getStatus']);
|
598
|
+
const sv = await this.runAsStep(async () => {
|
599
|
+
const { rows } = await this.pool.query(`SELECT workflow_uuid, status, name, class_name, config_name, authenticated_user, assumed_role, authenticated_roles, request, queue_name, executor_id, created_at, updated_at, application_version, application_id, recovery_attempts FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_status WHERE workflow_uuid=$1`, [workflowID]);
|
600
|
+
let value = null;
|
700
601
|
if (rows.length > 0) {
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
}
|
729
|
-
// Record the output if it is inside a workflow.
|
730
|
-
if (callerUUID !== undefined && newfunctionId !== undefined) {
|
731
|
-
await this.recordOperationOutput(callerUUID, newfunctionId, value, 'getStatus');
|
732
|
-
}
|
733
|
-
return value;
|
734
|
-
}
|
735
|
-
async getWorkflowResult(workflowUUID) {
|
602
|
+
value = {
|
603
|
+
workflowUUID: rows[0].workflow_uuid,
|
604
|
+
status: rows[0].status,
|
605
|
+
workflowName: rows[0].name,
|
606
|
+
output: null,
|
607
|
+
error: null,
|
608
|
+
workflowClassName: rows[0].class_name || '',
|
609
|
+
workflowConfigName: rows[0].config_name || '',
|
610
|
+
queueName: rows[0].queue_name || undefined,
|
611
|
+
authenticatedUser: rows[0].authenticated_user,
|
612
|
+
assumedRole: rows[0].assumed_role,
|
613
|
+
authenticatedRoles: utils_1.DBOSJSON.parse(rows[0].authenticated_roles),
|
614
|
+
request: utils_1.DBOSJSON.parse(rows[0].request),
|
615
|
+
executorId: rows[0].executor_id,
|
616
|
+
createdAt: Number(rows[0].created_at),
|
617
|
+
updatedAt: Number(rows[0].updated_at),
|
618
|
+
applicationVersion: rows[0].application_version,
|
619
|
+
applicationID: rows[0].application_id,
|
620
|
+
recoveryAttempts: Number(rows[0].recovery_attempts),
|
621
|
+
maxRetries: 0,
|
622
|
+
};
|
623
|
+
}
|
624
|
+
return value ? JSON.stringify(value) : null;
|
625
|
+
}, exports.DBOS_FUNCNAME_GETSTATUS, callerID, callerFN);
|
626
|
+
return sv ? JSON.parse(sv) : null;
|
627
|
+
}
|
628
|
+
async awaitWorkflowResult(workflowID, timeoutms) {
|
736
629
|
const pollingIntervalMs = 1000;
|
630
|
+
const et = timeoutms !== undefined ? new Date().getTime() + timeoutms : undefined;
|
737
631
|
while (true) {
|
738
|
-
const { rows } = await this.pool.query(`SELECT status, output, error FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_status WHERE workflow_uuid=$1`, [
|
632
|
+
const { rows } = await this.pool.query(`SELECT status, output, error FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_status WHERE workflow_uuid=$1`, [workflowID]);
|
739
633
|
if (rows.length > 0) {
|
740
634
|
const status = rows[0].status;
|
741
635
|
if (status === workflow_1.StatusString.SUCCESS) {
|
742
|
-
return
|
636
|
+
return { res: rows[0].output };
|
743
637
|
}
|
744
638
|
else if (status === workflow_1.StatusString.ERROR) {
|
745
|
-
|
639
|
+
return { err: rows[0].error };
|
640
|
+
}
|
641
|
+
}
|
642
|
+
if (et !== undefined) {
|
643
|
+
const ct = new Date().getTime();
|
644
|
+
if (et > ct) {
|
645
|
+
await (0, utils_1.sleepms)(Math.min(pollingIntervalMs, et - ct));
|
646
|
+
}
|
647
|
+
else {
|
648
|
+
break;
|
746
649
|
}
|
747
650
|
}
|
748
|
-
|
651
|
+
else {
|
652
|
+
await (0, utils_1.sleepms)(pollingIntervalMs);
|
653
|
+
}
|
749
654
|
}
|
655
|
+
return undefined;
|
750
656
|
}
|
751
657
|
/* BACKGROUND PROCESSES */
|
752
658
|
/**
|
@@ -948,14 +854,7 @@ class PostgresSystemDatabase {
|
|
948
854
|
return false;
|
949
855
|
}
|
950
856
|
// Reset the status of the task to "ENQUEUED"
|
951
|
-
|
952
|
-
UPDATE ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_status
|
953
|
-
SET status = $2
|
954
|
-
WHERE workflow_uuid = $1;
|
955
|
-
`, [workflowId, workflow_1.StatusString.ENQUEUED]);
|
956
|
-
if (wsRes.rowCount === 0) {
|
957
|
-
throw new Error(`UNREACHABLE: Workflow ${workflowId} is found in the workflow_queue table but not found in the workflow_status table`);
|
958
|
-
}
|
857
|
+
await this.recordWorkflowStatusChange(workflowId, workflow_1.StatusString.ENQUEUED, {}, client);
|
959
858
|
await client.query('COMMIT');
|
960
859
|
return true;
|
961
860
|
}
|