dbos 1.5.0a2__tar.gz → 1.5.0a4__tar.gz

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.
Files changed (108) hide show
  1. {dbos-1.5.0a2 → dbos-1.5.0a4}/PKG-INFO +1 -1
  2. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_admin_server.py +2 -2
  3. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_conductor/conductor.py +37 -0
  4. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_conductor/protocol.py +18 -0
  5. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_core.py +4 -4
  6. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_dbos.py +2 -2
  7. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_queue.py +0 -1
  8. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_recovery.py +4 -6
  9. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_workflow_commands.py +1 -2
  10. {dbos-1.5.0a2 → dbos-1.5.0a4}/pyproject.toml +1 -1
  11. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_admin_server.py +2 -2
  12. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_workflow_management.py +2 -1
  13. {dbos-1.5.0a2 → dbos-1.5.0a4}/LICENSE +0 -0
  14. {dbos-1.5.0a2 → dbos-1.5.0a4}/README.md +0 -0
  15. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/__init__.py +0 -0
  16. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/__main__.py +0 -0
  17. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_app_db.py +0 -0
  18. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_classproperty.py +0 -0
  19. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_client.py +0 -0
  20. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_context.py +0 -0
  21. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_croniter.py +0 -0
  22. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_dbos_config.py +0 -0
  23. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_debug.py +0 -0
  24. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_docker_pg_helper.py +0 -0
  25. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_error.py +0 -0
  26. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_event_loop.py +0 -0
  27. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_fastapi.py +0 -0
  28. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_flask.py +0 -0
  29. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_kafka.py +0 -0
  30. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_kafka_message.py +0 -0
  31. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_logger.py +0 -0
  32. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_migrations/env.py +0 -0
  33. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_migrations/script.py.mako +0 -0
  34. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -0
  35. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_migrations/versions/27ac6900c6ad_add_queue_dedup.py +0 -0
  36. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -0
  37. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py +0 -0
  38. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_migrations/versions/66478e1b95e5_consolidate_queues.py +0 -0
  39. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_migrations/versions/83f3732ae8e7_workflow_timeout.py +0 -0
  40. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_migrations/versions/933e86bdac6a_add_queue_priority.py +0 -0
  41. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_migrations/versions/a3b18ad34abe_added_triggers.py +0 -0
  42. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -0
  43. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_migrations/versions/d76646551a6c_workflow_queue.py +0 -0
  44. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_migrations/versions/d994145b47b6_consolidate_inputs.py +0 -0
  45. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_migrations/versions/eab0cc1d9a14_job_queue.py +0 -0
  46. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py +0 -0
  47. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_outcome.py +0 -0
  48. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_registrations.py +0 -0
  49. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_roles.py +0 -0
  50. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_scheduler.py +0 -0
  51. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_schemas/__init__.py +0 -0
  52. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_schemas/application_database.py +0 -0
  53. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_schemas/system_database.py +0 -0
  54. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_serialization.py +0 -0
  55. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_sys_db.py +0 -0
  56. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_templates/dbos-db-starter/README.md +0 -0
  57. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
  58. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_templates/dbos-db-starter/__package/main.py.dbos +0 -0
  59. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
  60. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_templates/dbos-db-starter/alembic.ini +0 -0
  61. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
  62. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -0
  63. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -0
  64. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +0 -0
  65. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
  66. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_tracer.py +0 -0
  67. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/_utils.py +0 -0
  68. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/cli/_github_init.py +0 -0
  69. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/cli/_template_init.py +0 -0
  70. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/cli/cli.py +0 -0
  71. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/dbos-config.schema.json +0 -0
  72. {dbos-1.5.0a2 → dbos-1.5.0a4}/dbos/py.typed +0 -0
  73. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/__init__.py +0 -0
  74. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/atexit_no_ctor.py +0 -0
  75. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/atexit_no_launch.py +0 -0
  76. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/classdefs.py +0 -0
  77. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/client_collateral.py +0 -0
  78. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/client_worker.py +0 -0
  79. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/conftest.py +0 -0
  80. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/dupname_classdefs1.py +0 -0
  81. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/dupname_classdefsa.py +0 -0
  82. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/more_classdefs.py +0 -0
  83. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/queuedworkflow.py +0 -0
  84. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_async.py +0 -0
  85. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_classdecorators.py +0 -0
  86. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_cli.py +0 -0
  87. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_client.py +0 -0
  88. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_concurrency.py +0 -0
  89. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_config.py +0 -0
  90. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_croniter.py +0 -0
  91. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_dbos.py +0 -0
  92. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_debug.py +0 -0
  93. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_docker_secrets.py +0 -0
  94. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_failures.py +0 -0
  95. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_fastapi.py +0 -0
  96. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_fastapi_roles.py +0 -0
  97. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_flask.py +0 -0
  98. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_kafka.py +0 -0
  99. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_outcome.py +0 -0
  100. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_package.py +0 -0
  101. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_queue.py +0 -0
  102. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_scheduler.py +0 -0
  103. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_schema_migration.py +0 -0
  104. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_singleton.py +0 -0
  105. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_spans.py +0 -0
  106. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_sqlalchemy.py +0 -0
  107. {dbos-1.5.0a2 → dbos-1.5.0a4}/tests/test_workflow_introspection.py +0 -0
  108. {dbos-1.5.0a2 → dbos-1.5.0a4}/version/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 1.5.0a2
