dbos 2.4.0a1__tar.gz → 2.4.0a3__tar.gz

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.

Potentially problematic release.


This version of dbos might be problematic. Click here for more details.

Files changed (99) hide show
  1. {dbos-2.4.0a1 → dbos-2.4.0a3}/PKG-INFO +1 -1
  2. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_admin_server.py +2 -0
  3. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_app_db.py +2 -0
  4. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_client.py +6 -0
  5. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_conductor/conductor.py +2 -0
  6. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_conductor/protocol.py +39 -1
  7. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_core.py +3 -0
  8. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_dbos.py +10 -0
  9. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_migration.py +33 -1
  10. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_schemas/system_database.py +3 -0
  11. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_sys_db.py +97 -162
  12. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_workflow_commands.py +19 -15
  13. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/cli/cli.py +1 -1
  14. {dbos-2.4.0a1 → dbos-2.4.0a3}/pyproject.toml +1 -1
  15. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_admin_server.py +75 -12
  16. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_queue.py +8 -2
  17. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_workflow_introspection.py +30 -2
  18. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_workflow_management.py +13 -1
  19. {dbos-2.4.0a1 → dbos-2.4.0a3}/LICENSE +0 -0
  20. {dbos-2.4.0a1 → dbos-2.4.0a3}/README.md +0 -0
  21. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/__init__.py +0 -0
  22. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/__main__.py +0 -0
  23. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_classproperty.py +0 -0
  24. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_context.py +0 -0
  25. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_croniter.py +0 -0
  26. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_dbos_config.py +0 -0
  27. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_debouncer.py +0 -0
  28. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_debug.py +0 -0
  29. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_docker_pg_helper.py +0 -0
  30. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_error.py +0 -0
  31. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_event_loop.py +0 -0
  32. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_fastapi.py +0 -0
  33. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_flask.py +0 -0
  34. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_kafka.py +0 -0
  35. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_kafka_message.py +0 -0
  36. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_logger.py +0 -0
  37. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_outcome.py +0 -0
  38. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_queue.py +0 -0
  39. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_recovery.py +0 -0
  40. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_registrations.py +0 -0
  41. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_roles.py +0 -0
  42. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_scheduler.py +0 -0
  43. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_schemas/__init__.py +0 -0
  44. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_schemas/application_database.py +0 -0
  45. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_serialization.py +0 -0
  46. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_sys_db_postgres.py +0 -0
  47. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_sys_db_sqlite.py +0 -0
  48. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_templates/dbos-db-starter/README.md +0 -0
  49. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
  50. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_templates/dbos-db-starter/__package/main.py.dbos +0 -0
  51. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
  52. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
  53. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_templates/dbos-db-starter/migrations/create_table.py.dbos +0 -0
  54. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
  55. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_tracer.py +0 -0
  56. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/_utils.py +0 -0
  57. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/cli/_github_init.py +0 -0
  58. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/cli/_template_init.py +0 -0
  59. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/cli/migration.py +0 -0
  60. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/dbos-config.schema.json +0 -0
  61. {dbos-2.4.0a1 → dbos-2.4.0a3}/dbos/py.typed +0 -0
  62. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/__init__.py +0 -0
  63. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/atexit_no_ctor.py +0 -0
  64. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/atexit_no_launch.py +0 -0
  65. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/classdefs.py +0 -0
  66. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/client_collateral.py +0 -0
  67. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/client_worker.py +0 -0
  68. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/conftest.py +0 -0
  69. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/dupname_classdefs1.py +0 -0
  70. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/dupname_classdefsa.py +0 -0
  71. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/more_classdefs.py +0 -0
  72. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/queuedworkflow.py +0 -0
  73. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/script_without_fastapi.py +0 -0
  74. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_async.py +0 -0
  75. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_async_workflow_management.py +0 -0
  76. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_classdecorators.py +0 -0
  77. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_cli.py +0 -0
  78. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_client.py +0 -0
  79. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_concurrency.py +0 -0
  80. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_config.py +0 -0
  81. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_croniter.py +0 -0
  82. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_dbos.py +0 -0
  83. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_debouncer.py +0 -0
  84. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_debug.py +0 -0
  85. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_docker_secrets.py +0 -0
  86. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_failures.py +0 -0
  87. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_fastapi.py +0 -0
  88. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_fastapi_roles.py +0 -0
  89. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_flask.py +0 -0
  90. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_kafka.py +0 -0
  91. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_outcome.py +0 -0
  92. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_package.py +0 -0
  93. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_scheduler.py +0 -0
  94. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_schema_migration.py +0 -0
  95. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_singleton.py +0 -0
  96. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_spans.py +0 -0
  97. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_sqlalchemy.py +0 -0
  98. {dbos-2.4.0a1 → dbos-2.4.0a3}/tests/test_streaming.py +0 -0
  99. {dbos-2.4.0a1 → dbos-2.4.0a3}/version/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 2.4.0a1
