dbos 0.22.0a2__py3-none-any.whl → 0.22.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.

Potentially problematic release.


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

dbos/_dbos.py CHANGED
@@ -997,6 +997,10 @@ def _dbos_exit_hook() -> None:
997
997
  )
998
998
  return
999
999
  if not _dbos_global_instance._launched:
1000
+ if _dbos_global_instance.fastapi is not None:
1001
+ # FastAPI lifespan middleware will call launch/destroy, so we can ignore this.
1002
+ # This is likely to happen during fastapi dev runs, where the reloader loads the module multiple times.
1003
+ return
1000
1004
  print("DBOS exiting; DBOS exists but launch() was not called")
1001
1005
  dbos_logger.warning("DBOS exiting; DBOS exists but launch() was not called")
1002
1006
  return
dbos/_queue.py CHANGED
@@ -76,7 +76,9 @@ def queue_thread(stop_event: threading.Event, dbos: "DBOS") -> None:
76
76
  execute_workflow_by_id(dbos, id)
77
77
  except OperationalError as e:
78
78
  # Ignore serialization error
79
- if not isinstance(e.orig, errors.SerializationFailure):
79
+ if not isinstance(
80
+ e.orig, (errors.SerializationFailure, errors.LockNotAvailable)
81
+ ):
80
82
  dbos.logger.warning(
81
83
  f"Exception encountered in queue thread: {traceback.format_exc()}"
82
84
  )
dbos/_sys_db.py CHANGED
@@ -189,6 +189,10 @@ class SystemDatabase:
189
189
  host=config["database"]["hostname"],
190
190
  port=config["database"]["port"],
191
191
  database="postgres",
192
+ # fills the "application_name" column in pg_stat_activity
193
+ query={
194
+ "application_name": f"dbos_transact_{os.environ.get('DBOS__VMID', 'local')}_{os.environ.get('DBOS__APPVERSION', '')}"
195
+ },
192
196
  )
193
197
  engine = sa.create_engine(postgres_db_url)
194
198
  with engine.connect() as conn:
@@ -207,6 +211,10 @@ class SystemDatabase:
207
211
  host=config["database"]["hostname"],
208
212
  port=config["database"]["port"],
209
213
  database=sysdb_name,
214
+ # fills the "application_name" column in pg_stat_activity
215
+ query={
216
+ "application_name": f"dbos_transact_{os.environ.get('DBOS__VMID', 'local')}_{os.environ.get('DBOS__APPVERSION', '')}"
217
+ },
210
218
  )
211
219
 
212
220
  # Create a connection pool for the system database
@@ -1307,6 +1315,55 @@ class SystemDatabase:
1307
1315
  # Dequeue functions eligible for this worker and ordered by the time at which they were enqueued.
1308
1316
  # If there is a global or local concurrency limit N, select only the N oldest enqueued
1309
1317
  # functions, else select all of them.
1318
+
1319
+ # First lets figure out how many tasks the worker can dequeue
1320
+ running_tasks_query = (
1321
+ sa.select(
1322
+ SystemSchema.workflow_queue.c.executor_id,
1323
+ sa.func.count().label("task_count"),
1324
+ )
1325
+ .where(SystemSchema.workflow_queue.c.queue_name == queue.name)
1326
+ .where(
1327
+ SystemSchema.workflow_queue.c.executor_id.isnot(
1328
+ None
1329
+ ) # Task is dequeued
1330
+ )
1331
+ .where(
1332
+ SystemSchema.workflow_queue.c.completed_at_epoch_ms.is_(
1333
+ None
1334
+ ) # Task is not completed
1335
+ )
1336
+ .group_by(SystemSchema.workflow_queue.c.executor_id)
1337
+ )
1338
+ running_tasks_result = c.execute(running_tasks_query).fetchall()
1339
+ running_tasks_result_dict = {row[0]: row[1] for row in running_tasks_result}
1340
+ running_tasks_for_this_worker = running_tasks_result_dict.get(
1341
+ executor_id, 0
1342
+ ) # Get count for current executor
1343
+
1344
+ max_tasks = float("inf")
1345
+ if queue.worker_concurrency is not None:
1346
+ # Worker local concurrency limit should always be >= running_tasks_for_this_worker
1347
+ # This should never happen but a check + warning doesn't hurt
1348
+ if running_tasks_for_this_worker > queue.worker_concurrency:
1349
+ dbos_logger.warning(
1350
+ f"Number of tasks on this worker ({running_tasks_for_this_worker}) exceeds the worker concurrency limit ({queue.worker_concurrency})"
1351
+ )
1352
+ max_tasks = max(
1353
+ 0, queue.worker_concurrency - running_tasks_for_this_worker
1354
+ )
1355
+ if queue.concurrency is not None:
1356
+ total_running_tasks = sum(running_tasks_result_dict.values())
1357
+ # Queue global concurrency limit should always be >= running_tasks_count
1358
+ # This should never happen but a check + warning doesn't hurt
1359
+ if total_running_tasks > queue.concurrency:
1360
+ dbos_logger.warning(
1361
+ f"Total running tasks ({total_running_tasks}) exceeds the global concurrency limit ({queue.concurrency})"
1362
+ )
1363
+ available_tasks = max(0, queue.concurrency - total_running_tasks)
1364
+ max_tasks = min(max_tasks, available_tasks)
1365
+
1366
+ # Lookup tasks
1310
1367
  query = (
1311
1368
  sa.select(
1312
1369
  SystemSchema.workflow_queue.c.workflow_uuid,
@@ -1315,29 +1372,25 @@ class SystemDatabase:
1315
1372
  )
1316
1373
  .where(SystemSchema.workflow_queue.c.queue_name == queue.name)
1317
1374
  .where(SystemSchema.workflow_queue.c.completed_at_epoch_ms == None)
1318
- .where(
1319
- # Only select functions that have not been started yet or have been started by this worker
1320
- or_(
1321
- SystemSchema.workflow_queue.c.executor_id == None,
1322
- SystemSchema.workflow_queue.c.executor_id == executor_id,
1323
- )
1324
- )
1375
+ .where(SystemSchema.workflow_queue.c.executor_id == None)
1325
1376
  .order_by(SystemSchema.workflow_queue.c.created_at_epoch_ms.asc())
1377
+ .with_for_update(nowait=True) # Error out early
1326
1378
  )
