dbos 2.3.0a5__tar.gz → 2.4.0a2__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.3.0a5 → dbos-2.4.0a2}/PKG-INFO +1 -1
  2. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_app_db.py +2 -0
  3. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_client.py +6 -0
  4. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_conductor/protocol.py +39 -1
  5. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_core.py +3 -0
  6. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_dbos.py +2 -0
  7. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_migration.py +47 -2
  8. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_schemas/system_database.py +3 -0
  9. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_sys_db.py +113 -159
  10. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_workflow_commands.py +15 -15
  11. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/cli/cli.py +1 -1
  12. {dbos-2.3.0a5 → dbos-2.4.0a2}/pyproject.toml +1 -1
  13. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_admin_server.py +70 -12
  14. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_queue.py +8 -2
  15. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_workflow_introspection.py +30 -2
  16. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_workflow_management.py +17 -1
  17. {dbos-2.3.0a5 → dbos-2.4.0a2}/LICENSE +0 -0
  18. {dbos-2.3.0a5 → dbos-2.4.0a2}/README.md +0 -0
  19. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/__init__.py +0 -0
  20. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/__main__.py +0 -0
  21. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_admin_server.py +0 -0
  22. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_classproperty.py +0 -0
  23. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_conductor/conductor.py +0 -0
  24. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_context.py +0 -0
  25. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_croniter.py +0 -0
  26. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_dbos_config.py +0 -0
  27. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_debouncer.py +0 -0
  28. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_debug.py +0 -0
  29. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_docker_pg_helper.py +0 -0
  30. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_error.py +0 -0
  31. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_event_loop.py +0 -0
  32. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_fastapi.py +0 -0
  33. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_flask.py +0 -0
  34. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_kafka.py +0 -0
  35. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_kafka_message.py +0 -0
  36. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_logger.py +0 -0
  37. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_outcome.py +0 -0
  38. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_queue.py +0 -0
  39. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_recovery.py +0 -0
  40. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_registrations.py +0 -0
  41. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_roles.py +0 -0
  42. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_scheduler.py +0 -0
  43. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_schemas/__init__.py +0 -0
  44. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_schemas/application_database.py +0 -0
  45. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_serialization.py +0 -0
  46. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_sys_db_postgres.py +0 -0
  47. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_sys_db_sqlite.py +0 -0
  48. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_templates/dbos-db-starter/README.md +0 -0
  49. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
  50. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_templates/dbos-db-starter/__package/main.py.dbos +0 -0
  51. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
  52. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
  53. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_templates/dbos-db-starter/migrations/create_table.py.dbos +0 -0
  54. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
  55. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_tracer.py +0 -0
  56. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/_utils.py +0 -0
  57. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/cli/_github_init.py +0 -0
  58. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/cli/_template_init.py +0 -0
  59. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/cli/migration.py +0 -0
  60. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/dbos-config.schema.json +0 -0
  61. {dbos-2.3.0a5 → dbos-2.4.0a2}/dbos/py.typed +0 -0
  62. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/__init__.py +0 -0
  63. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/atexit_no_ctor.py +0 -0
  64. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/atexit_no_launch.py +0 -0
  65. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/classdefs.py +0 -0
  66. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/client_collateral.py +0 -0
  67. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/client_worker.py +0 -0
  68. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/conftest.py +0 -0
  69. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/dupname_classdefs1.py +0 -0
  70. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/dupname_classdefsa.py +0 -0
  71. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/more_classdefs.py +0 -0
  72. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/queuedworkflow.py +0 -0
  73. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/script_without_fastapi.py +0 -0
  74. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_async.py +0 -0
  75. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_async_workflow_management.py +0 -0
  76. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_classdecorators.py +0 -0
  77. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_cli.py +0 -0
  78. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_client.py +0 -0
  79. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_concurrency.py +0 -0
  80. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_config.py +0 -0
  81. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_croniter.py +0 -0
  82. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_dbos.py +0 -0
  83. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_debouncer.py +0 -0
  84. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_debug.py +0 -0
  85. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_docker_secrets.py +0 -0
  86. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_failures.py +0 -0
  87. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_fastapi.py +0 -0
  88. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_fastapi_roles.py +0 -0
  89. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_flask.py +0 -0
  90. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_kafka.py +0 -0
  91. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_outcome.py +0 -0
  92. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_package.py +0 -0
  93. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_scheduler.py +0 -0
  94. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_schema_migration.py +0 -0
  95. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_singleton.py +0 -0
  96. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_spans.py +0 -0
  97. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_sqlalchemy.py +0 -0
  98. {dbos-2.3.0a5 → dbos-2.4.0a2}/tests/test_streaming.py +0 -0
  99. {dbos-2.3.0a5 → dbos-2.4.0a2}/version/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 2.3.0a5
