dbos 0.24.1__py3-none-any.whl → 0.25.0__py3-none-any.whl
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/__init__.py +5 -1
- dbos/__main__.py +3 -0
- dbos/_admin_server.py +28 -2
- dbos/_app_db.py +14 -15
- dbos/_client.py +206 -0
- dbos/_conductor/conductor.py +33 -2
- dbos/_conductor/protocol.py +47 -7
- dbos/_context.py +48 -0
- dbos/_core.py +173 -48
- dbos/_db_wizard.py +3 -7
- dbos/_dbos.py +134 -85
- dbos/_fastapi.py +4 -1
- dbos/_logger.py +14 -0
- dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py +46 -0
- dbos/_outcome.py +6 -2
- dbos/_queue.py +5 -5
- dbos/_schemas/system_database.py +2 -0
- dbos/_sys_db.py +159 -178
- dbos/_templates/dbos-db-starter/__package/main.py +6 -11
- dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +2 -4
- dbos/_workflow_commands.py +90 -63
- dbos/cli/_template_init.py +8 -3
- dbos/cli/cli.py +22 -6
- {dbos-0.24.1.dist-info → dbos-0.25.0.dist-info}/METADATA +2 -1
- {dbos-0.24.1.dist-info → dbos-0.25.0.dist-info}/RECORD +28 -26
- {dbos-0.24.1.dist-info → dbos-0.25.0.dist-info}/WHEEL +1 -1
- {dbos-0.24.1.dist-info → dbos-0.25.0.dist-info}/entry_points.txt +0 -0
- {dbos-0.24.1.dist-info → dbos-0.25.0.dist-info}/licenses/LICENSE +0 -0
dbos/_core.py
CHANGED
|
@@ -81,13 +81,12 @@ from ._sys_db import (
|
|
|
81
81
|
if TYPE_CHECKING:
|
|
82
82
|
from ._dbos import (
|
|
83
83
|
DBOS,
|
|
84
|
-
Workflow,
|
|
85
84
|
WorkflowHandle,
|
|
86
85
|
WorkflowHandleAsync,
|
|
87
|
-
WorkflowStatus,
|
|
88
86
|
DBOSRegistry,
|
|
89
87
|
IsolationLevel,
|
|
90
88
|
)
|
|
89
|
+
from ._workflow_commands import WorkflowStatus
|
|
91
90
|
|
|
92
91
|
from sqlalchemy.exc import DBAPIError, InvalidRequestError
|
|
93
92
|
|
|
@@ -109,7 +108,15 @@ class WorkflowHandleFuture(Generic[R]):
|
|
|
109
108
|
return self.workflow_id
|
|
110
109
|
|
|
111
110
|
def get_result(self) -> R:
|
|
112
|
-
|
|
111
|
+
try:
|
|
112
|
+
r = self.future.result()
|
|
113
|
+
except Exception as e:
|
|
114
|
+
serialized_e = _serialization.serialize_exception(e)
|
|
115
|
+
self.dbos._sys_db.record_get_result(self.workflow_id, None, serialized_e)
|
|
116
|
+
raise
|
|
117
|
+
serialized_r = _serialization.serialize(r)
|
|
118
|
+
self.dbos._sys_db.record_get_result(self.workflow_id, serialized_r, None)
|
|
119
|
+
return r
|
|
113
120
|
|
|
114
121
|
def get_status(self) -> "WorkflowStatus":
|
|
115
122
|
stat = self.dbos.get_workflow_status(self.workflow_id)
|
|
@@ -128,8 +135,15 @@ class WorkflowHandlePolling(Generic[R]):
|
|
|
128
135
|
return self.workflow_id
|
|
129
136
|
|
|
130
137
|
def get_result(self) -> R:
|
|
131
|
-
|
|
132
|
-
|
|
138
|
+
try:
|
|
139
|
+
r: R = self.dbos._sys_db.await_workflow_result(self.workflow_id)
|
|
140
|
+
except Exception as e:
|
|
141
|
+
serialized_e = _serialization.serialize_exception(e)
|
|
142
|
+
self.dbos._sys_db.record_get_result(self.workflow_id, None, serialized_e)
|
|
143
|
+
raise
|
|
144
|
+
serialized_r = _serialization.serialize(r)
|
|
145
|
+
self.dbos._sys_db.record_get_result(self.workflow_id, serialized_r, None)
|
|
146
|
+
return r
|
|
133
147
|
|
|
134
148
|
def get_status(self) -> "WorkflowStatus":
|
|
135
149
|
stat = self.dbos.get_workflow_status(self.workflow_id)
|
|
@@ -149,7 +163,22 @@ class WorkflowHandleAsyncTask(Generic[R]):
|
|
|
149
163
|
return self.workflow_id
|
|
150
164
|
|
|
151
165
|
async def get_result(self) -> R:
|
|
152
|
-
|
|
166
|
+
try:
|
|
167
|
+
r = await self.task
|
|
168
|
+
except Exception as e:
|
|
169
|
+
serialized_e = _serialization.serialize_exception(e)
|
|
170
|
+
await asyncio.to_thread(
|
|
171
|
+
self.dbos._sys_db.record_get_result,
|
|
172
|
+
self.workflow_id,
|
|
173
|
+
None,
|
|
174
|
+
serialized_e,
|
|
175
|
+
)
|
|
176
|
+
raise
|
|
177
|
+
serialized_r = _serialization.serialize(r)
|
|
178
|
+
await asyncio.to_thread(
|
|
179
|
+
self.dbos._sys_db.record_get_result, self.workflow_id, serialized_r, None
|
|
180
|
+
)
|
|
181
|
+
return r
|
|
153
182
|
|
|
154
183
|
async def get_status(self) -> "WorkflowStatus":
|
|
155
184
|
stat = await asyncio.to_thread(self.dbos.get_workflow_status, self.workflow_id)
|
|
@@ -168,10 +197,24 @@ class WorkflowHandleAsyncPolling(Generic[R]):
|
|
|
168
197
|
return self.workflow_id
|
|
169
198
|
|
|
170
199
|
async def get_result(self) -> R:
|
|
171
|
-
|
|
172
|
-
|
|
200
|
+
try:
|
|
201
|
+
r: R = await asyncio.to_thread(
|
|
202
|
+
self.dbos._sys_db.await_workflow_result, self.workflow_id
|
|
203
|
+
)
|
|
204
|
+
except Exception as e:
|
|
205
|
+
serialized_e = _serialization.serialize_exception(e)
|
|
206
|
+
await asyncio.to_thread(
|
|
207
|
+
self.dbos._sys_db.record_get_result,
|
|
208
|
+
self.workflow_id,
|
|
209
|
+
None,
|
|
210
|
+
serialized_e,
|
|
211
|
+
)
|
|
212
|
+
raise
|
|
213
|
+
serialized_r = _serialization.serialize(r)
|
|
214
|
+
await asyncio.to_thread(
|
|
215
|
+
self.dbos._sys_db.record_get_result, self.workflow_id, serialized_r, None
|
|
173
216
|
)
|
|
174
|
-
return
|
|
217
|
+
return r
|
|
175
218
|
|
|
176
219
|
async def get_status(self) -> "WorkflowStatus":
|
|
177
220
|
stat = await asyncio.to_thread(self.dbos.get_workflow_status, self.workflow_id)
|
|
@@ -236,22 +279,14 @@ def _init_workflow(
|
|
|
236
279
|
raise DBOSNonExistentWorkflowError(wfid)
|
|
237
280
|
wf_status = get_status_result["status"]
|
|
238
281
|
else:
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
dbos._sys_db.update_workflow_inputs(
|
|
248
|
-
wfid, _serialization.serialize_args(inputs)
|
|
249
|
-
)
|
|
250
|
-
else:
|
|
251
|
-
# Buffer the inputs for single-transaction workflows, but don't buffer the status
|
|
252
|
-
dbos._sys_db.buffer_workflow_inputs(
|
|
253
|
-
wfid, _serialization.serialize_args(inputs)
|
|
254
|
-
)
|
|
282
|
+
# Synchronously record the status and inputs for workflows
|
|
283
|
+
# TODO: Make this transactional (and with the queue step below)
|
|
284
|
+
wf_status = dbos._sys_db.insert_workflow_status(
|
|
285
|
+
status, max_recovery_attempts=max_recovery_attempts
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# TODO: Modify the inputs if they were changed by `update_workflow_inputs`
|
|
289
|
+
dbos._sys_db.update_workflow_inputs(wfid, _serialization.serialize_args(inputs))
|
|
255
290
|
|
|
256
291
|
if queue is not None and wf_status == WorkflowStatusString.ENQUEUED.value:
|
|
257
292
|
dbos._sys_db.enqueue(wfid, queue)
|
|
@@ -265,6 +300,18 @@ def _get_wf_invoke_func(
|
|
|
265
300
|
status: WorkflowStatusInternal,
|
|
266
301
|
) -> Callable[[Callable[[], R]], R]:
|
|
267
302
|
def persist(func: Callable[[], R]) -> R:
|
|
303
|
+
if not dbos.debug_mode and (
|
|
304
|
+
status["status"] == WorkflowStatusString.ERROR.value
|
|
305
|
+
or status["status"] == WorkflowStatusString.SUCCESS.value
|
|
306
|
+
):
|
|
307
|
+
dbos.logger.debug(
|
|
308
|
+
f"Workflow {status['workflow_uuid']} is already completed with status {status['status']}"
|
|
309
|
+
)
|
|
310
|
+
# Directly return the result if the workflow is already completed
|
|
311
|
+
recorded_result: R = dbos._sys_db.await_workflow_result(
|
|
312
|
+
status["workflow_uuid"]
|
|
313
|
+
)
|
|
314
|
+
return recorded_result
|
|
268
315
|
try:
|
|
269
316
|
output = func()
|
|
270
317
|
status["status"] = "SUCCESS"
|
|
@@ -273,16 +320,12 @@ def _get_wf_invoke_func(
|
|
|
273
320
|
if status["queue_name"] is not None:
|
|
274
321
|
queue = dbos._registry.queue_info_map[status["queue_name"]]
|
|
275
322
|
dbos._sys_db.remove_from_queue(status["workflow_uuid"], queue)
|
|
276
|
-
dbos._sys_db.
|
|
323
|
+
dbos._sys_db.update_workflow_status(status)
|
|
277
324
|
return output
|
|
278
325
|
except DBOSWorkflowConflictIDError:
|
|
279
|
-
#
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
status["workflow_uuid"], existing_workflow=False
|
|
283
|
-
)
|
|
284
|
-
output = wf_handle.get_result()
|
|
285
|
-
return output
|
|
326
|
+
# Await the workflow result
|
|
327
|
+
r: R = dbos._sys_db.await_workflow_result(status["workflow_uuid"])
|
|
328
|
+
return r
|
|
286
329
|
except DBOSWorkflowCancelledError as error:
|
|
287
330
|
raise
|
|
288
331
|
except Exception as error:
|
|
@@ -301,7 +344,7 @@ def _get_wf_invoke_func(
|
|
|
301
344
|
def _execute_workflow_wthread(
|
|
302
345
|
dbos: "DBOS",
|
|
303
346
|
status: WorkflowStatusInternal,
|
|
304
|
-
func: "
|
|
347
|
+
func: "Callable[P, R]",
|
|
305
348
|
ctx: DBOSContext,
|
|
306
349
|
*args: Any,
|
|
307
350
|
**kwargs: Any,
|
|
@@ -332,7 +375,7 @@ def _execute_workflow_wthread(
|
|
|
332
375
|
async def _execute_workflow_async(
|
|
333
376
|
dbos: "DBOS",
|
|
334
377
|
status: WorkflowStatusInternal,
|
|
335
|
-
func: "
|
|
378
|
+
func: "Callable[P, Coroutine[Any, Any, R]]",
|
|
336
379
|
ctx: DBOSContext,
|
|
337
380
|
*args: Any,
|
|
338
381
|
**kwargs: Any,
|
|
@@ -446,7 +489,7 @@ def _get_new_wf() -> tuple[str, DBOSContext]:
|
|
|
446
489
|
|
|
447
490
|
def start_workflow(
|
|
448
491
|
dbos: "DBOS",
|
|
449
|
-
func: "
|
|
492
|
+
func: "Callable[P, Union[R, Coroutine[Any, Any, R]]]",
|
|
450
493
|
queue_name: Optional[str],
|
|
451
494
|
execute_workflow: bool,
|
|
452
495
|
*args: P.args,
|
|
@@ -475,6 +518,15 @@ def start_workflow(
|
|
|
475
518
|
|
|
476
519
|
new_wf_id, new_wf_ctx = _get_new_wf()
|
|
477
520
|
|
|
521
|
+
ctx = new_wf_ctx
|
|
522
|
+
new_child_workflow_id = ctx.id_assigned_for_next_workflow
|
|
523
|
+
if ctx.has_parent():
|
|
524
|
+
child_workflow_id = dbos._sys_db.check_child_workflow(
|
|
525
|
+
ctx.parent_workflow_id, ctx.parent_workflow_fid
|
|
526
|
+
)
|
|
527
|
+
if child_workflow_id is not None:
|
|
528
|
+
return WorkflowHandlePolling(child_workflow_id, dbos)
|
|
529
|
+
|
|
478
530
|
status = _init_workflow(
|
|
479
531
|
dbos,
|
|
480
532
|
new_wf_ctx,
|
|
@@ -488,6 +540,13 @@ def start_workflow(
|
|
|
488
540
|
)
|
|
489
541
|
|
|
490
542
|
wf_status = status["status"]
|
|
543
|
+
if ctx.has_parent():
|
|
544
|
+
dbos._sys_db.record_child_workflow(
|
|
545
|
+
ctx.parent_workflow_id,
|
|
546
|
+
new_child_workflow_id,
|
|
547
|
+
ctx.parent_workflow_fid,
|
|
548
|
+
get_dbos_func_name(func),
|
|
549
|
+
)
|
|
491
550
|
|
|
492
551
|
if not execute_workflow or (
|
|
493
552
|
not dbos.debug_mode
|
|
@@ -496,9 +555,6 @@ def start_workflow(
|
|
|
496
555
|
or wf_status == WorkflowStatusString.SUCCESS.value
|
|
497
556
|
)
|
|
498
557
|
):
|
|
499
|
-
dbos.logger.debug(
|
|
500
|
-
f"Workflow {new_wf_id} already completed with status {wf_status}. Directly returning a workflow handle."
|
|
501
|
-
)
|
|
502
558
|
return WorkflowHandlePolling(new_wf_id, dbos)
|
|
503
559
|
|
|
504
560
|
future = dbos._executor.submit(
|
|
@@ -515,7 +571,7 @@ def start_workflow(
|
|
|
515
571
|
|
|
516
572
|
async def start_workflow_async(
|
|
517
573
|
dbos: "DBOS",
|
|
518
|
-
func: "
|
|
574
|
+
func: "Callable[P, Coroutine[Any, Any, R]]",
|
|
519
575
|
queue_name: Optional[str],
|
|
520
576
|
execute_workflow: bool,
|
|
521
577
|
*args: P.args,
|
|
@@ -544,6 +600,17 @@ async def start_workflow_async(
|
|
|
544
600
|
|
|
545
601
|
new_wf_id, new_wf_ctx = _get_new_wf()
|
|
546
602
|
|
|
603
|
+
ctx = new_wf_ctx
|
|
604
|
+
new_child_workflow_id = ctx.id_assigned_for_next_workflow
|
|
605
|
+
if ctx.has_parent():
|
|
606
|
+
child_workflow_id = await asyncio.to_thread(
|
|
607
|
+
dbos._sys_db.check_child_workflow,
|
|
608
|
+
ctx.parent_workflow_id,
|
|
609
|
+
ctx.parent_workflow_fid,
|
|
610
|
+
)
|
|
611
|
+
if child_workflow_id is not None:
|
|
612
|
+
return WorkflowHandleAsyncPolling(child_workflow_id, dbos)
|
|
613
|
+
|
|
547
614
|
status = await asyncio.to_thread(
|
|
548
615
|
_init_workflow,
|
|
549
616
|
dbos,
|
|
@@ -557,6 +624,15 @@ async def start_workflow_async(
|
|
|
557
624
|
max_recovery_attempts=fi.max_recovery_attempts,
|
|
558
625
|
)
|
|
559
626
|
|
|
627
|
+
if ctx.has_parent():
|
|
628
|
+
await asyncio.to_thread(
|
|
629
|
+
dbos._sys_db.record_child_workflow,
|
|
630
|
+
ctx.parent_workflow_id,
|
|
631
|
+
new_child_workflow_id,
|
|
632
|
+
ctx.parent_workflow_fid,
|
|
633
|
+
get_dbos_func_name(func),
|
|
634
|
+
)
|
|
635
|
+
|
|
560
636
|
wf_status = status["status"]
|
|
561
637
|
|
|
562
638
|
if not execute_workflow or (
|
|
@@ -566,9 +642,6 @@ async def start_workflow_async(
|
|
|
566
642
|
or wf_status == WorkflowStatusString.SUCCESS.value
|
|
567
643
|
)
|
|
568
644
|
):
|
|
569
|
-
dbos.logger.debug(
|
|
570
|
-
f"Workflow {new_wf_id} already completed with status {wf_status}. Directly returning a workflow handle."
|
|
571
|
-
)
|
|
572
645
|
return WorkflowHandleAsyncPolling(new_wf_id, dbos)
|
|
573
646
|
|
|
574
647
|
coro = _execute_workflow_async(dbos, status, func, new_wf_ctx, *args, **kwargs)
|
|
@@ -628,8 +701,30 @@ def workflow_wrapper(
|
|
|
628
701
|
|
|
629
702
|
wfOutcome = Outcome[R].make(functools.partial(func, *args, **kwargs))
|
|
630
703
|
|
|
704
|
+
workflow_id = None
|
|
705
|
+
|
|
631
706
|
def init_wf() -> Callable[[Callable[[], R]], R]:
|
|
707
|
+
|
|
708
|
+
def recorded_result(
|
|
709
|
+
c_wfid: str, dbos: "DBOS"
|
|
710
|
+
) -> Callable[[Callable[[], R]], R]:
|
|
711
|
+
def recorded_result_inner(func: Callable[[], R]) -> R:
|
|
712
|
+
r: R = dbos._sys_db.await_workflow_result(c_wfid)
|
|
713
|
+
return r
|
|
714
|
+
|
|
715
|
+
return recorded_result_inner
|
|
716
|
+
|
|
632
717
|
ctx = assert_current_dbos_context() # Now the child ctx
|
|
718
|
+
nonlocal workflow_id
|
|
719
|
+
workflow_id = ctx.workflow_id
|
|
720
|
+
|
|
721
|
+
if ctx.has_parent():
|
|
722
|
+
child_workflow_id = dbos._sys_db.check_child_workflow(
|
|
723
|
+
ctx.parent_workflow_id, ctx.parent_workflow_fid
|
|
724
|
+
)
|
|
725
|
+
if child_workflow_id is not None:
|
|
726
|
+
return recorded_result(child_workflow_id, dbos)
|
|
727
|
+
|
|
633
728
|
status = _init_workflow(
|
|
634
729
|
dbos,
|
|
635
730
|
ctx,
|
|
@@ -640,17 +735,44 @@ def workflow_wrapper(
|
|
|
640
735
|
temp_wf_type=get_temp_workflow_type(func),
|
|
641
736
|
max_recovery_attempts=max_recovery_attempts,
|
|
642
737
|
)
|
|
738
|
+
|
|
643
739
|
# TODO: maybe modify the parameters if they've been changed by `_init_workflow`
|
|
644
740
|
dbos.logger.debug(
|
|
645
741
|
f"Running workflow, id: {ctx.workflow_id}, name: {get_dbos_func_name(func)}"
|
|
646
742
|
)
|
|
647
743
|
|
|
744
|
+
if ctx.has_parent():
|
|
745
|
+
dbos._sys_db.record_child_workflow(
|
|
746
|
+
ctx.parent_workflow_id,
|
|
747
|
+
ctx.workflow_id,
|
|
748
|
+
ctx.parent_workflow_fid,
|
|
749
|
+
get_dbos_func_name(func),
|
|
750
|
+
)
|
|
751
|
+
|
|
648
752
|
return _get_wf_invoke_func(dbos, status)
|
|
649
753
|
|
|
754
|
+
def record_get_result(func: Callable[[], R]) -> R:
|
|
755
|
+
"""
|
|
756
|
+
If a child workflow is invoked synchronously, this records the implicit "getResult" where the
|
|
757
|
+
parent retrieves the child's output. It executes in the CALLER'S context, not the workflow's.
|
|
758
|
+
"""
|
|
759
|
+
try:
|
|
760
|
+
r = func()
|
|
761
|
+
except Exception as e:
|
|
762
|
+
serialized_e = _serialization.serialize_exception(e)
|
|
763
|
+
assert workflow_id is not None
|
|
764
|
+
dbos._sys_db.record_get_result(workflow_id, None, serialized_e)
|
|
765
|
+
raise
|
|
766
|
+
serialized_r = _serialization.serialize(r)
|
|
767
|
+
assert workflow_id is not None
|
|
768
|
+
dbos._sys_db.record_get_result(workflow_id, serialized_r, None)
|
|
769
|
+
return r
|
|
770
|
+
|
|
650
771
|
outcome = (
|
|
651
772
|
wfOutcome.wrap(init_wf)
|
|
652
773
|
.also(DBOSAssumeRole(rr))
|
|
653
774
|
.also(enterWorkflowCtxMgr(attributes))
|
|
775
|
+
.then(record_get_result)
|
|
654
776
|
)
|
|
655
777
|
return outcome() # type: ignore
|
|
656
778
|
|
|
@@ -853,6 +975,8 @@ def decorate_step(
|
|
|
853
975
|
) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
|
854
976
|
def decorator(func: Callable[P, R]) -> Callable[P, R]:
|
|
855
977
|
|
|
978
|
+
stepName = func.__qualname__
|
|
979
|
+
|
|
856
980
|
def invoke_step(*args: Any, **kwargs: Any) -> Any:
|
|
857
981
|
if dbosreg.dbos is None:
|
|
858
982
|
raise DBOSException(
|
|
@@ -877,7 +1001,7 @@ def decorate_step(
|
|
|
877
1001
|
|
|
878
1002
|
def on_exception(attempt: int, error: BaseException) -> float:
|
|
879
1003
|
dbos.logger.warning(
|
|
880
|
-
f"Step being automatically retried. (attempt {attempt} of {attempts}). {traceback.format_exc()}"
|
|
1004
|
+
f"Step being automatically retried. (attempt {attempt + 1} of {attempts}). {traceback.format_exc()}"
|
|
881
1005
|
)
|
|
882
1006
|
ctx = assert_current_dbos_context()
|
|
883
1007
|
ctx.get_current_span().add_event(
|
|
@@ -897,19 +1021,20 @@ def decorate_step(
|
|
|
897
1021
|
step_output: OperationResultInternal = {
|
|
898
1022
|
"workflow_uuid": ctx.workflow_id,
|
|
899
1023
|
"function_id": ctx.function_id,
|
|
1024
|
+
"function_name": stepName,
|
|
900
1025
|
"output": None,
|
|
901
1026
|
"error": None,
|
|
902
1027
|
}
|
|
903
1028
|
|
|
904
1029
|
try:
|
|
905
1030
|
output = func()
|
|
906
|
-
step_output["output"] = _serialization.serialize(output)
|
|
907
|
-
return output
|
|
908
1031
|
except Exception as error:
|
|
909
1032
|
step_output["error"] = _serialization.serialize_exception(error)
|
|
910
|
-
raise
|
|
911
|
-
finally:
|
|
912
1033
|
dbos._sys_db.record_operation_result(step_output)
|
|
1034
|
+
raise
|
|
1035
|
+
step_output["output"] = _serialization.serialize(output)
|
|
1036
|
+
dbos._sys_db.record_operation_result(step_output)
|
|
1037
|
+
return output
|
|
913
1038
|
|
|
914
1039
|
def check_existing_result() -> Union[NoResult, R]:
|
|
915
1040
|
ctx = assert_current_dbos_context()
|
dbos/_db_wizard.py
CHANGED
|
@@ -49,6 +49,7 @@ def db_wizard(config: "ConfigFile") -> "ConfigFile":
|
|
|
49
49
|
|
|
50
50
|
# 2. If the error is due to password authentication or the configuration is non-default, surface the error and exit.
|
|
51
51
|
error_str = str(db_connection_error)
|
|
52
|
+
dbos_logger.debug(f"Error connecting to Postgres: {error_str}")
|
|
52
53
|
if (
|
|
53
54
|
"password authentication failed" in error_str
|
|
54
55
|
or "28P01" in error_str
|
|
@@ -182,17 +183,12 @@ def _check_db_connectivity(config: "ConfigFile") -> Optional[Exception]:
|
|
|
182
183
|
host=config["database"]["hostname"],
|
|
183
184
|
port=config["database"]["port"],
|
|
184
185
|
database="postgres",
|
|
185
|
-
query={"connect_timeout": "
|
|
186
|
+
query={"connect_timeout": "2"},
|
|
186
187
|
)
|
|
187
188
|
postgres_db_engine = create_engine(postgres_db_url)
|
|
188
189
|
try:
|
|
189
190
|
with postgres_db_engine.connect() as conn:
|
|
190
|
-
|
|
191
|
-
if val != 1:
|
|
192
|
-
dbos_logger.error(
|
|
193
|
-
f"Unexpected value returned from database: expected 1, received {val}"
|
|
194
|
-
)
|
|
195
|
-
return Exception()
|
|
191
|
+
conn.execute(text("SELECT 1")).scalar()
|
|
196
192
|
except Exception as e:
|
|
197
193
|
return e
|
|
198
194
|
finally:
|