dbos 2.1.0a2__tar.gz → 2.1.0a3__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.
- {dbos-2.1.0a2 → dbos-2.1.0a3}/PKG-INFO +1 -1
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_scheduler.py +24 -14
- {dbos-2.1.0a2 → dbos-2.1.0a3}/pyproject.toml +1 -1
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_scheduler.py +7 -7
- {dbos-2.1.0a2 → dbos-2.1.0a3}/LICENSE +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/README.md +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/__init__.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/__main__.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_admin_server.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_app_db.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_classproperty.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_client.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_conductor/conductor.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_conductor/protocol.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_context.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_core.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_croniter.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_dbos.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_dbos_config.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_debouncer.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_debug.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_docker_pg_helper.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_error.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_event_loop.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_fastapi.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_flask.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_kafka.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_kafka_message.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_logger.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_migration.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_outcome.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_queue.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_recovery.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_registrations.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_roles.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_schemas/__init__.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_schemas/application_database.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_schemas/system_database.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_serialization.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_sys_db.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_sys_db_postgres.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_sys_db_sqlite.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_templates/dbos-db-starter/README.md +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_templates/dbos-db-starter/__package/main.py.dbos +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_templates/dbos-db-starter/migrations/create_table.py.dbos +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_tracer.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_utils.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_workflow_commands.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/cli/_github_init.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/cli/_template_init.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/cli/cli.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/cli/migration.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/dbos-config.schema.json +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/py.typed +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/__init__.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/atexit_no_ctor.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/atexit_no_launch.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/classdefs.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/client_collateral.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/client_worker.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/conftest.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/dupname_classdefs1.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/dupname_classdefsa.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/more_classdefs.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/queuedworkflow.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/script_without_fastapi.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_admin_server.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_async.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_async_workflow_management.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_classdecorators.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_cli.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_client.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_concurrency.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_config.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_croniter.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_dbos.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_debouncer.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_debug.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_docker_secrets.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_failures.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_fastapi.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_fastapi_roles.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_flask.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_kafka.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_outcome.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_package.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_queue.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_schema_migration.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_singleton.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_spans.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_sqlalchemy.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_streaming.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_workflow_introspection.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/tests/test_workflow_management.py +0 -0
- {dbos-2.1.0a2 → dbos-2.1.0a3}/version/__init__.py +0 -0
@@ -1,3 +1,4 @@
|
|
1
|
+
import random
|
1
2
|
import threading
|
2
3
|
import traceback
|
3
4
|
from datetime import datetime, timezone
|
@@ -15,28 +16,40 @@ from ._registrations import get_dbos_func_name
|
|
15
16
|
|
16
17
|
ScheduledWorkflow = Callable[[datetime, datetime], None]
|
17
18
|
|
18
|
-
scheduler_queue: Queue
|
19
|
-
|
20
19
|
|
21
20
|
def scheduler_loop(
|
22
21
|
func: ScheduledWorkflow, cron: str, stop_event: threading.Event
|
23
22
|
) -> None:
|
23
|
+
from dbos._dbos import _get_dbos_instance
|
24
|
+
|
25
|
+
dbos = _get_dbos_instance()
|
26
|
+
scheduler_queue = dbos._registry.get_internal_queue()
|
24
27
|
try:
|
25
28
|
iter = croniter(cron, datetime.now(timezone.utc), second_at_beginning=True)
|
26
|
-
except Exception
|
29
|
+
except Exception:
|
27
30
|
dbos_logger.error(
|
28
31
|
f'Cannot run scheduled function {get_dbos_func_name(func)}. Invalid crontab "{cron}"'
|
29
32
|
)
|
33
|
+
raise
|
30
34
|
while not stop_event.is_set():
|
31
|
-
|
32
|
-
|
33
|
-
|
35
|
+
next_exec_time = iter.get_next(datetime)
|
36
|
+
sleep_time = (next_exec_time - datetime.now(timezone.utc)).total_seconds()
|
37
|
+
sleep_time = max(0, sleep_time)
|
38
|
+
# To prevent a "thundering herd" problem in a distributed setting,
|
39
|
+
# apply jitter of up to 10% the sleep time, capped at 10 seconds
|
40
|
+
max_jitter = min(sleep_time / 10, 10)
|
41
|
+
jitter = random.uniform(0, max_jitter)
|
42
|
+
if stop_event.wait(timeout=sleep_time + jitter):
|
34
43
|
return
|
35
44
|
try:
|
36
|
-
|
37
|
-
f"sched-{get_dbos_func_name(func)}-{
|
38
|
-
)
|
39
|
-
|
45
|
+
workflowID = (
|
46
|
+
f"sched-{get_dbos_func_name(func)}-{next_exec_time.isoformat()}"
|
47
|
+
)
|
48
|
+
if not dbos._sys_db.get_workflow_status(workflowID):
|
49
|
+
with SetWorkflowID(workflowID):
|
50
|
+
scheduler_queue.enqueue(
|
51
|
+
func, next_exec_time, datetime.now(timezone.utc)
|
52
|
+
)
|
40
53
|
except Exception:
|
41
54
|
dbos_logger.warning(
|
42
55
|
f"Exception encountered in scheduler thread: {traceback.format_exc()})"
|
@@ -49,13 +62,10 @@ def scheduled(
|
|
49
62
|
def decorator(func: ScheduledWorkflow) -> ScheduledWorkflow:
|
50
63
|
try:
|
51
64
|
croniter(cron, datetime.now(timezone.utc), second_at_beginning=True)
|
52
|
-
except Exception
|
65
|
+
except Exception:
|
53
66
|
raise ValueError(
|
54
67
|
f'Invalid crontab "{cron}" for scheduled function function {get_dbos_func_name(func)}.'
|
55
68
|
)
|
56
|
-
|
57
|
-
global scheduler_queue
|
58
|
-
scheduler_queue = dbosreg.get_internal_queue()
|
59
69
|
stop_event = threading.Event()
|
60
70
|
dbosreg.register_poller(stop_event, scheduler_loop, func, cron, stop_event)
|
61
71
|
return func
|
@@ -102,7 +102,7 @@ def test_scheduled_workflow(dbos: DBOS) -> None:
|
|
102
102
|
wf_counter += 1
|
103
103
|
|
104
104
|
time.sleep(5)
|
105
|
-
assert wf_counter >
|
105
|
+
assert wf_counter > 1 and wf_counter <= 5
|
106
106
|
|
107
107
|
|
108
108
|
def test_appdb_downtime(dbos: DBOS, skip_with_sqlite: None) -> None:
|
@@ -123,7 +123,7 @@ def test_appdb_downtime(dbos: DBOS, skip_with_sqlite: None) -> None:
|
|
123
123
|
time.sleep(2)
|
124
124
|
assert dbos._app_db
|
125
125
|
simulate_db_restart(dbos._app_db.engine, 2)
|
126
|
-
time.sleep(
|
126
|
+
time.sleep(3)
|
127
127
|
assert wf_counter > 2
|
128
128
|
|
129
129
|
|
@@ -138,7 +138,7 @@ def test_sysdb_downtime(dbos: DBOS, skip_with_sqlite: None) -> None:
|
|
138
138
|
|
139
139
|
time.sleep(2)
|
140
140
|
simulate_db_restart(dbos._sys_db.engine, 2)
|
141
|
-
time.sleep(
|
141
|
+
time.sleep(3)
|
142
142
|
# We know there should be at least 2 occurrences from the 4 seconds when the DB was up.
|
143
143
|
# There could be more than 4, depending on the pace the machine...
|
144
144
|
assert wf_counter >= 2
|
@@ -154,7 +154,7 @@ def test_scheduled_transaction(dbos: DBOS) -> None:
|
|
154
154
|
txn_counter += 1
|
155
155
|
|
156
156
|
time.sleep(5)
|
157
|
-
assert txn_counter >
|
157
|
+
assert txn_counter > 1 and txn_counter <= 5
|
158
158
|
|
159
159
|
|
160
160
|
def test_scheduled_step(dbos: DBOS) -> None:
|
@@ -205,8 +205,8 @@ def test_scheduler_oaoo(dbos: DBOS) -> None:
|
|
205
205
|
nonlocal txn_counter
|
206
206
|
txn_counter += 1
|
207
207
|
|
208
|
-
time.sleep(
|
209
|
-
assert wf_counter >= 1 and wf_counter <=
|
208
|
+
time.sleep(4)
|
209
|
+
assert wf_counter >= 1 and wf_counter <= 4
|
210
210
|
max_tries = 10
|
211
211
|
for i in range(max_tries):
|
212
212
|
try:
|
@@ -223,7 +223,7 @@ def test_scheduler_oaoo(dbos: DBOS) -> None:
|
|
223
223
|
evt.set()
|
224
224
|
|
225
225
|
# Wait for workflows to finish
|
226
|
-
time.sleep(
|
226
|
+
time.sleep(3)
|
227
227
|
|
228
228
|
dbos._sys_db.update_workflow_outcome(workflow_id, "PENDING")
|
229
229
|
|
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
|
{dbos-2.1.0a2 → dbos-2.1.0a3}/dbos/_templates/dbos-db-starter/migrations/create_table.py.dbos
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
|