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.
- {dbos-0.26.0a7 → dbos-0.26.0a9}/PKG-INFO +1 -1
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_app_db.py +14 -5
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_conductor/conductor.py +1 -1
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_core.py +12 -20
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_dbos.py +61 -67
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_dbos_config.py +4 -54
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_debug.py +1 -1
- dbos-0.26.0a9/dbos/_docker_pg_helper.py +191 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_error.py +13 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_sys_db.py +120 -55
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_workflow_commands.py +3 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/cli/cli.py +17 -1
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/dbos-config.schema.json +0 -4
- {dbos-0.26.0a7 → dbos-0.26.0a9}/pyproject.toml +2 -1
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/queuedworkflow.py +1 -1
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_classdecorators.py +8 -8
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_client.py +2 -2
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_config.py +6 -117
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_dbos.py +8 -8
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_debug.py +2 -2
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_failures.py +81 -38
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_fastapi.py +1 -1
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_flask.py +1 -1
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_queue.py +8 -6
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_scheduler.py +1 -1
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_workflow_introspection.py +34 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_workflow_management.py +2 -0
- dbos-0.26.0a7/dbos/_cloudutils/authentication.py +0 -163
- dbos-0.26.0a7/dbos/_cloudutils/cloudutils.py +0 -254
- dbos-0.26.0a7/dbos/_cloudutils/databases.py +0 -241
- dbos-0.26.0a7/dbos/_db_wizard.py +0 -220
- dbos-0.26.0a7/tests/test_dbwizard.py +0 -84
- {dbos-0.26.0a7 → dbos-0.26.0a9}/LICENSE +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/README.md +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/__init__.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/__main__.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_admin_server.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_classproperty.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_client.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_conductor/protocol.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_context.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_croniter.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_fastapi.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_flask.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_kafka.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_kafka_message.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_logger.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/env.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/script.py.mako +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/a3b18ad34abe_added_triggers.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/d76646551a6c_workflow_queue.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/eab0cc1d9a14_job_queue.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_outcome.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_queue.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_recovery.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_registrations.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_request.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_roles.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_scheduler.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_schemas/__init__.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_schemas/application_database.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_schemas/system_database.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_serialization.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/README.md +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/__package/main.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/alembic.ini +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_tracer.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/_utils.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/cli/_github_init.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/cli/_template_init.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/dbos/py.typed +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/__init__.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/atexit_no_ctor.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/atexit_no_launch.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/classdefs.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/client_collateral.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/client_worker.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/conftest.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/more_classdefs.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_admin_server.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_async.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_concurrency.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_croniter.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_docker_secrets.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_fastapi_roles.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_kafka.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_outcome.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_package.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_schema_migration.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_singleton.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_spans.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/tests/test_sqlalchemy.py +0 -0
- {dbos-0.26.0a7 → dbos-0.26.0a9}/version/__init__.py +0 -0
@@ -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,
|
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 ==
|
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":
|
189
|
-
"error":
|
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.
|
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
|
-
|
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
|
-
|
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":
|
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
|
-
|
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":
|
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
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
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
|
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
|
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
|
-
|
959
|
-
|
960
|
-
|
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
|
-
|
966
|
-
|
967
|
-
|
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
|
-
|
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
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
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
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
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,
|
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.
|
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
|
|