3
+ Version: 2.4.0a3
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -338,6 +338,7 @@ class AdminRequestHandler(BaseHTTPRequestHandler):
338
338
  end_time=filters.get("end_time"),
339
339
  status=filters.get("status"),
340
340
  app_version=filters.get("application_version"),
341
+ forked_from=filters.get("forked_from"),
341
342
  name=filters.get("workflow_name"),
342
343
  limit=filters.get("limit"),
343
344
  offset=filters.get("offset"),
@@ -364,6 +365,7 @@ class AdminRequestHandler(BaseHTTPRequestHandler):
364
365
  start_time=filters.get("start_time"),
365
366
  end_time=filters.get("end_time"),
366
367
  status=filters.get("status"),
368
+ forked_from=filters.get("forked_from"),
367
369
  name=filters.get("workflow_name"),
368
370
  limit=filters.get("limit"),
369
371
  offset=filters.get("offset"),
@@ -201,6 +201,8 @@ class ApplicationDatabase(ABC):
201
201
  else row[3]
202
202
  ),
203
203
  child_workflow_id=None,
204
+ started_at_epoch_ms=None,
205
+ completed_at_epoch_ms=None,
204
206
  )
205
207
  for row in rows
206
208
  ]
@@ -149,9 +149,11 @@ class DBOSClient:
149
149
  self._sys_db = SystemDatabase.create(
150
150
  system_database_url=system_database_url,
151
151
  engine_kwargs={
152
+ "connect_args": {"application_name": "dbos_transact_client"},
152
153
  "pool_timeout": 30,
153
154
  "max_overflow": 0,
154
155
  "pool_size": 2,
156
+ "pool_pre_ping": True,
155
157
  },
156
158
  engine=system_database_engine,
157
159
  schema=dbos_system_schema,
@@ -162,9 +164,11 @@ class DBOSClient:
162
164
  self._app_db = ApplicationDatabase.create(
163
165
  database_url=application_database_url,
164
166
  engine_kwargs={
167
+ "connect_args": {"application_name": "dbos_transact_client"},
165
168
  "pool_timeout": 30,
166
169
  "max_overflow": 0,
167
170
  "pool_size": 2,
171
+ "pool_pre_ping": True,
168
172
  },
169
173
  schema=dbos_system_schema,
170
174
  serializer=serializer,
@@ -234,6 +238,7 @@ class DBOSClient:
234
238
  ),
235
239
  "inputs": self._serializer.serialize(inputs),
236
240
  "queue_partition_key": enqueue_options_internal["queue_partition_key"],
241
+ "forked_from": None,
237
242
  }
238
243
 
239
244
  self._sys_db.init_workflow(
@@ -300,6 +305,7 @@ class DBOSClient:
300
305
  "priority": 0,
301
306
  "inputs": self._serializer.serialize({"args": (), "kwargs": {}}),
302
307
  "queue_partition_key": None,
308
+ "forked_from": None,
303
309
  }
304
310
  with self._sys_db.engine.begin() as conn:
