dbos 1.1.0a2__py3-none-any.whl → 1.1.0a3__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/_sys_db.py CHANGED
@@ -222,6 +222,47 @@ class StepInfo(TypedDict):
222
222
  _dbos_null_topic = "__null__topic__"
223
223
 
224
224
 
225
+ class ConditionCount(TypedDict):
226
+ condition: threading.Condition
227
+ count: int
228
+
229
+
230
+ class ThreadSafeConditionDict:
231
+ def __init__(self) -> None:
232
+ self._dict: Dict[str, ConditionCount] = {}
233
+ self._lock = threading.Lock()
234
+
235
+ def get(self, key: str) -> Optional[threading.Condition]:
236
+ with self._lock:
237
+ if key not in self._dict:
238
+ # Key does not exist, return None
239
+ return None
240
+ return self._dict[key]["condition"]
241
+
242
+ def set(
243
+ self, key: str, value: threading.Condition
244
+ ) -> tuple[bool, threading.Condition]:
245
+ with self._lock:
246
+ if key in self._dict:
247
+ # Key already exists, do not overwrite. Increment the wait count.
248
+ cc = self._dict[key]
249
+ cc["count"] += 1
250
+ return False, cc["condition"]
251
+ self._dict[key] = ConditionCount(condition=value, count=1)
252
+ return True, value
253
+
254
+ def pop(self, key: str) -> None:
255
+ with self._lock:
256
+ if key in self._dict:
257
+ cc = self._dict[key]
258
+ cc["count"] -= 1
259
+ if cc["count"] == 0:
260
+ # No more threads waiting on this condition, remove it
261
+ del self._dict[key]
262
+ else:
263
+ dbos_logger.warning(f"Key {key} not found in condition dictionary.")
264
+
265
+
225
266
  class SystemDatabase:
226
267
 
227
268
  def __init__(
@@ -248,8 +289,8 @@ class SystemDatabase:
248
289
  self._engine_kwargs = engine_kwargs
249
290
 
250
291
  self.notification_conn: Optional[psycopg.connection.Connection] = None
251
- self.notifications_map: Dict[str, threading.Condition] = {}
252
- self.workflow_events_map: Dict[str, threading.Condition] = {}
292
+ self.notifications_map = ThreadSafeConditionDict()
293
+ self.workflow_events_map = ThreadSafeConditionDict()
253
294
 
254
295
  # Now we can run background processes
255
296
  self._run_background_processes = True
@@ -1288,7 +1329,12 @@ class SystemDatabase:
1288
1329
  condition = threading.Condition()
1289
1330
  # Must acquire first before adding to the map. Otherwise, the notification listener may notify it before the condition is acquired and waited.
1290
1331
  condition.acquire()
1291
- self.notifications_map[payload] = condition
1332
+ success, _ = self.notifications_map.set(payload, condition)
1333
+ if not success:
1334
+ # This should not happen, but if it does, it means the workflow is executed concurrently.
1335
+ condition.release()
1336
+ self.notifications_map.pop(payload)
1337
+ raise DBOSWorkflowConflictIDError(workflow_uuid)
1292
1338
 
1293
1339
  # Check if the key is already in the database. If not, wait for the notification.
1294
1340
  init_recv: Sequence[Any]
@@ -1381,11 +1427,11 @@ class SystemDatabase:
1381
1427
  f"Received notification on channel: {channel}, payload: {notify.payload}"
1382
1428
  )
1383
1429
  if channel == "dbos_notifications_channel":
1384
- if (
1385
- notify.payload
1386
- and notify.payload in self.notifications_map
1387
- ):
1388
- condition = self.notifications_map[notify.payload]
1430
+ if notify.payload:
1431
+ condition = self.notifications_map.get(notify.payload)
1432
+ if condition is None:
1433
+ # No condition found for this payload
1434
+ continue
1389
1435
  condition.acquire()
1390
1436
  condition.notify_all()
1391
1437
  condition.release()
@@ -1393,11 +1439,11 @@ class SystemDatabase:
1393
1439
  f"Signaled notifications condition for {notify.payload}"
1394
1440
  )
1395
1441
  elif channel == "dbos_workflow_events_channel":
1396
- if (
1397
- notify.payload
1398
- and notify.payload in self.workflow_events_map
1399
- ):
1400
- condition = self.workflow_events_map[notify.payload]
1442
+ if notify.payload:
1443
+ condition = self.workflow_events_map.get(notify.payload)
1444
+ if condition is None:
1445
+ # No condition found for this payload
1446
+ continue
1401
1447
  condition.acquire()
1402
1448
  condition.notify_all()
1403
1449
  condition.release()
@@ -1535,8 +1581,13 @@ class SystemDatabase:
1535
1581
 
1536
1582
  payload = f"{target_uuid}::{key}"
1537
1583
  condition = threading.Condition()
1538
- self.workflow_events_map[payload] = condition
1539
1584
  condition.acquire()
1585
+ success, existing_condition = self.workflow_events_map.set(payload, condition)
1586
+ if not success:
1587
+ # Wait on the existing condition
1588
+ condition.release()
1589
+ condition = existing_condition
1590
+ condition.acquire()
1540
1591
 
1541
1592
  # Check if the key is already in the database. If not, wait for the notification.
1542
1593
  init_recv: Sequence[Any]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 1.1.0a2
3
+ Version: 1.1.0a3
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.1.0a2.dist-info/METADATA,sha256=X9RcRNberl8VXnm_V76fVezhh20QcbKpoFto07FXjhs,13267
2
- dbos-1.1.0a2.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
- dbos-1.1.0a2.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
- dbos-1.1.0a2.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
1
+ dbos-1.1.0a3.dist-info/METADATA,sha256=wwmIbv3PGphti-I6d0es4O3hZMHu7m7hXZbMfrko2WM,13267
2
+ dbos-1.1.0a3.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
+ dbos-1.1.0a3.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
+ dbos-1.1.0a3.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=A_28_nJ1nBBYDmCxtklJR9O2v14JRMtD1rAo_D4y8Kc,9764
@@ -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=P4cxqDIF8EXfYuWA36gt-JHneDVO_lMwtnNrT0WVWB4,81311
50
+ dbos/_sys_db.py,sha256=cgUpkgVF8jz_dcseShiJl4jFqoPlF24UadjVZ7LQ2qc,83235
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.1.0a2.dist-info/RECORD,,
70
+ dbos-1.1.0a3.dist-info/RECORD,,
File without changes