3
+ Version: 1.5.0a4
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -139,8 +139,8 @@ class AdminRequestHandler(BaseHTTPRequestHandler):
139
139
  self._end_headers()
140
140
  elif self.path == _global_timeout_path:
141
141
  inputs = json.loads(post_data.decode("utf-8"))
142
- timeout_ms = inputs.get("timeout_ms", None)
143
- global_timeout(self.dbos, timeout_ms)
142
+ cutoff_epoch_timestamp_ms = inputs.get("cutoff_epoch_timestamp_ms", None)
143
+ global_timeout(self.dbos, cutoff_epoch_timestamp_ms)
144
144
  self.send_response(204)
145
145
  self._end_headers()
146
146
  else:
@@ -13,7 +13,9 @@ from websockets.sync.connection import Connection
13
13
  from dbos._context import SetWorkflowID
14
14
  from dbos._utils import GlobalParams
15
15
  from dbos._workflow_commands import (
16
+ garbage_collect,
16
17
  get_workflow,
18
+ global_timeout,
17
19
  list_queued_workflows,
18
20
  list_workflow_steps,
19
21
  list_workflows,
@@ -356,6 +358,41 @@ class ConductorWebsocket(threading.Thread):
356
358
  error_message=error_message,
357
359
  )
358
360
  websocket.send(list_steps_response.to_json())
361
+ elif msg_type == p.MessageType.RETENTION:
362
+ retention_message = p.RetentionRequest.from_json(message)
363
+ success = True
364
+ try:
365
+ garbage_collect(
366
+ self.dbos,
367
+ cutoff_epoch_timestamp_ms=retention_message.body[
368
+ "gc_cutoff_epoch_ms"
369
+ ],
370
+ rows_threshold=retention_message.body[
371
+ "gc_rows_threshold"
372
+ ],
373
+ )
374
+ if (
375
+ retention_message.body["timeout_cutoff_epoch_ms"]
376
+ is not None
377
+ ):
378
+ global_timeout(
379
+ self.dbos,
380
+ retention_message.body[
381
+ "timeout_cutoff_epoch_ms"
382
+ ],
383
+ )
384
+ except Exception as e:
385
+ error_message = f"Exception encountered during enforcing retention policy: {traceback.format_exc()}"
386
+ self.dbos.logger.error(error_message)
387
+ success = False
388
+
389
+ retention_response = p.RetentionResponse(
390
+ type=p.MessageType.RETENTION,
391
+ request_id=base_message.request_id,
392
+ success=success,
393
+ error_message=error_message,
394
+ )
395
+ websocket.send(retention_response.to_json())
359
396
  else:
