dbos 0.8.0a7__py3-none-any.whl → 0.9.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.

Potentially problematic release.


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

@@ -140,6 +140,10 @@
140
140
  "start": {
141
141
  "type": "array",
142
142
  "description": "Specify commands to run to start your application (Python only)"
143
+ },
144
+ "admin_port": {
145
+ "type": "number",
146
+ "description": "The port number of the admin server (Default: 3001)"
143
147
  }
144
148
  }
145
149
  },
dbos/dbos.py CHANGED
@@ -348,7 +348,10 @@ class DBOS:
348
348
  self._executor_field = ThreadPoolExecutor(max_workers=64)
349
349
  self._sys_db_field = SystemDatabase(self.config)
350
350
  self._app_db_field = ApplicationDatabase(self.config)
351
- self._admin_server_field = AdminServer(dbos=self)
351
+ admin_port = self.config["runtimeConfig"].get("admin_port")
352
+ if admin_port is None:
353
+ admin_port = 3001
354
+ self._admin_server_field = AdminServer(dbos=self, port=admin_port)
352
355
 
353
356
  if not os.environ.get("DBOS__VMID"):
354
357
  workflow_ids = self._sys_db.get_pending_workflows("local")
@@ -550,6 +553,7 @@ class DBOS:
550
553
  recovery_attempts=stat["recovery_attempts"],
551
554
  class_name=stat["class_name"],
552
555
  config_name=stat["config_name"],
556
+ queue_name=stat["queue_name"],
553
557
  authenticated_user=stat["authenticated_user"],
554
558
  assumed_role=stat["assumed_role"],