305
311
  self._sys_db._insert_workflow_status(
@@ -233,6 +233,7 @@ class ConductorWebsocket(threading.Thread):
233
233
  end_time=body["end_time"],
234
234
  status=body["status"],
235
235
  app_version=body["application_version"],
236
+ forked_from=body["forked_from"],
236
237
  name=body["workflow_name"],
237
238
  limit=body["limit"],
238
239
  offset=body["offset"],
@@ -267,6 +268,7 @@ class ConductorWebsocket(threading.Thread):
267
268
  start_time=q_body["start_time"],
268
269
  end_time=q_body["end_time"],
269
270
  status=q_body["status"],
271
+ forked_from=q_body["forked_from"],
270
272
  name=q_body["workflow_name"],
271
273
  limit=q_body["limit"],
272
274
  offset=q_body["offset"],
@@ -118,6 +118,7 @@ class ListWorkflowsBody(TypedDict, total=False):
118
118
  end_time: Optional[str]
119
119
  status: Optional[str]
120
120
  application_version: Optional[str]
121
+ forked_from: Optional[str]
121
122
  limit: Optional[int]
122
123
  offset: Optional[int]
123
124
  sort_desc: bool
@@ -143,6 +144,12 @@ class WorkflowsOutput:
143
144
  QueueName: Optional[str]
144
145
  ApplicationVersion: Optional[str]
145
146
  ExecutorID: Optional[str]
147
+ WorkflowTimeoutMS: Optional[str]
148
+ WorkflowDeadlineEpochMS: Optional[str]
149
+ DeduplicationID: Optional[str]
150
+ Priority: Optional[str]
151
+ QueuePartitionKey: Optional[str]
152
+ ForkedFrom: Optional[str]
146
153
 
147
154
  @classmethod
148
155
  def from_workflow_information(cls, info: WorkflowStatus) -> "WorkflowsOutput":
@@ -152,12 +159,22 @@ class WorkflowsOutput:
152
159
  inputs_str = str(info.input) if info.input is not None else None
153
160
  outputs_str = str(info.output) if info.output is not None else None
154
161
  error_str = str(info.error) if info.error is not None else None
155
- request_str = None
156
162
  roles_str = (
157
163
  str(info.authenticated_roles)
158
164
  if info.authenticated_roles is not None
159
165
  else None
160
166
  )
167
+ workflow_timeout_ms_str = (
168
+ str(info.workflow_timeout_ms)
169
+ if info.workflow_timeout_ms is not None
170
+ else None
171
+ )
172
+ workflow_deadline_epoch_ms_str = (
173
+ str(info.workflow_deadline_epoch_ms)
174
+ if info.workflow_deadline_epoch_ms is not None
175
+ else None
176
+ )
177
+ priority_str = str(info.priority) if info.priority is not None else None
161
178
 
162
179
  return cls(
163
180
  WorkflowUUID=info.workflow_id,
@@ -176,6 +193,12 @@ class WorkflowsOutput:
176
193
  QueueName=info.queue_name,
177
194
  ApplicationVersion=info.app_version,
178
195
  ExecutorID=info.executor_id,
196
+ WorkflowTimeoutMS=workflow_timeout_ms_str,
197
+ WorkflowDeadlineEpochMS=workflow_deadline_epoch_ms_str,
198
+ DeduplicationID=info.deduplication_id,
199
+ Priority=priority_str,
200
+ QueuePartitionKey=info.queue_partition_key,
201
+ ForkedFrom=info.forked_from,
179
202
  )
180
203
 
181
204
 
@@ -186,14 +209,28 @@ class WorkflowSteps:
186
209
  output: Optional[str]
187
210
  error: Optional[str]
188
211
  child_workflow_id: Optional[str]
212
+ started_at_epoch_ms: Optional[str]
213
+ completed_at_epoch_ms: Optional[str]
189
214
 
190
215
  @classmethod
191
216
  def from_step_info(cls, info: StepInfo) -> "WorkflowSteps":
192
217
  output_str = str(info["output"]) if info["output"] is not None else None
193
218
  error_str = str(info["error"]) if info["error"] is not None else None
219
+ started_at_str = (
220
+ str(info["started_at_epoch_ms"])
221
+ if info["started_at_epoch_ms"] is not None
222
+ else None
223
+ )
224
+ completed_at_str = (
225
+ str(info["completed_at_epoch_ms"])
226
+ if info["completed_at_epoch_ms"] is not None
227
+ else None
228
+ )
194
229
  return cls(
195
230
  function_id=info["function_id"],
196
231
  function_name=info["function_name"],
232
+ started_at_epoch_ms=started_at_str,
233
+ completed_at_epoch_ms=completed_at_str,
197
234
  output=output_str,
198
235
  error=error_str,
199
236
  child_workflow_id=info["child_workflow_id"],
@@ -216,6 +253,7 @@ class ListQueuedWorkflowsBody(TypedDict, total=False):
216
253
  start_time: Optional[str]
217
254
  end_time: Optional[str]
218
255
  status: Optional[str]
256
+ forked_from: Optional[str]
219
257
  queue_name: Optional[str]
220
258
  limit: Optional[int]
221
259
  offset: Optional[int]
@@ -300,6 +300,7 @@ def _init_workflow(
300
300
  if enqueue_options is not None
301
301
  else None
302
302
  ),
303
+ "forked_from": None,
303
304
  }
304
305
 
305
306
  # Synchronously record the status and inputs for workflows
@@ -316,6 +317,7 @@ def _init_workflow(
316
317
  "function_name": wf_name,
317
318
  "output": None,
318
319
  "error": dbos._serializer.serialize(e),
320
+ "started_at_epoch_ms": int(time.time() * 1000),
319
321
  }
320
322
  dbos._sys_db.record_operation_result(result)
321
323
  raise
@@ -1118,6 +1120,7 @@ def decorate_step(
1118
1120
  "function_name": step_name,
1119
1121
  "output": None,
1120
1122
  "error": None,
1123
+ "started_at_epoch_ms": int(time.time() * 1000),
1121
1124
  }
1122
1125
 
1123
1126
  try:
@@ -1127,7 +1127,9 @@ class DBOS:
1127
1127
  end_time: Optional[str] = None,
1128
1128
  name: Optional[str] = None,
1129
1129
  app_version: Optional[str] = None,
1130
+ forked_from: Optional[str] = None,
1130
1131
  user: Optional[str] = None,
1132
+ queue_name: Optional[str] = None,
1131
1133
  limit: Optional[int] = None,
1132
1134
  offset: Optional[int] = None,
1133
1135
  sort_desc: bool = False,
@@ -1144,6 +1146,7 @@ class DBOS:
1144
1146
  end_time=end_time,
1145
1147
  name=name,
1146
1148
  app_version=app_version,
1149
+ forked_from=forked_from,
1147
1150
  user=user,
1148
1151
  limit=limit,
1149
1152
  offset=offset,
@@ -1151,6 +1154,7 @@ class DBOS:
1151
1154
  workflow_id_prefix=workflow_id_prefix,
1152
1155
  load_input=load_input,
1153
1156
  load_output=load_output,
1157
+ queue_name=queue_name,
1154
1158
  )
