dbos 0.23.0a13__py3-none-any.whl → 0.24.0a1__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/_scheduler.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import threading
2
+ import traceback
2
3
  from datetime import datetime, timezone
3
4
  from typing import TYPE_CHECKING, Callable
4
5
 
@@ -33,8 +34,10 @@ def scheduler_loop(
33
34
  with SetWorkflowID(f"sched-{func.__qualname__}-{nextExecTime.isoformat()}"):
34
35
  try:
35
36
  scheduler_queue.enqueue(func, nextExecTime, datetime.now(timezone.utc))
36
- except Exception as e:
37
- dbos_logger.warning(f"Error scheduling workflow: ", e)
37
+ except Exception:
38
+ dbos_logger.warning(
39
+ f"Exception encountered in scheduler thread: {traceback.format_exc()})"
40
+ )
38
41
 
39
42
 
40
43
  def scheduled(
@@ -154,7 +154,7 @@ class SystemSchema:
154
154
  nullable=False,
155
155
  primary_key=True,
156
156
  ),
157
- Column("executor_id", Text),
157
+ # Column("executor_id", Text), # This column is deprecated. Do *not* use it.
158
158
  Column("queue_name", Text, nullable=False),
159
159
  Column(
160
160
  "created_at_epoch_ms",
dbos/_sys_db.py CHANGED
@@ -202,7 +202,11 @@ class SystemDatabase:
202
202
 
203
203
  # Create a connection pool for the system database
204
204
  self.engine = sa.create_engine(
205
- system_db_url, pool_size=20, max_overflow=5, pool_timeout=30
205
+ system_db_url,
206
+ pool_size=20,
207
+ max_overflow=5,
208
+ pool_timeout=30,
209
+ connect_args={"connect_timeout": 10},
206
210
  )
207
211
 
208
212
  # Run a schema migration for the system database
@@ -1324,24 +1328,32 @@ class SystemDatabase:
1324
1328
  # If there is a global or local concurrency limit N, select only the N oldest enqueued
1325
1329
  # functions, else select all of them.
1326
1330
 
1327
- # First lets figure out how many tasks the worker can dequeue
1331
+ # First lets figure out how many tasks are eligible for dequeue.
1332
+ # This means figuring out how many unstarted tasks are within the local and global concurrency limits
1328
1333
  running_tasks_query = (
1329
1334
  sa.select(
1330
- SystemSchema.workflow_queue.c.executor_id,
1335
+ SystemSchema.workflow_status.c.executor_id,
1331
1336
  sa.func.count().label("task_count"),
1332
1337
  )
1338
+ .select_from(
1339
+ SystemSchema.workflow_queue.join(
1340
+ SystemSchema.workflow_status,
1341
+ SystemSchema.workflow_queue.c.workflow_uuid
1342
+ == SystemSchema.workflow_status.c.workflow_uuid,
1343
+ )
1344
+ )
1333
1345
  .where(SystemSchema.workflow_queue.c.queue_name == queue.name)
1334
1346
  .where(
1335
- SystemSchema.workflow_queue.c.executor_id.isnot(
1347
+ SystemSchema.workflow_queue.c.started_at_epoch_ms.isnot(
1336
1348
  None
1337
- ) # Task is dequeued
1349
+ ) # Task is started
1338
1350
  )
1339
1351
  .where(
1340
1352
  SystemSchema.workflow_queue.c.completed_at_epoch_ms.is_(
1341
1353
  None
1342
- ) # Task is not completed
1354
+ ) # Task is not completed.
1343
1355
  )
1344
- .group_by(SystemSchema.workflow_queue.c.executor_id)
1356
+ .group_by(SystemSchema.workflow_status.c.executor_id)
1345
1357
  )
1346
1358
  running_tasks_result = c.execute(running_tasks_query).fetchall()
1347
1359
  running_tasks_result_dict = {row[0]: row[1] for row in running_tasks_result}
@@ -1351,12 +1363,6 @@ class SystemDatabase:
1351
1363
 
1352
1364
  max_tasks = float("inf")
1353
1365
  if queue.worker_concurrency is not None:
1354
- # Worker local concurrency limit should always be >= running_tasks_for_this_worker
1355
- # This should never happen but a check + warning doesn't hurt
1356
- if running_tasks_for_this_worker > queue.worker_concurrency:
1357
- dbos_logger.warning(
1358
- f"Number of tasks on this worker ({running_tasks_for_this_worker}) exceeds the worker concurrency limit ({queue.worker_concurrency})"
1359
- )
1360
1366
  max_tasks = max(
1361
1367
  0, queue.worker_concurrency - running_tasks_for_this_worker
1362
1368
  )
@@ -1371,16 +1377,14 @@ class SystemDatabase:
1371
1377
  available_tasks = max(0, queue.concurrency - total_running_tasks)
1372
1378
  max_tasks = min(max_tasks, available_tasks)
1373
1379
 
1374
- # Lookup tasks
1380
+ # Lookup unstarted/uncompleted tasks (not running)
1375
1381
  query = (
1376
1382
  sa.select(
1377
1383
  SystemSchema.workflow_queue.c.workflow_uuid,
1378
- SystemSchema.workflow_queue.c.started_at_epoch_ms,
1379
- SystemSchema.workflow_queue.c.executor_id,
1380
1384
  )
1381
1385
  .where(SystemSchema.workflow_queue.c.queue_name == queue.name)
1386
+ .where(SystemSchema.workflow_queue.c.started_at_epoch_ms == None)
1382
1387
  .where(SystemSchema.workflow_queue.c.completed_at_epoch_ms == None)
1383
- .where(SystemSchema.workflow_queue.c.executor_id == None)
1384
1388
  .order_by(SystemSchema.workflow_queue.c.created_at_epoch_ms.asc())
1385
1389
  .with_for_update(nowait=True) # Error out early
1386
1390
  )
@@ -1423,7 +1427,7 @@ class SystemDatabase:
1423
1427
  c.execute(
1424
1428
  SystemSchema.workflow_queue.update()
1425
1429
  .where(SystemSchema.workflow_queue.c.workflow_uuid == id)
1426
- .values(started_at_epoch_ms=start_time_ms, executor_id=executor_id)
1430
+ .values(started_at_epoch_ms=start_time_ms)
1427
1431
  )
1428
1432
  ret_ids.append(id)
1429
1433
 
@@ -1468,23 +1472,26 @@ class SystemDatabase:
1468
1472
 
1469
1473
  with self.engine.connect() as conn:
1470
1474
  with conn.begin() as transaction:
1475
+ # Reset the start time in the queue to mark it as not started
1471
1476
  res = conn.execute(
1472
1477
  sa.update(SystemSchema.workflow_queue)
1473
1478
  .where(SystemSchema.workflow_queue.c.workflow_uuid == workflow_id)
1474
- .values(executor_id=None, started_at_epoch_ms=None)
1479
+ .where(
1480
+ SystemSchema.workflow_queue.c.completed_at_epoch_ms.is_(None)
1481
+ )
1482
+ .values(started_at_epoch_ms=None)
1475
1483
  )
1476
1484
 
1477
- # If no rows were affected, the workflow is not anymore in the queue
1485
+ # If no rows were affected, the workflow is not anymore in the queue or was already completed
1478
1486
  if res.rowcount == 0:
1479
1487
  transaction.rollback()
1480
1488
  return False
1481
1489
 
1490
+ # Reset the status of the task to "ENQUEUED"
1482
1491
  res = conn.execute(
1483
1492
  sa.update(SystemSchema.workflow_status)
1484
1493
  .where(SystemSchema.workflow_status.c.workflow_uuid == workflow_id)
1485
- .values(
1486
- executor_id=None, status=WorkflowStatusString.ENQUEUED.value
1487
- )
1494
+ .values(status=WorkflowStatusString.ENQUEUED.value)
1488
1495
  )
1489
1496
  if res.rowcount == 0:
1490
1497
  # This should never happen
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.23.0a13
3
+ Version: 0.24.0a1
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.23.0a13.dist-info/METADATA,sha256=NGDpQapDUzGaa6Iu-0sMoP32p_Fi_Co7-WFC_kACtIU,5556
2
- dbos-0.23.0a13.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
- dbos-0.23.0a13.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
- dbos-0.23.0a13.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
1
+ dbos-0.24.0a1.dist-info/METADATA,sha256=UuArfBoytGeWjufDI1gg6odzVSbkLTCGu_ZYtZFe_uM,5555
2
+ dbos-0.24.0a1.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
+ dbos-0.24.0a1.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
+ dbos-0.24.0a1.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
5
5
  dbos/__init__.py,sha256=CxRHBHEthPL4PZoLbZhp3rdm44-KkRTT2-7DkK9d4QQ,724
6
6
  dbos/__main__.py,sha256=P7jAr-7L9XE5mrsQ7i4b-bLr2ap1tCQfhMByLCRWDj0,568
7
7
  dbos/_admin_server.py,sha256=YiVn5lywz2Vg8_juyNHOYl0HVEy48--7b4phwK7r92o,5732
@@ -40,12 +40,12 @@ dbos/_recovery.py,sha256=4KyZb0XJEUGH7ekYT1kpx38i6y5vygPeH75Ta7RZjYo,2596
40
40
  dbos/_registrations.py,sha256=_zy6k944Ll8QwqU12Kr3OP23ukVtm8axPNN1TS_kJRc,6717
41
41
  dbos/_request.py,sha256=cX1B3Atlh160phgS35gF1VEEV4pD126c9F3BDgBmxZU,929
42
42
  dbos/_roles.py,sha256=iOsgmIAf1XVzxs3gYWdGRe1B880YfOw5fpU7Jwx8_A8,2271
43
- dbos/_scheduler.py,sha256=0I3e8Y-OIBG3wiUCIskShd-Sk_eUFCFyRB5u4L7IHXI,1940
43
+ dbos/_scheduler.py,sha256=boG4BdcncFa3WxR97T5Oou4ppR0TgrEa2QQkjzpFEHU,2028
44
44
  dbos/_schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
45
  dbos/_schemas/application_database.py,sha256=KeyoPrF7hy_ODXV7QNike_VFSD74QBRfQ76D7QyE9HI,966
46
- dbos/_schemas/system_database.py,sha256=rwp4EvCSaXcUoMaRczZCvETCxGp72k3-hvLyGUDkih0,5163
46
+ dbos/_schemas/system_database.py,sha256=16146P4TLjAGjTYykOs_KUd2c_geJ5fuhk0ko85C65M,5211
47
47
  dbos/_serialization.py,sha256=YCYv0qKAwAZ1djZisBC7khvKqG-5OcIv9t9EC5PFIog,1743
48
- dbos/_sys_db.py,sha256=05kxY9YY9iUZ1jQmfpymJuZS8Oi69ju_mF_M6lJi8qU,64513
48
+ dbos/_sys_db.py,sha256=qzVPkg64fvTwDGOI_ATQ4JySiH6szyo6NmRrZaCeDwQ,64674
49
49
  dbos/_templates/dbos-db-starter/README.md,sha256=GhxhBj42wjTt1fWEtwNriHbJuKb66Vzu89G4pxNHw2g,930
50
50
  dbos/_templates/dbos-db-starter/__package/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
51
  dbos/_templates/dbos-db-starter/__package/main.py,sha256=eI0SS9Nwj-fldtiuSzIlIG6dC91GXXwdRsoHxv6S_WI,2719
@@ -65,4 +65,4 @@ dbos/cli/cli.py,sha256=ThomRytw7EP5iOcrjEgwnpaWgXNTLfnFEBBvCGHxtJs,15590
65
65
  dbos/dbos-config.schema.json,sha256=X5TpXNcARGceX0zQs0fVgtZW_Xj9uBbY5afPt9Rz9yk,5741
66
66
  dbos/py.typed,sha256=QfzXT1Ktfk3Rj84akygc7_42z0lRpCq0Ilh8OXI6Zas,44
67
67
  version/__init__.py,sha256=L4sNxecRuqdtSFdpUGX3TtBi9KL3k7YsZVIvv-fv9-A,1678
68
- dbos-0.23.0a13.dist-info/RECORD,,
68
+ dbos-0.24.0a1.dist-info/RECORD,,