dbos 0.28.0a12__tar.gz → 0.28.0a15__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.28.0a12 → dbos-0.28.0a15}/PKG-INFO +1 -1
  2. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_app_db.py +14 -49
  3. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_client.py +17 -6
  4. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_conductor/conductor.py +32 -1
  5. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_conductor/protocol.py +19 -2
  6. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_dbos.py +18 -21
  7. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_dbos_config.py +142 -114
  8. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_sys_db.py +23 -50
  9. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/cli/cli.py +56 -14
  10. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/dbos-config.schema.json +0 -10
  11. {dbos-0.28.0a12 → dbos-0.28.0a15}/pyproject.toml +1 -1
  12. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/conftest.py +32 -15
  13. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_config.py +497 -452
  14. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_docker_secrets.py +20 -185
  15. {dbos-0.28.0a12 → dbos-0.28.0a15}/LICENSE +0 -0
  16. {dbos-0.28.0a12 → dbos-0.28.0a15}/README.md +0 -0
  17. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/__init__.py +0 -0
  18. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/__main__.py +0 -0
  19. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_admin_server.py +0 -0
  20. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_classproperty.py +0 -0
  21. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_context.py +0 -0
  22. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_core.py +0 -0
  23. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_croniter.py +0 -0
  24. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_debug.py +0 -0
  25. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_docker_pg_helper.py +0 -0
  26. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_error.py +0 -0
  27. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_event_loop.py +0 -0
  28. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_fastapi.py +0 -0
  29. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_flask.py +0 -0
  30. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_kafka.py +0 -0
  31. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_kafka_message.py +0 -0
  32. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_logger.py +0 -0
  33. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_migrations/env.py +0 -0
  34. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_migrations/script.py.mako +0 -0
  35. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -0
  36. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_migrations/versions/27ac6900c6ad_add_queue_dedup.py +0 -0
  37. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -0
  38. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py +0 -0
  39. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_migrations/versions/83f3732ae8e7_workflow_timeout.py +0 -0
  40. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_migrations/versions/933e86bdac6a_add_queue_priority.py +0 -0
  41. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_migrations/versions/a3b18ad34abe_added_triggers.py +0 -0
  42. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -0
  43. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_migrations/versions/d76646551a6c_workflow_queue.py +0 -0
  44. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_migrations/versions/eab0cc1d9a14_job_queue.py +0 -0
  45. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py +0 -0
  46. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_outcome.py +0 -0
  47. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_queue.py +0 -0
  48. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_recovery.py +0 -0
  49. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_registrations.py +0 -0
  50. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_roles.py +0 -0
  51. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_scheduler.py +0 -0
  52. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_schemas/__init__.py +0 -0
  53. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_schemas/application_database.py +0 -0
  54. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_schemas/system_database.py +0 -0
  55. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_serialization.py +0 -0
  56. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_templates/dbos-db-starter/README.md +0 -0
  57. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
  58. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_templates/dbos-db-starter/__package/main.py.dbos +0 -0
  59. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
  60. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_templates/dbos-db-starter/alembic.ini +0 -0
  61. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
  62. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -0
  63. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -0
  64. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +0 -0
  65. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
  66. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_tracer.py +0 -0
  67. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_utils.py +0 -0
  68. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/_workflow_commands.py +0 -0
  69. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/cli/_github_init.py +0 -0
  70. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/cli/_template_init.py +0 -0
  71. {dbos-0.28.0a12 → dbos-0.28.0a15}/dbos/py.typed +0 -0
  72. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/__init__.py +0 -0
  73. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/atexit_no_ctor.py +0 -0
  74. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/atexit_no_launch.py +0 -0
  75. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/classdefs.py +0 -0
  76. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/client_collateral.py +0 -0
  77. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/client_worker.py +0 -0
  78. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/dupname_classdefs1.py +0 -0
  79. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/dupname_classdefsa.py +0 -0
  80. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/more_classdefs.py +0 -0
  81. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/queuedworkflow.py +0 -0
  82. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_admin_server.py +0 -0
  83. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_async.py +0 -0
  84. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_classdecorators.py +0 -0
  85. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_client.py +0 -0
  86. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_concurrency.py +0 -0
  87. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_croniter.py +0 -0
  88. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_dbos.py +0 -0
  89. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_debug.py +0 -0
  90. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_failures.py +0 -0
  91. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_fastapi.py +0 -0
  92. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_fastapi_roles.py +0 -0
  93. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_flask.py +0 -0
  94. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_kafka.py +0 -0
  95. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_outcome.py +0 -0
  96. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_package.py +0 -0
  97. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_queue.py +0 -0
  98. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_scheduler.py +0 -0
  99. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_schema_migration.py +0 -0
  100. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_singleton.py +0 -0
  101. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_spans.py +0 -0
  102. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_sqlalchemy.py +0 -0
  103. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_workflow_introspection.py +0 -0
  104. {dbos-0.28.0a12 → dbos-0.28.0a15}/tests/test_workflow_management.py +0 -0
  105. {dbos-0.28.0a12 → dbos-0.28.0a15}/version/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.28.0a12
