dbos 0.26.0a7__tar.gz → 0.26.0a9__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.
Files changed (105) hide show
  1. {dbos-0.26.0a7 → dbos-0.26.0a9}/PKG-INFO +1 -1
  2. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_app_db.py +14 -5
  3. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_conductor/conductor.py +1 -1
  4. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_core.py +12 -20
  5. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_dbos.py +61 -67
  6. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_dbos_config.py +4 -54
  7. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_debug.py +1 -1
  8. dbos-0.26.0a9/dbos/_docker_pg_helper.py +191 -0
  9. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_error.py +13 -0
  10. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_sys_db.py +120 -55
  11. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_workflow_commands.py +3 -0
  12. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/cli/cli.py +17 -1
  13. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/dbos-config.schema.json +0 -4
  14. {dbos-0.26.0a7 → dbos-0.26.0a9}/pyproject.toml +2 -1
  15. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/queuedworkflow.py +1 -1
  16. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_classdecorators.py +8 -8
  17. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_client.py +2 -2
  18. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_config.py +6 -117
  19. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_dbos.py +8 -8
  20. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_debug.py +2 -2
  21. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_failures.py +81 -38
  22. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_fastapi.py +1 -1
  23. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_flask.py +1 -1
  24. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_queue.py +8 -6
  25. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_scheduler.py +1 -1
  26. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_workflow_introspection.py +34 -0
  27. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_workflow_management.py +2 -0
  28. dbos-0.26.0a7/dbos/_cloudutils/authentication.py +0 -163
  29. dbos-0.26.0a7/dbos/_cloudutils/cloudutils.py +0 -254
  30. dbos-0.26.0a7/dbos/_cloudutils/databases.py +0 -241
  31. dbos-0.26.0a7/dbos/_db_wizard.py +0 -220
  32. dbos-0.26.0a7/tests/test_dbwizard.py +0 -84
  33. {dbos-0.26.0a7 → dbos-0.26.0a9}/LICENSE +0 -0
  34. {dbos-0.26.0a7 → dbos-0.26.0a9}/README.md +0 -0
  35. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/__init__.py +0 -0
  36. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/__main__.py +0 -0
  37. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_admin_server.py +0 -0
  38. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_classproperty.py +0 -0
  39. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_client.py +0 -0
  40. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_conductor/protocol.py +0 -0
  41. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_context.py +0 -0
  42. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_croniter.py +0 -0
  43. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_fastapi.py +0 -0
  44. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_flask.py +0 -0
  45. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_kafka.py +0 -0
  46. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_kafka_message.py +0 -0
  47. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_logger.py +0 -0
  48. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/env.py +0 -0
  49. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/script.py.mako +0 -0
  50. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -0
  51. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -0
  52. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py +0 -0
  53. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/a3b18ad34abe_added_triggers.py +0 -0
  54. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -0
  55. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/d76646551a6c_workflow_queue.py +0 -0
  56. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/eab0cc1d9a14_job_queue.py +0 -0
  57. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py +0 -0
  58. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_outcome.py +0 -0
  59. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_queue.py +0 -0
  60. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_recovery.py +0 -0
  61. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_registrations.py +0 -0
  62. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_request.py +0 -0
  63. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_roles.py +0 -0
  64. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_scheduler.py +0 -0
  65. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_schemas/__init__.py +0 -0
  66. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_schemas/application_database.py +0 -0
  67. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_schemas/system_database.py +0 -0
  68. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_serialization.py +0 -0
  69. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/README.md +0 -0
  70. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
  71. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/__package/main.py +0 -0
  72. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
  73. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/alembic.ini +0 -0
  74. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
  75. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -0
  76. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -0
  77. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +0 -0
  78. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
  79. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_tracer.py +0 -0
  80. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_utils.py +0 -0
  81. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/cli/_github_init.py +0 -0
  82. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/cli/_template_init.py +0 -0
  83. {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/py.typed +0 -0
  84. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/__init__.py +0 -0
  85. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/atexit_no_ctor.py +0 -0
  86. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/atexit_no_launch.py +0 -0
  87. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/classdefs.py +0 -0
  88. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/client_collateral.py +0 -0
  89. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/client_worker.py +0 -0
  90. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/conftest.py +0 -0
  91. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/more_classdefs.py +0 -0
  92. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_admin_server.py +0 -0
  93. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_async.py +0 -0
  94. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_concurrency.py +0 -0
  95. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_croniter.py +0 -0
  96. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_docker_secrets.py +0 -0
  97. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_fastapi_roles.py +0 -0
  98. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_kafka.py +0 -0
  99. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_outcome.py +0 -0
  100. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_package.py +0 -0
  101. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_schema_migration.py +0 -0
  102. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_singleton.py +0 -0
  103. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_spans.py +0 -0
  104. {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_sqlalchemy.py +0 -0
  105. {dbos-0.26.0a7 → dbos-0.26.0a9}/version/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.26.0a7
3
+ Version: 0.26.0a9
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -8,7 +8,7 @@ from sqlalchemy.orm import Session, sessionmaker
8
8
 
9
9
  from . import _serialization
10
10
  from ._dbos_config import ConfigFile, DatabaseConfig
11
- from ._error import DBOSWorkflowConflictIDError
11
+ from ._error import DBOSUnexpectedStepError, DBOSWorkflowConflictIDError
12
12
  from ._schemas.application_database import ApplicationSchema
13
13
  from ._sys_db import StepInfo
14
14
 
@@ -171,22 +171,31 @@ class ApplicationDatabase:
171
171
 
172
172
  @staticmethod
173
173
  def check_transaction_execution(
174
- session: Session, workflow_uuid: str, function_id: int
174
+ session: Session, workflow_id: str, function_id: int, function_name: str
175
175
  ) -> Optional[RecordedResult]:
176
176
  rows = session.execute(
177
177
  sa.select(
178
178
  ApplicationSchema.transaction_outputs.c.output,
179
179
  ApplicationSchema.transaction_outputs.c.error,
180
+ ApplicationSchema.transaction_outputs.c.function_name,
180
181
  ).where(
181
- ApplicationSchema.transaction_outputs.c.workflow_uuid == workflow_uuid,
182
+ ApplicationSchema.transaction_outputs.c.workflow_uuid == workflow_id,
182
183
  ApplicationSchema.transaction_outputs.c.function_id == function_id,
183
184
  )
184
185
  ).all()
185
186
  if len(rows) == 0:
186
187
  return None
188
+ output, error, recorded_function_name = rows[0][0], rows[0][1], rows[0][2]
189
+ if function_name != recorded_function_name:
190
+ raise DBOSUnexpectedStepError(
191
+ workflow_id=workflow_id,
192
+ step_id=function_id,
193
+ expected_name=function_name,
194
+ recorded_name=recorded_function_name,
195
+ )
187
196
  result: RecordedResult = {
188
- "output": rows[0][0],
189
- "error": rows[0][1],
197
+ "output": output,
198
+ "error": error,
190
199
  }
191
200
  return result
192
201
 
@@ -67,7 +67,7 @@ class ConductorWebsocket(threading.Thread):
67
67
  recovery_message = p.RecoveryRequest.from_json(message)
68
68
  success = True
69
69
  try:
70
- self.dbos.recover_pending_workflows(
70
+ self.dbos._recover_pending_workflows(
71
71
  recovery_message.executor_ids
72
72
  )
73
73
  except Exception as e:
@@ -52,6 +52,7 @@ from ._error import (
52
52
  DBOSMaxStepRetriesExceeded,
53
53
  DBOSNonExistentWorkflowError,
54
54
  DBOSRecoveryError,
55
+ DBOSUnexpectedStepError,
55
56
  DBOSWorkflowCancelledError,
56
57
  DBOSWorkflowConflictIDError,
57
58
  DBOSWorkflowFunctionNotFoundError,
@@ -783,7 +784,7 @@ def decorate_transaction(
783
784
  ) -> Callable[[F], F]:
784
785
  def decorator(func: F) -> F:
785
786
 
786
- transactionName = func.__qualname__
787
+ transaction_name = func.__qualname__
787
788
 
788
789
  def invoke_tx(*args: Any, **kwargs: Any) -> Any:
789
790
  if dbosreg.dbos is None:
@@ -791,13 +792,14 @@ def decorate_transaction(
791
792
  f"Function {func.__name__} invoked before DBOS initialized"
792
793
  )
793
794
 
795
+ dbos = dbosreg.dbos
794
796
  ctx = assert_current_dbos_context()
795
- if dbosreg.is_workflow_cancelled(ctx.workflow_id):
797
+ status = dbos._sys_db.get_workflow_status(ctx.workflow_id)
798
+ if status and status["status"] == WorkflowStatusString.CANCELLED.value:
796
799
  raise DBOSWorkflowCancelledError(
797
800
  f"Workflow {ctx.workflow_id} is cancelled. Aborting transaction {func.__name__}."
798
801
  )
799
802
 
800
- dbos = dbosreg.dbos
801
803
  with dbos._app_db.sessionmaker() as session:
802
804
  attributes: TracedAttributes = {
803
805
  "name": func.__name__,
@@ -813,18 +815,12 @@ def decorate_transaction(
813
815
  "txn_snapshot": "", # TODO: add actual snapshot
814
816
  "executor_id": None,
815
817
  "txn_id": None,
816
- "function_name": transactionName,
818
+ "function_name": transaction_name,
817
819
  }
818
820
  retry_wait_seconds = 0.001
819
821
  backoff_factor = 1.5
820
822
  max_retry_wait_seconds = 2.0
821
823
  while True:
822
-
823
- if dbosreg.is_workflow_cancelled(ctx.workflow_id):
824
- raise DBOSWorkflowCancelledError(
825
- f"Workflow {ctx.workflow_id} is cancelled. Aborting transaction {func.__name__}."
826
- )
827
-
828
824
  has_recorded_error = False
829
825
  txn_error: Optional[Exception] = None
830
826
  try:
@@ -841,6 +837,7 @@ def decorate_transaction(
841
837
  session,
842
838
  ctx.workflow_id,
843
839
  ctx.function_id,
840
+ transaction_name,
844
841
  )
845
842
  )
846
843
  if dbos.debug_mode and recorded_output is None:
@@ -904,6 +901,8 @@ def decorate_transaction(
904
901
  )
905
902
  txn_error = invalid_request_error
906
903
  raise
904
+ except DBOSUnexpectedStepError:
905
+ raise
907
906
  except Exception as error:
908
907
  txn_error = error
909
908
  raise
@@ -969,7 +968,7 @@ def decorate_step(
969
968
  ) -> Callable[[Callable[P, R]], Callable[P, R]]:
970
969
  def decorator(func: Callable[P, R]) -> Callable[P, R]:
971
970
 
972
- stepName = func.__qualname__
971
+ step_name = func.__qualname__
973
972
 
974
973
  def invoke_step(*args: Any, **kwargs: Any) -> Any:
975
974
  if dbosreg.dbos is None:
@@ -983,13 +982,6 @@ def decorate_step(
983
982
  "operationType": OperationType.STEP.value,
984
983
  }
985
984
 
986
- # Check if the workflow is cancelled
987
- ctx = assert_current_dbos_context()
988
- if dbosreg.is_workflow_cancelled(ctx.workflow_id):
989
- raise DBOSWorkflowCancelledError(
990
- f"Workflow {ctx.workflow_id} is cancelled. Aborting step {func.__name__}."
991
- )
992
-
993
985
  attempts = max_attempts if retries_allowed else 1
994
986
  max_retry_interval_seconds: float = 3600 # 1 Hour
995
987
 
@@ -1017,7 +1009,7 @@ def decorate_step(
1017
1009
  step_output: OperationResultInternal = {
1018
1010
  "workflow_uuid": ctx.workflow_id,
1019
1011
  "function_id": ctx.function_id,
1020
- "function_name": stepName,
1012
+ "function_name": step_name,
1021
1013
  "output": None,
1022
1014
  "error": None,
1023
1015
  }
@@ -1035,7 +1027,7 @@ def decorate_step(
1035
1027
  def check_existing_result() -> Union[NoResult, R]:
1036
1028
  ctx = assert_current_dbos_context()
1037
1029
  recorded_output = dbos._sys_db.check_operation_execution(
1038
- ctx.workflow_id, ctx.function_id
1030
+ ctx.workflow_id, ctx.function_id, step_name
1039
1031
  )
1040
1032
  if dbos.debug_mode and recorded_output is None:
1041
1033
  raise DBOSException("Step output not found in debug mode")
@@ -166,7 +166,6 @@ class DBOSRegistry:
166
166
  self.pollers: list[RegisteredJob] = []
167
167
  self.dbos: Optional[DBOS] = None
168
168
  self.config: Optional[ConfigFile] = None
169
- self.workflow_cancelled_map: dict[str, bool] = {}
170
169
 
171
170
  def register_wf_function(self, name: str, wrapped_func: F, functype: str) -> None:
172
171
  if name in self.function_type_map:
@@ -215,15 +214,6 @@ class DBOSRegistry:
215
214
  else:
216
215
  self.instance_info_map[fn] = inst
217
216
 
218
- def cancel_workflow(self, workflow_id: str) -> None:
219
- self.workflow_cancelled_map[workflow_id] = True
220
-
221
- def is_workflow_cancelled(self, workflow_id: str) -> bool:
222
- return self.workflow_cancelled_map.get(workflow_id, False)
223
-
224
- def clear_workflow_cancelled(self, workflow_id: str) -> None:
225
- self.workflow_cancelled_map.pop(workflow_id, None)
226
-
227
217
  def compute_app_version(self) -> str:
228
218
  """
229
219
  An application's version is computed from a hash of the source of its workflows.
@@ -740,32 +730,11 @@ class DBOS:
740
730
  @classmethod
741
731
  def get_workflow_status(cls, workflow_id: str) -> Optional[WorkflowStatus]:
742
732
  """Return the status of a workflow execution."""
743
- sys_db = _get_dbos_instance()._sys_db
744
- ctx = get_local_dbos_context()
745
- if ctx and ctx.is_within_workflow():
746
- ctx.function_id += 1
747
- res = sys_db.check_operation_execution(ctx.workflow_id, ctx.function_id)
748
- if res is not None:
749
- if res["output"]:
750
- resstat: WorkflowStatus = _serialization.deserialize(res["output"])
751
- return resstat
752
- else:
753
- raise DBOSException(
754
- "Workflow status record not found. This should not happen! \033[1m Hint: Check if your workflow is deterministic.\033[0m"
755
- )
756
- stat = get_workflow(_get_dbos_instance()._sys_db, workflow_id, True)
757
-
758
- if ctx and ctx.is_within_workflow():
759
- sys_db.record_operation_result(
760
- {
761
- "workflow_uuid": ctx.workflow_id,
762
- "function_id": ctx.function_id,
763
- "function_name": "DBOS.getStatus",
764
- "output": _serialization.serialize(stat),
765
- "error": None,
766
- }
767
- )
768
- return stat
733
+
734
+ def fn() -> Optional[WorkflowStatus]:
735
+ return get_workflow(_get_dbos_instance()._sys_db, workflow_id, True)
736
+
737
+ return _get_dbos_instance()._sys_db.call_function_as_step(fn, "DBOS.getStatus")
769
738
 
770
739
  @classmethod
771
740
  async def get_workflow_status_async(
@@ -941,12 +910,12 @@ class DBOS:
941
910
  )
942
911
 
943
912
  @classmethod
944
- def execute_workflow_id(cls, workflow_id: str) -> WorkflowHandle[Any]:
913
+ def _execute_workflow_id(cls, workflow_id: str) -> WorkflowHandle[Any]:
945
914
  """Execute a workflow by ID (for recovery)."""
946
915
  return execute_workflow_by_id(_get_dbos_instance(), workflow_id)
947
916
 
948
917
  @classmethod
949
- def recover_pending_workflows(
918
+ def _recover_pending_workflows(
950
919
  cls, executor_ids: List[str] = ["local"]
951
920
  ) -> List[WorkflowHandle[Any]]:
952
921
  """Find all PENDING workflows and execute them."""
@@ -955,22 +924,37 @@ class DBOS:
955
924
  @classmethod
956
925
  def cancel_workflow(cls, workflow_id: str) -> None:
957
926
  """Cancel a workflow by ID."""
958
- dbos_logger.info(f"Cancelling workflow: {workflow_id}")
959
- _get_dbos_instance()._sys_db.cancel_workflow(workflow_id)
960
- _get_or_create_dbos_registry().cancel_workflow(workflow_id)
927
+
928
+ def fn() -> None:
929
+ dbos_logger.info(f"Cancelling workflow: {workflow_id}")
930
+ _get_dbos_instance()._sys_db.cancel_workflow(workflow_id)
931
+
932
+ return _get_dbos_instance()._sys_db.call_function_as_step(
933
+ fn, "DBOS.cancelWorkflow"
934
+ )
961
935
 
962
936
  @classmethod
963
937
  def resume_workflow(cls, workflow_id: str) -> WorkflowHandle[Any]:
964
938
  """Resume a workflow by ID."""
965
- dbos_logger.info(f"Resuming workflow: {workflow_id}")
966
- _get_dbos_instance()._sys_db.resume_workflow(workflow_id)
967
- _get_or_create_dbos_registry().clear_workflow_cancelled(workflow_id)
939
+
940
+ def fn() -> None:
941
+ dbos_logger.info(f"Resuming workflow: {workflow_id}")
942
+ _get_dbos_instance()._sys_db.resume_workflow(workflow_id)
943
+
944
+ _get_dbos_instance()._sys_db.call_function_as_step(fn, "DBOS.resumeWorkflow")
968
945
  return cls.retrieve_workflow(workflow_id)
969
946
 
970
947
  @classmethod
971
948
  def restart_workflow(cls, workflow_id: str) -> WorkflowHandle[Any]:
972
949
  """Restart a workflow with a new workflow ID"""
973
- forked_workflow_id = _get_dbos_instance()._sys_db.fork_workflow(workflow_id)
950
+
951
+ def fn() -> str:
952
+ dbos_logger.info(f"Restarting workflow: {workflow_id}")
953
+ return _get_dbos_instance()._sys_db.fork_workflow(workflow_id)
954
+
955
+ forked_workflow_id = _get_dbos_instance()._sys_db.call_function_as_step(
956
+ fn, "DBOS.restartWorkflow"
957
+ )
974
958
  return cls.retrieve_workflow(forked_workflow_id)
975
959
 
976
960
  @classmethod
@@ -988,18 +972,23 @@ class DBOS:
988
972
  offset: Optional[int] = None,
989
973
  sort_desc: bool = False,
990
974
  ) -> List[WorkflowStatus]:
991
- return list_workflows(
992
- _get_dbos_instance()._sys_db,
993
- workflow_ids=workflow_ids,
994
- status=status,
995
- start_time=start_time,
996
- end_time=end_time,
997
- name=name,
998
- app_version=app_version,
999
- user=user,
1000
- limit=limit,
1001
- offset=offset,
1002
- sort_desc=sort_desc,
975
+ def fn() -> List[WorkflowStatus]:
976
+ return list_workflows(
977
+ _get_dbos_instance()._sys_db,
978
+ workflow_ids=workflow_ids,
979
+ status=status,
980
+ start_time=start_time,
981
+ end_time=end_time,
982
+ name=name,
983
+ app_version=app_version,
984
+ user=user,
985
+ limit=limit,
986
+ offset=offset,
987
+ sort_desc=sort_desc,
988
+ )
989
+
990
+ return _get_dbos_instance()._sys_db.call_function_as_step(
991
+ fn, "DBOS.listWorkflows"
1003
992
  )
1004
993
 
1005
994
  @classmethod
@@ -1015,16 +1004,21 @@ class DBOS:
1015
1004
  offset: Optional[int] = None,
1016
1005
  sort_desc: bool = False,
1017
1006
  ) -> List[WorkflowStatus]:
1018
- return list_queued_workflows(
1019
- _get_dbos_instance()._sys_db,
1020
- queue_name=queue_name,
1021
- status=status,
1022
- start_time=start_time,
1023
- end_time=end_time,
1024
- name=name,
1025
- limit=limit,
1026
- offset=offset,
1027
- sort_desc=sort_desc,
1007
+ def fn() -> List[WorkflowStatus]:
1008
+ return list_queued_workflows(
1009
+ _get_dbos_instance()._sys_db,
1010
+ queue_name=queue_name,
1011
+ status=status,
1012
+ start_time=start_time,
1013
+ end_time=end_time,
1014
+ name=name,
1015
+ limit=limit,
1016
+ offset=offset,
1017
+ sort_desc=sort_desc,
1018
+ )
1019
+
1020
+ return _get_dbos_instance()._sys_db.call_function_as_step(
1021
+ fn, "DBOS.listQueuedWorkflows"
1028
1022
  )
1029
1023
 
1030
1024
  @classproperty
@@ -15,7 +15,6 @@ from jsonschema import ValidationError, validate
15
15
  from rich import print
16
16
  from sqlalchemy import URL, make_url
17
17
 
18
- from ._db_wizard import db_wizard, load_db_connection
19
18
  from ._error import DBOSInitializationError
20
19
  from ._logger import dbos_logger
21
20
 
@@ -70,7 +69,6 @@ class DatabaseConfig(TypedDict, total=False):
70
69
  sys_db_pool_size: Optional[int]
71
70
  ssl: Optional[bool]
72
71
  ssl_ca: Optional[str]
73
- local_suffix: Optional[bool]
74
72
  migrate: Optional[List[str]]
75
73
  rollback: Optional[List[str]]
76
74
 
@@ -288,7 +286,6 @@ def load_config(
288
286
  config_file_path: str = DBOS_CONFIG_PATH,
289
287
  *,
290
288
  run_process_config: bool = True,
291
- use_db_wizard: bool = True,
292
289
  silent: bool = False,
293
290
  ) -> ConfigFile:
294
291
  """
@@ -339,13 +336,12 @@ def load_config(
339
336
 
340
337
  data = cast(ConfigFile, data)
341
338
  if run_process_config:
342
- data = process_config(data=data, use_db_wizard=use_db_wizard, silent=silent)
339
+ data = process_config(data=data, silent=silent)
343
340
  return data # type: ignore
344
341
 
345
342
 
346
343
  def process_config(
347
344
  *,
348
- use_db_wizard: bool = True,
349
345
  data: ConfigFile,
350
346
  silent: bool = False,
351
347
  ) -> ConfigFile:
@@ -372,22 +368,17 @@ def process_config(
372
368
  # database_url takes precedence over database config, but we need to preserve rollback and migrate if they exist
373
369
  migrate = data["database"].get("migrate", False)
374
370
  rollback = data["database"].get("rollback", False)
375
- local_suffix = data["database"].get("local_suffix", False)
376
371
  if data.get("database_url"):
377
372
  dbconfig = parse_database_url_to_dbconfig(cast(str, data["database_url"]))
378
373
  if migrate:
379
374
  dbconfig["migrate"] = cast(List[str], migrate)
380
375
  if rollback:
381
376
  dbconfig["rollback"] = cast(List[str], rollback)
382
- if local_suffix:
383
- dbconfig["local_suffix"] = cast(bool, local_suffix)
384
377
  data["database"] = dbconfig
385
378
 
386
379
  if "app_db_name" not in data["database"] or not (data["database"]["app_db_name"]):
387
380
  data["database"]["app_db_name"] = _app_name_to_db_name(data["name"])
388
381
 
389
- # Load the DB connection file. Use its values for missing connection parameters. Use defaults otherwise.
390
- db_connection = load_db_connection()
391
382
  connection_passed_in = data["database"].get("hostname", None) is not None
392
383
 
393
384
  dbos_dbport: Optional[int] = None
@@ -397,49 +388,22 @@ def process_config(
397
388
  dbos_dbport = int(dbport_env)
398
389
  except ValueError:
399
390
  pass
400
- dbos_dblocalsuffix: Optional[bool] = None
401
- dblocalsuffix_env = os.getenv("DBOS_DBLOCALSUFFIX")
402
- if dblocalsuffix_env:
403
- try:
404
- dbos_dblocalsuffix = dblocalsuffix_env.casefold() == "true".casefold()
405
- except ValueError:
406
- pass
407
391
 
408
392
  data["database"]["hostname"] = (
409
- os.getenv("DBOS_DBHOST")
410
- or data["database"].get("hostname")
411
- or db_connection.get("hostname")
412
- or "localhost"
393
+ os.getenv("DBOS_DBHOST") or data["database"].get("hostname") or "localhost"
413
394
  )
414
395
 
415
- data["database"]["port"] = (
416
- dbos_dbport or data["database"].get("port") or db_connection.get("port") or 5432
417
- )
396
+ data["database"]["port"] = dbos_dbport or data["database"].get("port") or 5432
418
397
  data["database"]["username"] = (
419
- os.getenv("DBOS_DBUSER")
420
- or data["database"].get("username")
421
- or db_connection.get("username")
422
- or "postgres"
398
+ os.getenv("DBOS_DBUSER") or data["database"].get("username") or "postgres"
423
399
  )
424
400
  data["database"]["password"] = (
425
401
  os.getenv("DBOS_DBPASSWORD")
426
402
  or data["database"].get("password")
427
- or db_connection.get("password")
428
403
  or os.environ.get("PGPASSWORD")
429
404
  or "dbos"
430
405
  )
431
406
 
432
- local_suffix = False
433
- dbcon_local_suffix = db_connection.get("local_suffix")
434
- if dbcon_local_suffix is not None:
435
- local_suffix = dbcon_local_suffix
436
- db_local_suffix = data["database"].get("local_suffix")
437
- if db_local_suffix is not None:
438
- local_suffix = db_local_suffix
439
- if dbos_dblocalsuffix is not None:
440
- local_suffix = dbos_dblocalsuffix
441
- data["database"]["local_suffix"] = local_suffix
442
-
443
407
  if not data["database"].get("app_db_pool_size"):
444
408
  data["database"]["app_db_pool_size"] = 20
445
409
  if not data["database"].get("sys_db_pool_size"):
@@ -454,10 +418,6 @@ def process_config(
454
418
  elif "run_admin_server" not in data["runtimeConfig"]:
455
419
  data["runtimeConfig"]["run_admin_server"] = True
456
420
 
457
- # Check the connectivity to the database and make sure it's properly configured
458
- # Note, never use db wizard if the DBOS is running in debug mode (i.e. DBOS_DEBUG_WORKFLOW_ID env var is set)
459
- debugWorkflowId = os.getenv("DBOS_DEBUG_WORKFLOW_ID")
460
-
461
421
  # Pretty-print where we've loaded database connection information from, respecting the log level
462
422
  if not silent and logs["logLevel"] == "INFO" or logs["logLevel"] == "DEBUG":
463
423
  d = data["database"]
@@ -470,21 +430,11 @@ def process_config(
470
430
  print(
471
431
  f"[bold blue]Using database connection string: {conn_string}[/bold blue]"
472
432
  )
473
- elif db_connection.get("hostname"):
474
- print(
475
- f"[bold blue]Loading database connection string from .dbos/db_connection: {conn_string}[/bold blue]"
476
- )
477
433
  else:
478
434
  print(
479
435
  f"[bold blue]Using default database connection string: {conn_string}[/bold blue]"
480
436
  )
481
437
 
482
- if use_db_wizard and debugWorkflowId is None:
483
- data = db_wizard(data)
484
-
485
- if "local_suffix" in data["database"] and data["database"]["local_suffix"]:
486
- data["database"]["app_db_name"] = f"{data['database']['app_db_name']}_local"
487
-
488
438
  # Return data as ConfigFile type
489
439
  return data
490
440
 
@@ -28,7 +28,7 @@ def debug_workflow(workflow_id: str, entrypoint: Union[str, PythonModule]) -> No
28
28
 
29
29
  DBOS.logger.info(f"Debugging workflow {workflow_id}...")
30
30
  DBOS.launch(debug_mode=True)
31
- handle = DBOS.execute_workflow_id(workflow_id)
31
+ handle = DBOS._execute_workflow_id(workflow_id)
32
32
  handle.get_result()
33
33
  DBOS.logger.info("Workflow Debugging complete. Exiting process.")
34
34