555
559
  authenticated_roles=(
@@ -756,6 +760,7 @@ class WorkflowStatus:
756
760
  name(str): The workflow function name
757
761
  class_name(str): For member functions, the name of the class containing the workflow function
758
762
  config_name(str): For instance member functions, the name of the class instance for the execution
763
+ queue_name(str): For workflows that are or were queued, the queue name
759
764
  authenticated_user(str): The user who invoked the workflow
760
765
  assumed_role(str): The access role used by the user to allow access to the workflow function
761
766
  authenticated_roles(List[str]): List of all access roles available to the authenticated user
@@ -768,6 +773,7 @@ class WorkflowStatus:
768
773
  name: str
769
774
  class_name: Optional[str]
770
775
  config_name: Optional[str]
776
+ queue_name: Optional[str]
771
777
  authenticated_user: Optional[str]
772
778
  assumed_role: Optional[str]
773
779
  authenticated_roles: Optional[List[str]]
dbos/dbos_config.py CHANGED
@@ -14,6 +14,7 @@ from dbos.logger import dbos_logger
14
14
 
15
15
  class RuntimeConfig(TypedDict, total=False):
16
16
  start: List[str]
17
+ admin_port: Optional[int]
17
18
 
18
19
 
19
20
  class DatabaseConfig(TypedDict, total=False):
@@ -1,4 +1,5 @@
1
- """fix_job_queue
1
+ """
2
+ Fix job queue PK.
2
3
 
3
4
  Revision ID: 50f3227f0b4b
4
5
  Revises: eab0cc1d9a14
@@ -1,4 +1,5 @@
1
- """job_queue_limiter
1
+ """
2
+ Adjust workflow queue to add columns for rate limiter.
2
3
 
3
4
  Revision ID: d76646551a6b
4
5
  Revises: 50f3227f0b4b
@@ -0,0 +1,28 @@
1
+ """workflow_queue
2
+
3
+ Revision ID: d76646551a6c
4
+ Revises: d76646551a6b
5
+ Create Date: 2024-09-27 12:00:00.0
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ import sqlalchemy as sa
12
+ from alembic import op
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = "d76646551a6c"
16
+ down_revision: Union[str, None] = "d76646551a6b"
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ op.rename_table("job_queue", "workflow_queue", schema="dbos")
23
+ op.execute("CREATE VIEW dbos.job_queue AS SELECT * FROM dbos.workflow_queue;")
24
+
25
+
26
+ def downgrade() -> None:
27
+ op.execute("DROP VIEW dbos.job_queue;")
28
+ op.rename_table("workflow_queue", "job_queue", schema="dbos")
@@ -1,4 +1,5 @@
1
- """job_queue
1
+ """
2
+ Add workflow queue table.
2
3
 
3
4
  Revision ID: eab0cc1d9a14
4
5
  Revises: a3b18ad34abe
dbos/queue.py CHANGED
@@ -8,21 +8,31 @@ if TYPE_CHECKING:
8
8
  from dbos.dbos import DBOS, Workflow, WorkflowHandle
9
9
 
10
10
 
11
- # Limit the maximum number of functions from this queue
12
- # that can be started in a given period. If the limit is 5
13
- # and the period is 10, no more than 5 functions can be
14
- # started per 10 seconds.
15
- class Limiter(TypedDict):
11
+ class QueueRateLimit(TypedDict):
12
+ """
13
+ Limit the maximum number of workflows from this queue that can be started in a given period.
14
+
15
+ If the limit is 5 and the period is 10, no more than 5 functions can be
16
+ started per 10 seconds.
17
+ """
18
+
16
19
  limit: int
17
20
  period: float
18
21
 
19
22
 
20
23
  class Queue:
24
+ """
25
+ Workflow queue.
26
+
27
+ Workflow queues allow workflows to be started at a later time, based on concurrency and
28
+ rate limits.
29
+ """
30
+
21
31
  def __init__(
22
32
  self,
23
33
  name: str,
24
34
  concurrency: Optional[int] = None,
25
- limiter: Optional[Limiter] = None,
35
+ limiter: Optional[QueueRateLimit] = None,
26
36
  ) -> None:
27
37
  self.name = name
28
38
  self.concurrency = concurrency
dbos/request.py CHANGED
@@ -13,6 +13,7 @@ class Address(NamedTuple):
13
13
  class Request:
14
14
  """
15
15
  Serializable HTTP Request object.
16
+
16
17
  Attributes:
17
18
  base_url(str): Base of URL requested, as in application code
18
19
  client(Optional[Address]): HTTP Client
@@ -2,6 +2,7 @@ import threading
2
2
  from datetime import datetime, timezone
3
3
  from typing import TYPE_CHECKING, Callable
4
4
 
5
+ from dbos.logger import dbos_logger
5
6
  from dbos.queue import Queue
6
7
 
7
8
  if TYPE_CHECKING:
@@ -18,7 +19,12 @@ scheduler_queue: Queue
18
19
  def scheduler_loop(
19
20
  func: ScheduledWorkflow, cron: str, stop_event: threading.Event
20
21
  ) -> None:
21
- iter = croniter(cron, datetime.now(timezone.utc), second_at_beginning=True)
22
+ try:
23
+ iter = croniter(cron, datetime.now(timezone.utc), second_at_beginning=True)
24
+ except Exception as e:
25
+ dbos_logger.error(
26
+ f'Cannot run scheduled function {func.__name__}. Invalid crontab "{cron}"'
27
+ )
22
28
  while not stop_event.is_set():
23
29
  nextExecTime = iter.get_next(datetime)
24
30
  sleepTime = nextExecTime - datetime.now(timezone.utc)
@@ -31,6 +31,6 @@ class ApplicationSchema:
31
31
  nullable=False,
32
32
  server_default=text("(EXTRACT(epoch FROM now()) * 1000::numeric)::bigint"),
33
33
  ),
34
- Index("workflow_status_created_at_index", "created_at"),
34
+ Index("transaction_outputs_created_at_index", "created_at"),
35
35
  PrimaryKeyConstraint("workflow_uuid", "function_id"),
36
36
  )
@@ -142,8 +142,8 @@ class SystemSchema:
142
142
  Column("last_run_time", BigInteger, nullable=False),
143
143
  )
144
144
 
