dbos 0.26.0a18__tar.gz → 0.26.0a19__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.0a18 → dbos-0.26.0a19}/PKG-INFO +1 -1
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_app_db.py +4 -1
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_client.py +31 -1
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_dbos.py +21 -30
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_workflow_commands.py +24 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/pyproject.toml +1 -1
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/client_collateral.py +15 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_client.py +53 -1
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_workflow_introspection.py +36 -66
- {dbos-0.26.0a18 → dbos-0.26.0a19}/LICENSE +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/README.md +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/__init__.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/__main__.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_admin_server.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_classproperty.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_conductor/conductor.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_conductor/protocol.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_context.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_core.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_croniter.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_dbos_config.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_debug.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_docker_pg_helper.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_error.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_event_loop.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_fastapi.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_flask.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_kafka.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_kafka_message.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_logger.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_migrations/env.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_migrations/script.py.mako +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_migrations/versions/a3b18ad34abe_added_triggers.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_migrations/versions/d76646551a6c_workflow_queue.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_migrations/versions/eab0cc1d9a14_job_queue.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_outcome.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_queue.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_recovery.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_registrations.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_request.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_roles.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_scheduler.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_schemas/__init__.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_schemas/application_database.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_schemas/system_database.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_serialization.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_sys_db.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_templates/dbos-db-starter/README.md +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_templates/dbos-db-starter/__package/main.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_templates/dbos-db-starter/alembic.ini +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_tracer.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_utils.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/cli/_github_init.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/cli/_template_init.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/cli/cli.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/dbos-config.schema.json +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/py.typed +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/__init__.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/atexit_no_ctor.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/atexit_no_launch.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/classdefs.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/client_worker.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/conftest.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/dupname_classdefs1.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/dupname_classdefsa.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/more_classdefs.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/queuedworkflow.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_admin_server.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_async.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_classdecorators.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_concurrency.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_config.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_croniter.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_dbos.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_debug.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_docker_secrets.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_failures.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_fastapi.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_fastapi_roles.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_flask.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_kafka.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_outcome.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_package.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_queue.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_scheduler.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_schema_migration.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_singleton.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_spans.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_sqlalchemy.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/tests/test_workflow_management.py +0 -0
- {dbos-0.26.0a18 → dbos-0.26.0a19}/version/__init__.py +0 -0
@@ -74,9 +74,12 @@ class ApplicationDatabase:
|
|
74
74
|
database["connectionTimeoutMillis"] / 1000
|
75
75
|
)
|
76
76
|
|
77
|
+
pool_size = database.get("app_db_pool_size")
|
78
|
+
if pool_size is None:
|
79
|
+
pool_size = 20
|
77
80
|
self.engine = sa.create_engine(
|
78
81
|
app_db_url,
|
79
|
-
pool_size=
|
82
|
+
pool_size=pool_size,
|
80
83
|
max_overflow=0,
|
81
84
|
pool_timeout=30,
|
82
85
|
connect_args=connect_args,
|
@@ -3,6 +3,8 @@ import sys
|
|
3
3
|
import uuid
|
4
4
|
from typing import Any, Generic, List, Optional, TypedDict, TypeVar
|
5
5
|
|
6
|
+
from dbos._app_db import ApplicationDatabase
|
7
|
+
|
6
8
|
if sys.version_info < (3, 11):
|
7
9
|
from typing_extensions import NotRequired
|
8
10
|
else:
|
@@ -14,11 +16,18 @@ from dbos._dbos_config import parse_database_url_to_dbconfig
|
|
14
16
|
from dbos._error import DBOSNonExistentWorkflowError
|
15
17
|
from dbos._registrations import DEFAULT_MAX_RECOVERY_ATTEMPTS
|
16
18
|
from dbos._serialization import WorkflowInputs
|
17
|
-
from dbos._sys_db import
|
19
|
+
from dbos._sys_db import (
|
20
|
+
StepInfo,
|
21
|
+
SystemDatabase,
|
22
|
+
WorkflowStatusInternal,
|
23
|
+
WorkflowStatusString,
|
24
|
+
)
|
18
25
|
from dbos._workflow_commands import (
|
19
26
|
WorkflowStatus,
|
27
|
+
fork_workflow,
|
20
28
|
get_workflow,
|
21
29
|
list_queued_workflows,
|
30
|
+
list_workflow_steps,
|
22
31
|
list_workflows,
|
23
32
|
)
|
24
33
|
|
@@ -82,6 +91,7 @@ class DBOSClient:
|
|
82
91
|
if system_database is not None:
|
83
92
|
db_config["sys_db_name"] = system_database
|
84
93
|
self._sys_db = SystemDatabase(db_config)
|
94
|
+
self._app_db = ApplicationDatabase(db_config)
|
85
95
|
|
86
96
|
def destroy(self) -> None:
|
87
97
|
self._sys_db.destroy()
|
@@ -321,3 +331,23 @@ class DBOSClient:
|
|
321
331
|
offset=offset,
|
322
332
|
sort_desc=sort_desc,
|
323
333
|
)
|
334
|
+
|
335
|
+
def list_workflow_steps(self, workflow_id: str) -> List[StepInfo]:
|
336
|
+
return list_workflow_steps(self._sys_db, self._app_db, workflow_id)
|
337
|
+
|
338
|
+
async def list_workflow_steps_async(self, workflow_id: str) -> List[StepInfo]:
|
339
|
+
return await asyncio.to_thread(self.list_workflow_steps, workflow_id)
|
340
|
+
|
341
|
+
def fork_workflow(self, workflow_id: str, start_step: int) -> WorkflowHandle[R]:
|
342
|
+
forked_workflow_id = fork_workflow(
|
343
|
+
self._sys_db, self._app_db, workflow_id, start_step
|
344
|
+
)
|
345
|
+
return WorkflowHandleClientPolling[R](forked_workflow_id, self._sys_db)
|
346
|
+
|
347
|
+
async def fork_workflow_async(
|
348
|
+
self, workflow_id: str, start_step: int
|
349
|
+
) -> WorkflowHandleAsync[R]:
|
350
|
+
forked_workflow_id = await asyncio.to_thread(
|
351
|
+
fork_workflow, self._sys_db, self._app_db, workflow_id, start_step
|
352
|
+
)
|
353
|
+
return WorkflowHandleClientAsyncPolling[R](forked_workflow_id, self._sys_db)
|
@@ -34,6 +34,7 @@ from dbos._conductor.conductor import ConductorWebsocket
|
|
34
34
|
from dbos._utils import INTERNAL_QUEUE_NAME, GlobalParams
|
35
35
|
from dbos._workflow_commands import (
|
36
36
|
WorkflowStatus,
|
37
|
+
fork_workflow,
|
37
38
|
list_queued_workflows,
|
38
39
|
list_workflows,
|
39
40
|
)
|
@@ -67,7 +68,7 @@ from ._registrations import (
|
|
67
68
|
)
|
68
69
|
from ._roles import default_required_roles, required_roles
|
69
70
|
from ._scheduler import ScheduledWorkflow, scheduled
|
70
|
-
from ._sys_db import reset_system_database
|
71
|
+
from ._sys_db import StepInfo, reset_system_database
|
71
72
|
from ._tracer import dbos_tracer
|
72
73
|
|
73
74
|
if TYPE_CHECKING:
|
@@ -113,7 +114,7 @@ from ._error import (
|
|
113
114
|
from ._event_loop import BackgroundEventLoop
|
114
115
|
from ._logger import add_otlp_to_all_loggers, config_logger, dbos_logger, init_logger
|
115
116
|
from ._sys_db import SystemDatabase
|
116
|
-
from ._workflow_commands import WorkflowStatus, get_workflow
|
117
|
+
from ._workflow_commands import WorkflowStatus, get_workflow, list_workflow_steps
|
117
118
|
|
118
119
|
# Most DBOS functions are just any callable F, so decorators / wrappers work on F
|
119
120
|
# There are cases where the parameters P and return value R should be separate
|
@@ -959,40 +960,19 @@ class DBOS:
|
|
959
960
|
@classmethod
|
960
961
|
def restart_workflow(cls, workflow_id: str) -> WorkflowHandle[Any]:
|
961
962
|
"""Restart a workflow with a new workflow ID"""
|
962
|
-
|
963
963
|
return cls.fork_workflow(workflow_id, 1)
|
964
964
|
|
965
965
|
@classmethod
|
966
|
-
def fork_workflow(
|
967
|
-
|
968
|
-
) -> WorkflowHandle[Any]:
|
969
|
-
"""Restart a workflow with a new workflow ID"""
|
970
|
-
|
971
|
-
def get_max_function_id(workflow_uuid: str) -> int:
|
972
|
-
max_transactions = (
|
973
|
-
_get_dbos_instance()._app_db.get_max_function_id(workflow_uuid) or 0
|
974
|
-
)
|
975
|
-
max_operations = (
|
976
|
-
_get_dbos_instance()._sys_db.get_max_function_id(workflow_uuid) or 0
|
977
|
-
)
|
978
|
-
return max(max_transactions, max_operations)
|
979
|
-
|
980
|
-
max_function_id = get_max_function_id(workflow_id)
|
981
|
-
if max_function_id > 0 and start_step > max_function_id:
|
982
|
-
raise DBOSException(
|
983
|
-
f"Cannot fork workflow {workflow_id} at step {start_step}. The workflow has {max_function_id} steps."
|
984
|
-
)
|
966
|
+
def fork_workflow(cls, workflow_id: str, start_step: int) -> WorkflowHandle[Any]:
|
967
|
+
"""Restart a workflow with a new workflow ID from a specific step"""
|
985
968
|
|
986
969
|
def fn() -> str:
|
987
|
-
forked_workflow_id = str(uuid.uuid4())
|
988
970
|
dbos_logger.info(f"Forking workflow: {workflow_id} from step {start_step}")
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
return _get_dbos_instance()._sys_db.fork_workflow(
|
995
|
-
workflow_id, forked_workflow_id, start_step
|
971
|
+
return fork_workflow(
|
972
|
+
_get_dbos_instance()._sys_db,
|
973
|
+
_get_dbos_instance()._app_db,
|
974
|
+
workflow_id,
|
975
|
+
start_step,
|
996
976
|
)
|
997
977
|
|
998
978
|
new_id = _get_dbos_instance()._sys_db.call_function_as_step(
|
@@ -1066,6 +1046,17 @@ class DBOS:
|
|
1066
1046
|
fn, "DBOS.listQueuedWorkflows"
|
1067
1047
|
)
|
1068
1048
|
|
1049
|
+
@classmethod
|
1050
|
+
def list_workflow_steps(cls, workflow_id: str) -> List[StepInfo]:
|
1051
|
+
def fn() -> List[StepInfo]:
|
1052
|
+
return list_workflow_steps(
|
1053
|
+
_get_dbos_instance()._sys_db, _get_dbos_instance()._app_db, workflow_id
|
1054
|
+
)
|
1055
|
+
|
1056
|
+
return _get_dbos_instance()._sys_db.call_function_as_step(
|
1057
|
+
fn, "DBOS.listWorkflowSteps"
|
1058
|
+
)
|
1059
|
+
|
1069
1060
|
@classproperty
|
1070
1061
|
def logger(cls) -> Logger:
|
1071
1062
|
"""Return the DBOS `Logger` for the current context."""
|
@@ -2,6 +2,8 @@ import json
|
|
2
2
|
import uuid
|
3
3
|
from typing import Any, List, Optional
|
4
4
|
|
5
|
+
from dbos._error import DBOSException
|
6
|
+
|
5
7
|
from . import _serialization
|
6
8
|
from ._app_db import ApplicationDatabase
|
7
9
|
from ._sys_db import (
|
@@ -185,3 +187,25 @@ def list_workflow_steps(
|
|
185
187
|
merged_steps = steps + transactions
|
186
188
|
merged_steps.sort(key=lambda step: step["function_id"])
|
187
189
|
return merged_steps
|
190
|
+
|
191
|
+
|
192
|
+
def fork_workflow(
|
193
|
+
sys_db: SystemDatabase,
|
194
|
+
app_db: ApplicationDatabase,
|
195
|
+
workflow_id: str,
|
196
|
+
start_step: int,
|
197
|
+
) -> str:
|
198
|
+
def get_max_function_id(workflow_uuid: str) -> int:
|
199
|
+
max_transactions = app_db.get_max_function_id(workflow_uuid) or 0
|
200
|
+
max_operations = sys_db.get_max_function_id(workflow_uuid) or 0
|
201
|
+
return max(max_transactions, max_operations)
|
202
|
+
|
203
|
+
max_function_id = get_max_function_id(workflow_id)
|
204
|
+
if max_function_id > 0 and start_step > max_function_id:
|
205
|
+
raise DBOSException(
|
206
|
+
f"Cannot fork workflow {workflow_id} from step {start_step}. The workflow has {max_function_id} steps."
|
207
|
+
)
|
208
|
+
forked_workflow_id = str(uuid.uuid4())
|
209
|
+
app_db.clone_workflow_transactions(workflow_id, forked_workflow_id, start_step)
|
210
|
+
sys_db.fork_workflow(workflow_id, forked_workflow_id, start_step)
|
211
|
+
return forked_workflow_id
|
@@ -36,3 +36,18 @@ def event_test(key: str, value: str, update: Optional[int] = None) -> str:
|
|
36
36
|
DBOS.sleep(update)
|
37
37
|
DBOS.set_event(key, f"updated-{value}")
|
38
38
|
return f"{key}-{value}"
|
39
|
+
|
40
|
+
|
41
|
+
@DBOS.transaction()
|
42
|
+
def test_txn(x: int) -> int:
|
43
|
+
return x
|
44
|
+
|
45
|
+
|
46
|
+
@DBOS.step()
|
47
|
+
def test_step(x: int) -> int:
|
48
|
+
return x
|
49
|
+
|
50
|
+
|
51
|
+
@DBOS.workflow()
|
52
|
+
def fork_test(x: int) -> int:
|
53
|
+
return test_txn(x) + test_step(x)
|
@@ -8,10 +8,11 @@ import time
|
|
8
8
|
import uuid
|
9
9
|
from typing import Any, Optional, TypedDict, cast
|
10
10
|
|
11
|
+
import pytest
|
11
12
|
import sqlalchemy as sa
|
12
13
|
|
13
14
|
from dbos import DBOS, ConfigFile, DBOSClient, EnqueueOptions, Queue, SetWorkflowID
|
14
|
-
from dbos._dbos import WorkflowHandle
|
15
|
+
from dbos._dbos import WorkflowHandle, WorkflowHandleAsync
|
15
16
|
from dbos._schemas.system_database import SystemSchema
|
16
17
|
from dbos._sys_db import SystemDatabase
|
17
18
|
from dbos._utils import GlobalParams
|
@@ -397,3 +398,54 @@ def test_client_retrieve_wf_done(client: DBOSClient, dbos: DBOS) -> None:
|
|
397
398
|
assert handle1.get_workflow_id() == handle2.get_workflow_id()
|
398
399
|
result2 = handle2.get_result()
|
399
400
|
assert result2 == message
|
401
|
+
|
402
|
+
|
403
|
+
def test_client_fork(dbos: DBOS, client: DBOSClient) -> None:
|
404
|
+
run_client_collateral()
|
405
|
+
|
406
|
+
options: EnqueueOptions = {
|
407
|
+
"queue_name": "test_queue",
|
408
|
+
"workflow_name": "fork_test",
|
409
|
+
}
|
410
|
+
|
411
|
+
input = 5
|
412
|
+
handle: WorkflowHandle[int] = client.enqueue(options, input)
|
413
|
+
assert handle.get_result() == input * 2
|
414
|
+
assert len(client.list_workflow_steps(handle.workflow_id)) == 2
|
415
|
+
|
416
|
+
forked_handle: WorkflowHandle[int] = client.fork_workflow(handle.workflow_id, 1)
|
417
|
+
assert forked_handle.workflow_id != handle.workflow_id
|
418
|
+
assert forked_handle.get_result() == input * 2
|
419
|
+
|
420
|
+
forked_handle = client.fork_workflow(handle.workflow_id, 2)
|
421
|
+
assert forked_handle.workflow_id != handle.workflow_id
|
422
|
+
assert forked_handle.get_result() == input * 2
|
423
|
+
|
424
|
+
assert len(client.list_workflows()) == 3
|
425
|
+
|
426
|
+
|
427
|
+
@pytest.mark.asyncio
|
428
|
+
async def test_client_fork_async(dbos: DBOS, client: DBOSClient) -> None:
|
429
|
+
run_client_collateral()
|
430
|
+
|
431
|
+
options: EnqueueOptions = {
|
432
|
+
"queue_name": "test_queue",
|
433
|
+
"workflow_name": "fork_test",
|
434
|
+
}
|
435
|
+
|
436
|
+
input = 5
|
437
|
+
handle: WorkflowHandleAsync[int] = await client.enqueue_async(options, input)
|
438
|
+
assert await handle.get_result() == input * 2
|
439
|
+
assert len(await client.list_workflow_steps_async(handle.workflow_id)) == 2
|
440
|
+
|
441
|
+
forked_handle: WorkflowHandleAsync[int] = await client.fork_workflow_async(
|
442
|
+
handle.workflow_id, 1
|
443
|
+
)
|
444
|
+
assert forked_handle.workflow_id != handle.workflow_id
|
445
|
+
assert await forked_handle.get_result() == input * 2
|
446
|
+
|
447
|
+
forked_handle = await client.fork_workflow_async(handle.workflow_id, 2)
|
448
|
+
assert forked_handle.workflow_id != handle.workflow_id
|
449
|
+
assert await forked_handle.get_result() == input * 2
|
450
|
+
|
451
|
+
assert len(await client.list_workflows_async()) == 3
|
@@ -188,9 +188,7 @@ def test_get_workflow(dbos: DBOS, config: ConfigFile, sys_db: SystemDatabase) ->
|
|
188
188
|
assert info.workflow_id == wfUuid, f"Expected workflow_uuid to be {wfUuid}"
|
189
189
|
|
190
190
|
|
191
|
-
def test_queued_workflows(
|
192
|
-
dbos: DBOS, sys_db: SystemDatabase, app_db: ApplicationDatabase
|
193
|
-
) -> None:
|
191
|
+
def test_queued_workflows(dbos: DBOS) -> None:
|
194
192
|
queued_steps = 5
|
195
193
|
step_events = [threading.Event() for _ in range(queued_steps)]
|
196
194
|
event = threading.Event()
|
@@ -295,7 +293,7 @@ def test_queued_workflows(
|
|
295
293
|
assert len(workflows) == 0
|
296
294
|
|
297
295
|
# Test the steps are listed properly
|
298
|
-
steps =
|
296
|
+
steps = DBOS.list_workflow_steps(handle.workflow_id)
|
299
297
|
assert len(steps) == queued_steps * 2
|
300
298
|
for i in range(queued_steps):
|
301
299
|
# Check the enqueues
|
@@ -314,7 +312,7 @@ def test_queued_workflows(
|
|
314
312
|
child_workflows = DBOS.list_workflows(name=f"<temp>.{blocking_step.__qualname__}")
|
315
313
|
assert (len(child_workflows)) == queued_steps
|
316
314
|
for i, c in enumerate(child_workflows):
|
317
|
-
steps =
|
315
|
+
steps = DBOS.list_workflow_steps(c.workflow_id)
|
318
316
|
assert len(steps) == 1
|
319
317
|
assert steps[0]["function_id"] == 1
|
320
318
|
assert steps[0]["function_name"] == blocking_step.__qualname__
|
@@ -323,9 +321,7 @@ def test_queued_workflows(
|
|
323
321
|
assert steps[0]["error"] is None
|
324
322
|
|
325
323
|
|
326
|
-
def test_list_2steps_sleep(
|
327
|
-
dbos: DBOS, sys_db: SystemDatabase, app_db: ApplicationDatabase
|
328
|
-
) -> None:
|
324
|
+
def test_list_2steps_sleep(dbos: DBOS) -> None:
|
329
325
|
|
330
326
|
@DBOS.workflow()
|
331
327
|
def simple_workflow() -> None:
|
@@ -346,16 +342,14 @@ def test_list_2steps_sleep(
|
|
346
342
|
with SetWorkflowID(wfid):
|
347
343
|
simple_workflow()
|
348
344
|
|
349
|
-
wfsteps =
|
345
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
350
346
|
assert len(wfsteps) == 3
|
351
347
|
assert wfsteps[0]["function_name"] == stepOne.__qualname__
|
352
348
|
assert wfsteps[1]["function_name"] == stepTwo.__qualname__
|
353
349
|
assert wfsteps[2]["function_name"] == "DBOS.sleep"
|
354
350
|
|
355
351
|
|
356
|
-
def test_send_recv(
|
357
|
-
dbos: DBOS, sys_db: SystemDatabase, app_db: ApplicationDatabase
|
358
|
-
) -> None:
|
352
|
+
def test_send_recv(dbos: DBOS) -> None:
|
359
353
|
|
360
354
|
@DBOS.workflow()
|
361
355
|
def send_workflow(target: str) -> None:
|
@@ -374,19 +368,17 @@ def test_send_recv(
|
|
374
368
|
with SetWorkflowID(wfid_s):
|
375
369
|
send_workflow(wfid_r)
|
376
370
|
|
377
|
-
wfsteps_send =
|
371
|
+
wfsteps_send = DBOS.list_workflow_steps(wfid_s)
|
378
372
|
assert len(wfsteps_send) == 1
|
379
373
|
assert wfsteps_send[0]["function_name"] == "DBOS.send"
|
380
374
|
|
381
|
-
wfsteps_recv =
|
375
|
+
wfsteps_recv = DBOS.list_workflow_steps(wfid_r)
|
382
376
|
assert len(wfsteps_recv) == 2
|
383
377
|
assert wfsteps_recv[1]["function_name"] == "DBOS.sleep"
|
384
378
|
assert wfsteps_recv[0]["function_name"] == "DBOS.recv"
|
385
379
|
|
386
380
|
|
387
|
-
def test_set_get_event(
|
388
|
-
dbos: DBOS, sys_db: SystemDatabase, app_db: ApplicationDatabase
|
389
|
-
) -> None:
|
381
|
+
def test_set_get_event(dbos: DBOS) -> None:
|
390
382
|
value = "Hello, World!"
|
391
383
|
|
392
384
|
@DBOS.workflow()
|
@@ -404,7 +396,7 @@ def test_set_get_event(
|
|
404
396
|
with SetWorkflowID(wfid):
|
405
397
|
assert set_get_workflow() == value
|
406
398
|
|
407
|
-
wfsteps =
|
399
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
408
400
|
assert len(wfsteps) == 5
|
409
401
|
assert wfsteps[0]["function_name"] == "DBOS.setEvent"
|
410
402
|
assert wfsteps[1]["function_name"] == stepOne.__qualname__
|
@@ -419,9 +411,7 @@ def test_set_get_event(
|
|
419
411
|
assert wfsteps[4]["error"] == None
|
420
412
|
|
421
413
|
|
422
|
-
def test_callchild_first_sync(
|
423
|
-
dbos: DBOS, sys_db: SystemDatabase, app_db: ApplicationDatabase
|
424
|
-
) -> None:
|
414
|
+
def test_callchild_first_sync(dbos: DBOS) -> None:
|
425
415
|
|
426
416
|
@DBOS.workflow()
|
427
417
|
def parentWorkflow() -> str:
|
@@ -446,7 +436,7 @@ def test_callchild_first_sync(
|
|
446
436
|
with SetWorkflowID(wfid):
|
447
437
|
child_id = parentWorkflow()
|
448
438
|
|
449
|
-
wfsteps =
|
439
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
450
440
|
assert len(wfsteps) == 4
|
451
441
|
assert wfsteps[0]["function_name"] == child_workflow.__qualname__
|
452
442
|
assert wfsteps[0]["child_workflow_id"] == child_id
|
@@ -461,9 +451,7 @@ def test_callchild_first_sync(
|
|
461
451
|
|
462
452
|
|
463
453
|
@pytest.mark.asyncio
|
464
|
-
async def test_callchild_direct_asyncio(
|
465
|
-
dbos: DBOS, sys_db: SystemDatabase, app_db: ApplicationDatabase
|
466
|
-
) -> None:
|
454
|
+
async def test_callchild_direct_asyncio(dbos: DBOS) -> None:
|
467
455
|
|
468
456
|
@DBOS.workflow()
|
469
457
|
async def parentWorkflow() -> str:
|
@@ -488,7 +476,7 @@ async def test_callchild_direct_asyncio(
|
|
488
476
|
with SetWorkflowID(wfid):
|
489
477
|
child_id = await parentWorkflow()
|
490
478
|
|
491
|
-
wfsteps =
|
479
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
492
480
|
assert len(wfsteps) == 4
|
493
481
|
assert wfsteps[0]["function_name"] == child_workflow.__qualname__
|
494
482
|
assert wfsteps[0]["child_workflow_id"] == child_id
|
@@ -502,9 +490,7 @@ async def test_callchild_direct_asyncio(
|
|
502
490
|
assert wfsteps[3]["function_name"] == stepTwo.__qualname__
|
503
491
|
|
504
492
|
|
505
|
-
def test_callchild_last_sync(
|
506
|
-
dbos: DBOS, sys_db: SystemDatabase, app_db: ApplicationDatabase
|
507
|
-
) -> None:
|
493
|
+
def test_callchild_last_sync(dbos: DBOS) -> None:
|
508
494
|
|
509
495
|
@DBOS.workflow()
|
510
496
|
def parentWorkflow() -> None:
|
@@ -529,7 +515,7 @@ def test_callchild_last_sync(
|
|
529
515
|
with SetWorkflowID(wfid):
|
530
516
|
parentWorkflow()
|
531
517
|
|
532
|
-
wfsteps =
|
518
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
533
519
|
assert len(wfsteps) == 4
|
534
520
|
assert wfsteps[0]["function_name"] == stepOne.__qualname__
|
535
521
|
assert wfsteps[1]["function_name"] == stepTwo.__qualname__
|
@@ -537,9 +523,7 @@ def test_callchild_last_sync(
|
|
537
523
|
assert wfsteps[3]["function_name"] == "DBOS.getResult"
|
538
524
|
|
539
525
|
|
540
|
-
def test_callchild_first_async_thread(
|
541
|
-
dbos: DBOS, sys_db: SystemDatabase, app_db: ApplicationDatabase
|
542
|
-
) -> None:
|
526
|
+
def test_callchild_first_async_thread(dbos: DBOS) -> None:
|
543
527
|
|
544
528
|
@DBOS.workflow()
|
545
529
|
def parentWorkflow() -> None:
|
@@ -565,7 +549,7 @@ def test_callchild_first_async_thread(
|
|
565
549
|
with SetWorkflowID(wfid):
|
566
550
|
parentWorkflow()
|
567
551
|
|
568
|
-
wfsteps =
|
552
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
569
553
|
assert len(wfsteps) == 4
|
570
554
|
assert wfsteps[0]["function_name"] == child_workflow.__qualname__
|
571
555
|
assert wfsteps[1]["function_name"] == "DBOS.getStatus"
|
@@ -573,9 +557,7 @@ def test_callchild_first_async_thread(
|
|
573
557
|
assert wfsteps[3]["function_name"] == stepTwo.__qualname__
|
574
558
|
|
575
559
|
|
576
|
-
def test_list_steps_errors(
|
577
|
-
dbos: DBOS, sys_db: SystemDatabase, app_db: ApplicationDatabase
|
578
|
-
) -> None:
|
560
|
+
def test_list_steps_errors(dbos: DBOS) -> None:
|
579
561
|
queue = Queue("test-queue")
|
580
562
|
|
581
563
|
@DBOS.step()
|
@@ -601,7 +583,7 @@ def test_list_steps_errors(
|
|
601
583
|
with SetWorkflowID(wfid):
|
602
584
|
with pytest.raises(Exception):
|
603
585
|
call_step()
|
604
|
-
wfsteps =
|
586
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
605
587
|
assert len(wfsteps) == 1
|
606
588
|
assert wfsteps[0]["function_name"] == failing_step.__qualname__
|
607
589
|
assert wfsteps[0]["child_workflow_id"] == None
|
@@ -613,7 +595,7 @@ def test_list_steps_errors(
|
|
613
595
|
with SetWorkflowID(wfid):
|
614
596
|
with pytest.raises(Exception):
|
615
597
|
start_step()
|
616
|
-
wfsteps =
|
598
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
617
599
|
assert len(wfsteps) == 2
|
618
600
|
assert wfsteps[0]["function_name"] == f"<temp>.{failing_step.__qualname__}"
|
619
601
|
assert wfsteps[0]["child_workflow_id"] == f"{wfid}-1"
|
@@ -629,7 +611,7 @@ def test_list_steps_errors(
|
|
629
611
|
with SetWorkflowID(wfid):
|
630
612
|
with pytest.raises(Exception):
|
631
613
|
enqueue_step()
|
632
|
-
wfsteps =
|
614
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
633
615
|
assert len(wfsteps) == 2
|
634
616
|
assert wfsteps[0]["function_name"] == f"<temp>.{failing_step.__qualname__}"
|
635
617
|
assert wfsteps[0]["child_workflow_id"] == f"{wfid}-1"
|
@@ -642,9 +624,7 @@ def test_list_steps_errors(
|
|
642
624
|
|
643
625
|
|
644
626
|
@pytest.mark.asyncio
|
645
|
-
async def test_list_steps_errors_async(
|
646
|
-
dbos: DBOS, sys_db: SystemDatabase, app_db: ApplicationDatabase
|
647
|
-
) -> None:
|
627
|
+
async def test_list_steps_errors_async(dbos: DBOS) -> None:
|
648
628
|
queue = Queue("test-queue")
|
649
629
|
|
650
630
|
@DBOS.step()
|
@@ -670,7 +650,7 @@ async def test_list_steps_errors_async(
|
|
670
650
|
with SetWorkflowID(wfid):
|
671
651
|
with pytest.raises(Exception):
|
672
652
|
await call_step()
|
673
|
-
wfsteps =
|
653
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
674
654
|
assert len(wfsteps) == 1
|
675
655
|
assert wfsteps[0]["function_name"] == failing_step.__qualname__
|
676
656
|
assert wfsteps[0]["child_workflow_id"] == None
|
@@ -682,7 +662,7 @@ async def test_list_steps_errors_async(
|
|
682
662
|
with SetWorkflowID(wfid):
|
683
663
|
with pytest.raises(Exception):
|
684
664
|
await start_step()
|
685
|
-
wfsteps =
|
665
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
686
666
|
assert len(wfsteps) == 2
|
687
667
|
assert wfsteps[0]["function_name"] == f"<temp>.{failing_step.__qualname__}"
|
688
668
|
assert wfsteps[0]["child_workflow_id"] == f"{wfid}-1"
|
@@ -698,7 +678,7 @@ async def test_list_steps_errors_async(
|
|
698
678
|
with SetWorkflowID(wfid):
|
699
679
|
with pytest.raises(Exception):
|
700
680
|
await enqueue_step()
|
701
|
-
wfsteps =
|
681
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
702
682
|
assert len(wfsteps) == 2
|
703
683
|
assert wfsteps[0]["function_name"] == f"<temp>.{failing_step.__qualname__}"
|
704
684
|
assert wfsteps[0]["child_workflow_id"] == f"{wfid}-1"
|
@@ -710,9 +690,7 @@ async def test_list_steps_errors_async(
|
|
710
690
|
assert isinstance(wfsteps[1]["error"], Exception)
|
711
691
|
|
712
692
|
|
713
|
-
def test_callchild_middle_async_thread(
|
714
|
-
dbos: DBOS, sys_db: SystemDatabase, app_db: ApplicationDatabase
|
715
|
-
) -> None:
|
693
|
+
def test_callchild_middle_async_thread(dbos: DBOS) -> None:
|
716
694
|
|
717
695
|
@DBOS.workflow()
|
718
696
|
def parentWorkflow() -> str:
|
@@ -739,7 +717,7 @@ def test_callchild_middle_async_thread(
|
|
739
717
|
with SetWorkflowID(wfid):
|
740
718
|
child_id = parentWorkflow()
|
741
719
|
|
742
|
-
wfsteps =
|
720
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
743
721
|
assert len(wfsteps) == 5
|
744
722
|
assert wfsteps[0]["function_name"] == stepOne.__qualname__
|
745
723
|
assert wfsteps[0]["child_workflow_id"] == None
|
@@ -761,9 +739,7 @@ def test_callchild_middle_async_thread(
|
|
761
739
|
|
762
740
|
|
763
741
|
@pytest.mark.asyncio
|
764
|
-
async def test_callchild_first_asyncio(
|
765
|
-
dbos: DBOS, sys_db: SystemDatabase, app_db: ApplicationDatabase
|
766
|
-
) -> None:
|
742
|
+
async def test_callchild_first_asyncio(dbos: DBOS) -> None:
|
767
743
|
|
768
744
|
@DBOS.workflow()
|
769
745
|
async def parentWorkflow() -> str:
|
@@ -790,7 +766,7 @@ async def test_callchild_first_asyncio(
|
|
790
766
|
handle = await dbos.start_workflow_async(parentWorkflow)
|
791
767
|
child_id = await handle.get_result()
|
792
768
|
|
793
|
-
wfsteps =
|
769
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
794
770
|
assert len(wfsteps) == 4
|
795
771
|
assert wfsteps[0]["function_name"] == child_workflow.__qualname__
|
796
772
|
assert wfsteps[0]["child_workflow_id"] == child_id
|
@@ -883,9 +859,7 @@ async def test_callchild_rerun_asyncio(dbos: DBOS) -> None:
|
|
883
859
|
assert res1 == res2
|
884
860
|
|
885
861
|
|
886
|
-
def test_list_transaction(
|
887
|
-
dbos: DBOS, sys_db: SystemDatabase, app_db: ApplicationDatabase
|
888
|
-
) -> None:
|
862
|
+
def test_list_transaction(dbos: DBOS) -> None:
|
889
863
|
|
890
864
|
@DBOS.workflow()
|
891
865
|
def simple_workflow() -> None:
|
@@ -906,7 +880,7 @@ def test_list_transaction(
|
|
906
880
|
with SetWorkflowID(wfid):
|
907
881
|
simple_workflow()
|
908
882
|
|
909
|
-
wfsteps =
|
883
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
910
884
|
assert len(wfsteps) == 3
|
911
885
|
assert wfsteps[0]["function_name"] == transactionOne.__qualname__
|
912
886
|
assert wfsteps[0]["output"] == "a test transaction"
|
@@ -915,9 +889,7 @@ def test_list_transaction(
|
|
915
889
|
assert wfsteps[2]["function_name"] == "DBOS.sleep"
|
916
890
|
|
917
891
|
|
918
|
-
def test_list_transaction_error(
|
919
|
-
dbos: DBOS, sys_db: SystemDatabase, app_db: ApplicationDatabase
|
920
|
-
) -> None:
|
892
|
+
def test_list_transaction_error(dbos: DBOS) -> None:
|
921
893
|
|
922
894
|
@DBOS.workflow()
|
923
895
|
def simple_workflow() -> None:
|
@@ -946,7 +918,7 @@ def test_list_transaction_error(
|
|
946
918
|
with SetWorkflowID(wfid):
|
947
919
|
simple_workflow()
|
948
920
|
|
949
|
-
wfsteps =
|
921
|
+
wfsteps = DBOS.list_workflow_steps(wfid)
|
950
922
|
assert len(wfsteps) == 4
|
951
923
|
assert wfsteps[0]["function_name"] == transactionOne.__qualname__
|
952
924
|
assert wfsteps[0]["output"] == "a test transaction"
|
@@ -992,9 +964,7 @@ def test_list_workflows_as_step(dbos: DBOS) -> None:
|
|
992
964
|
assert handle_two.get_result() == 1
|
993
965
|
|
994
966
|
|
995
|
-
def test_call_as_step_within_step(
|
996
|
-
dbos: DBOS, sys_db: SystemDatabase, app_db: ApplicationDatabase
|
997
|
-
) -> None:
|
967
|
+
def test_call_as_step_within_step(dbos: DBOS) -> None:
|
998
968
|
# If we call any util functions within a step, it should be called directly without checkpointing
|
999
969
|
|
1000
970
|
@DBOS.step()
|
@@ -1016,7 +986,7 @@ def test_call_as_step_within_step(
|
|
1016
986
|
status = getStatusWorkflow()
|
1017
987
|
assert status == WorkflowStatusString.PENDING.value
|
1018
988
|
|
1019
|
-
steps =
|
989
|
+
steps = DBOS.list_workflow_steps(wfid)
|
1020
990
|
|
1021
991
|
assert len(steps) == 1
|
1022
992
|
assert steps[0]["function_name"] == getStatus.__qualname__
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py
RENAMED
File without changes
|
File without changes
|
{dbos-0.26.0a18 → dbos-0.26.0a19}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|