1327
- # Set a dequeue limit if necessary
1328
- if queue.worker_concurrency is not None:
1329
- query = query.limit(queue.worker_concurrency)
1330
- elif queue.concurrency is not None:
1331
- query = query.limit(queue.concurrency)
1379
+ # Apply limit only if max_tasks is finite
1380
+ if max_tasks != float("inf"):
1381
+ query = query.limit(int(max_tasks))
1332
1382
 
1333
1383
  rows = c.execute(query).fetchall()
1334
1384
 
1335
- # Now, get the workflow IDs of functions that have not yet been started
1336
- dequeued_ids: List[str] = [row[0] for row in rows if row[1] is None]
1385
+ # Get the workflow IDs
1386
+ dequeued_ids: List[str] = [row[0] for row in rows]
1387
+ if len(dequeued_ids) > 0:
1388
+ dbos_logger.debug(
1389
+ f"[{queue.name}] dequeueing {len(dequeued_ids)} task(s)"
1390
+ )
1337
1391
  ret_ids: list[str] = []
1338
- dbos_logger.debug(f"[{queue.name}] dequeueing {len(dequeued_ids)} task(s)")
1339
- for id in dequeued_ids:
1340
1392
 
1393
+ for id in dequeued_ids:
1341
1394
  # If we have a limiter, stop starting functions when the number
1342
1395
  # of functions started this period exceeds the limit.
1343
1396
  if queue.limiter is not None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.22.0a2
3
+ Version: 0.22.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-0.22.0a2.dist-info/METADATA,sha256=WWwhiPH-2zKnFO--GclmHu2CfPaIBZkEBs6DsFHYSMY,5309
2
- dbos-0.22.0a2.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
- dbos-0.22.0a2.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
- dbos-0.22.0a2.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
1
+ dbos-0.22.0a4.dist-info/METADATA,sha256=3hz0YdIdTpfM3YyJqBeoKy5ADFI91UA3j-MW4lNhBfs,5309
2
+ dbos-0.22.0a4.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
+ dbos-0.22.0a4.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
+ dbos-0.22.0a4.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=PvII-lcUgii0WqaDb9-56gfQ_69VoWthKAkKexRebuk,35461
14
14
  dbos/_croniter.py,sha256=hbhgfsHBqclUS8VeLnJ9PSE9Z54z6mi4nnrr1aUXn0k,47561
15
15
  dbos/_db_wizard.py,sha256=xgKLna0_6Xi50F3o8msRosXba8NScHlpJR5ICVCkHDQ,7534
16
- dbos/_dbos.py,sha256=wAjdlUgDSIC_Q8D_GZYDoiKaxjtr6KNHeq6DDuUh9do,36340
16
+ dbos/_dbos.py,sha256=klpuF1Wj05_qsWiKnLSWfei-16-K-g4BQ5R0MMfTuNE,36620
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
@@ -31,7 +31,7 @@ dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py,sha256=8PyFi8rd6CN-m
31
31
  dbos/_migrations/versions/d76646551a6c_workflow_queue.py,sha256=G942nophZ2uC2vc4hGBC02Ptng1715roTjY3xiyzZU4,729
32
32
  dbos/_migrations/versions/eab0cc1d9a14_job_queue.py,sha256=uvhFOtqbBreCePhAxZfIT0qCAI7BiZTou9wt6QnbY7c,1412
33
33
  dbos/_outcome.py,sha256=FDMgWVjZ06vm9xO-38H17mTqBImUYQxgKs_bDCSIAhE,6648
34
- dbos/_queue.py,sha256=o_aczwualJTMoXb0XXL-Y5QH77OEukWzuerogbWi2ho,2779
34
+ dbos/_queue.py,sha256=eZiapBcyn70-viW0y9fo7u09V6_VF5ACNGJxD-U_dNM,2844
35
35
  dbos/_recovery.py,sha256=rek9rm2CaENbbl_vu3To-BdXop7tMEyGvtoNiJLVxjQ,2772
36
36
  dbos/_registrations.py,sha256=_zy6k944Ll8QwqU12Kr3OP23ukVtm8axPNN1TS_kJRc,6717
37
37
  dbos/_request.py,sha256=cX1B3Atlh160phgS35gF1VEEV4pD126c9F3BDgBmxZU,929
@@ -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=B8I-MUOHcXalrtOewomKuxbyRvYdItxQhHsfJ1IjunA,59719
44
+ dbos/_sys_db.py,sha256=pH4vfPAAFVqu0KAfUFSUDKp5T-4OrwaLWJylMrzDFL4,62421
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.22.0a2.dist-info/RECORD,,
63
+ dbos-0.22.0a4.dist-info/RECORD,,