3
+ Version: 2.4.0a2
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -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(
@@ -143,6 +143,13 @@ class WorkflowsOutput:
143
143
  QueueName: Optional[str]
144
144
  ApplicationVersion: Optional[str]
145
145
  ExecutorID: Optional[str]
146
+ WorkflowTimeoutMS: Optional[str]
147
+ WorkflowDeadlineEpochMS: Optional[str]
148
+ DeduplicationID: Optional[str]
149
+ Priority: Optional[str]
150
+ QueuePartitionKey: Optional[str]
151
+ ForkedFrom: Optional[str]
152
+ ForkedTo: Optional[list[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,13 @@ 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,
202
+ ForkedTo=info.forked_to,
179
203
  )
180
204
 
181
205
 
@@ -186,14 +210,28 @@ class WorkflowSteps:
186
210
  output: Optional[str]
187
211
  error: Optional[str]
188
212
  child_workflow_id: Optional[str]
213
+ started_at_epoch_ms: Optional[str]
214
+ completed_at_epoch_ms: Optional[str]
189
215
 
190
216
  @classmethod
191
217
  def from_step_info(cls, info: StepInfo) -> "WorkflowSteps":
192
218
  output_str = str(info["output"]) if info["output"] is not None else None
193
219
  error_str = str(info["error"]) if info["error"] is not None else None
220
+ started_at_str = (
221
+ str(info["started_at_epoch_ms"])
222
+ if info["started_at_epoch_ms"] is not None
223
+ else None
224
+ )
225
+ completed_at_str = (
226
+ str(info["completed_at_epoch_ms"])
227
+ if info["completed_at_epoch_ms"] is not None
228
+ else None
229
+ )
194
230
  return cls(
195
231
  function_id=info["function_id"],
196
232
  function_name=info["function_name"],
233
+ started_at_epoch_ms=started_at_str,
234
+ completed_at_epoch_ms=completed_at_str,
197
235
  output=output_str,
198
236
  error=error_str,
199
237
  child_workflow_id=info["child_workflow_id"],
@@ -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:
@@ -1128,6 +1128,7 @@ class DBOS:
1128
1128
  name: Optional[str] = None,
1129
1129
  app_version: Optional[str] = None,
1130
1130
  user: Optional[str] = None,
1131
+ queue_name: Optional[str] = None,
1131
1132
  limit: Optional[int] = None,
1132
1133
  offset: Optional[int] = None,
1133
1134
  sort_desc: bool = False,
@@ -1151,6 +1152,7 @@ class DBOS:
1151
1152
  workflow_id_prefix=workflow_id_prefix,
1152
1153
  load_input=load_input,
1153
1154
  load_output=load_output,
1155
+ queue_name=queue_name,
1154
1156
  )
1155
1157
 
1156
1158
  return _get_dbos_instance()._sys_db.call_function_as_step(
@@ -209,8 +209,32 @@ ALTER TABLE \"{schema}\".workflow_status ADD COLUMN queue_partition_key TEXT;
209
209
  """
210
210
 
211
211
 
212
+ def get_dbos_migration_three(schema: str) -> str:
213
+ return f"""
214
+ create index "idx_workflow_status_queue_status_started" on \"{schema}\"."workflow_status" ("queue_name", "status", "started_at_epoch_ms")
215
+ """
216
+
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
+ """
222
+
223
+
224
+ def get_dbos_migration_five(schema: str) -> str:
225
+ return f"""
226
+ ALTER TABLE \"{schema}\".operation_outputs ADD COLUMN started_at_epoch_ms BIGINT, ADD COLUMN completed_at_epoch_ms BIGINT;
227
+ """
228
+
229
+
212
230
  def get_dbos_migrations(schema: str) -> list[str]:
213
- return [get_dbos_migration_one(schema), get_dbos_migration_two(schema)]
231
+ return [
232
+ get_dbos_migration_one(schema),
233
+ get_dbos_migration_two(schema),
234
+ get_dbos_migration_three(schema),
235
+ get_dbos_migration_four(schema),
236
+ get_dbos_migration_five(schema),
237
+ ]
214
238
 
215
239
 
216
240
  def get_sqlite_timestamp_expr() -> str:
@@ -303,4 +327,25 @@ sqlite_migration_two = """
303
327
  ALTER TABLE workflow_status ADD COLUMN queue_partition_key TEXT;
304
328
  """
305
329
 
306
- sqlite_migrations = [sqlite_migration_one, sqlite_migration_two]
330
+ sqlite_migration_three = """
331
+ CREATE INDEX "idx_workflow_status_queue_status_started"
332
+ ON "workflow_status" ("queue_name", "status", "started_at_epoch_ms")
333
+ """
334
+
335
+ sqlite_migration_four = """
336
+ ALTER TABLE workflow_status ADD COLUMN forked_from TEXT;
337
+ """
338
+
339
+ sqlite_migration_five = """
340
+ ALTER TABLE operation_outputs ADD COLUMN started_at_epoch_ms BIGINT;
341
+ ALTER TABLE operation_outputs ADD COLUMN completed_at_epoch_ms BIGINT;
342
+ """
343
+
344
+
345
+ sqlite_migrations = [
346
+ sqlite_migration_one,
347
+ sqlite_migration_two,
348
+ sqlite_migration_three,
349
+ sqlite_migration_four,
350
+ sqlite_migration_five,
351
+ ]
@@ -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