dbos 0.23.0a9__tar.gz → 0.23.0a10__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.
Potentially problematic release.
This version of dbos might be problematic. Click here for more details.
- {dbos-0.23.0a9 → dbos-0.23.0a10}/PKG-INFO +1 -1
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_recovery.py +12 -6
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_sys_db.py +26 -12
- {dbos-0.23.0a9 → dbos-0.23.0a10}/pyproject.toml +1 -1
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_queue.py +35 -1
- {dbos-0.23.0a9 → dbos-0.23.0a10}/LICENSE +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/README.md +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/__init__.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/__main__.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_admin_server.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_app_db.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_classproperty.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_cloudutils/authentication.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_cloudutils/cloudutils.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_cloudutils/databases.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_context.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_core.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_croniter.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_db_wizard.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_dbos.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_dbos_config.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_debug.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_error.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_fastapi.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_flask.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_kafka.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_kafka_message.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_logger.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_migrations/env.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_migrations/script.py.mako +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_migrations/versions/a3b18ad34abe_added_triggers.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_migrations/versions/d76646551a6c_workflow_queue.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_migrations/versions/eab0cc1d9a14_job_queue.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_outcome.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_queue.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_registrations.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_request.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_roles.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_scheduler.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_schemas/__init__.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_schemas/application_database.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_schemas/system_database.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_serialization.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_templates/dbos-db-starter/README.md +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_templates/dbos-db-starter/__package/main.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_templates/dbos-db-starter/alembic.ini +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_tracer.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_utils.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_workflow_commands.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/cli/_github_init.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/cli/_template_init.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/cli/cli.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/dbos-config.schema.json +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/py.typed +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/__init__.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/atexit_no_ctor.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/atexit_no_launch.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/classdefs.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/conftest.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/more_classdefs.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/queuedworkflow.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_admin_server.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_async.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_classdecorators.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_concurrency.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_config.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_croniter.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_dbos.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_debug.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_failures.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_fastapi.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_fastapi_roles.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_flask.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_kafka.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_outcome.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_package.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_scheduler.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_schema_migration.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_singleton.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_spans.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_sqlalchemy.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_workflow_cancel.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/tests/test_workflow_cmds.py +0 -0
- {dbos-0.23.0a9 → dbos-0.23.0a10}/version/__init__.py +0 -0
|
@@ -27,8 +27,9 @@ def startup_recovery_thread(
|
|
|
27
27
|
pending_workflow.queue_name
|
|
28
28
|
and pending_workflow.queue_name != "_dbos_internal_queue"
|
|
29
29
|
):
|
|
30
|
-
dbos._sys_db.clear_queue_assignment(pending_workflow.workflow_uuid)
|
|
31
|
-
|
|
30
|
+
cleared = dbos._sys_db.clear_queue_assignment(pending_workflow.workflow_uuid)
|
|
31
|
+
if cleared:
|
|
32
|
+
continue
|
|
32
33
|
execute_workflow_by_id(dbos, pending_workflow.workflow_uuid)
|
|
33
34
|
pending_workflows.remove(pending_workflow)
|
|
34
35
|
except DBOSWorkflowFunctionNotFoundError:
|
|
@@ -56,10 +57,15 @@ def recover_pending_workflows(
|
|
|
56
57
|
and pending_workflow.queue_name != "_dbos_internal_queue"
|
|
57
58
|
):
|
|
58
59
|
try:
|
|
59
|
-
dbos._sys_db.clear_queue_assignment(pending_workflow.workflow_uuid)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
cleared = dbos._sys_db.clear_queue_assignment(pending_workflow.workflow_uuid)
|
|
61
|
+
if cleared:
|
|
62
|
+
workflow_handles.append(
|
|
63
|
+
dbos.retrieve_workflow(pending_workflow.workflow_uuid)
|
|
64
|
+
)
|
|
65
|
+
else:
|
|
66
|
+
workflow_handles.append(
|
|
67
|
+
execute_workflow_by_id(dbos, pending_workflow.workflow_uuid)
|
|
68
|
+
)
|
|
63
69
|
except Exception as e:
|
|
64
70
|
dbos.logger.error(e)
|
|
65
71
|
else:
|
|
@@ -1460,21 +1460,35 @@ class SystemDatabase:
|
|
|
1460
1460
|
.values(completed_at_epoch_ms=int(time.time() * 1000))
|
|
1461
1461
|
)
|
|
1462
1462
|
|
|
1463
|
-
|
|
1463
|
+
|
|
1464
|
+
def clear_queue_assignment(self, workflow_id: str) -> bool:
|
|
1464
1465
|
if self._debug_mode:
|
|
1465
1466
|
raise Exception("called clear_queue_assignment in debug mode")
|
|
1466
|
-
with self.engine.begin() as c:
|
|
1467
|
-
c.execute(
|
|
1468
|
-
sa.update(SystemSchema.workflow_queue)
|
|
1469
|
-
.where(SystemSchema.workflow_queue.c.workflow_uuid == workflow_id)
|
|
1470
|
-
.values(executor_id=None, started_at_epoch_ms=None)
|
|
1471
|
-
)
|
|
1472
|
-
c.execute(
|
|
1473
|
-
sa.update(SystemSchema.workflow_status)
|
|
1474
|
-
.where(SystemSchema.workflow_status.c.workflow_uuid == workflow_id)
|
|
1475
|
-
.values(executor_id=None, status=WorkflowStatusString.ENQUEUED.value)
|
|
1476
|
-
)
|
|
1477
1467
|
|
|
1468
|
+
with self.engine.connect() as conn:
|
|
1469
|
+
with conn.begin() as transaction:
|
|
1470
|
+
res = conn.execute(
|
|
1471
|
+
sa.update(SystemSchema.workflow_queue)
|
|
1472
|
+
.where(SystemSchema.workflow_queue.c.workflow_uuid == workflow_id)
|
|
1473
|
+
.values(executor_id=None, started_at_epoch_ms=None)
|
|
1474
|
+
)
|
|
1475
|
+
|
|
1476
|
+
# If no rows were affected, the workflow is not anymore in the queue
|
|
1477
|
+
if res.rowcount == 0:
|
|
1478
|
+
transaction.rollback()
|
|
1479
|
+
return False
|
|
1480
|
+
|
|
1481
|
+
res = conn.execute(
|
|
1482
|
+
sa.update(SystemSchema.workflow_status)
|
|
1483
|
+
.where(SystemSchema.workflow_status.c.workflow_uuid == workflow_id)
|
|
1484
|
+
.values(executor_id=None, status=WorkflowStatusString.ENQUEUED.value)
|
|
1485
|
+
)
|
|
1486
|
+
if res.rowcount == 0:
|
|
1487
|
+
# This should never happen
|
|
1488
|
+
raise Exception(
|
|
1489
|
+
f"UNREACHABLE: Workflow {workflow_id} is found in the workflow_queue table but not found in the workflow_status table"
|
|
1490
|
+
)
|
|
1491
|
+
return True
|
|
1478
1492
|
|
|
1479
1493
|
def reset_system_database(config: ConfigFile) -> None:
|
|
1480
1494
|
sysdb_name = (
|
|
@@ -19,7 +19,7 @@ from dbos import (
|
|
|
19
19
|
WorkflowHandle,
|
|
20
20
|
)
|
|
21
21
|
from dbos._schemas.system_database import SystemSchema
|
|
22
|
-
from dbos._sys_db import WorkflowStatusString
|
|
22
|
+
from dbos._sys_db import WorkflowStatusString, _buffer_flush_interval_secs
|
|
23
23
|
from tests.conftest import default_config, queue_entries_are_cleaned_up
|
|
24
24
|
|
|
25
25
|
|
|
@@ -900,6 +900,40 @@ def test_resuming_queued_workflows(dbos: DBOS) -> None:
|
|
|
900
900
|
assert queue_entries_are_cleaned_up(dbos)
|
|
901
901
|
|
|
902
902
|
|
|
903
|
+
# Test a race condition between removing a task from the queue and flushing the status buffer
|
|
904
|
+
def test_resuming_already_completed_queue_workflow(dbos: DBOS) -> None:
|
|
905
|
+
dbos._sys_db._run_background_processes = False # Disable buffer flush
|
|
906
|
+
|
|
907
|
+
start_event = threading.Event()
|
|
908
|
+
counter = 0
|
|
909
|
+
@DBOS.workflow()
|
|
910
|
+
def test_step() -> None:
|
|
911
|
+
start_event.set()
|
|
912
|
+
nonlocal counter
|
|
913
|
+
counter += 1
|
|
914
|
+
|
|
915
|
+
queue = Queue("test_queue")
|
|
916
|
+
handle = queue.enqueue(test_step)
|
|
917
|
+
start_event.wait()
|
|
918
|
+
start_event.clear()
|
|
919
|
+
time.sleep(_buffer_flush_interval_secs)
|
|
920
|
+
assert handle.get_status().status == WorkflowStatusString.PENDING.value # Not flushed
|
|
921
|
+
assert counter == 1 # But, really, it's completed
|
|
922
|
+
dbos._sys_db._workflow_status_buffer = {} # Clear buffer (simulates a process restart)
|
|
923
|
+
|
|
924
|
+
# Recovery picks up on the workflow and recovers it
|
|
925
|
+
recovered_ids = DBOS.recover_pending_workflows()
|
|
926
|
+
assert len(recovered_ids) == 1
|
|
927
|
+
assert recovered_ids[0].get_workflow_id() == handle.get_workflow_id()
|
|
928
|
+
start_event.wait()
|
|
929
|
+
assert counter == 2 # The workflow ran again
|
|
930
|
+
time.sleep(_buffer_flush_interval_secs) # This is actually to wait that _get_wf_invoke_func buffers the status
|
|
931
|
+
dbos._sys_db._flush_workflow_status_buffer() # Manually flush
|
|
932
|
+
assert handle.get_status().status == WorkflowStatusString.SUCCESS.value # Is recovered
|
|
933
|
+
assert handle.get_status().executor_id == "local"
|
|
934
|
+
assert handle.get_status().recovery_attempts == 2
|
|
935
|
+
|
|
936
|
+
|
|
903
937
|
def test_dlq_enqueued_workflows(dbos: DBOS) -> None:
|
|
904
938
|
start_event = threading.Event()
|
|
905
939
|
blocking_event = threading.Event()
|
|
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
|
{dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py
RENAMED
|
File without changes
|
|
File without changes
|
{dbos-0.23.0a9 → dbos-0.23.0a10}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py
RENAMED
|
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
|
|
File without changes
|