3
+ Version: 0.28.0a15
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -1,4 +1,4 @@
1
- from typing import List, Optional, TypedDict
1
+ from typing import Any, Dict, List, Optional, TypedDict
2
2
 
3
3
  import sqlalchemy as sa
4
4
  import sqlalchemy.dialects.postgresql as pg
@@ -7,7 +7,6 @@ from sqlalchemy.exc import DBAPIError
7
7
  from sqlalchemy.orm import Session, sessionmaker
8
8
 
9
9
  from . import _serialization
10
- from ._dbos_config import DatabaseConfig
11
10
  from ._error import DBOSUnexpectedStepError, DBOSWorkflowConflictIDError
12
11
  from ._schemas.application_database import ApplicationSchema
13
12
  from ._sys_db import StepInfo
@@ -31,67 +30,33 @@ class RecordedResult(TypedDict):
31
30
 
32
31
  class ApplicationDatabase:
33
32
 
34
- def __init__(self, database: DatabaseConfig, *, debug_mode: bool = False):
35
-
36
- app_db_name = database["app_db_name"]
33
+ def __init__(
34
+ self,
35
+ *,
36
+ database_url: str,
37
+ engine_kwargs: Dict[str, Any],
38
+ debug_mode: bool = False,
39
+ ):
40
+ app_db_url = sa.make_url(database_url).set(drivername="postgresql+psycopg")
37
41
 
38
42
  # If the application database does not already exist, create it
39
43
  if not debug_mode:
