@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.
Files changed (69) hide show
  1. package/dbos-config.schema.json +0 -4
  2. package/dist/dbos-config.schema.json +0 -4
  3. package/dist/schemas/system_db_schema.d.ts +1 -0
  4. package/dist/schemas/system_db_schema.d.ts.map +1 -1
  5. package/dist/schemas/user_db_schema.d.ts +1 -0
  6. package/dist/schemas/user_db_schema.d.ts.map +1 -1
  7. package/dist/schemas/user_db_schema.js.map +1 -1
  8. package/dist/src/client.d.ts.map +1 -1
  9. package/dist/src/client.js +14 -17
  10. package/dist/src/client.js.map +1 -1
  11. package/dist/src/dbos-executor.d.ts +7 -4
  12. package/dist/src/dbos-executor.d.ts.map +1 -1
  13. package/dist/src/dbos-executor.js +132 -91
  14. package/dist/src/dbos-executor.js.map +1 -1
  15. package/dist/src/dbos-runtime/cli.d.ts.map +1 -1
  16. package/dist/src/dbos-runtime/cli.js +10 -0
  17. package/dist/src/dbos-runtime/cli.js.map +1 -1
  18. package/dist/src/dbos-runtime/config.d.ts +24 -2
  19. package/dist/src/dbos-runtime/config.d.ts.map +1 -1
  20. package/dist/src/dbos-runtime/config.js +151 -71
  21. package/dist/src/dbos-runtime/config.js.map +1 -1
  22. package/dist/src/dbos-runtime/docker_pg_helper.d.ts +21 -0
  23. package/dist/src/dbos-runtime/docker_pg_helper.d.ts.map +1 -0
  24. package/dist/src/dbos-runtime/docker_pg_helper.js +137 -0
  25. package/dist/src/dbos-runtime/docker_pg_helper.js.map +1 -0
  26. package/dist/src/dbos-runtime/migrate.d.ts.map +1 -1
  27. package/dist/src/dbos-runtime/migrate.js +8 -6
  28. package/dist/src/dbos-runtime/migrate.js.map +1 -1
  29. package/dist/src/dbos-runtime/runtime.d.ts.map +1 -1
  30. package/dist/src/dbos-runtime/runtime.js +0 -2
  31. package/dist/src/dbos-runtime/runtime.js.map +1 -1
  32. package/dist/src/dbos-runtime/workflow_management.d.ts +5 -4
  33. package/dist/src/dbos-runtime/workflow_management.d.ts.map +1 -1
  34. package/dist/src/dbos-runtime/workflow_management.js +14 -16
  35. package/dist/src/dbos-runtime/workflow_management.js.map +1 -1
  36. package/dist/src/dbos.d.ts +1 -0
  37. package/dist/src/dbos.d.ts.map +1 -1
  38. package/dist/src/dbos.js +41 -22
  39. package/dist/src/dbos.js.map +1 -1
  40. package/dist/src/error.d.ts +7 -0
  41. package/dist/src/error.d.ts.map +1 -1
  42. package/dist/src/error.js +15 -1
  43. package/dist/src/error.js.map +1 -1
  44. package/dist/src/eventreceiver.d.ts +1 -1
  45. package/dist/src/eventreceiver.d.ts.map +1 -1
  46. package/dist/src/httpServer/server.d.ts.map +1 -1
  47. package/dist/src/httpServer/server.js +8 -19
  48. package/dist/src/httpServer/server.js.map +1 -1
  49. package/dist/src/system_database.d.ts +84 -53
  50. package/dist/src/system_database.d.ts.map +1 -1
  51. package/dist/src/system_database.js +195 -296
  52. package/dist/src/system_database.js.map +1 -1
  53. package/dist/src/user_database.d.ts.map +1 -1
  54. package/dist/src/user_database.js +8 -2
  55. package/dist/src/user_database.js.map +1 -1
  56. package/dist/src/workflow.d.ts +3 -2
  57. package/dist/src/workflow.d.ts.map +1 -1
  58. package/dist/src/workflow.js +36 -26
  59. package/dist/src/workflow.js.map +1 -1
  60. package/dist/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +1 -1
  62. package/dist/src/dbos-runtime/db_connection.d.ts +0 -10
  63. package/dist/src/dbos-runtime/db_connection.d.ts.map +0 -1
  64. package/dist/src/dbos-runtime/db_connection.js +0 -59
  65. package/dist/src/dbos-runtime/db_connection.js.map +0 -1
  66. package/dist/src/dbos-runtime/db_wizard.d.ts +0 -3
  67. package/dist/src/dbos-runtime/db_wizard.d.ts.map +0 -1
  68. package/dist/src/dbos-runtime/db_wizard.js +0 -170
  69. 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