1155
1159
 
1156
1160
  return _get_dbos_instance()._sys_db.call_function_as_step(
@@ -1167,6 +1171,7 @@ class DBOS:
1167
1171
  end_time: Optional[str] = None,
1168
1172
  name: Optional[str] = None,
1169
1173
  app_version: Optional[str] = None,
1174
+ forked_from: Optional[str] = None,
1170
1175
  user: Optional[str] = None,
1171
1176
  limit: Optional[int] = None,
1172
1177
  offset: Optional[int] = None,
@@ -1184,6 +1189,7 @@ class DBOS:
1184
1189
  end_time=end_time,
1185
1190
  name=name,
1186
1191
  app_version=app_version,
1192
+ forked_from=forked_from,
1187
1193
  user=user,
1188
1194
  limit=limit,
1189
1195
  offset=offset,
@@ -1199,6 +1205,7 @@ class DBOS:
1199
1205
  *,
1200
1206
  queue_name: Optional[str] = None,
1201
1207
  status: Optional[Union[str, List[str]]] = None,
1208
+ forked_from: Optional[str] = None,
1202
1209
  start_time: Optional[str] = None,
1203
1210
  end_time: Optional[str] = None,
1204
1211
  name: Optional[str] = None,
@@ -1212,6 +1219,7 @@ class DBOS:
1212
1219
  _get_dbos_instance()._sys_db,
1213
1220
  queue_name=queue_name,
1214
1221
  status=status,
1222
+ forked_from=forked_from,
1215
1223
  start_time=start_time,
1216
1224
  end_time=end_time,
1217
1225
  name=name,
@@ -1231,6 +1239,7 @@ class DBOS:
1231
1239
  *,
1232
1240
  queue_name: Optional[str] = None,
1233
1241
  status: Optional[Union[str, List[str]]] = None,
1242
+ forked_from: Optional[str] = None,
1234
1243
  start_time: Optional[str] = None,
1235
1244
  end_time: Optional[str] = None,
1236
1245
  name: Optional[str] = None,
@@ -1244,6 +1253,7 @@ class DBOS:
1244
1253
  cls.list_queued_workflows,
1245
1254
  queue_name=queue_name,
1246
1255
  status=status,
1256
+ forked_from=forked_from,
1247
1257
  start_time=start_time,
1248
1258
  end_time=end_time,
1249
1259
  name=name,
@@ -215,11 +215,26 @@ create index "idx_workflow_status_queue_status_started" on \"{schema}\"."workflo
215
215
  """
216
216
 
217
217
 
218
+ def get_dbos_migration_four(schema: str) -> str:
219
+ return f"""
220
+ ALTER TABLE \"{schema}\".workflow_status ADD COLUMN forked_from TEXT;
221
+ CREATE INDEX "idx_workflow_status_forked_from" ON \"{schema}\"."workflow_status" ("forked_from")
222
+ """
223
+
224
+
225
+ def get_dbos_migration_five(schema: str) -> str:
226
+ return f"""
227
+ ALTER TABLE \"{schema}\".operation_outputs ADD COLUMN started_at_epoch_ms BIGINT, ADD COLUMN completed_at_epoch_ms BIGINT;
228
+ """
229
+
230
+
218
231
  def get_dbos_migrations(schema: str) -> list[str]:
219
232
  return [
220
233
  get_dbos_migration_one(schema),
221
234
  get_dbos_migration_two(schema),
222
235
  get_dbos_migration_three(schema),
236
+ get_dbos_migration_four(schema),
237
+ get_dbos_migration_five(schema),
223
238
  ]
224
239
 
225
240
 
@@ -318,4 +333,21 @@ CREATE INDEX "idx_workflow_status_queue_status_started"
318
333
  ON "workflow_status" ("queue_name", "status", "started_at_epoch_ms")
319
334
  """
320
335
 
321
- sqlite_migrations = [sqlite_migration_one, sqlite_migration_two, sqlite_migration_three]
336
+ sqlite_migration_four = """
337
+ ALTER TABLE workflow_status ADD COLUMN forked_from TEXT;
338
+ CREATE INDEX "idx_workflow_status_forked_from" ON "workflow_status" ("forked_from")
339
+ """
340
+
341
+ sqlite_migration_five = """
342
+ ALTER TABLE operation_outputs ADD COLUMN started_at_epoch_ms BIGINT;
343
+ ALTER TABLE operation_outputs ADD COLUMN completed_at_epoch_ms BIGINT;
344
+ """
345
+
346
+
347
+ sqlite_migrations = [
348
+ sqlite_migration_one,
349
+ sqlite_migration_two,
350
+ sqlite_migration_three,
351
+ sqlite_migration_four,
352
+ sqlite_migration_five,
353
+ ]
@@ -78,6 +78,7 @@ class SystemSchema:
78
78
  Column("inputs", Text()),
79
79
  Column("priority", Integer(), nullable=False, server_default=text("'0'::int")),
80
80
  Column("queue_partition_key", Text()),
81
+ Column("forked_from", Text()),
81
82
  Index("workflow_status_created_at_index", "created_at"),
82
83
  Index("workflow_status_executor_id_index", "executor_id"),
83
84
  Index("workflow_status_status_index", "status"),
@@ -104,6 +105,8 @@ class SystemSchema:
104
105
  Column("output", Text, nullable=True),
105
106
  Column("error", Text, nullable=True),
106
107
  Column("child_workflow_id", Text, nullable=True),
108
+ Column("started_at_epoch_ms", BigInteger, nullable=True),
109
+ Column("completed_at_epoch_ms", BigInteger, nullable=True),
107
110
  PrimaryKeyConstraint("workflow_uuid", "function_id"),
108
111
  )
109
112