360
397
  self.dbos.logger.warning(
361
398
  f"Unexpected message type: {msg_type}"
@@ -18,6 +18,7 @@ class MessageType(str, Enum):
18
18
  EXIST_PENDING_WORKFLOWS = "exist_pending_workflows"
19
19
  LIST_STEPS = "list_steps"
20
20
  FORK_WORKFLOW = "fork_workflow"
21
+ RETENTION = "retention"
21
22
 
22
23
 
23
24
  T = TypeVar("T", bound="BaseMessage")
@@ -280,3 +281,20 @@ class ForkWorkflowRequest(BaseMessage):
280
281
  class ForkWorkflowResponse(BaseMessage):
281
282
  new_workflow_id: Optional[str]
282
283
  error_message: Optional[str] = None
284
+
285
+
286
+ class RetentionBody(TypedDict):
287
+ gc_cutoff_epoch_ms: Optional[int]
288
+ gc_rows_threshold: Optional[int]
289
+ timeout_cutoff_epoch_ms: Optional[int]
290
+
291
+
292
+ @dataclass
293
+ class RetentionRequest(BaseMessage):
294
+ body: RetentionBody
295
+
296
+
297
+ @dataclass
298
+ class RetentionResponse(BaseMessage):
299
+ success: bool
300
+ error_message: Optional[str] = None
@@ -404,9 +404,9 @@ def _execute_workflow_wthread(
404
404
  return dbos._background_event_loop.submit_coroutine(
405
405
  cast(Pending[R], result)()
406
406
  )
407
- except Exception:
407
+ except Exception as e:
408
408
  dbos.logger.error(
409
- f"Exception encountered in asynchronous workflow: {traceback.format_exc()}"
409
+ f"Exception encountered in asynchronous workflow:", exc_info=e
410
410
  )
411
411
  raise
412
412
 
@@ -430,9 +430,9 @@ async def _execute_workflow_async(
430
430
  _get_wf_invoke_func(dbos, status)
431
431
  )
432
432
  return await result()
433
- except Exception:
433
+ except Exception as e:
434
434
  dbos.logger.error(
435
- f"Exception encountered in asynchronous workflow: {traceback.format_exc()}"
435
+ f"Exception encountered in asynchronous workflow:", exc_info=e
436
436
  )
437
437
  raise
438
438
 
@@ -521,8 +521,8 @@ class DBOS:
521
521
  handler.flush()
522
522
  add_otlp_to_all_loggers()
523
523
  add_transformer_to_all_loggers()
524
- except Exception:
525
- dbos_logger.error(f"DBOS failed to launch: {traceback.format_exc()}")
524
+ except Exception as e:
525
+ dbos_logger.error(f"DBOS failed to launch:", exc_info=e)
526
526
  raise
527
527
 
528
528
  @classmethod
@@ -1,5 +1,4 @@
1
1
  import threading
2
- import traceback
3
2
  from typing import TYPE_CHECKING, Any, Callable, Coroutine, Optional, TypedDict
4
3
 
5
4
  from psycopg import errors
@@ -1,7 +1,5 @@
1
- import os
2
1
  import threading
3
2
  import time
4
- import traceback
5
3
  from typing import TYPE_CHECKING, Any, List
6
4
 
7
5
  from dbos._utils import GlobalParams
@@ -39,9 +37,9 @@ def startup_recovery_thread(
39
37
  time.sleep(1)
40
38
  except Exception as e:
41
39
  dbos.logger.error(
42
- f"Exception encountered when recovering workflows: {traceback.format_exc()}"
40
+ f"Exception encountered when recovering workflows:", exc_info=e
43
41
  )
44
- raise e
42
+ raise
45
43
 
46
44
 
47
45
  def recover_pending_workflows(
@@ -59,9 +57,9 @@ def recover_pending_workflows(
59
57
  workflow_handles.append(handle)
60
58
  except Exception as e:
61
59
  dbos.logger.error(
62
- f"Exception encountered when recovering workflows: {traceback.format_exc()}"
60
+ f"Exception encountered when recovering workflows:", exc_info=e
63
61
  )
64
- raise e
62
+ raise
65
63
  dbos.logger.info(
66
64
  f"Recovering {len(pending_workflows)} workflows for executor {executor_id} from version {GlobalParams.app_version}"
67
65
  )
@@ -141,8 +141,7 @@ def garbage_collect(
141
141
  dbos._app_db.garbage_collect(cutoff_epoch_timestamp_ms, pending_workflow_ids)
142
142
 
143
143
 
144
- def global_timeout(dbos: "DBOS", timeout_ms: int) -> None:
145
- cutoff_epoch_timestamp_ms = int(time.time() * 1000) - timeout_ms
144
+ def global_timeout(dbos: "DBOS", cutoff_epoch_timestamp_ms: int) -> None:
146
145
  cutoff_iso = datetime.fromtimestamp(cutoff_epoch_timestamp_ms / 1000).isoformat()
147
146
  for workflow in dbos.list_workflows(
148
147
  status=WorkflowStatusString.PENDING.value, end_time=cutoff_iso
@@ -27,7 +27,7 @@ dependencies = [
27
27
  ]
28
28
  requires-python = ">=3.9"
29
29
  readme = "README.md"
30
- version = "1.5.0a2"
30
+ version = "1.5.0a4"
31
31
 
32
32
  [project.license]
33
33
  text = "MIT"
@@ -484,10 +484,10 @@ def test_admin_global_timeout(dbos: DBOS) -> None:
484
484
 
485
485
  handle = DBOS.start_workflow(workflow)
486
486
  time.sleep(1)
487
-
487
+ cutoff_epoch_timestamp_ms = int(time.time() * 1000) - 1000
488
488
  response = requests.post(
489
489
  f"http://localhost:3001/dbos-global-timeout",
490
- json={"timeout_ms": 1000},
490
+ json={"cutoff_epoch_timestamp_ms": cutoff_epoch_timestamp_ms},
491
491
  timeout=5,
492
492
  )
493
493
  response.raise_for_status()
@@ -737,7 +737,8 @@ def test_global_timeout(dbos: DBOS) -> None:
737
737
  # Wait one second, start one final workflow, then timeout all workflows started more than one second ago
738
738
  time.sleep(1)
739
739
  final_handle = DBOS.start_workflow(blocked_workflow)
740
- global_timeout(dbos, 1000)
740
+ cutoff_epoch_timestamp_ms = int(time.time() * 1000) - 1000
741
+ global_timeout(dbos, cutoff_epoch_timestamp_ms)
741
742
 
742
743
  # Verify all workflows started before the global timeout are cancelled
743
744
  for handle in handles:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes