dbos 0.20.0a9__py3-none-any.whl → 0.21.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/_context.py CHANGED
@@ -63,7 +63,6 @@ class DBOSContext:
63
63
  self.parent_workflow_fid: int = -1
64
64
  self.workflow_id: str = ""
65
65
  self.function_id: int = -1
66
- self.in_recovery: bool = False
67
66
 
68
67
  self.curr_step_function_id: int = -1
69
68
  self.curr_tx_function_id: int = -1
@@ -82,7 +81,6 @@ class DBOSContext:
82
81
  rv.is_within_set_workflow_id_block = self.is_within_set_workflow_id_block
83
82
  rv.parent_workflow_id = self.workflow_id
84
83
  rv.parent_workflow_fid = self.function_id
85
- rv.in_recovery = self.in_recovery
86
84
  rv.authenticated_user = self.authenticated_user
87
85
  rv.authenticated_roles = (
88
86
  self.authenticated_roles[:]
@@ -335,34 +333,6 @@ class SetWorkflowID:
335
333
  return False # Did not handle
336
334
 
337
335
 
338
- class SetWorkflowRecovery:
339
- def __init__(self) -> None:
340
- self.created_ctx = False
341
-
342
- def __enter__(self) -> SetWorkflowRecovery:
343
- # Code to create a basic context
344
- ctx = get_local_dbos_context()
345
- if ctx is None:
346
- self.created_ctx = True
347
- _set_local_dbos_context(DBOSContext())
348
- assert_current_dbos_context().in_recovery = True
349
-
350
- return self
351
-
352
- def __exit__(
353
- self,
354
- exc_type: Optional[Type[BaseException]],
355
- exc_value: Optional[BaseException],
356
- traceback: Optional[TracebackType],
357
- ) -> Literal[False]:
358
- assert assert_current_dbos_context().in_recovery == True
359
- assert_current_dbos_context().in_recovery = False
360
- # Code to clean up the basic context if we created it
361
- if self.created_ctx:
362
- _clear_local_dbos_context()
363
- return False # Did not handle
364
-
365
-
366
336
  class EnterDBOSWorkflow(AbstractContextManager[DBOSContext, Literal[False]]):
367
337
  def __init__(self, attributes: TracedAttributes) -> None:
368
338
  self.created_ctx = False
dbos/_core.py CHANGED
@@ -185,8 +185,8 @@ def _init_workflow(
185
185
  # Synchronously record the status and inputs for workflows and single-step workflows
186
186
  # We also have to do this for single-step workflows because of the foreign key constraint on the operation outputs table
187
187
  # TODO: Make this transactional (and with the queue step below)
188
- wf_status = dbos._sys_db.update_workflow_status(
189
- status, False, ctx.in_recovery, max_recovery_attempts=max_recovery_attempts
188
+ wf_status = dbos._sys_db.insert_workflow_status(
189
+ status, max_recovery_attempts=max_recovery_attempts
190
190
  )
191
191
  # TODO: Modify the inputs if they were changed by `update_workflow_inputs`
192
192
  dbos._sys_db.update_workflow_inputs(wfid, _serialization.serialize_args(inputs))
dbos/_dbos.py CHANGED
@@ -801,7 +801,7 @@ class DBOS:
801
801
  def cancel_workflow(cls, workflow_id: str) -> None:
802
802
  """Cancel a workflow by ID."""
803
803
  _get_dbos_instance()._sys_db.set_workflow_status(
804
- workflow_id, WorkflowStatusString.CANCELLED, False
804
+ workflow_id, WorkflowStatusString.CANCELLED
805
805
  )
806
806
 
807
807
  @classmethod
dbos/_recovery.py CHANGED
@@ -4,7 +4,6 @@ import time
4
4
  import traceback
5
5
  from typing import TYPE_CHECKING, Any, List
6
6
 
7
- from ._context import SetWorkflowRecovery
8
7
  from ._core import execute_workflow_by_id
9
8
  from ._error import DBOSWorkflowFunctionNotFoundError
10
9
 
@@ -19,8 +18,7 @@ def startup_recovery_thread(dbos: "DBOS", workflow_ids: List[str]) -> None:
19
18
  while not stop_event.is_set() and len(workflow_ids) > 0:
20
19
  try:
21
20
  for workflowID in list(workflow_ids):
22
- with SetWorkflowRecovery():
23
- execute_workflow_by_id(dbos, workflowID)
21
+ execute_workflow_by_id(dbos, workflowID)
24
22
  workflow_ids.remove(workflowID)
25
23
  except DBOSWorkflowFunctionNotFoundError:
26
24
  time.sleep(1)
@@ -45,8 +43,7 @@ def recover_pending_workflows(
45
43
  dbos.logger.debug(f"Pending workflows: {workflow_ids}")
46
44
 
47
45
  for workflowID in workflow_ids:
48
- with SetWorkflowRecovery():
49
- handle = execute_workflow_by_id(dbos, workflowID)
46
+ handle = execute_workflow_by_id(dbos, workflowID)
50
47
  workflow_handles.append(handle)
51
48
 
52
49
  dbos.logger.info("Recovered pending workflows")
dbos/_sys_db.py CHANGED
@@ -243,66 +243,50 @@ class SystemDatabase:
243
243
  dbos_logger.debug("Waiting for system buffers to be exported")
244
244
  time.sleep(1)
245
245
 
246
- def update_workflow_status(
246
+ def insert_workflow_status(
247
247
  self,
248
248
  status: WorkflowStatusInternal,
249
- replace: bool = True,
250
- in_recovery: bool = False,
251
249
  *,
252
- conn: Optional[sa.Connection] = None,
253
250
  max_recovery_attempts: int = DEFAULT_MAX_RECOVERY_ATTEMPTS,
254
251
  ) -> WorkflowStatuses:
255
252
  wf_status: WorkflowStatuses = status["status"]
256
253
 
257
- cmd = pg.insert(SystemSchema.workflow_status).values(
258
- workflow_uuid=status["workflow_uuid"],
259
- status=status["status"],
260
- name=status["name"],
261
- class_name=status["class_name"],
262
- config_name=status["config_name"],
263
- output=status["output"],
264
- error=status["error"],
265
- executor_id=status["executor_id"],
266
- application_version=status["app_version"],
267
- application_id=status["app_id"],
268
- request=status["request"],
269
- authenticated_user=status["authenticated_user"],
270
- authenticated_roles=status["authenticated_roles"],
271
- assumed_role=status["assumed_role"],
272
- queue_name=status["queue_name"],
273
- )
274
- if replace:
275
- cmd = cmd.on_conflict_do_update(
276
- index_elements=["workflow_uuid"],
277
- set_=dict(
278
- status=status["status"],
279
- output=status["output"],
280
- error=status["error"],
281
- ),
282
- )
283
- elif in_recovery:
284
- cmd = cmd.on_conflict_do_update(
285
- index_elements=["workflow_uuid"],
286
- set_=dict(
287
- recovery_attempts=SystemSchema.workflow_status.c.recovery_attempts
288
- + 1,
254
+ cmd = (
255
+ pg.insert(SystemSchema.workflow_status)
256
+ .values(
257
+ workflow_uuid=status["workflow_uuid"],
258
+ status=status["status"],
259
+ name=status["name"],
260
+ class_name=status["class_name"],
261
+ config_name=status["config_name"],
262
+ output=status["output"],
263
+ error=status["error"],
264
+ executor_id=status["executor_id"],
265
+ application_version=status["app_version"],
266
+ application_id=status["app_id"],
267
+ request=status["request"],
268
+ authenticated_user=status["authenticated_user"],
269
+ authenticated_roles=status["authenticated_roles"],
270
+ assumed_role=status["assumed_role"],
271
+ queue_name=status["queue_name"],
272
+ recovery_attempts=(
273
+ 1 if wf_status != WorkflowStatusString.ENQUEUED.value else 0
289
274
  ),
290
275
  )
291
- else:
292
- # A blank update so that we can return the existing status
293
- cmd = cmd.on_conflict_do_update(
276
+ .on_conflict_do_update(
294
277
  index_elements=["workflow_uuid"],
295
278
  set_=dict(
296
- recovery_attempts=SystemSchema.workflow_status.c.recovery_attempts
279
+ recovery_attempts=(
280
+ SystemSchema.workflow_status.c.recovery_attempts + 1
281
+ ),
297
282
  ),
298
283
  )
284
+ )
285
+
299
286
  cmd = cmd.returning(SystemSchema.workflow_status.c.recovery_attempts, SystemSchema.workflow_status.c.status, SystemSchema.workflow_status.c.name, SystemSchema.workflow_status.c.class_name, SystemSchema.workflow_status.c.config_name, SystemSchema.workflow_status.c.queue_name) # type: ignore
300
287
 
301
- if conn is not None:
302
- results = conn.execute(cmd)
303
- else:
304
- with self.engine.begin() as c:
305
- results = c.execute(cmd)
288
+ with self.engine.begin() as c:
289
+ results = c.execute(cmd)
306
290
 
307
291
  row = results.fetchone()
308
292
  if row is not None:
@@ -325,7 +309,9 @@ class SystemDatabase:
325
309
  if err_msg is not None:
326
310
  raise DBOSConflictingWorkflowError(status["workflow_uuid"], err_msg)
327
311
 
328
- if in_recovery and recovery_attempts > max_recovery_attempts:
312
+ # Every time we start executing a workflow (and thus attempt to insert its status), we increment `recovery_attempts` by 1.
313
+ # When this number becomes equal to `maxRetries + 1`, we mark the workflow as `RETRIES_EXCEEDED`.
314
+ if recovery_attempts > max_recovery_attempts + 1:
329
315
  with self.engine.begin() as c:
330
316
  c.execute(
331
317
  sa.delete(SystemSchema.workflow_queue).where(
@@ -352,17 +338,62 @@ class SystemDatabase:
352
338
  status["workflow_uuid"], max_recovery_attempts
353
339
  )
354
340
 
355
- # Record we have exported status for this single-transaction workflow
341
+ return wf_status
342
+
343
+ def update_workflow_status(
344
+ self,
345
+ status: WorkflowStatusInternal,
346
+ *,
347
+ conn: Optional[sa.Connection] = None,
348
+ ) -> None:
349
+ wf_status: WorkflowStatuses = status["status"]
350
+
351
+ cmd = (
352
+ pg.insert(SystemSchema.workflow_status)
353
+ .values(
354
+ workflow_uuid=status["workflow_uuid"],
355
+ status=status["status"],
356
+ name=status["name"],
357
+ class_name=status["class_name"],
358
+ config_name=status["config_name"],
359
+ output=status["output"],
360
+ error=status["error"],
361
+ executor_id=status["executor_id"],
362
+ application_version=status["app_version"],
363
+ application_id=status["app_id"],
364
+ request=status["request"],
365
+ authenticated_user=status["authenticated_user"],
366
+ authenticated_roles=status["authenticated_roles"],
367
+ assumed_role=status["assumed_role"],
368
+ queue_name=status["queue_name"],
369
+ recovery_attempts=(
370
+ 1 if wf_status != WorkflowStatusString.ENQUEUED.value else 0
371
+ ),
372
+ )
373
+ .on_conflict_do_update(
374
+ index_elements=["workflow_uuid"],
375
+ set_=dict(
376
+ status=status["status"],
377
+ output=status["output"],
378
+ error=status["error"],
379
+ ),
380
+ )
381
+ )
382
+
383
+ if conn is not None:
384
+ conn.execute(cmd)
385
+ else:
386
+ with self.engine.begin() as c:
387
+ c.execute(cmd)
388
+
389
+ # If this is a single-transaction workflow, record that its status has been exported
356
390
  if status["workflow_uuid"] in self._temp_txn_wf_ids:
357
391
  self._exported_temp_txn_wf_status.add(status["workflow_uuid"])
358
392
 
359
- return wf_status
360
-
361
393
  def set_workflow_status(
362
394
  self,
363
395
  workflow_uuid: str,
364
396
  status: WorkflowStatusString,
365
- reset_recovery_attempts: bool,
366
397
  ) -> None:
367
398
  with self.engine.begin() as c:
368
399
  stmt = (
@@ -374,17 +405,6 @@ class SystemDatabase:
374
405
  )
375
406
  c.execute(stmt)
376
407
 
377
- if reset_recovery_attempts:
378
- with self.engine.begin() as c:
379
- stmt = (
380
- sa.update(SystemSchema.workflow_status)
381
- .where(
382
- SystemSchema.workflow_status.c.workflow_uuid == workflow_uuid
383
- )
384
- .values(recovery_attempts=reset_recovery_attempts)
385
- )
386
- c.execute(stmt)
387
-
388
408
  def get_workflow_status(
389
409
  self, workflow_uuid: str
390
410
  ) -> Optional[WorkflowStatusInternal]:
@@ -116,7 +116,7 @@ def _cancel_workflow(config: ConfigFile, uuid: str) -> None:
116
116
 
117
117
  try:
118
118
  sys_db = SystemDatabase(config)
119
- sys_db.set_workflow_status(uuid, WorkflowStatusString.CANCELLED, False)
119
+ sys_db.set_workflow_status(uuid, WorkflowStatusString.CANCELLED)
120
120
  return
121
121
 
122
122
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.20.0a9
3
+ Version: 0.21.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-0.20.0a9.dist-info/METADATA,sha256=NX9gLPLU9KG0-Rt5dpCc6Em3XznKk6yJ3kN1nO7pG9I,5309
2
- dbos-0.20.0a9.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
- dbos-0.20.0a9.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
- dbos-0.20.0a9.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
1
+ dbos-0.21.0a3.dist-info/METADATA,sha256=pYEcDMgafZVNUAn-kuU_BMF-kwBGN-RKkSpK1jP41cY,5309
2
+ dbos-0.21.0a3.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
+ dbos-0.21.0a3.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
+ dbos-0.21.0a3.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
@@ -9,11 +9,11 @@ dbos/_classproperty.py,sha256=f0X-_BySzn3yFDRKB2JpCbLYQ9tLwt1XftfshvY7CBs,626
9
9
  dbos/_cloudutils/authentication.py,sha256=V0fCWQN9stCkhbuuxgPTGpvuQcDqfU3KAxPAh01vKW4,5007
10
10
  dbos/_cloudutils/cloudutils.py,sha256=5e3CW1deSW-dI5G3QN0XbiVsBhyqT8wu7fuV2f8wtGU,7688
11
11
  dbos/_cloudutils/databases.py,sha256=x4187Djsyoa-QaG3Kog8JT2_GERsnqa93LIVanmVUmg,8393
12
- dbos/_context.py,sha256=RH08s_nee95vgxdz6AsYuVWF1LuJSVtOyIifblsa4pw,18760
13
- dbos/_core.py,sha256=-2oh2-NicMJBwTwrd2EQBQm4Vu0caozFeoS9Kj47DzM,36588
12
+ dbos/_context.py,sha256=FHB_fpE4fQt4fIJvAmMMsbY4xHwH77gsW01cFsRZjsE,17779
13
+ dbos/_core.py,sha256=nGiXyYgV8H5TRRZG0e8HCd5IZimufYQLmKNr7nBbwbo,36564
14
14
  dbos/_croniter.py,sha256=hbhgfsHBqclUS8VeLnJ9PSE9Z54z6mi4nnrr1aUXn0k,47561
15
15
  dbos/_db_wizard.py,sha256=xgKLna0_6Xi50F3o8msRosXba8NScHlpJR5ICVCkHDQ,7534
16
- dbos/_dbos.py,sha256=1PG142hzPBFguAbuBXaKS-YwzRdaIUW8087JCi78RmU,36193
16
+ dbos/_dbos.py,sha256=wjiOp7orMYm-jbHsstu7fnkKN3hRjY3V6ifKyyyJ_WY,36186
17
17
  dbos/_dbos_config.py,sha256=h_q1gzudhsAMVkGMD0qQ6kLic6YhdJgzm50YFSIx9Bo,8196
18
18
  dbos/_error.py,sha256=vtaSsG0QW6cRlwfZ4zzZWy_IHCZlomwSlrDyGWuyn8c,4337
19
19
  dbos/_fastapi.py,sha256=ke03vqsSYDnO6XeOtOVFXj0-f-v1MGsOxa9McaROvNc,3616
@@ -32,7 +32,7 @@ dbos/_migrations/versions/d76646551a6c_workflow_queue.py,sha256=G942nophZ2uC2vc4
32
32
  dbos/_migrations/versions/eab0cc1d9a14_job_queue.py,sha256=uvhFOtqbBreCePhAxZfIT0qCAI7BiZTou9wt6QnbY7c,1412
33
33
  dbos/_outcome.py,sha256=FDMgWVjZ06vm9xO-38H17mTqBImUYQxgKs_bDCSIAhE,6648
34
34
  dbos/_queue.py,sha256=o_aczwualJTMoXb0XXL-Y5QH77OEukWzuerogbWi2ho,2779
35
- dbos/_recovery.py,sha256=jbzGYxICA2drzyzlBSy2UiXhKV_16tBVacKQdTkqf-w,2008
35
+ dbos/_recovery.py,sha256=ehruOebVRl8qHftydStGZd-VjJ7p9oYtYXDwyT4ZHRY,1874
36
36
  dbos/_registrations.py,sha256=mei6q6_3R5uei8i_Wo_TqGZs85s10shOekDX41sFYD0,6642
37
37
  dbos/_request.py,sha256=cX1B3Atlh160phgS35gF1VEEV4pD126c9F3BDgBmxZU,929
38
38
  dbos/_roles.py,sha256=iOsgmIAf1XVzxs3gYWdGRe1B880YfOw5fpU7Jwx8_A8,2271
@@ -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=eXFXzmw_bq5Qp3s2_OzjkQKQj9HxMbP4AyJ2VQnJ08g,53786
44
+ dbos/_sys_db.py,sha256=HSOM4oQGC4XEEEOphoovKi670mnEdukddBaB2GEZ8K0,54607
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
@@ -53,11 +53,11 @@ dbos/_templates/dbos-db-starter/migrations/script.py.mako,sha256=MEqL-2qATlST9TA
53
53
  dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py,sha256=U5thFWGqNN4QLrNXT7wUUqftIFDNE5eSdqD8JNW1mec,942
54
54
  dbos/_templates/dbos-db-starter/start_postgres_docker.py,sha256=lQVLlYO5YkhGPEgPqwGc7Y8uDKse9HsWv5fynJEFJHM,1681
55
55
  dbos/_tracer.py,sha256=rvBY1RQU6DO7rL7EnaJJxGcmd4tP_PpGqUEE6imZnhY,2518
56
- dbos/_workflow_commands.py,sha256=tj-gJARjDJ5aYo0ii2udTAU4l36vbeXwmOYh8Q4y_ac,4625
56
+ dbos/_workflow_commands.py,sha256=w9chfcnmfeZMtt9jsimnIpEtL71xnvGLMY6HaAzRz4Y,4618
57
57
  dbos/cli/_github_init.py,sha256=Y_bDF9gfO2jB1id4FV5h1oIxEJRWyqVjhb7bNEa5nQ0,3224
58
58
  dbos/cli/_template_init.py,sha256=AfuMaO8bmr9WsPNHr6j2cp7kjVVZDUpH7KpbTg0hhFs,2722
59
59
  dbos/cli/cli.py,sha256=07TXdfDhImEOjB2-yhWJc1CK07_CSF-xF7TYCtB1kRY,12410
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.20.0a9.dist-info/RECORD,,
63
+ dbos-0.21.0a3.dist-info/RECORD,,