40
- postgres_db_url = sa.URL.create(
41
- "postgresql+psycopg",
42
- username=database["username"],
43
- password=database["password"],
44
- host=database["hostname"],
45
- port=database["port"],
46
- database="postgres",
44
+ postgres_db_engine = sa.create_engine(
45
+ app_db_url.set(database="postgres"),
46
+ **engine_kwargs,
47
47
  )
48
- postgres_db_engine = sa.create_engine(postgres_db_url)
49
48
  with postgres_db_engine.connect() as conn:
50
49
  conn.execution_options(isolation_level="AUTOCOMMIT")
51
50
  if not conn.execute(
52
51
  sa.text("SELECT 1 FROM pg_database WHERE datname=:db_name"),
53
- parameters={"db_name": app_db_name},
52
+ parameters={"db_name": app_db_url.database},
54
53
  ).scalar():
55
- conn.execute(sa.text(f"CREATE DATABASE {app_db_name}"))
54
+ conn.execute(sa.text(f"CREATE DATABASE {app_db_url.database}"))
56
55
  postgres_db_engine.dispose()
57
56
 
58
- # Create a connection pool for the application database
59
- app_db_url = sa.URL.create(
60
- "postgresql+psycopg",
61
- username=database["username"],
62
- password=database["password"],
63
- host=database["hostname"],
64
- port=database["port"],
65
- database=app_db_name,
66
- )
67
-
68
- connect_args = {}
69
- if (
70
- "connectionTimeoutMillis" in database
71
- and database["connectionTimeoutMillis"]
72
- ):
73
- connect_args["connect_timeout"] = int(
74
- database["connectionTimeoutMillis"] / 1000
75
- )
76
-
77
- pool_size = database.get("app_db_pool_size")
78
- if pool_size is None:
79
- pool_size = 20
80
-
81
- engine_kwargs = database.get("db_engine_kwargs")
82
57
  if engine_kwargs is None:
83
58
  engine_kwargs = {}
84
59
 
85
- # Respect user-provided values. Otherwise, set defaults.
86
- if "pool_size" not in engine_kwargs:
87
- engine_kwargs["pool_size"] = pool_size
88
- if "max_overflow" not in engine_kwargs:
89
- engine_kwargs["max_overflow"] = 0
90
- if "pool_timeout" not in engine_kwargs:
91
- engine_kwargs["pool_timeout"] = 30
92
- if "connect_args" not in engine_kwargs:
93
- engine_kwargs["connect_args"] = connect_args
94
-
95
60
  self.engine = sa.create_engine(
96
61
  app_db_url,
97
62
  **engine_kwargs,
@@ -15,7 +15,6 @@ else:
15
15
 
16
16
  from dbos import _serialization
17
17
  from dbos._dbos import WorkflowHandle, WorkflowHandleAsync
18
- from dbos._dbos_config import parse_database_url_to_dbconfig
19
18
  from dbos._error import DBOSException, DBOSNonExistentWorkflowError
20
19
  from dbos._registrations import DEFAULT_MAX_RECOVERY_ATTEMPTS
21
20
  from dbos._serialization import WorkflowInputs
@@ -100,11 +99,23 @@ class WorkflowHandleClientAsyncPolling(Generic[R]):
100
99
 
101
100
  class DBOSClient:
102
101
  def __init__(self, database_url: str, *, system_database: Optional[str] = None):
103
- db_config = parse_database_url_to_dbconfig(database_url)
104
- if system_database is not None:
105
- db_config["sys_db_name"] = system_database
106
- self._sys_db = SystemDatabase(db_config)
107
- self._app_db = ApplicationDatabase(db_config)
102
+ self._sys_db = SystemDatabase(
103
+ database_url=database_url,
104
+ engine_kwargs={
105
+ "pool_timeout": 30,
106
+ "max_overflow": 0,
107
+ "pool_size": 2,
108
+ },
109
+ sys_db_name=system_database,
110
+ )
111
+ self._app_db = ApplicationDatabase(
112
+ database_url=database_url,
113
+ engine_kwargs={
114
+ "pool_timeout": 30,
115
+ "max_overflow": 0,
116
+ "pool_size": 2,
117
+ },
118
+ )
108
119
  self._db_url = database_url
109
120
 
110
121
  def destroy(self) -> None:
@@ -2,6 +2,7 @@ import socket
2
2
  import threading
3
3
  import time
4
4
  import traceback
5
+ import uuid
5
6
  from importlib.metadata import version
6
7
  from typing import TYPE_CHECKING, Optional
7
8
 
@@ -9,6 +10,7 @@ from websockets import ConnectionClosed, ConnectionClosedOK, InvalidStatus
9
10
  from websockets.sync.client import connect
10
11
  from websockets.sync.connection import Connection
11
12
 
13
+ from dbos._context import SetWorkflowID
12
14
  from dbos._utils import GlobalParams
13
15
  from dbos._workflow_commands import (
14
16
  get_workflow,
@@ -168,10 +170,11 @@ class ConductorWebsocket(threading.Thread):
168
170
  )
169
171
  websocket.send(resume_response.to_json())
170
172
  elif msg_type == p.MessageType.RESTART:
173
+ # TODO: deprecate this message type in favor of Fork
171
174
  restart_message = p.RestartRequest.from_json(message)
172
175
  success = True
173
176
  try:
174
- self.dbos.restart_workflow(restart_message.workflow_id)
177
+ self.dbos.fork_workflow(restart_message.workflow_id, 1)
175
178
  except Exception as e:
176
179
  error_message = f"Exception encountered when restarting workflow {restart_message.workflow_id}: {traceback.format_exc()}"
177
180
  self.dbos.logger.error(error_message)
@@ -183,6 +186,34 @@ class ConductorWebsocket(threading.Thread):
183
186
  error_message=error_message,
184
187
  )
185
188
  websocket.send(restart_response.to_json())
189
+ elif msg_type == p.MessageType.FORK_WORKFLOW:
190
+ fork_message = p.ForkWorkflowRequest.from_json(message)
191
+ new_workflow_id = fork_message.body["new_workflow_id"]
192
+ if new_workflow_id is None:
193
+ new_workflow_id = str(uuid.uuid4())
194
+ workflow_id = fork_message.body["workflow_id"]
195
+ start_step = fork_message.body["start_step"]
196
+ app_version = fork_message.body["application_version"]
197
+ try:
198
+ with SetWorkflowID(new_workflow_id):
199
+ new_handle = self.dbos.fork_workflow(
200
+ workflow_id,
201
+ start_step,
202
+ application_version=app_version,
203
+ )
204
+ new_workflow_id = new_handle.workflow_id
205
+ except Exception as e:
206
+ error_message = f"Exception encountered when forking workflow {workflow_id} to new workflow {new_workflow_id} on step {start_step}, app version {app_version}: {traceback.format_exc()}"
207
+ self.dbos.logger.error(error_message)
208
+ new_workflow_id = None
209
+
210
+ fork_response = p.ForkWorkflowResponse(
211
+ type=p.MessageType.FORK_WORKFLOW,
212
+ request_id=base_message.request_id,
213
+ new_workflow_id=new_workflow_id,
214
+ error_message=error_message,
215
+ )
216
+ websocket.send(fork_response.to_json())
186
217
  elif msg_type == p.MessageType.LIST_WORKFLOWS:
187
218
  list_workflows_message = p.ListWorkflowsRequest.from_json(
188
219
  message
@@ -17,6 +17,7 @@ class MessageType(str, Enum):
17
17
  GET_WORKFLOW = "get_workflow"
18
18
  EXIST_PENDING_WORKFLOWS = "exist_pending_workflows"
19
19
  LIST_STEPS = "list_steps"
20
+ FORK_WORKFLOW = "fork_workflow"
20
21
 
21
22
 
22
23
  T = TypeVar("T", bound="BaseMessage")
@@ -133,7 +134,6 @@ class WorkflowsOutput:
133
134
  AuthenticatedRoles: Optional[str]
134
135
  Input: Optional[str]
135
136
  Output: Optional[str]
136
- Request: Optional[str]
137
137
  Error: Optional[str]
138
138
  CreatedAt: Optional[str]
139
139
  UpdatedAt: Optional[str]
@@ -167,7 +167,6 @@ class WorkflowsOutput:
167
167
  AuthenticatedRoles=roles_str,
168
168
  Input=inputs_str,
169
169
  Output=outputs_str,
170
- Request=request_str,
171
170
  Error=error_str,
172
171
  CreatedAt=created_at_str,
173
172
  UpdatedAt=updated_at_str,
@@ -263,3 +262,21 @@ class ListStepsRequest(BaseMessage):
263
262
  class ListStepsResponse(BaseMessage):
264
263
  output: Optional[List[WorkflowSteps]]
265
264
  error_message: Optional[str] = None
265
+
266
+
267
+ class ForkWorkflowBody(TypedDict):
268
+ workflow_id: str
269
+ start_step: int
270
+ application_version: Optional[str]
271
+ new_workflow_id: Optional[str]
272
+
273
+
274
+ @dataclass
275
+ class ForkWorkflowRequest(BaseMessage):
276
+ body: ForkWorkflowBody
277
+
278
+
279
+ @dataclass
280
+ class ForkWorkflowResponse(BaseMessage):
281
+ new_workflow_id: Optional[str]
282
+ error_message: Optional[str] = None
@@ -24,8 +24,6 @@ from typing import (
24
24
  Tuple,
25
25
  Type,
26
26
  TypeVar,
27
- Union,
28
- cast,
29
27
  )
30
28
 
31
29
  from opentelemetry.trace import Span
@@ -64,7 +62,6 @@ from ._registrations import (
64
62
  )
65
63
  from ._roles import default_required_roles, required_roles
66
64
  from ._scheduler import ScheduledWorkflow, scheduled
67
- from ._schemas.system_database import SystemSchema
68
65
  from ._sys_db import StepInfo, SystemDatabase, WorkflowStatus, reset_system_database
69
66
  from ._tracer import DBOSTracer, dbos_tracer
70
67
 
@@ -73,7 +70,7 @@ if TYPE_CHECKING:
73
70
  from ._kafka import _KafkaConsumerWorkflow
74
71
  from flask import Flask
75
72
 
76
- from sqlalchemy import URL
73
+ from sqlalchemy import make_url
77
74
  from sqlalchemy.orm import Session
78
75
 
79
76
  if sys.version_info < (3, 10):
@@ -418,11 +415,19 @@ class DBOS:
418
415
  dbos_logger.info(f"Application version: {GlobalParams.app_version}")
419
416
  self._executor_field = ThreadPoolExecutor(max_workers=64)
420
417
  self._background_event_loop.start()
418
+ assert self._config["database_url"] is not None
419
+ assert self._config["database"]["sys_db_engine_kwargs"] is not None
421
420
  self._sys_db_field = SystemDatabase(
422
- self._config["database"], debug_mode=debug_mode
421
+ database_url=self._config["database_url"],
422
+ engine_kwargs=self._config["database"]["sys_db_engine_kwargs"],
423
+ sys_db_name=self._config["database"]["sys_db_name"],
424
+ debug_mode=debug_mode,
423
425
  )
426
+ assert self._config["database"]["db_engine_kwargs"] is not None
424
427
  self._app_db_field = ApplicationDatabase(
425
- self._config["database"], debug_mode=debug_mode
428
+ database_url=self._config["database_url"],
429
+ engine_kwargs=self._config["database"]["db_engine_kwargs"],
430
+ debug_mode=debug_mode,
426
431
  )
427
432
 
428
433
  if debug_mode:
@@ -530,21 +535,13 @@ class DBOS:
530
535
  not self._launched
531
536
  ), "The system database cannot be reset after DBOS is launched. Resetting the system database is a destructive operation that should only be used in a test environment."
532
537
 
533
- sysdb_name = (
534
- self._config["database"]["sys_db_name"]
535
- if "sys_db_name" in self._config["database"]
536
- and self._config["database"]["sys_db_name"]
537
- else self._config["database"]["app_db_name"] + SystemSchema.sysdb_suffix
538
- )
539
- postgres_db_url = URL.create(
540
- "postgresql+psycopg",
541
- username=self._config["database"]["username"],
542
- password=self._config["database"]["password"],
543
- host=self._config["database"]["hostname"],
544
- port=self._config["database"]["port"],
545
- database="postgres",
546
- )
547
- reset_system_database(postgres_db_url, sysdb_name)
538
+ sysdb_name = self._config["database"]["sys_db_name"]
539
+ assert sysdb_name is not None
540
+
541
+ assert self._config["database_url"] is not None
542
+ pg_db_url = make_url(self._config["database_url"]).set(database="postgres")
543
+
544
+ reset_system_database(pg_db_url, sysdb_name)
548
545
 
549
546
  def _destroy(self) -> None:
550
547
  self._initialized = False