dbos 0.27.0a11__tar.gz → 0.27.1__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 (105) hide show
  1. {dbos-0.27.0a11 → dbos-0.27.1}/PKG-INFO +1 -1
  2. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_admin_server.py +3 -3
  3. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_core.py +1 -1
  4. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_dbos.py +11 -6
  5. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_recovery.py +1 -1
  6. {dbos-0.27.0a11 → dbos-0.27.1}/pyproject.toml +1 -1
  7. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_admin_server.py +54 -2
  8. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_scheduler.py +2 -1
  9. {dbos-0.27.0a11 → dbos-0.27.1}/LICENSE +0 -0
  10. {dbos-0.27.0a11 → dbos-0.27.1}/README.md +0 -0
  11. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/__init__.py +0 -0
  12. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/__main__.py +0 -0
  13. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_app_db.py +0 -0
  14. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_classproperty.py +0 -0
  15. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_client.py +0 -0
  16. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_conductor/conductor.py +0 -0
  17. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_conductor/protocol.py +0 -0
  18. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_context.py +0 -0
  19. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_croniter.py +0 -0
  20. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_dbos_config.py +0 -0
  21. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_debug.py +0 -0
  22. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_docker_pg_helper.py +0 -0
  23. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_error.py +0 -0
  24. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_event_loop.py +0 -0
  25. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_fastapi.py +0 -0
  26. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_flask.py +0 -0
  27. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_kafka.py +0 -0
  28. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_kafka_message.py +0 -0
  29. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_logger.py +0 -0
  30. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_migrations/env.py +0 -0
  31. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_migrations/script.py.mako +0 -0
  32. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -0
  33. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_migrations/versions/27ac6900c6ad_add_queue_dedup.py +0 -0
  34. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -0
  35. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py +0 -0
  36. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_migrations/versions/83f3732ae8e7_workflow_timeout.py +0 -0
  37. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_migrations/versions/a3b18ad34abe_added_triggers.py +0 -0
  38. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -0
  39. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_migrations/versions/d76646551a6c_workflow_queue.py +0 -0
  40. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_migrations/versions/eab0cc1d9a14_job_queue.py +0 -0
  41. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py +0 -0
  42. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_outcome.py +0 -0
  43. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_queue.py +0 -0
  44. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_registrations.py +0 -0
  45. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_request.py +0 -0
  46. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_roles.py +0 -0
  47. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_scheduler.py +0 -0
  48. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_schemas/__init__.py +0 -0
  49. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_schemas/application_database.py +0 -0
  50. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_schemas/system_database.py +0 -0
  51. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_serialization.py +0 -0
  52. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_sys_db.py +0 -0
  53. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_templates/dbos-db-starter/README.md +0 -0
  54. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
  55. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_templates/dbos-db-starter/__package/main.py +0 -0
  56. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
  57. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_templates/dbos-db-starter/alembic.ini +0 -0
  58. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
  59. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -0
  60. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -0
  61. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +0 -0
  62. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
  63. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_tracer.py +0 -0
  64. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_utils.py +0 -0
  65. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/_workflow_commands.py +0 -0
  66. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/cli/_github_init.py +0 -0
  67. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/cli/_template_init.py +0 -0
  68. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/cli/cli.py +0 -0
  69. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/dbos-config.schema.json +0 -0
  70. {dbos-0.27.0a11 → dbos-0.27.1}/dbos/py.typed +0 -0
  71. {dbos-0.27.0a11 → dbos-0.27.1}/tests/__init__.py +0 -0
  72. {dbos-0.27.0a11 → dbos-0.27.1}/tests/atexit_no_ctor.py +0 -0
  73. {dbos-0.27.0a11 → dbos-0.27.1}/tests/atexit_no_launch.py +0 -0
  74. {dbos-0.27.0a11 → dbos-0.27.1}/tests/classdefs.py +0 -0
  75. {dbos-0.27.0a11 → dbos-0.27.1}/tests/client_collateral.py +0 -0
  76. {dbos-0.27.0a11 → dbos-0.27.1}/tests/client_worker.py +0 -0
  77. {dbos-0.27.0a11 → dbos-0.27.1}/tests/conftest.py +0 -0
  78. {dbos-0.27.0a11 → dbos-0.27.1}/tests/dupname_classdefs1.py +0 -0
  79. {dbos-0.27.0a11 → dbos-0.27.1}/tests/dupname_classdefsa.py +0 -0
  80. {dbos-0.27.0a11 → dbos-0.27.1}/tests/more_classdefs.py +0 -0
  81. {dbos-0.27.0a11 → dbos-0.27.1}/tests/queuedworkflow.py +0 -0
  82. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_async.py +0 -0
  83. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_classdecorators.py +0 -0
  84. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_client.py +0 -0
  85. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_concurrency.py +0 -0
  86. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_config.py +0 -0
  87. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_croniter.py +0 -0
  88. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_dbos.py +0 -0
  89. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_debug.py +0 -0
  90. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_docker_secrets.py +0 -0
  91. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_failures.py +0 -0
  92. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_fastapi.py +0 -0
  93. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_fastapi_roles.py +0 -0
  94. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_flask.py +0 -0
  95. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_kafka.py +0 -0
  96. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_outcome.py +0 -0
  97. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_package.py +0 -0
  98. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_queue.py +0 -0
  99. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_schema_migration.py +0 -0
  100. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_singleton.py +0 -0
  101. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_spans.py +0 -0
  102. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_sqlalchemy.py +0 -0
  103. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_workflow_introspection.py +0 -0
  104. {dbos-0.27.0a11 → dbos-0.27.1}/tests/test_workflow_management.py +0 -0
  105. {dbos-0.27.0a11 → dbos-0.27.1}/version/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.27.0a11
