dbos 0.21.0a5__py3-none-any.whl → 0.21.0a7__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.

Potentially problematic release.


This version of dbos might be problematic. Click here for more details.

dbos/_dbos.py CHANGED
@@ -56,7 +56,7 @@ from ._registrations import (
56
56
  )
57
57
  from ._roles import default_required_roles, required_roles
58
58
  from ._scheduler import ScheduledWorkflow, scheduled
59
- from ._sys_db import WorkflowStatusString, reset_system_database
59
+ from ._sys_db import reset_system_database
60
60
  from ._tracer import dbos_tracer
61
61
 
62
62
  if TYPE_CHECKING:
@@ -613,6 +613,7 @@ class DBOS:
613
613
  workflow_id=workflow_id,
614
614
  status=stat["status"],
615
615
  name=stat["name"],
616
+ executor_id=stat["executor_id"],
616
617
  recovery_attempts=stat["recovery_attempts"],
617
618
  class_name=stat["class_name"],
618
619
  config_name=stat["config_name"],
@@ -909,6 +910,7 @@ class WorkflowStatus:
909
910
  workflow_id(str): The ID of the workflow execution
910
911
  status(str): The status of the execution, from `WorkflowStatusString`
911
912
  name(str): The workflow function name
913
+ executor_id(str): The ID of the executor running the workflow
912
914
  class_name(str): For member functions, the name of the class containing the workflow function
913
915
  config_name(str): For instance member functions, the name of the class instance for the execution
914
916
  queue_name(str): For workflows that are or were queued, the queue name
@@ -922,6 +924,7 @@ class WorkflowStatus:
922
924
  workflow_id: str
923
925
  status: str
924
926
  name: str
927
+ executor_id: Optional[str]
925
928
  class_name: Optional[str]
926
929
  config_name: Optional[str]
927
930
  queue_name: Optional[str]
dbos/_recovery.py CHANGED
@@ -6,20 +6,29 @@ from typing import TYPE_CHECKING, Any, List
6
6
 
7
7
  from ._core import execute_workflow_by_id
8
8
  from ._error import DBOSWorkflowFunctionNotFoundError
9
+ from ._sys_db import GetPendingWorkflowsOutput
9
10
 
10
11
  if TYPE_CHECKING:
11
12
  from ._dbos import DBOS, WorkflowHandle
12
13
 
13
14
 
14
- def startup_recovery_thread(dbos: "DBOS", workflow_ids: List[str]) -> None:
15
+ def startup_recovery_thread(
16
+ dbos: "DBOS", pending_workflows: List[GetPendingWorkflowsOutput]
17
+ ) -> None:
15
18
  """Attempt to recover local pending workflows on startup using a background thread."""
16
19
  stop_event = threading.Event()
17
20
  dbos.stop_events.append(stop_event)
18
- while not stop_event.is_set() and len(workflow_ids) > 0:
21
+ while not stop_event.is_set() and len(pending_workflows) > 0:
19
22
  try:
20
- for workflowID in list(workflow_ids):
21
- execute_workflow_by_id(dbos, workflowID)
22
- workflow_ids.remove(workflowID)
23
+ for pending_workflow in list(pending_workflows):
24
+ if (
25
+ pending_workflow.queue_name
26
+ and pending_workflow.queue_name != "_dbos_internal_queue"
27
+ ):
28
+ dbos._sys_db.clear_queue_assignment(pending_workflow.workflow_uuid)
29
+ continue
30
+ execute_workflow_by_id(dbos, pending_workflow.workflow_uuid)
31
+ pending_workflows.remove(pending_workflow)
23
32
  except DBOSWorkflowFunctionNotFoundError:
24
33
  time.sleep(1)
25
34
  except Exception as e:
@@ -39,12 +48,23 @@ def recover_pending_workflows(
39
48
  f"Skip local recovery because it's running in a VM: {os.environ.get('DBOS__VMID')}"
40
49
  )
41
50
  dbos.logger.debug(f"Recovering pending workflows for executor: {executor_id}")