145
- job_queue = Table(
146
- "job_queue",
145
+ workflow_queue = Table(
146
+ "workflow_queue",
147
147
  metadata_obj,
148
148
  Column(
149
149
  "workflow_uuid",
dbos/system_database.py CHANGED
@@ -983,7 +983,7 @@ class SystemDatabase:
983
983
  return value
984
984
 
985
985
  def _flush_workflow_status_buffer(self) -> None:
986
- """Export the workflow status buffer to the database, up to the batch size"""
986
+ """Export the workflow status buffer to the database, up to the batch size."""
987
987
  if len(self._workflow_status_buffer) == 0:
988
988
  return
989
989
 
@@ -1079,7 +1079,7 @@ class SystemDatabase:
1079
1079
  def enqueue(self, workflow_id: str, queue_name: str) -> None:
1080
1080
  with self.engine.begin() as c:
1081
1081
  c.execute(
1082
- pg.insert(SystemSchema.job_queue)
1082
+ pg.insert(SystemSchema.workflow_queue)
1083
1083
  .values(
1084
1084
  workflow_uuid=workflow_id,
1085
1085
  queue_name=queue_name,
@@ -1099,10 +1099,13 @@ class SystemDatabase:
1099
1099
  if queue.limiter is not None:
1100
1100
  query = (
1101
1101
  sa.select(sa.func.count())
1102
- .select_from(SystemSchema.job_queue)
1103
- .where(SystemSchema.job_queue.c.started_at_epoch_ms.isnot(None))
1102
+ .select_from(SystemSchema.workflow_queue)
1103
+ .where(SystemSchema.workflow_queue.c.queue_name == queue.name)
1104
1104
  .where(
1105
- SystemSchema.job_queue.c.started_at_epoch_ms
1105
+ SystemSchema.workflow_queue.c.started_at_epoch_ms.isnot(None)
1106
+ )
1107
+ .where(
1108
+ SystemSchema.workflow_queue.c.started_at_epoch_ms
1106
1109
  > start_time_ms - limiter_period_ms
1107
1110
  )
1108
1111
  )
@@ -1116,12 +1119,12 @@ class SystemDatabase:
1116
1119
  # functions, else select all of them.
1117
1120
  query = (
1118
1121
  sa.select(
1119
- SystemSchema.job_queue.c.workflow_uuid,
1120
- SystemSchema.job_queue.c.started_at_epoch_ms,
1122
+ SystemSchema.workflow_queue.c.workflow_uuid,
1123
+ SystemSchema.workflow_queue.c.started_at_epoch_ms,
1121
1124
  )
1122
- .where(SystemSchema.job_queue.c.queue_name == queue.name)
1123
- .where(SystemSchema.job_queue.c.completed_at_epoch_ms == None)
1124
- .order_by(SystemSchema.job_queue.c.created_at_epoch_ms.asc())
1125
+ .where(SystemSchema.workflow_queue.c.queue_name == queue.name)
1126
+ .where(SystemSchema.workflow_queue.c.completed_at_epoch_ms == None)
1127
+ .order_by(SystemSchema.workflow_queue.c.created_at_epoch_ms.asc())
1125
1128
  )
1126
1129
  if queue.concurrency is not None:
1127
1130
  query = query.limit(queue.concurrency)
@@ -1152,8 +1155,8 @@ class SystemDatabase:
1152
1155
 
1153
1156
  # Then give it a start time
1154
1157
  c.execute(
1155
- SystemSchema.job_queue.update()
1156
- .where(SystemSchema.job_queue.c.workflow_uuid == id)
1158
+ SystemSchema.workflow_queue.update()
1159
+ .where(SystemSchema.workflow_queue.c.workflow_uuid == id)
1157
1160
  .values(started_at_epoch_ms=start_time_ms)
1158
1161
  )
1159
1162
  ret_ids.append(id)
@@ -1163,10 +1166,11 @@ class SystemDatabase:
1163
1166
  # deleted on completion.
1164
1167
  if queue.limiter is not None:
1165
1168
  c.execute(
1166
- sa.delete(SystemSchema.job_queue)
1167
- .where(SystemSchema.job_queue.c.completed_at_epoch_ms != None)
1169
+ sa.delete(SystemSchema.workflow_queue)
1170
+ .where(SystemSchema.workflow_queue.c.completed_at_epoch_ms != None)
1171
+ .where(SystemSchema.workflow_queue.c.queue_name == queue.name)
1168
1172
  .where(
1169
- SystemSchema.job_queue.c.started_at_epoch_ms
1173
+ SystemSchema.workflow_queue.c.started_at_epoch_ms
1170
1174
  < start_time_ms - limiter_period_ms
1171
1175
  )
1172
1176
  )
@@ -1178,13 +1182,13 @@ class SystemDatabase:
1178
1182
  with self.engine.begin() as c:
1179
1183
  if queue.limiter is None:
1180
1184
  c.execute(
1181
- sa.delete(SystemSchema.job_queue).where(
1182
- SystemSchema.job_queue.c.workflow_uuid == workflow_id
1185
+ sa.delete(SystemSchema.workflow_queue).where(
1186
+ SystemSchema.workflow_queue.c.workflow_uuid == workflow_id
1183
1187
  )
1184
1188
  )
1185
1189
  else:
1186
1190
  c.execute(
1187
- sa.update(SystemSchema.job_queue)
1188
- .where(SystemSchema.job_queue.c.workflow_uuid == workflow_id)
1191
+ sa.update(SystemSchema.workflow_queue)
1192
+ .where(SystemSchema.workflow_queue.c.workflow_uuid == workflow_id)
1189
1193
  .values(completed_at_epoch_ms=int(time.time() * 1000))
1190
1194
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.8.0a7
3
+ Version: 0.9.0
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -1,16 +1,16 @@
1
- dbos-0.8.0a7.dist-info/METADATA,sha256=cqNzAf7TbC3n_DDGTPUj_xawggwMydLnIQ5056iFDUI,5010
2
- dbos-0.8.0a7.dist-info/WHEEL,sha256=Vza3XR51HW1KmFP0iIMUVYIvz0uQuKJpIXKYOBGQyFQ,90
3
- dbos-0.8.0a7.dist-info/entry_points.txt,sha256=z6GcVANQV7Uw_82H9Ob2axJX6V3imftyZsljdh-M1HU,54
4
- dbos-0.8.0a7.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
1
+ dbos-0.9.0.dist-info/METADATA,sha256=gWFC4kG7fb8EJzLVKgrlZJXCEHBqYgyCWtg4Qn_Yc2k,5008
2
+ dbos-0.9.0.dist-info/WHEEL,sha256=pM0IBB6ZwH3nkEPhtcp50KvKNX-07jYtnb1g1m6Z4Co,90
3
+ dbos-0.9.0.dist-info/entry_points.txt,sha256=z6GcVANQV7Uw_82H9Ob2axJX6V3imftyZsljdh-M1HU,54
4
+ dbos-0.9.0.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
5
5
  dbos/__init__.py,sha256=-h1QgWNL11CiLlHEKa2ycAJVJw5SXYZ4BGNNWBAiE9k,726
6
6
  dbos/admin_sever.py,sha256=Qg5T3YRrbPW05PR_99yAaxgo1ugQrAp_uTeTqSfjm_k,3397
7
7
  dbos/application_database.py,sha256=knFK8We8y6WrIpnFCKvFq5hvSuFQqUuJqOqDpSVMCPI,5521
8
8
  dbos/cli.py,sha256=z5dXbbnGWzSC3E1rfS8Lp1_OIImzcDKM7jP-iu_Q4aI,8602
9
9
  dbos/context.py,sha256=4MsxZdoh1WIsgoUsaxo0B6caGN6xq2WC60MzbBppzGk,17738
10
10
  dbos/core.py,sha256=ggsRC2XicvNI1qqruEFoqxoTU5oSSnhMZvDih3AG_3A,30879
11
- dbos/dbos-config.schema.json,sha256=azpfmoDZg7WfSy3kvIsk9iEiKB_-VZt03VEOoXJAkqE,5331
12
- dbos/dbos.py,sha256=LnqX7rFETpcyxT9YHs4Uc3uOB4EDQC-zis3UFQU4smc,29705
13
- dbos/dbos_config.py,sha256=NJVze2GkKgYUmcPP31Unb-QpsA0TzImEeQGJgVq6W6k,5352
11
+ dbos/dbos-config.schema.json,sha256=kshzCdX6fXW7yqRiMz9a324HLe76IrPeRDgzcAbgck0,5481
12
+ dbos/dbos.py,sha256=Gpfhpy-fFQh0nYbTTOabfOKD42Gpl72zgVuzzaCURtI,30015
13
+ dbos/dbos_config.py,sha256=r-epY2JSSyqsZVEYKf-lPs6aQUsZaSDC-Js1HHR84kY,5382
14
14
  dbos/decorators.py,sha256=lbPefsLK6Cya4cb7TrOcLglOpGT3pc6qjZdsQKlfZLg,629
15
15
  dbos/error.py,sha256=UETk8CoZL-TO2Utn1-E7OSWelhShWmKM-fOlODMR9PE,3893
16
16
  dbos/fastapi.py,sha256=gx9hlpxYOiwbuhSlbY9bn5C-F_FsCbrJvkX9ZAvDG6U,3418
@@ -20,23 +20,24 @@ dbos/kafka_message.py,sha256=NYvOXNG3Qn7bghn1pv3fg4Pbs86ILZGcK4IB-MLUNu0,409
20
20
  dbos/logger.py,sha256=D-aFSZUCHBP34J1IZ5YNkTrJW-rDiH3py_v9jLU4Yrk,3565
21
21
  dbos/migrations/env.py,sha256=38SIGVbmn_VV2x2u1aHLcPOoWgZ84eCymf3g_NljmbU,1626
22
22
  dbos/migrations/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
23
- dbos/migrations/versions/50f3227f0b4b_fix_job_queue.py,sha256=ZtnsZFMuon-D0n8V5BR10jQEqJPUsYsOwt29FAoKG8g,868
23
+ dbos/migrations/versions/50f3227f0b4b_fix_job_queue.py,sha256=ZBYrtTdxy64HxIAlOes89fVIk2P1gNaJack7wuC_epg,873
24
24
  dbos/migrations/versions/5c361fc04708_added_system_tables.py,sha256=QMgFMb0aLgC25YicsvPSr6AHRCA6Zd66hyaRUhwKzrQ,6404
25
25
  dbos/migrations/versions/a3b18ad34abe_added_triggers.py,sha256=Rv0ZsZYZ_WdgGEULYsPfnp4YzaO5L198gDTgYY39AVA,2022
26
- dbos/migrations/versions/d76646551a6b_job_queue_limiter.py,sha256=M1upulBOLXm9ORJc2Q6PA0AwX0CA3zgqc-NhYS-eNPY,948
27
- dbos/migrations/versions/eab0cc1d9a14_job_queue.py,sha256=_9-FCW-zOpCQfblTS_yRLtFiUaWlC1tM4BoKBTDeH9k,1395
26
+ dbos/migrations/versions/d76646551a6b_job_queue_limiter.py,sha256=8PyFi8rd6CN-mUro43wGhsg5wcQWKZPRHD6jw8R5pVc,986
27
+ dbos/migrations/versions/d76646551a6c_workflow_queue.py,sha256=G942nophZ2uC2vc4hGBC02Ptng1715roTjY3xiyzZU4,729
28
+ dbos/migrations/versions/eab0cc1d9a14_job_queue.py,sha256=uvhFOtqbBreCePhAxZfIT0qCAI7BiZTou9wt6QnbY7c,1412
28
29
  dbos/py.typed,sha256=QfzXT1Ktfk3Rj84akygc7_42z0lRpCq0Ilh8OXI6Zas,44
29
- dbos/queue.py,sha256=4MIWYDdl3DhYsnayy747xF1Jgdq2qvV4On3KJbJ4NDU,1764
30
+ dbos/queue.py,sha256=DT5dFIDZGnC4GpgI1Tph8fh5VvClpokugv-2ow4qyiQ,1947
30
31
  dbos/recovery.py,sha256=zqtO_ExGoIErLMVnbneU3VeHLVWvhV4jnfqssAVlQQk,2016
31
32
  dbos/registrations.py,sha256=mei6q6_3R5uei8i_Wo_TqGZs85s10shOekDX41sFYD0,6642
32
- dbos/request.py,sha256=-FIwtknayvRl6OjvqO4V2GySVzSdP1Ft3cc9ZBS-PLY,928
33
+ dbos/request.py,sha256=cX1B3Atlh160phgS35gF1VEEV4pD126c9F3BDgBmxZU,929
33
34
  dbos/roles.py,sha256=7Lh7uwUq1dpa6TXCOHre4mPTd5qmXzK_QPkvYR52DXg,2285
34
35
  dbos/scheduler/croniter.py,sha256=hbhgfsHBqclUS8VeLnJ9PSE9Z54z6mi4nnrr1aUXn0k,47561
35
- dbos/scheduler/scheduler.py,sha256=Sz4EIpAtur7so2YajTic64GrTpa4qPw8QxXn0M34v80,1360
36
+ dbos/scheduler/scheduler.py,sha256=KpcBid6qIbqLqLdrQQqEQnRBTvo_XwtVuvUba3Ed5Go,1560
36
37
  dbos/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- dbos/schemas/application_database.py,sha256=q_Wr2XbiZNBYFkOtu7uKavo1T_cSOBblxKGHThYGGsY,962
38
- dbos/schemas/system_database.py,sha256=-37sNXfx6cNGyzndj9mrWQLDH5iIBrwsT56ZiJ56Sj0,5116
39
- dbos/system_database.py,sha256=x-TpKHIS187KED6BxPozvMLt6Qjhgh3dYSKZkd6epM0,47764
38
+ dbos/schemas/application_database.py,sha256=KeyoPrF7hy_ODXV7QNike_VFSD74QBRfQ76D7QyE9HI,966
39
+ dbos/schemas/system_database.py,sha256=7iw7eHJzEvkatHMOaHORoSvtfisF73wW5j8hRt_Ph14,5126
40
+ dbos/system_database.py,sha256=jS0JV3HW2nxKlCVAahTkraXpUpfy1pv2eirzQFpX6J4,48067
40
41
  dbos/templates/hello/README.md,sha256=GhxhBj42wjTt1fWEtwNriHbJuKb66Vzu89G4pxNHw2g,930
41
42
  dbos/templates/hello/__package/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
43
  dbos/templates/hello/__package/main.py,sha256=eI0SS9Nwj-fldtiuSzIlIG6dC91GXXwdRsoHxv6S_WI,2719
@@ -50,4 +51,4 @@ dbos/templates/hello/start_postgres_docker.py,sha256=lQVLlYO5YkhGPEgPqwGc7Y8uDKs
50
51
  dbos/tracer.py,sha256=GaXDhdKKF_IQp5SAMipGXiDVwteRKjNbrXyYCH1mor0,2520
51
52
  dbos/utils.py,sha256=lwRymY-y7GprAS8pKmbICQvOJd5eGxKGTxCMFn0OwaQ,1739
52
53
  version/__init__.py,sha256=L4sNxecRuqdtSFdpUGX3TtBi9KL3k7YsZVIvv-fv9-A,1678
53
- dbos-0.8.0a7.dist-info/RECORD,,
54
+ dbos-0.9.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: pdm-backend (2.4.1)
2
+ Generator: pdm-backend (2.4.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any