dbos 0.24.0a7__tar.gz → 0.24.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.
Potentially problematic release.
This version of dbos might be problematic. Click here for more details.
- {dbos-0.24.0a7 → dbos-0.24.0a9}/PKG-INFO +1 -1
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_conductor/conductor.py +84 -48
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_conductor/protocol.py +14 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_dbos_config.py +29 -24
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/cli/cli.py +1 -1
- {dbos-0.24.0a7 → dbos-0.24.0a9}/pyproject.toml +1 -1
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_config.py +53 -2
- {dbos-0.24.0a7 → dbos-0.24.0a9}/LICENSE +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/README.md +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/__init__.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/__main__.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_admin_server.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_app_db.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_classproperty.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_cloudutils/authentication.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_cloudutils/cloudutils.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_cloudutils/databases.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_context.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_core.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_croniter.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_db_wizard.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_dbos.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_debug.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_error.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_fastapi.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_flask.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_kafka.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_kafka_message.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_logger.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_migrations/env.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_migrations/script.py.mako +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_migrations/versions/a3b18ad34abe_added_triggers.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_migrations/versions/d76646551a6c_workflow_queue.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_migrations/versions/eab0cc1d9a14_job_queue.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_outcome.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_queue.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_recovery.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_registrations.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_request.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_roles.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_scheduler.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_schemas/__init__.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_schemas/application_database.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_schemas/system_database.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_serialization.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_sys_db.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_templates/dbos-db-starter/README.md +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_templates/dbos-db-starter/__package/main.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_templates/dbos-db-starter/alembic.ini +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_tracer.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_utils.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_workflow_commands.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/cli/_github_init.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/cli/_template_init.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/dbos-config.schema.json +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/py.typed +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/__init__.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/atexit_no_ctor.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/atexit_no_launch.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/classdefs.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/conftest.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/more_classdefs.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/queuedworkflow.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_admin_server.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_async.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_classdecorators.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_concurrency.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_croniter.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_dbos.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_dbwizard.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_debug.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_failures.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_fastapi.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_fastapi_roles.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_flask.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_kafka.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_outcome.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_package.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_queue.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_scheduler.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_schema_migration.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_singleton.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_spans.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_sqlalchemy.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_workflow_cancel.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/tests/test_workflow_cmds.py +0 -0
- {dbos-0.24.0a7 → dbos-0.24.0a9}/version/__init__.py +0 -0
|
@@ -33,7 +33,7 @@ class ConductorWebsocket(threading.Thread):
|
|
|
33
33
|
def run(self) -> None:
|
|
34
34
|
while not self.evt.is_set():
|
|
35
35
|
try:
|
|
36
|
-
with connect(self.url) as websocket:
|
|
36
|
+
with connect(self.url, open_timeout=5) as websocket:
|
|
37
37
|
self.websocket = websocket
|
|
38
38
|
while not self.evt.is_set():
|
|
39
39
|
message = websocket.recv()
|
|
@@ -44,6 +44,7 @@ class ConductorWebsocket(threading.Thread):
|
|
|
44
44
|
continue
|
|
45
45
|
base_message = p.BaseMessage.from_json(message)
|
|
46
46
|
msg_type = base_message.type
|
|
47
|
+
error_message = None
|
|
47
48
|
if msg_type == p.MessageType.EXECUTOR_INFO:
|
|
48
49
|
info_response = p.ExecutorInfoResponse(
|
|
49
50
|
type=p.MessageType.EXECUTOR_INFO,
|
|
@@ -61,14 +62,14 @@ class ConductorWebsocket(threading.Thread):
|
|
|
61
62
|
recovery_message.executor_ids
|
|
62
63
|
)
|
|
63
64
|
except Exception as e:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
)
|
|
65
|
+
error_message = f"Exception encountered when recovering workflows: {traceback.format_exc()}"
|
|
66
|
+
self.dbos.logger.error(error_message)
|
|
67
67
|
success = False
|
|
68
68
|
recovery_response = p.RecoveryResponse(
|
|
69
69
|
type=p.MessageType.RECOVERY,
|
|
70
70
|
request_id=base_message.request_id,
|
|
71
71
|
success=success,
|
|
72
|
+
error_message=error_message,
|
|
72
73
|
)
|
|
73
74
|
websocket.send(recovery_response.to_json())
|
|
74
75
|
elif msg_type == p.MessageType.CANCEL:
|
|
@@ -77,14 +78,14 @@ class ConductorWebsocket(threading.Thread):
|
|
|
77
78
|
try:
|
|
78
79
|
self.dbos.cancel_workflow(cancel_message.workflow_id)
|
|
79
80
|
except Exception as e:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
)
|
|
81
|
+
error_message = f"Exception encountered when cancelling workflow {cancel_message.workflow_id}: {traceback.format_exc()}"
|
|
82
|
+
self.dbos.logger.error(error_message)
|
|
83
83
|
success = False
|
|
84
84
|
cancel_response = p.CancelResponse(
|
|
85
85
|
type=p.MessageType.CANCEL,
|
|
86
86
|
request_id=base_message.request_id,
|
|
87
87
|
success=success,
|
|
88
|
+
error_message=error_message,
|
|
88
89
|
)
|
|
89
90
|
websocket.send(cancel_response.to_json())
|
|
90
91
|
elif msg_type == p.MessageType.RESUME:
|
|
@@ -93,14 +94,14 @@ class ConductorWebsocket(threading.Thread):
|
|
|
93
94
|
try:
|
|
94
95
|
self.dbos.resume_workflow(resume_message.workflow_id)
|
|
95
96
|
except Exception as e:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
)
|
|
97
|
+
error_message = f"Exception encountered when resuming workflow {resume_message.workflow_id}: {traceback.format_exc()}"
|
|
98
|
+
self.dbos.logger.error(error_message)
|
|
99
99
|
success = False
|
|
100
100
|
resume_response = p.ResumeResponse(
|
|
101
101
|
type=p.MessageType.RESUME,
|
|
102
102
|
request_id=base_message.request_id,
|
|
103
103
|
success=success,
|
|
104
|
+
error_message=error_message,
|
|
104
105
|
)
|
|
105
106
|
websocket.send(resume_response.to_json())
|
|
106
107
|
elif msg_type == p.MessageType.RESTART:
|
|
@@ -109,14 +110,14 @@ class ConductorWebsocket(threading.Thread):
|
|
|
109
110
|
try:
|
|
110
111
|
self.dbos.restart_workflow(restart_message.workflow_id)
|
|
111
112
|
except Exception as e:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
)
|
|
113
|
+
error_message = f"Exception encountered when restarting workflow {restart_message.workflow_id}: {traceback.format_exc()}"
|
|
114
|
+
self.dbos.logger.error(error_message)
|
|
115
115
|
success = False
|
|
116
116
|
restart_response = p.RestartResponse(
|
|
117
117
|
type=p.MessageType.RESTART,
|
|
118
118
|
request_id=base_message.request_id,
|
|
119
119
|
success=success,
|
|
120
|
+
error_message=error_message,
|
|
120
121
|
)
|
|
121
122
|
websocket.send(restart_response.to_json())
|
|
122
123
|
elif msg_type == p.MessageType.LIST_WORKFLOWS:
|
|
@@ -124,20 +125,26 @@ class ConductorWebsocket(threading.Thread):
|
|
|
124
125
|
message
|
|
125
126
|
)
|
|
126
127
|
body = list_workflows_message.body
|
|
127
|
-
infos =
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
128
|
+
infos = []
|
|
129
|
+
try:
|
|
130
|
+
infos = list_workflows(
|
|
131
|
+
self.dbos._sys_db,
|
|
132
|
+
workflow_ids=body["workflow_uuids"],
|
|
133
|
+
user=body["authenticated_user"],
|
|
134
|
+
start_time=body["start_time"],
|
|
135
|
+
end_time=body["end_time"],
|
|
136
|
+
status=body["status"],
|
|
137
|
+
request=False,
|
|
138
|
+
app_version=body["application_version"],
|
|
139
|
+
name=body["workflow_name"],
|
|
140
|
+
limit=body["limit"],
|
|
141
|
+
offset=body["offset"],
|
|
142
|
+
sort_desc=body["sort_desc"],
|
|
143
|
+
)
|
|
144
|
+
except Exception as e:
|
|
145
|
+
error_message = f"Exception encountered when listing workflows: {traceback.format_exc()}"
|
|
146
|
+
self.dbos.logger.error(error_message)
|
|
147
|
+
|
|
141
148
|
list_workflows_response = p.ListWorkflowsResponse(
|
|
142
149
|
type=p.MessageType.LIST_WORKFLOWS,
|
|
143
150
|
request_id=base_message.request_id,
|
|
@@ -145,6 +152,7 @@ class ConductorWebsocket(threading.Thread):
|
|
|
145
152
|
p.WorkflowsOutput.from_workflow_information(i)
|
|
146
153
|
for i in infos
|
|
147
154
|
],
|
|
155
|
+
error_message=error_message,
|
|
148
156
|
)
|
|
149
157
|
websocket.send(list_workflows_response.to_json())
|
|
150
158
|
elif msg_type == p.MessageType.LIST_QUEUED_WORKFLOWS:
|
|
@@ -152,18 +160,24 @@ class ConductorWebsocket(threading.Thread):
|
|
|
152
160
|
p.ListQueuedWorkflowsRequest.from_json(message)
|
|
153
161
|
)
|
|
154
162
|
q_body = list_queued_workflows_message.body
|
|
155
|
-
infos =
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
163
|
+
infos = []
|
|
164
|
+
try:
|
|
165
|
+
infos = list_queued_workflows(
|
|
166
|
+
self.dbos._sys_db,
|
|
167
|
+
start_time=q_body["start_time"],
|
|
168
|
+
end_time=q_body["end_time"],
|
|
169
|
+
status=q_body["status"],
|
|
170
|
+
request=False,
|
|
171
|
+
name=q_body["workflow_name"],
|
|
172
|
+
limit=q_body["limit"],
|
|
173
|
+
offset=q_body["offset"],
|
|
174
|
+
queue_name=q_body["queue_name"],
|
|
175
|
+
sort_desc=q_body["sort_desc"],
|
|
176
|
+
)
|
|
177
|
+
except Exception as e:
|
|
178
|
+
error_message = f"Exception encountered when listing queued workflows: {traceback.format_exc()}"
|
|
179
|
+
self.dbos.logger.error(error_message)
|
|
180
|
+
|
|
167
181
|
list_queued_workflows_response = (
|
|
168
182
|
p.ListQueuedWorkflowsResponse(
|
|
169
183
|
type=p.MessageType.LIST_QUEUED_WORKFLOWS,
|
|
@@ -172,6 +186,7 @@ class ConductorWebsocket(threading.Thread):
|
|
|
172
186
|
p.WorkflowsOutput.from_workflow_information(i)
|
|
173
187
|
for i in infos
|
|
174
188
|
],
|
|
189
|
+
error_message=error_message,
|
|
175
190
|
)
|
|
176
191
|
)
|
|
177
192
|
websocket.send(list_queued_workflows_response.to_json())
|
|
@@ -179,11 +194,17 @@ class ConductorWebsocket(threading.Thread):
|
|
|
179
194
|
get_workflow_message = p.GetWorkflowRequest.from_json(
|
|
180
195
|
message
|
|
181
196
|
)
|
|
182
|
-
info =
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
197
|
+
info = None
|
|
198
|
+
try:
|
|
199
|
+
info = get_workflow(
|
|
200
|
+
self.dbos._sys_db,
|
|
201
|
+
get_workflow_message.workflow_id,
|
|
202
|
+
getRequest=False,
|
|
203
|
+
)
|
|
204
|
+
except Exception as e:
|
|
205
|
+
error_message = f"Exception encountered when getting workflow {get_workflow_message.workflow_id}: {traceback.format_exc()}"
|
|
206
|
+
self.dbos.logger.error(error_message)
|
|
207
|
+
|
|
187
208
|
get_workflow_response = p.GetWorkflowResponse(
|
|
188
209
|
type=p.MessageType.GET_WORKFLOW,
|
|
189
210
|
request_id=base_message.request_id,
|
|
@@ -192,21 +213,29 @@ class ConductorWebsocket(threading.Thread):
|
|
|
192
213
|
if info is not None
|
|
193
214
|
else None
|
|
194
215
|
),
|
|
216
|
+
error_message=error_message,
|
|
195
217
|
)
|
|
196
218
|
websocket.send(get_workflow_response.to_json())
|
|
197
219
|
elif msg_type == p.MessageType.EXIST_PENDING_WORKFLOWS:
|
|
198
220
|
exist_pending_workflows_message = (
|
|
199
221
|
p.ExistPendingWorkflowsRequest.from_json(message)
|
|
200
222
|
)
|
|
201
|
-
pending_wfs =
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
223
|
+
pending_wfs = []
|
|
224
|
+
try:
|
|
225
|
+
pending_wfs = self.dbos._sys_db.get_pending_workflows(
|
|
226
|
+
exist_pending_workflows_message.executor_id,
|
|
227
|
+
exist_pending_workflows_message.application_version,
|
|
228
|
+
)
|
|
229
|
+
except Exception as e:
|
|
230
|
+
error_message = f"Exception encountered when checking for pending workflows: {traceback.format_exc()}"
|
|
231
|
+
self.dbos.logger.error(error_message)
|
|
232
|
+
|
|
205
233
|
exist_pending_workflows_response = (
|
|
206
234
|
p.ExistPendingWorkflowsResponse(
|
|
207
235
|
type=p.MessageType.EXIST_PENDING_WORKFLOWS,
|
|
208
236
|
request_id=base_message.request_id,
|
|
209
237
|
exist=len(pending_wfs) > 0,
|
|
238
|
+
error_message=error_message,
|
|
210
239
|
)
|
|
211
240
|
)
|
|
212
241
|
websocket.send(exist_pending_workflows_response.to_json())
|
|
@@ -214,6 +243,13 @@ class ConductorWebsocket(threading.Thread):
|
|
|
214
243
|
self.dbos.logger.warning(
|
|
215
244
|
f"Unexpected message type: {msg_type}"
|
|
216
245
|
)
|
|
246
|
+
unknown_message = p.BaseResponse(
|
|
247
|
+
request_id=base_message.request_id,
|
|
248
|
+
type=msg_type,
|
|
249
|
+
error_message="Unknown message type",
|
|
250
|
+
)
|
|
251
|
+
# Still need to send a response to the conductor
|
|
252
|
+
websocket.send(unknown_message.to_json())
|
|
217
253
|
except ConnectionClosedOK:
|
|
218
254
|
self.dbos.logger.info("Conductor connection terminated")
|
|
219
255
|
break
|
|
@@ -45,6 +45,11 @@ class BaseMessage:
|
|
|
45
45
|
return json.dumps(dict_data)
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
@dataclass
|
|
49
|
+
class BaseResponse(BaseMessage):
|
|
50
|
+
error_message: Optional[str] = None
|
|
51
|
+
|
|
52
|
+
|
|
48
53
|
@dataclass
|
|
49
54
|
class ExecutorInfoRequest(BaseMessage):
|
|
50
55
|
pass
|
|
@@ -54,6 +59,7 @@ class ExecutorInfoRequest(BaseMessage):
|
|
|
54
59
|
class ExecutorInfoResponse(BaseMessage):
|
|
55
60
|
executor_id: str
|
|
56
61
|
application_version: str
|
|
62
|
+
error_message: Optional[str] = None
|
|
57
63
|
|
|
58
64
|
|
|
59
65
|
@dataclass
|
|
@@ -64,6 +70,7 @@ class RecoveryRequest(BaseMessage):
|
|
|
64
70
|
@dataclass
|
|
65
71
|
class RecoveryResponse(BaseMessage):
|
|
66
72
|
success: bool
|
|
73
|
+
error_message: Optional[str] = None
|
|
67
74
|
|
|
68
75
|
|
|
69
76
|
@dataclass
|
|
@@ -74,6 +81,7 @@ class CancelRequest(BaseMessage):
|
|
|
74
81
|
@dataclass
|
|
75
82
|
class CancelResponse(BaseMessage):
|
|
76
83
|
success: bool
|
|
84
|
+
error_message: Optional[str] = None
|
|
77
85
|
|
|
78
86
|
|
|
79
87
|
@dataclass
|
|
@@ -84,6 +92,7 @@ class ResumeRequest(BaseMessage):
|
|
|
84
92
|
@dataclass
|
|
85
93
|
class ResumeResponse(BaseMessage):
|
|
86
94
|
success: bool
|
|
95
|
+
error_message: Optional[str] = None
|
|
87
96
|
|
|
88
97
|
|
|
89
98
|
@dataclass
|
|
@@ -94,6 +103,7 @@ class RestartRequest(BaseMessage):
|
|
|
94
103
|
@dataclass
|
|
95
104
|
class RestartResponse(BaseMessage):
|
|
96
105
|
success: bool
|
|
106
|
+
error_message: Optional[str] = None
|
|
97
107
|
|
|
98
108
|
|
|
99
109
|
class ListWorkflowsBody(TypedDict):
|
|
@@ -165,6 +175,7 @@ class ListWorkflowsRequest(BaseMessage):
|
|
|
165
175
|
@dataclass
|
|
166
176
|
class ListWorkflowsResponse(BaseMessage):
|
|
167
177
|
output: List[WorkflowsOutput]
|
|
178
|
+
error_message: Optional[str] = None
|
|
168
179
|
|
|
169
180
|
|
|
170
181
|
class ListQueuedWorkflowsBody(TypedDict):
|
|
@@ -186,6 +197,7 @@ class ListQueuedWorkflowsRequest(BaseMessage):
|
|
|
186
197
|
@dataclass
|
|
187
198
|
class ListQueuedWorkflowsResponse(BaseMessage):
|
|
188
199
|
output: List[WorkflowsOutput]
|
|
200
|
+
error_message: Optional[str] = None
|
|
189
201
|
|
|
190
202
|
|
|
191
203
|
@dataclass
|
|
@@ -196,6 +208,7 @@ class GetWorkflowRequest(BaseMessage):
|
|
|
196
208
|
@dataclass
|
|
197
209
|
class GetWorkflowResponse(BaseMessage):
|
|
198
210
|
output: Optional[WorkflowsOutput]
|
|
211
|
+
error_message: Optional[str] = None
|
|
199
212
|
|
|
200
213
|
|
|
201
214
|
@dataclass
|
|
@@ -207,3 +220,4 @@ class ExistPendingWorkflowsRequest(BaseMessage):
|
|
|
207
220
|
@dataclass
|
|
208
221
|
class ExistPendingWorkflowsResponse(BaseMessage):
|
|
209
222
|
exist: bool
|
|
223
|
+
error_message: Optional[str] = None
|
|
@@ -202,7 +202,7 @@ def translate_dbos_config_to_config_file(config: DBOSConfig) -> ConfigFile:
|
|
|
202
202
|
return translated_config
|
|
203
203
|
|
|
204
204
|
|
|
205
|
-
def _substitute_env_vars(content: str) -> str:
|
|
205
|
+
def _substitute_env_vars(content: str, silent: bool = False) -> str:
|
|
206
206
|
regex = r"\$\{([^}]+)\}" # Regex to match ${VAR_NAME} style placeholders
|
|
207
207
|
|
|
208
208
|
def replace_func(match: re.Match[str]) -> str:
|
|
@@ -210,7 +210,7 @@ def _substitute_env_vars(content: str) -> str:
|
|
|
210
210
|
value = os.environ.get(
|
|
211
211
|
var_name, ""
|
|
212
212
|
) # If the env variable is not set, return an empty string
|
|
213
|
-
if value == "":
|
|
213
|
+
if value == "" and not silent:
|
|
214
214
|
dbos_logger.warning(
|
|
215
215
|
f"Variable {var_name} would be substituted from the process environment into dbos-config.yaml, but is not defined"
|
|
216
216
|
)
|
|
@@ -233,7 +233,7 @@ def get_dbos_database_url(config_file_path: str = DBOS_CONFIG_PATH) -> str:
|
|
|
233
233
|
str: Database URL for the application database
|
|
234
234
|
|
|
235
235
|
"""
|
|
236
|
-
dbos_config =
|
|
236
|
+
dbos_config = load_config(config_file_path, run_process_config=True)
|
|
237
237
|
db_url = URL.create(
|
|
238
238
|
"postgresql+psycopg",
|
|
239
239
|
username=dbos_config["database"]["username"],
|
|
@@ -267,7 +267,7 @@ def load_config(
|
|
|
267
267
|
|
|
268
268
|
with open(config_file_path, "r") as file:
|
|
269
269
|
content = file.read()
|
|
270
|
-
substituted_content = _substitute_env_vars(content)
|
|
270
|
+
substituted_content = _substitute_env_vars(content, silent=silent)
|
|
271
271
|
data = yaml.safe_load(substituted_content)
|
|
272
272
|
|
|
273
273
|
if not isinstance(data, dict):
|
|
@@ -299,7 +299,6 @@ def process_config(
|
|
|
299
299
|
data: ConfigFile,
|
|
300
300
|
silent: bool = False,
|
|
301
301
|
) -> ConfigFile:
|
|
302
|
-
|
|
303
302
|
if "name" not in data:
|
|
304
303
|
raise DBOSInitializationError(f"Configuration must specify an application name")
|
|
305
304
|
|
|
@@ -337,25 +336,9 @@ def process_config(
|
|
|
337
336
|
if "app_db_name" not in data["database"] or not (data["database"]["app_db_name"]):
|
|
338
337
|
data["database"]["app_db_name"] = _app_name_to_db_name(data["name"])
|
|
339
338
|
|
|
340
|
-
# Load the DB connection file. Use its values for missing
|
|
339
|
+
# Load the DB connection file. Use its values for missing connection parameters. Use defaults otherwise.
|
|
341
340
|
db_connection = load_db_connection()
|
|
342
|
-
|
|
343
|
-
if os.getenv("DBOS_DBHOST"):
|
|
344
|
-
print(
|
|
345
|
-
"[bold blue]Loading database connection parameters from debug environment variables[/bold blue]"
|
|
346
|
-
)
|
|
347
|
-
elif data["database"].get("hostname"):
|
|
348
|
-
print(
|
|
349
|
-
"[bold blue]Loading database connection parameters from dbos-config.yaml[/bold blue]"
|
|
350
|
-
)
|
|
351
|
-
elif db_connection.get("hostname"):
|
|
352
|
-
print(
|
|
353
|
-
"[bold blue]Loading database connection parameters from .dbos/db_connection[/bold blue]"
|
|
354
|
-
)
|
|
355
|
-
else:
|
|
356
|
-
print(
|
|
357
|
-
"[bold blue]Using default database connection parameters (localhost)[/bold blue]"
|
|
358
|
-
)
|
|
341
|
+
connection_passed_in = data["database"].get("hostname", None) is not None
|
|
359
342
|
|
|
360
343
|
dbos_dbport: Optional[int] = None
|
|
361
344
|
dbport_env = os.getenv("DBOS_DBPORT")
|
|
@@ -424,6 +407,28 @@ def process_config(
|
|
|
424
407
|
# Check the connectivity to the database and make sure it's properly configured
|
|
425
408
|
# Note, never use db wizard if the DBOS is running in debug mode (i.e. DBOS_DEBUG_WORKFLOW_ID env var is set)
|
|
426
409
|
debugWorkflowId = os.getenv("DBOS_DEBUG_WORKFLOW_ID")
|
|
410
|
+
|
|
411
|
+
# Pretty-print where we've loaded database connection information from, respecting the log level
|
|
412
|
+
if not silent and logs["logLevel"] == "INFO" or logs["logLevel"] == "DEBUG":
|
|
413
|
+
d = data["database"]
|
|
414
|
+
conn_string = f"postgresql://{d['username']}:*****@{d['hostname']}:{d['port']}/{d['app_db_name']}"
|
|
415
|
+
if os.getenv("DBOS_DBHOST"):
|
|
416
|
+
print(
|
|
417
|
+
f"[bold blue]Loading database connection string from debug environment variables: {conn_string}[/bold blue]"
|
|
418
|
+
)
|
|
419
|
+
elif connection_passed_in:
|
|
420
|
+
print(
|
|
421
|
+
f"[bold blue]Using database connection string: {conn_string}[/bold blue]"
|
|
422
|
+
)
|
|
423
|
+
elif db_connection.get("hostname"):
|
|
424
|
+
print(
|
|
425
|
+
f"[bold blue]Loading database connection string from .dbos/db_connection: {conn_string}[/bold blue]"
|
|
426
|
+
)
|
|
427
|
+
else:
|
|
428
|
+
print(
|
|
429
|
+
f"[bold blue]Using default database connection string: {conn_string}[/bold blue]"
|
|
430
|
+
)
|
|
431
|
+
|
|
427
432
|
if use_db_wizard and debugWorkflowId is None:
|
|
428
433
|
data = db_wizard(data)
|
|
429
434
|
|
|
@@ -539,7 +544,7 @@ def check_config_consistency(
|
|
|
539
544
|
) -> None:
|
|
540
545
|
# First load the config file and check whether it is present
|
|
541
546
|
try:
|
|
542
|
-
config = load_config(config_file_path)
|
|
547
|
+
config = load_config(config_file_path, silent=True, run_process_config=False)
|
|
543
548
|
except FileNotFoundError:
|
|
544
549
|
dbos_logger.debug(
|
|
545
550
|
f"No configuration file {config_file_path} found. Skipping consistency check with provided config."
|
|
@@ -41,7 +41,7 @@ def _on_windows() -> bool:
|
|
|
41
41
|
help="Start your DBOS application using the start commands in 'dbos-config.yaml'"
|
|
42
42
|
)
|
|
43
43
|
def start() -> None:
|
|
44
|
-
config = load_config()
|
|
44
|
+
config = load_config(run_process_config=False, silent=True)
|
|
45
45
|
start_commands = config["runtimeConfig"]["start"]
|
|
46
46
|
typer.echo("Executing start commands from 'dbos-config.yaml'")
|
|
47
47
|
for command in start_commands:
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
from unittest.mock import mock_open
|
|
5
|
+
from urllib.parse import quote
|
|
5
6
|
|
|
6
7
|
import pytest
|
|
7
8
|
import pytest_mock
|
|
8
|
-
from sqlalchemy import event
|
|
9
|
+
from sqlalchemy import URL, event
|
|
9
10
|
|
|
10
11
|
# Public API
|
|
11
|
-
from dbos import DBOS, load_config
|
|
12
|
+
from dbos import DBOS, get_dbos_database_url, load_config
|
|
12
13
|
from dbos._dbos_config import (
|
|
13
14
|
ConfigFile,
|
|
14
15
|
DBOSConfig,
|
|
@@ -1361,3 +1362,53 @@ def test_configured_app_db_connect_timeout():
|
|
|
1361
1362
|
pass
|
|
1362
1363
|
|
|
1363
1364
|
dbos.destroy()
|
|
1365
|
+
|
|
1366
|
+
|
|
1367
|
+
def test_get_dbos_database_url(mocker):
|
|
1368
|
+
mock_config = """
|
|
1369
|
+
name: "some-app"
|
|
1370
|
+
database:
|
|
1371
|
+
hostname: 'localhost'
|
|
1372
|
+
port: 5432
|
|
1373
|
+
username: 'postgres'
|
|
1374
|
+
password: ${PGPASSWORD}
|
|
1375
|
+
app_db_name: 'some_db'
|
|
1376
|
+
"""
|
|
1377
|
+
mocker.patch(
|
|
1378
|
+
"builtins.open", side_effect=generate_mock_open(mock_filename, mock_config)
|
|
1379
|
+
)
|
|
1380
|
+
|
|
1381
|
+
expected_url = URL.create(
|
|
1382
|
+
"postgresql+psycopg",
|
|
1383
|
+
username="postgres",
|
|
1384
|
+
password=os.environ.get("PGPASSWORD"),
|
|
1385
|
+
host="localhost",
|
|
1386
|
+
port=5432,
|
|
1387
|
+
database="some_db",
|
|
1388
|
+
).render_as_string(hide_password=False)
|
|
1389
|
+
assert get_dbos_database_url() == expected_url
|
|
1390
|
+
|
|
1391
|
+
|
|
1392
|
+
def test_get_dbos_database_url_local_suffix(mocker):
|
|
1393
|
+
mock_config = """
|
|
1394
|
+
name: "some-app"
|
|
1395
|
+
database:
|
|
1396
|
+
hostname: 'localhost'
|
|
1397
|
+
port: 5432
|
|
1398
|
+
username: 'postgres'
|
|
1399
|
+
password: ${PGPASSWORD}
|
|
1400
|
+
app_db_name: 'some_db'
|
|
1401
|
+
local_suffix: true
|
|
1402
|
+
"""
|
|
1403
|
+
mocker.patch(
|
|
1404
|
+
"builtins.open", side_effect=generate_mock_open(mock_filename, mock_config)
|
|
1405
|
+
)
|
|
1406
|
+
expected_url = URL.create(
|
|
1407
|
+
"postgresql+psycopg",
|
|
1408
|
+
username="postgres",
|
|
1409
|
+
password=os.environ.get("PGPASSWORD"),
|
|
1410
|
+
host="localhost",
|
|
1411
|
+
port=5432,
|
|
1412
|
+
database="some_db_local",
|
|
1413
|
+
).render_as_string(hide_password=False)
|
|
1414
|
+
assert get_dbos_database_url() == expected_url
|
|
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
|
{dbos-0.24.0a7 → dbos-0.24.0a9}/dbos/_migrations/versions/5c361fc04708_added_system_tables.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
|