- const context_2 = require("./context");
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
- this.systemPoolConfig = { ...pgPoolConfig };
58
- this.systemPoolConfig.database = systemDatabaseName;
59
- this.systemPoolConfig.connectionTimeoutMillis = PostgresSystemDatabase.connectionTimeoutMillis;
60
- // This sets the application_name column in pg_stat_activity
61
- this.systemPoolConfig.application_name = `dbos_transact_${utils_1.globalParams.executorID}_${utils_1.globalParams.appVersion}`;
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 recordWorkflowOutput(workflowUUID, status) {
205
- await this.pool.query(`INSERT INTO ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_status (
206
- workflow_uuid,
207
- status,
208
- name,
209
- class_name,
210
- config_name,
211
- queue_name,
212
- authenticated_user,
213
- assumed_role,
214
- authenticated_roles,
215
- request,
216
- output,
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 recordWorkflowError(workflowUUID, status) {
244
- await this.pool.query(`INSERT INTO ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_status (
245
- workflow_uuid,
246
- status,
247
- name,
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(workflowUUID) {
290
- const { rows } = await this.pool.query(`SELECT inputs FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_inputs WHERE workflow_uuid=$1`, [workflowUUID]);
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 checkOperationOutput(workflowUUID, functionID) {
297
- const { rows } = await this.pool.query(`SELECT output, error FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs WHERE workflow_uuid=$1 AND function_id=$2`, [workflowUUID, functionID]);
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 dbos_executor_1.dbosNull;
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 null;
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 getWorkflowSteps(workflowUUID) {
318
- const { rows } = await this.pool.query(`SELECT function_id, function_name, output, error, child_workflow_id FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs WHERE workflow_uuid=$1`, [workflowUUID]);
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 recordOperationOutput(workflowUUID, functionID, output, functionName) {
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 (workflow_uuid, function_id, output, function_name) VALUES ($1, $2, $3, $4);`, [workflowUUID, functionID, serialOutput, functionName]);
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(workflowUUID);
284
+ throw new error_1.DBOSWorkflowConflictUUIDError(workflowID);
335
285
  }
336
286
  else {
337
287
  throw err;
338
288
  }
339
289
  }
340
290
  }
341
- async recordGetResult(resultWorkflowID, output, error) {
342
- const ctx = (0, context_1.getCurrentContextStore)();
343
- // Only record getResult called in workflow functions
344
- if (ctx === undefined || !(0, context_1.isInWorkflowCtx)(ctx)) {
345
- return;
346
- }
347
- // Record getResult as a step
348
- const functionID = (0, context_1.assertCurrentWorkflowContext)().functionIDGetIncrement();
349
- // Because there's no corresponding check, we do nothing on conflict
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
- async recordChildWorkflow(parentUUID, childUUID, functionID, functionName) {
370
- try {
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
- * Guard the operation, throwing an error if a conflicting execution is detected.
386
- */
387
- async recordNotificationOutput(client, workflowUUID, functionID, output, functionName) {
388
- try {
389
- await client.query(`INSERT INTO ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs (workflow_uuid, function_id, output, function_name) VALUES ($1, $2, $3, $4);`, [workflowUUID, functionID, utils_1.DBOSJSON.stringify(output), functionName]);
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
- const endTimeMs = Date.now() + durationMS;
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(workflowUUID, functionID, destinationUUID, message, topic) {
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
- const { rows } = await client.query(`SELECT output FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs WHERE workflow_uuid=$1 AND function_id=$2`, [workflowUUID, functionID]);
421
- if (rows.length > 0) {
422
- await client.query('ROLLBACK');
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: ${destinationUUID}`);
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(workflowUUID, functionID, timeoutFunctionID, topic, timeoutSeconds = dbos_executor_1.DBOSExecutor.defaultNotificationTimeoutSec) {
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 checkRows = (await this.pool.query(`SELECT output FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs WHERE workflow_uuid=$1 AND function_id=$2`, [workflowUUID, functionID])).rows;
448
- if (checkRows.length > 0) {
449
- return utils_1.DBOSJSON.parse(checkRows[0].output);
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;`, [workflowUUID, topic])).rows;
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 = `${workflowUUID}::${topic}`;
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(workflowUUID, timeoutFunctionID, timeoutSeconds * 1000);
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.*;`, [workflowUUID, topic])).rows;
410
+ RETURNING notifications.*;`, [workflowID, topic])).rows;
502
411
  if (finalRecvRows.length > 0) {
503
- message = utils_1.DBOSJSON.parse(finalRecvRows[0].message);
412
+ message = finalRecvRows[0].message;
504
413
  }
505
- await this.recordNotificationOutput(client, workflowUUID, functionID, message, 'DBOS.recv');
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(workflowUUID, functionID, key, message) {
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
- let { rows } = await client.query(`SELECT output FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs WHERE workflow_uuid=$1 AND function_id=$2`, [workflowUUID, functionID]);
523
- if (rows.length > 0) {
524
- await client.query('ROLLBACK');
525
- return;
526
- }
527
- ({ rows } = await client.query(`INSERT INTO ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_events (workflow_uuid, key, value)
528
- VALUES ($1, $2, $3)
529
- ON CONFLICT (workflow_uuid, key)
530
- DO UPDATE SET value = $3
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(workflowUUID, key, timeoutSeconds, callerWorkflow) {
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 { rows } = await this.pool.query(`
548
- SELECT output
549
- FROM ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.operation_outputs
550
- WHERE workflow_uuid=$1 AND function_id=$2`, [callerWorkflow.workflowUUID, callerWorkflow.functionID]);
551
- if (rows.length > 0) {
552
- return utils_1.DBOSJSON.parse(rows[0].output);
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 = `${workflowUUID}::${key}`;
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;`, [workflowUUID, key])).rows;
476
+ WHERE workflow_uuid=$1 AND key=$2;`, [workflowID, key])).rows;
571
477
  if (initRecvRows.length > 0) {
572
- value = utils_1.DBOSJSON.parse(initRecvRows[0].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.workflowUUID, callerWorkflow.timeoutFunctionID ?? -1, timeoutSeconds * 1000);
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;`, [workflowUUID, key])).rows;
510
+ WHERE workflow_uuid=$1 AND key=$2;`, [workflowID, key])).rows;
605
511
  if (finalRecvRows.length > 0) {
606
- value = utils_1.DBOSJSON.parse(finalRecvRows[0].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.recordOperationOutput(callerWorkflow.workflowUUID, callerWorkflow.functionID, value, 'DBOS.getEvent');
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(workflowUUID, status, resetRecoveryAttempts) {
620
- await this.pool.query(`UPDATE ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_status SET status=$1 WHERE workflow_uuid=$2`, [status, workflowUUID]);
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(workflowUUID) {
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`, [workflowUUID]);
632
- await client.query(`UPDATE ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_status
633
- SET status = $1
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(workflowUUID) {
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`, [workflowUUID]);
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`, [workflowUUID]);
565
+ WHERE workflow_uuid = $1`, [workflowID]);
661
566
  // Update status to pending and reset recovery attempts
662
- await client.query(`UPDATE ${dbos_executor_1.DBOSExecutor.systemDBSchemaName}.workflow_status
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(workflowUUID, callerUUID) {
676
- const internalStatus = await this.getWorkflowStatusInternal(workflowUUID, callerUUID);
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(workflowUUID, callerUUID) {
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 wfctx = (0, context_2.getCurrentDBOSContext)();
696
- let newfunctionId = undefined;
697
- if (callerUUID !== undefined && wfctx !== undefined) {
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
- return utils_1.DBOSJSON.parse(rows[0].output);
702
- }
703
- }
704
- 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`, [workflowUUID]);
705
- let value = null;
706
- if (rows.length > 0) {
707
- value = {
708
- workflowUUID: rows[0].workflow_uuid,
709
- status: rows[0].status,
710
- workflowName: rows[0].name,
711
- output: undefined,
712
- error: '',
713
- workflowClassName: rows[0].class_name || '',
714
- workflowConfigName: rows[0].config_name || '',
715
- queueName: rows[0].queue_name || undefined,
716
- authenticatedUser: rows[0].authenticated_user,
717
- assumedRole: rows[0].assumed_role,
718
- authenticatedRoles: utils_1.DBOSJSON.parse(rows[0].authenticated_roles),
719
- request: utils_1.DBOSJSON.parse(rows[0].request),
720
- executorId: rows[0].executor_id,
721
- createdAt: Number(rows[0].created_at),
722
- updatedAt: Number(rows[0].updated_at),
723
- applicationVersion: rows[0].application_version,
724
- applicationID: rows[0].application_id,
725
- recoveryAttempts: Number(rows[0].recovery_attempts),
726
- maxRetries: 0,
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`, [workflowUUID]);
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 utils_1.DBOSJSON.parse(rows[0].output);
636
+ return { res: rows[0].output };
743
637
  }
744
638
  else if (status === workflow_1.StatusString.ERROR) {
745
- throw (0, serialize_error_1.deserializeError)(utils_1.DBOSJSON.parse(rows[0].error));
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
- await (0, utils_1.sleepms)(pollingIntervalMs);
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
- const wsRes = await client.query(`
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
  }