3
+ Version: 0.27.1
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -66,11 +66,11 @@ class AdminRequestHandler(BaseHTTPRequestHandler):
66
66
  elif self.path == _deactivate_path:
67
67
  if not AdminRequestHandler.is_deactivated:
68
68
  dbos_logger.info(
69
- f"Deactivating DBOS executor {GlobalParams.executor_id} with version {GlobalParams.app_version}. This executor will complete existing workflows but will not start new workflows."
69
+ f"Deactivating DBOS executor {GlobalParams.executor_id} with version {GlobalParams.app_version}. This executor will complete existing workflows but will not create new workflows."
70
70
  )
71
71
  AdminRequestHandler.is_deactivated = True
72
- # Stop all scheduled workflows, queues, and kafka loops
73
- for event in self.dbos.stop_events:
72
+ # Stop all event receivers (scheduler and Kafka threads)
73
+ for event in self.dbos.poller_stop_events:
74
74
  event.set()
75
75
  self.send_response(200)
76
76
  self._end_headers()
@@ -296,7 +296,7 @@ def _init_workflow(
296
296
 
297
297
  if workflow_deadline_epoch_ms is not None:
298
298
  evt = threading.Event()
299
- dbos.stop_events.append(evt)
299
+ dbos.background_thread_stop_events.append(evt)
300
300
 
301
301
  def timeout_func() -> None:
302
302
  try:
@@ -197,7 +197,7 @@ class DBOSRegistry:
197
197
  self, evt: threading.Event, func: Callable[..., Any], *args: Any, **kwargs: Any
198
198
  ) -> None:
199
199
  if self.dbos and self.dbos._launched:
200
- self.dbos.stop_events.append(evt)
200
+ self.dbos.poller_stop_events.append(evt)
201
201
  self.dbos._executor.submit(func, *args, **kwargs)
202
202
  else:
203
203
  self.pollers.append((evt, func, args, kwargs))
@@ -330,7 +330,10 @@ class DBOS:
330
330
  self._registry: DBOSRegistry = _get_or_create_dbos_registry()
331
331
  self._registry.dbos = self
332
332
  self._admin_server_field: Optional[AdminServer] = None
333
- self.stop_events: List[threading.Event] = []
333
+ # Stop internal background threads (queue thread, timeout threads, etc.)
334
+ self.background_thread_stop_events: List[threading.Event] = []
335
+ # Stop pollers (event receivers) that can create new workflows (scheduler, Kafka)
336
+ self.poller_stop_events: List[threading.Event] = []
334
337
  self.fastapi: Optional["FastAPI"] = fastapi
335
338
  self.flask: Optional["Flask"] = flask
336
339
  self._executor_field: Optional[ThreadPoolExecutor] = None
@@ -502,7 +505,7 @@ class DBOS:
502
505
 
503
506
  # Start the queue thread
504
507
  evt = threading.Event()
505
- self.stop_events.append(evt)
508
+ self.background_thread_stop_events.append(evt)
506
509
  bg_queue_thread = threading.Thread(
507
510
  target=queue_thread, args=(evt, self), daemon=True
508
511
  )
@@ -515,7 +518,7 @@ class DBOS:
515
518
  dbos_domain = os.environ.get("DBOS_DOMAIN", "cloud.dbos.dev")
516
519
  self.conductor_url = f"wss://{dbos_domain}/conductor/v1alpha1"
517
520
  evt = threading.Event()