42
- workflow_ids = dbos._sys_db.get_pending_workflows(executor_id)
43
- dbos.logger.debug(f"Pending workflows: {workflow_ids}")
44
-
45
- for workflowID in workflow_ids:
46
- handle = execute_workflow_by_id(dbos, workflowID)
47
- workflow_handles.append(handle)
51
+ pending_workflows = dbos._sys_db.get_pending_workflows(executor_id)
52
+ for pending_workflow in pending_workflows:
53
+ if (
54
+ pending_workflow.queue_name
55
+ and pending_workflow.queue_name != "_dbos_internal_queue"
56
+ ):
57
+ try:
58
+ dbos._sys_db.clear_queue_assignment(pending_workflow.workflow_uuid)
59
+ workflow_handles.append(
60
+ dbos.retrieve_workflow(pending_workflow.workflow_uuid)
61
+ )
62
+ except Exception as e:
63
+ dbos.logger.error(e)
64
+ else:
65
+ workflow_handles.append(
66
+ execute_workflow_by_id(dbos, pending_workflow.workflow_uuid)
67
+ )
48
68
 
49
69
  dbos.logger.info("Recovered pending workflows")
50
70
  return workflow_handles
dbos/_sys_db.py CHANGED
@@ -140,6 +140,12 @@ class GetWorkflowsOutput:
140
140
  self.workflow_uuids = workflow_uuids
141
141
 
142
142
 
143
+ class GetPendingWorkflowsOutput:
144
+ def __init__(self, *, workflow_uuid: str, queue_name: Optional[str] = None):
145
+ self.workflow_uuid: str = workflow_uuid
146
+ self.queue_name: Optional[str] = queue_name
147
+
148
+
143
149
  class WorkflowInformation(TypedDict, total=False):
144
150
  workflow_uuid: str
145
151
  status: WorkflowStatuses # The status of the workflow.
@@ -465,6 +471,7 @@ class SystemDatabase:
465
471
  SystemSchema.workflow_status.c.authenticated_roles,
466
472
  SystemSchema.workflow_status.c.assumed_role,
467
473
  SystemSchema.workflow_status.c.queue_name,
474
+ SystemSchema.workflow_status.c.executor_id,
468
475
  ).where(SystemSchema.workflow_status.c.workflow_uuid == workflow_uuid)
469
476
  ).fetchone()
470
477
  if row is None:
@@ -479,7 +486,7 @@ class SystemDatabase:
479
486
  "error": None,
480
487
  "app_id": None,
481
488
  "app_version": None,
482
- "executor_id": None,
489
+ "executor_id": row[10],
483
490
  "request": row[2],
484
491
  "recovery_attempts": row[3],
485
492
  "authenticated_user": row[6],
@@ -665,7 +672,7 @@ class SystemDatabase:
665
672
 
666
673
  def get_workflows(self, input: GetWorkflowsInput) -> GetWorkflowsOutput:
667
674
  query = sa.select(SystemSchema.workflow_status.c.workflow_uuid).order_by(
668
- SystemSchema.workflow_status.c.created_at.desc()
675
+ SystemSchema.workflow_status.c.created_at.asc()
669
676
  )
670
677
  if input.name:
671
678
  query = query.where(SystemSchema.workflow_status.c.name == input.name)
@@ -711,7 +718,7 @@ class SystemDatabase:
711
718
  SystemSchema.workflow_queue.c.workflow_uuid
712
719
  == SystemSchema.workflow_status.c.workflow_uuid,
713
720
  )
714
- .order_by(SystemSchema.workflow_status.c.created_at.desc())
721
+ .order_by(SystemSchema.workflow_status.c.created_at.asc())
715
722
  )
716
723
 
717
724
  if input.get("name"):
@@ -746,16 +753,27 @@ class SystemDatabase:
746
753
 
747
754
  return GetWorkflowsOutput(workflow_uuids)
748
755
 
749
- def get_pending_workflows(self, executor_id: str) -> list[str]:
756
+ def get_pending_workflows(
757
+ self, executor_id: str
758
+ ) -> list[GetPendingWorkflowsOutput]:
750
759
  with self.engine.begin() as c:
