dbos 1.2.0a2__py3-none-any.whl → 1.2.0a4__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/_dbos.py CHANGED
@@ -90,7 +90,6 @@ from ._context import (
90
90
  from ._dbos_config import (
91
91
  ConfigFile,
92
92
  DBOSConfig,
93
- check_config_consistency,
94
93
  overwrite_config,
95
94
  process_config,
96
95
  set_env_vars,
@@ -324,7 +323,6 @@ class DBOS:
324
323
  unvalidated_config = translate_dbos_config_to_config_file(config)
325
324
  if os.environ.get("DBOS__CLOUD") == "true":
326
325
  unvalidated_config = overwrite_config(unvalidated_config)
327
- check_config_consistency(name=unvalidated_config["name"])
328
326
 
329
327
  if unvalidated_config is not None:
330
328
  self._config: ConfigFile = process_config(data=unvalidated_config)
dbos/_dbos_config.py CHANGED
@@ -529,26 +529,3 @@ def overwrite_config(provided_config: ConfigFile) -> ConfigFile:
529
529
  del provided_config["env"]
530
530
 
531
531
  return provided_config
532
-
533
-
534
- def check_config_consistency(
535
- *,
536
- name: str,
537
- config_file_path: str = DBOS_CONFIG_PATH,
538
- ) -> None:
539
- # First load the config file and check whether it is present
540
- try:
541
- config = load_config(config_file_path, silent=True, run_process_config=False)
542
- except FileNotFoundError:
543
- dbos_logger.debug(
544
- f"No configuration file {config_file_path} found. Skipping consistency check with provided config."
545
- )
546
- return
547
- except Exception as e:
548
- raise e
549
-
550
- # Check the name
551
- if name != config["name"]:
552
- raise DBOSInitializationError(
553
- f"Provided app name '{name}' does not match the app name '{config['name']}' in {config_file_path}."
554
- )
dbos/_queue.py CHANGED
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Any, Callable, Coroutine, Optional, TypedDict
5
5
  from psycopg import errors
6
6
  from sqlalchemy.exc import OperationalError
7
7
 
8
+ from dbos._logger import dbos_logger
8
9
  from dbos._utils import GlobalParams
9
10
 
10
11
  from ._core import P, R, execute_workflow_by_id, start_workflow, start_workflow_async
@@ -56,6 +57,8 @@ class Queue:
56
57
  from ._dbos import _get_or_create_dbos_registry
57
58
 
58
59
  registry = _get_or_create_dbos_registry()
60
+ if self.name in registry.queue_info_map:
61
+ dbos_logger.warning(f"Queue {name} has already been declared")
59
62
  registry.queue_info_map[self.name] = self
60
63
 
61
64
  def enqueue(
dbos/_sys_db.py CHANGED
@@ -1710,13 +1710,8 @@ class SystemDatabase:
1710
1710
  if num_recent_queries >= queue.limiter["limit"]:
1711
1711
  return []
1712
1712
 
1713
- # Dequeue functions eligible for this worker and ordered by the time at which they were enqueued.
1714
- # If there is a global or local concurrency limit N, select only the N oldest enqueued
1715
- # functions, else select all of them.
1716
-
1717
- # First lets figure out how many tasks are eligible for dequeue.
1718
- # This means figuring out how many unstarted tasks are within the local and global concurrency limits
1719
- running_tasks_query = (
1713
+ # Count how many workflows on this queue are currently PENDING both locally and globally.
1714
+ pending_tasks_query = (
1720
1715
  sa.select(
1721
1716
  SystemSchema.workflow_status.c.executor_id,
1722
1717
  sa.func.count().label("task_count"),
@@ -1730,41 +1725,37 @@ class SystemDatabase:
1730
1725
  )
1731
1726
  .where(SystemSchema.workflow_queue.c.queue_name == queue.name)
1732
1727
  .where(
1733
- SystemSchema.workflow_queue.c.started_at_epoch_ms.isnot(
1734
- None
1735
- ) # Task is started
1736
- )
1737
- .where(
1738
- SystemSchema.workflow_queue.c.completed_at_epoch_ms.is_(
1739
- None
1740
- ) # Task is not completed.
1728
+ SystemSchema.workflow_status.c.status
1729
+ == WorkflowStatusString.PENDING.value
1741
1730
  )
1742
1731
  .group_by(SystemSchema.workflow_status.c.executor_id)
1743
1732
  )
1744
- running_tasks_result = c.execute(running_tasks_query).fetchall()
1745
- running_tasks_result_dict = {row[0]: row[1] for row in running_tasks_result}
1746
- running_tasks_for_this_worker = running_tasks_result_dict.get(
1747
- executor_id, 0
1748
- ) # Get count for current executor
1733
+ pending_workflows = c.execute(pending_tasks_query).fetchall()
1734
+ pending_workflows_dict = {row[0]: row[1] for row in pending_workflows}
1735
+ local_pending_workflows = pending_workflows_dict.get(executor_id, 0)
1749
1736
 
1737
+ # Compute max_tasks, the number of workflows that can be dequeued given local and global concurrency limits,
1750
1738
  max_tasks = float("inf")
1751
1739
  if queue.worker_concurrency is not None:
1752
- max_tasks = max(
1753
- 0, queue.worker_concurrency - running_tasks_for_this_worker
1754
- )
1740
+ # Print a warning if the local concurrency limit is violated
1741
+ if local_pending_workflows > queue.worker_concurrency:
1742
+ dbos_logger.warning(
1743
+ f"The number of local pending workflows ({local_pending_workflows}) on queue {queue.name} exceeds the local concurrency limit ({queue.worker_concurrency})"
1744
+ )
1745
+ max_tasks = max(0, queue.worker_concurrency - local_pending_workflows)
1746
+
1755
1747
  if queue.concurrency is not None:
1756
- total_running_tasks = sum(running_tasks_result_dict.values())
1757
- # Queue global concurrency limit should always be >= running_tasks_count
1758
- # This should never happen but a check + warning doesn't hurt
1759
- if total_running_tasks > queue.concurrency:
1748
+ global_pending_workflows = sum(pending_workflows_dict.values())
1749
+ # Print a warning if the global concurrency limit is violated
1750
+ if global_pending_workflows > queue.concurrency:
1760
1751
  dbos_logger.warning(
1761
- f"Total running tasks ({total_running_tasks}) exceeds the global concurrency limit ({queue.concurrency})"
1752
+ f"The total number of pending workflows ({global_pending_workflows}) on queue {queue.name} exceeds the global concurrency limit ({queue.concurrency})"
1762
1753
  )
1763
- available_tasks = max(0, queue.concurrency - total_running_tasks)
1754
+ available_tasks = max(0, queue.concurrency - global_pending_workflows)
1764
1755
  max_tasks = min(max_tasks, available_tasks)
1765
1756
 
1766
1757
  # Retrieve the first max_tasks workflows in the queue.
1767
- # Only retrieve workflows of the appropriate version (or without version set)
1758
+ # Only retrieve workflows of the local version (or without version set)
1768
1759
  query = (
1769
1760
  sa.select(
1770
1761
  SystemSchema.workflow_queue.c.workflow_uuid,
@@ -1777,8 +1768,10 @@ class SystemDatabase:
1777
1768
  )
1778
1769
  )
1779
1770
  .where(SystemSchema.workflow_queue.c.queue_name == queue.name)
1780
- .where(SystemSchema.workflow_queue.c.started_at_epoch_ms == None)
1781
- .where(SystemSchema.workflow_queue.c.completed_at_epoch_ms == None)
1771
+ .where(
1772
+ SystemSchema.workflow_status.c.status
1773
+ == WorkflowStatusString.ENQUEUED.value
1774
+ )
1782
1775
  .where(
1783
1776
  sa.or_(
1784
1777
  SystemSchema.workflow_status.c.application_version
@@ -1807,20 +1800,16 @@ class SystemDatabase:
1807
1800
  ret_ids: list[str] = []
1808
1801
 
1809
1802
  for id in dequeued_ids:
1810
- # If we have a limiter, stop starting functions when the number
1811
- # of functions started this period exceeds the limit.
1803
+ # If we have a limiter, stop dequeueing workflows when the number
1804
+ # of workflows started this period exceeds the limit.
1812
1805
  if queue.limiter is not None:
1813
1806
  if len(ret_ids) + num_recent_queries >= queue.limiter["limit"]:
1814
1807
  break
1815
1808
 
1816
- # To start a function, first set its status to PENDING and update its executor ID
1817
- res = c.execute(
1809
+ # To start a workflow, first set its status to PENDING and update its executor ID
1810
+ c.execute(
1818
1811
  SystemSchema.workflow_status.update()
1819
1812
  .where(SystemSchema.workflow_status.c.workflow_uuid == id)
1820
- .where(
1821
- SystemSchema.workflow_status.c.status
1822
- == WorkflowStatusString.ENQUEUED.value
1823
- )
1824
1813
  .values(
1825
1814
  status=WorkflowStatusString.PENDING.value,
1826
1815
  application_version=app_version,
@@ -1843,16 +1832,15 @@ class SystemDatabase:
1843
1832
  ),
1844
1833
  )
1845
1834
  )
1846
- if res.rowcount > 0:
1847
- # Then give it a start time and assign the executor ID
1848
- c.execute(
1849
- SystemSchema.workflow_queue.update()
1850
- .where(SystemSchema.workflow_queue.c.workflow_uuid == id)
1851
- .values(started_at_epoch_ms=start_time_ms)
1852
- )
1853
- ret_ids.append(id)
1835
+ # Then give it a start time
1836
+ c.execute(
1837
+ SystemSchema.workflow_queue.update()
1838
+ .where(SystemSchema.workflow_queue.c.workflow_uuid == id)
1839
+ .values(started_at_epoch_ms=start_time_ms)
1840
+ )
1841
+ ret_ids.append(id)
1854
1842
 
1855
- # If we have a limiter, garbage-collect all completed functions started
1843
+ # If we have a limiter, garbage-collect all completed workflows started
1856
1844
  # before the period. If there's no limiter, there's no need--they were
1857
1845
  # deleted on completion.
1858
1846
  if queue.limiter is not None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 1.2.0a2
3
+ Version: 1.2.0a4
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-1.2.0a2.dist-info/METADATA,sha256=at-2zS4N-BoXxmKlOeZt7HRA5shIG9pu3pb98t8VFNs,13267
2
- dbos-1.2.0a2.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
- dbos-1.2.0a2.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
- dbos-1.2.0a2.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
1
+ dbos-1.2.0a4.dist-info/METADATA,sha256=Tx0t3cKXZZ4AFcoCMceTSlObY86rhSLkxAedOQw5V6c,13267
2
+ dbos-1.2.0a4.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
+ dbos-1.2.0a4.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
+ dbos-1.2.0a4.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
5
5
  dbos/__init__.py,sha256=NssPCubaBxdiKarOWa-wViz1hdJSkmBGcpLX_gQ4NeA,891
6
6
  dbos/__main__.py,sha256=G7Exn-MhGrVJVDbgNlpzhfh8WMX_72t3_oJaFT9Lmt8,653
7
7
  dbos/_admin_server.py,sha256=TWXi4drrzKFpKkUmEJpJkQBZxAtOalnhtYicEn2nDK0,10618
@@ -13,8 +13,8 @@ dbos/_conductor/protocol.py,sha256=wgOFZxmS81bv0WCB9dAyg0s6QzldpzVKQDoSPeaX0Ws,6
13
13
  dbos/_context.py,sha256=5ajoWAmToAfzzmMLylnJZoL4Ny9rBwZWuG05sXadMIA,24798
14
14
  dbos/_core.py,sha256=7ukQH_KClBaMFy0sVTSR5tWylW-RqI9qaReBY-LDKrk,48316
15
15
  dbos/_croniter.py,sha256=XHAyUyibs_59sJQfSNWkP7rqQY6_XrlfuuCxk4jYqek,47559
16
- dbos/_dbos.py,sha256=MuMYbtqUyk2uihCH8aMVDeHmn_P8X8-udqeNT1RLesY,47365
17
- dbos/_dbos_config.py,sha256=IufNrIC-M2xSNTXyT_KXlEdfB3j03pPLv_nE0fEq4_U,20955
16
+ dbos/_dbos.py,sha256=1EhH7r6v2vwW3Z74nK6_Zw8InE1jSXedEsztz0I4ggA,47269
17
+ dbos/_dbos_config.py,sha256=BFL2ol4nrqOPEiu1Dj-Nk3HRiVih0DecOgCdMyENOSQ,20233
18
18
  dbos/_debug.py,sha256=MNlQVZ6TscGCRQeEEL0VE8Uignvr6dPeDDDefS3xgIE,1823
19
19
  dbos/_docker_pg_helper.py,sha256=tLJXWqZ4S-ExcaPnxg_i6cVxL6ZxrYlZjaGsklY-s2I,6115
20
20
  dbos/_error.py,sha256=q0OQJZTbR8FFHV9hEpAGpz9oWBT5L509zUhmyff7FJw,8500
@@ -38,7 +38,7 @@ dbos/_migrations/versions/d76646551a6c_workflow_queue.py,sha256=G942nophZ2uC2vc4
38
38
  dbos/_migrations/versions/eab0cc1d9a14_job_queue.py,sha256=uvhFOtqbBreCePhAxZfIT0qCAI7BiZTou9wt6QnbY7c,1412
39
39
  dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py,sha256=m90Lc5YH0ZISSq1MyxND6oq3RZrZKrIqEsZtwJ1jWxA,1049
40
40
  dbos/_outcome.py,sha256=EXxBg4jXCVJsByDQ1VOCIedmbeq_03S6d-p1vqQrLFU,6810
41
- dbos/_queue.py,sha256=aKCGahWBGJOLOv5PCOOId96Va3YQ4ICuHWXy-eQXohE,3526
41
+ dbos/_queue.py,sha256=6cmqB1DoCJUh-y7DetneZRrL5jM5mw0xG9qj7jPu8EE,3687
42
42
  dbos/_recovery.py,sha256=jVMexjfCCNopzyn8gVQzJCmGJaP9G3C1EFaoCQ_Nh7g,2564
43
43
  dbos/_registrations.py,sha256=CZt1ElqDjCT7hz6iyT-1av76Yu-iuwu_c9lozO87wvM,7303
44
44
  dbos/_roles.py,sha256=iOsgmIAf1XVzxs3gYWdGRe1B880YfOw5fpU7Jwx8_A8,2271
@@ -47,7 +47,7 @@ dbos/_schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  dbos/_schemas/application_database.py,sha256=SypAS9l9EsaBHFn9FR8jmnqt01M74d9AF1AMa4m2hhI,1040
48
48
  dbos/_schemas/system_database.py,sha256=3Z0L72bOgHnusK1hBaETWU9RfiLBP0QnS-fdu41i0yY,5835
49
49
  dbos/_serialization.py,sha256=bWuwhXSQcGmiazvhJHA5gwhrRWxtmFmcCFQSDJnqqkU,3666
50
- dbos/_sys_db.py,sha256=IMmRbeIcrsOFJfVcBhMkDWiA3_SvxeKbOGipFiplHPM,83735
50
+ dbos/_sys_db.py,sha256=BZdUrFHG8Ze77hIuxwHpsnE--6UymjjhlH7cA3yP_-0,83230
51
51
  dbos/_templates/dbos-db-starter/README.md,sha256=GhxhBj42wjTt1fWEtwNriHbJuKb66Vzu89G4pxNHw2g,930
52
52
  dbos/_templates/dbos-db-starter/__package/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
53
  dbos/_templates/dbos-db-starter/__package/main.py.dbos,sha256=aQnBPSSQpkB8ERfhf7gB7P9tsU6OPKhZscfeh0yiaD8,2702
@@ -67,4 +67,4 @@ dbos/cli/cli.py,sha256=HinoCGrAUTiSeq7AAoCFfhdiE0uDw7vLMuDMN1_YTLI,20705
67
67
  dbos/dbos-config.schema.json,sha256=CjaspeYmOkx6Ip_pcxtmfXJTn_YGdSx_0pcPBF7KZmo,6060
68
68
  dbos/py.typed,sha256=QfzXT1Ktfk3Rj84akygc7_42z0lRpCq0Ilh8OXI6Zas,44
69
69
  version/__init__.py,sha256=L4sNxecRuqdtSFdpUGX3TtBi9KL3k7YsZVIvv-fv9-A,1678
70
- dbos-1.2.0a2.dist-info/RECORD,,
70
+ dbos-1.2.0a4.dist-info/RECORD,,
File without changes