518
- self.stop_events.append(evt)
521
+ self.background_thread_stop_events.append(evt)
519
522
  self.conductor_websocket = ConductorWebsocket(
520
523
  self,
521
524
  conductor_url=self.conductor_url,
@@ -527,7 +530,7 @@ class DBOS:
527
530
 
528
531
  # Grab any pollers that were deferred and start them
529
532
  for evt, func, args, kwargs in self._registry.pollers:
530
- self.stop_events.append(evt)
533
+ self.poller_stop_events.append(evt)
531
534
  poller_thread = threading.Thread(
532
535
  target=func, args=args, kwargs=kwargs, daemon=True
533
536
  )
@@ -583,7 +586,9 @@ class DBOS:
583
586
 
584
587
  def _destroy(self) -> None:
585
588
  self._initialized = False
586
- for event in self.stop_events:
589
+ for event in self.poller_stop_events:
590
+ event.set()
591
+ for event in self.background_thread_stop_events:
587
592
  event.set()
588
593
  self._background_event_loop.stop()
589
594
  if self._sys_db_field is not None:
@@ -29,7 +29,7 @@ def startup_recovery_thread(
29
29
  ) -> None:
30
30
  """Attempt to recover local pending workflows on startup using a background thread."""
31
31
  stop_event = threading.Event()
32
- dbos.stop_events.append(stop_event)
32
+ dbos.background_thread_stop_events.append(stop_event)
33
33
  while not stop_event.is_set() and len(pending_workflows) > 0:
34
34
  try:
35
35
  for pending_workflow in list(pending_workflows):
@@ -28,7 +28,7 @@ dependencies = [
28
28
  ]
29
29
  requires-python = ">=3.9"
30
30
  readme = "README.md"
31
- version = "0.27.0a11"
31
+ version = "0.27.1"
32
32
 
33
33
  [project.license]
34
34
  text = "MIT"
@@ -3,6 +3,7 @@ import socket
3
3
  import threading
4
4
  import time
5
5
  import uuid
6
+ from datetime import datetime
6
7
 
7
8
  import pytest
8
9
  import requests
@@ -65,8 +66,59 @@ def test_admin_endpoints(dbos: DBOS) -> None:
65
66
  response = requests.get("http://localhost:3001/deactivate", timeout=5)
66
67
  assert response.status_code == 200
67
68
 
68
- for event in dbos.stop_events:
69
- assert event.is_set(), "Event is not set!"
69
+ for event in dbos.poller_stop_events:
70
+ assert event.is_set()
71
+
72
+
73
+ def test_deactivate(dbos: DBOS, config: ConfigFile) -> None:
74
+ wf_counter: int = 0
75
+
76
+ queue = Queue("example-queue")
77
+
78
+ @DBOS.scheduled("* * * * * *")
79
+ @DBOS.workflow()
80
+ def test_workflow(scheduled: datetime, actual: datetime) -> None:
81
+ nonlocal wf_counter
82
+ wf_counter += 1
83
+
84
+ @DBOS.workflow()
85
+ def regular_workflow() -> int:
86
+ return 5
87
+
88
+ # Let the scheduled workflow run
89
+ time.sleep(2)
90
+ val = wf_counter
91
+ assert val > 0
92
+ # Deactivate--scheduled workflow should stop
93
+ response = requests.get("http://localhost:3001/deactivate", timeout=5)
94
+ assert response.status_code == 200
95
+ for event in dbos.poller_stop_events:
96
+ assert event.is_set()
97
+ # Verify the scheduled workflow does not run anymore
98
+ time.sleep(3)
99
+ assert wf_counter <= val + 1
100
+ # Enqueue a workflow, verify it still runs
101
+ assert queue.enqueue(regular_workflow).get_result() == 5
102
+
103
+ # Test deferred event receivers
104
+ DBOS.destroy(destroy_registry=True)
105
+ dbos = DBOS(config=config)
106
+
107
+ @DBOS.scheduled("* * * * * *")
108
+ @DBOS.workflow()
109
+ def deferred_workflow(scheduled: datetime, actual: datetime) -> None:
110
+ nonlocal wf_counter
111
+ wf_counter += 1
112
+
113
+ DBOS.launch()
114
+ assert len(dbos.poller_stop_events) > 0
115
+ for event in dbos.poller_stop_events:
116
+ assert not event.is_set()
117
+ # Deactivate--scheduled workflow should stop
118
+ response = requests.get("http://localhost:3001/deactivate", timeout=5)
119
+ assert response.status_code == 200
120
+ for event in dbos.poller_stop_events:
121
+ assert event.is_set()
70
122
 
71
123
 
72
124
  def test_admin_recovery(config: ConfigFile) -> None:
@@ -202,7 +202,8 @@ def test_scheduler_oaoo(dbos: DBOS) -> None:
202
202
  time.sleep(1)
203
203
 
204
204
  # Stop the scheduled workflow
205
- dbos.stop_events[0].set()
205
+ for evt in dbos.poller_stop_events:
206
+ evt.set()
206
207
 
207
208
  dbos._sys_db.update_workflow_status(
208
209
  {
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