751
760
  rows = c.execute(
752
- sa.select(SystemSchema.workflow_status.c.workflow_uuid).where(
761
+ sa.select(
762
+ SystemSchema.workflow_status.c.workflow_uuid,
763
+ SystemSchema.workflow_status.c.queue_name,
764
+ ).where(
753
765
  SystemSchema.workflow_status.c.status
754
766
  == WorkflowStatusString.PENDING.value,
755
767
  SystemSchema.workflow_status.c.executor_id == executor_id,
756
768
  )
757
769
  ).fetchall()
758
- return [row[0] for row in rows]
770
+ return [
771
+ GetPendingWorkflowsOutput(
772
+ workflow_uuid=row.workflow_uuid,
773
+ queue_name=row.queue_name,
774
+ )
775
+ for row in rows
776
+ ]
759
777
 
760
778
  def record_operation_result(
761
779
  self, result: OperationResultInternal, conn: Optional[sa.Connection] = None
@@ -1375,6 +1393,19 @@ class SystemDatabase:
1375
1393
  .values(completed_at_epoch_ms=int(time.time() * 1000))
1376
1394
  )
1377
1395
 
1396
+ def clear_queue_assignment(self, workflow_id: str) -> None:
1397
+ with self.engine.begin() as c:
1398
+ c.execute(
1399
+ sa.update(SystemSchema.workflow_queue)
1400
+ .where(SystemSchema.workflow_queue.c.workflow_uuid == workflow_id)
1401
+ .values(executor_id=None, started_at_epoch_ms=None)
1402
+ )
1403
+ c.execute(
1404
+ sa.update(SystemSchema.workflow_status)
1405
+ .where(SystemSchema.workflow_status.c.workflow_uuid == workflow_id)
1406
+ .values(executor_id=None, status=WorkflowStatusString.ENQUEUED.value)
1407
+ )
1408
+
1378
1409
 
1379
1410
  def reset_system_database(config: ConfigFile) -> None:
1380
1411
  sysdb_name = (
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.21.0a5
3
+ Version: 0.21.0a7
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -1,7 +1,7 @@
1
- dbos-0.21.0a5.dist-info/METADATA,sha256=1LAPN_Eu4X7ptR9xM02sR1tryFaQ42KeKGkeOm3yCQQ,5309
2
- dbos-0.21.0a5.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
- dbos-0.21.0a5.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
- dbos-0.21.0a5.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
1
+ dbos-0.21.0a7.dist-info/METADATA,sha256=76OQDkyQg_N8Yg1zzmRXJ34Kofg1rX7VgTkhX7mMlnI,5309
2
+ dbos-0.21.0a7.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
+ dbos-0.21.0a7.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
+ dbos-0.21.0a7.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
5
5
  dbos/__init__.py,sha256=CxRHBHEthPL4PZoLbZhp3rdm44-KkRTT2-7DkK9d4QQ,724
6
6
  dbos/_admin_server.py,sha256=PJgneZG9-64TapZrPeJtt73puAswRImCE5uce2k2PKU,4750
7
7
  dbos/_app_db.py,sha256=_tv2vmPjjiaikwgxH3mqxgJ4nUUcG2-0uMXKWCqVu1c,5509
@@ -13,7 +13,7 @@ dbos/_context.py,sha256=FHB_fpE4fQt4fIJvAmMMsbY4xHwH77gsW01cFsRZjsE,17779
13
13
  dbos/_core.py,sha256=nGiXyYgV8H5TRRZG0e8HCd5IZimufYQLmKNr7nBbwbo,36564
14
14
  dbos/_croniter.py,sha256=hbhgfsHBqclUS8VeLnJ9PSE9Z54z6mi4nnrr1aUXn0k,47561
15
15
  dbos/_db_wizard.py,sha256=xgKLna0_6Xi50F3o8msRosXba8NScHlpJR5ICVCkHDQ,7534
16
- dbos/_dbos.py,sha256=y5RgXPxdNsnketphphGMlaYZABFEQpr78UK-Xyja6dk,36216
16
+ dbos/_dbos.py,sha256=wAjdlUgDSIC_Q8D_GZYDoiKaxjtr6KNHeq6DDuUh9do,36340
17
17
  dbos/_dbos_config.py,sha256=DfiqVVxNqnafkocSzLqBp1Ig5vCviDTDK_GO3zTtQqI,8298
18
18
  dbos/_error.py,sha256=vtaSsG0QW6cRlwfZ4zzZWy_IHCZlomwSlrDyGWuyn8c,4337
19
19
  dbos/_fastapi.py,sha256=ke03vqsSYDnO6XeOtOVFXj0-f-v1MGsOxa9McaROvNc,3616
@@ -32,7 +32,7 @@ dbos/_migrations/versions/d76646551a6c_workflow_queue.py,sha256=G942nophZ2uC2vc4
32
32
  dbos/_migrations/versions/eab0cc1d9a14_job_queue.py,sha256=uvhFOtqbBreCePhAxZfIT0qCAI7BiZTou9wt6QnbY7c,1412
33
33
  dbos/_outcome.py,sha256=FDMgWVjZ06vm9xO-38H17mTqBImUYQxgKs_bDCSIAhE,6648
34
34
  dbos/_queue.py,sha256=o_aczwualJTMoXb0XXL-Y5QH77OEukWzuerogbWi2ho,2779
35
- dbos/_recovery.py,sha256=ehruOebVRl8qHftydStGZd-VjJ7p9oYtYXDwyT4ZHRY,1874
35
+ dbos/_recovery.py,sha256=rek9rm2CaENbbl_vu3To-BdXop7tMEyGvtoNiJLVxjQ,2772
36
36
  dbos/_registrations.py,sha256=mei6q6_3R5uei8i_Wo_TqGZs85s10shOekDX41sFYD0,6642
37
37
  dbos/_request.py,sha256=cX1B3Atlh160phgS35gF1VEEV4pD126c9F3BDgBmxZU,929
38
38
  dbos/_roles.py,sha256=iOsgmIAf1XVzxs3gYWdGRe1B880YfOw5fpU7Jwx8_A8,2271
@@ -41,7 +41,7 @@ dbos/_schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
41
  dbos/_schemas/application_database.py,sha256=KeyoPrF7hy_ODXV7QNike_VFSD74QBRfQ76D7QyE9HI,966
42
42
  dbos/_schemas/system_database.py,sha256=rwp4EvCSaXcUoMaRczZCvETCxGp72k3-hvLyGUDkih0,5163
43
43
  dbos/_serialization.py,sha256=YCYv0qKAwAZ1djZisBC7khvKqG-5OcIv9t9EC5PFIog,1743
44
- dbos/_sys_db.py,sha256=DLoSddcraHPUMiA5zWIck9Kes7_UO39RL2CRPqtYyz0,58247
44
+ dbos/_sys_db.py,sha256=U5rXoS2gA4vm8YT6Rja_YyP2EXWLlo1HqDka1tnpRjk,59460
45
45
  dbos/_templates/dbos-db-starter/README.md,sha256=GhxhBj42wjTt1fWEtwNriHbJuKb66Vzu89G4pxNHw2g,930
46
46
  dbos/_templates/dbos-db-starter/__package/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  dbos/_templates/dbos-db-starter/__package/main.py,sha256=eI0SS9Nwj-fldtiuSzIlIG6dC91GXXwdRsoHxv6S_WI,2719
@@ -60,4 +60,4 @@ dbos/cli/cli.py,sha256=_tXw2IQrWW7fV_h51f_R99vEBSi6aMLz-vCOxKaENiQ,14155
60
60
  dbos/dbos-config.schema.json,sha256=X5TpXNcARGceX0zQs0fVgtZW_Xj9uBbY5afPt9Rz9yk,5741
61
61
  dbos/py.typed,sha256=QfzXT1Ktfk3Rj84akygc7_42z0lRpCq0Ilh8OXI6Zas,44
62
62
  version/__init__.py,sha256=L4sNxecRuqdtSFdpUGX3TtBi9KL3k7YsZVIvv-fv9-A,1678
63
- dbos-0.21.0a5.dist-info/RECORD,,
63
+ dbos-0.21.0a7.dist-info/RECORD,,