prefect 3.6.6__py3-none-any.whl → 3.6.7.dev3__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.
- prefect/_build_info.py +3 -3
- prefect/_experimental/plugins/spec.py +20 -1
- prefect/server/api/background_workers.py +4 -0
- prefect/server/api/server.py +1 -0
- prefect/server/database/configurations.py +34 -0
- prefect/server/services/base.py +0 -8
- prefect/server/services/foreman.py +175 -201
- prefect/server/services/late_runs.py +56 -113
- prefect/server/services/repossessor.py +66 -49
- prefect/server/services/telemetry.py +86 -115
- prefect/settings/models/_defaults.py +0 -1
- prefect/variables.py +59 -6
- {prefect-3.6.6.dist-info → prefect-3.6.7.dev3.dist-info}/METADATA +1 -1
- {prefect-3.6.6.dist-info → prefect-3.6.7.dev3.dist-info}/RECORD +17 -17
- {prefect-3.6.6.dist-info → prefect-3.6.7.dev3.dist-info}/WHEEL +0 -0
- {prefect-3.6.6.dist-info → prefect-3.6.7.dev3.dist-info}/entry_points.txt +0 -0
- {prefect-3.6.6.dist-info → prefect-3.6.7.dev3.dist-info}/licenses/LICENSE +0 -0
prefect/_build_info.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Generated by versioningit
|
|
2
|
-
__version__ = "3.6.
|
|
3
|
-
__build_date__ = "2025-12-
|
|
4
|
-
__git_commit__ = "
|
|
2
|
+
__version__ = "3.6.7.dev3"
|
|
3
|
+
__build_date__ = "2025-12-14 08:09:01.617063+00:00"
|
|
4
|
+
__git_commit__ = "96ab33fd33df46b6a16e3015b5bd1131e61f3ee1"
|
|
5
5
|
__dirty__ = False
|
|
@@ -9,7 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
import logging
|
|
11
11
|
from dataclasses import dataclass
|
|
12
|
-
from typing import Callable, Mapping, Optional
|
|
12
|
+
from typing import Any, Callable, Mapping, Optional
|
|
13
13
|
|
|
14
14
|
import pluggy
|
|
15
15
|
|
|
@@ -83,3 +83,22 @@ class HookSpec:
|
|
|
83
83
|
- May be async or sync
|
|
84
84
|
- Exceptions are caught and logged unless required=True in strict mode
|
|
85
85
|
"""
|
|
86
|
+
|
|
87
|
+
@hookspec
|
|
88
|
+
def set_database_connection_params(
|
|
89
|
+
self, connection_url: str, settings: Any
|
|
90
|
+
) -> Mapping[str, Any]:
|
|
91
|
+
"""
|
|
92
|
+
Set additional database connection parameters.
|
|
93
|
+
|
|
94
|
+
This hook is called when creating a database engine. It allows plugins
|
|
95
|
+
to provide additional connection parameters, such as authentication
|
|
96
|
+
tokens or SSL configuration.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
connection_url: The database connection URL
|
|
100
|
+
settings: The current Prefect settings
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Dictionary of connection parameters to merge into connect_args
|
|
104
|
+
"""
|
|
@@ -14,10 +14,12 @@ from prefect.server.services.cancellation_cleanup import (
|
|
|
14
14
|
cancel_child_task_runs,
|
|
15
15
|
cancel_subflow_run,
|
|
16
16
|
)
|
|
17
|
+
from prefect.server.services.late_runs import mark_flow_run_late
|
|
17
18
|
from prefect.server.services.pause_expirations import fail_expired_pause
|
|
18
19
|
from prefect.server.services.perpetual_services import (
|
|
19
20
|
register_and_schedule_perpetual_services,
|
|
20
21
|
)
|
|
22
|
+
from prefect.server.services.repossessor import revoke_expired_lease
|
|
21
23
|
|
|
22
24
|
# Task functions to register with docket for background processing
|
|
23
25
|
task_functions: list[Callable[..., Any]] = [
|
|
@@ -30,6 +32,8 @@ task_functions: list[Callable[..., Any]] = [
|
|
|
30
32
|
cancel_child_task_runs,
|
|
31
33
|
cancel_subflow_run,
|
|
32
34
|
fail_expired_pause,
|
|
35
|
+
mark_flow_run_late,
|
|
36
|
+
revoke_expired_lease,
|
|
33
37
|
]
|
|
34
38
|
|
|
35
39
|
|
prefect/server/api/server.py
CHANGED
|
@@ -156,6 +156,7 @@ def _install_sqlite_locked_log_filter() -> None:
|
|
|
156
156
|
|
|
157
157
|
filter_ = _SQLiteLockedOperationalErrorFilter()
|
|
158
158
|
logging.getLogger("uvicorn.error").addFilter(filter_)
|
|
159
|
+
logging.getLogger("docket.worker").addFilter(filter_)
|
|
159
160
|
_SQLITE_LOCKED_LOG_FILTER = filter_
|
|
160
161
|
|
|
161
162
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import logging
|
|
3
4
|
import sqlite3
|
|
4
5
|
import ssl
|
|
5
6
|
import traceback
|
|
@@ -25,6 +26,12 @@ from sqlalchemy.ext.asyncio import (
|
|
|
25
26
|
from sqlalchemy.pool import ConnectionPoolEntry
|
|
26
27
|
from typing_extensions import TypeAlias
|
|
27
28
|
|
|
29
|
+
from prefect._experimental.plugins import (
|
|
30
|
+
HookSpec,
|
|
31
|
+
build_manager,
|
|
32
|
+
call_async_hook,
|
|
33
|
+
load_entry_point_plugins,
|
|
34
|
+
)
|
|
28
35
|
from prefect._internal.observability import configure_logfire
|
|
29
36
|
from prefect.settings import (
|
|
30
37
|
PREFECT_API_DATABASE_CONNECTION_TIMEOUT,
|
|
@@ -279,6 +286,33 @@ class AsyncPostgresConfiguration(BaseDatabaseConfiguration):
|
|
|
279
286
|
pg_ctx.verify_mode = ssl.CERT_REQUIRED
|
|
280
287
|
connect_args["ssl"] = pg_ctx
|
|
281
288
|
|
|
289
|
+
# Initialize plugin manager
|
|
290
|
+
if get_current_settings().experiments.plugins.enabled:
|
|
291
|
+
pm = build_manager(HookSpec)
|
|
292
|
+
load_entry_point_plugins(
|
|
293
|
+
pm,
|
|
294
|
+
allow=get_current_settings().experiments.plugins.allow,
|
|
295
|
+
deny=get_current_settings().experiments.plugins.deny,
|
|
296
|
+
logger=logging.getLogger("prefect.plugins"),
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
# Call set_database_connection_params hook
|
|
300
|
+
results = await call_async_hook(
|
|
301
|
+
pm,
|
|
302
|
+
"set_database_connection_params",
|
|
303
|
+
connection_url=self.connection_url,
|
|
304
|
+
settings=get_current_settings(),
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
for _, params, error in results:
|
|
308
|
+
if error:
|
|
309
|
+
# Log error but don't fail, other plugins might succeed
|
|
310
|
+
logging.getLogger("prefect.server.database").warning(
|
|
311
|
+
"Plugin failed to set database connection params: %s", error
|
|
312
|
+
)
|
|
313
|
+
elif params:
|
|
314
|
+
connect_args.update(params)
|
|
315
|
+
|
|
282
316
|
if connect_args:
|
|
283
317
|
kwargs["connect_args"] = connect_args
|
|
284
318
|
|
prefect/server/services/base.py
CHANGED
|
@@ -38,22 +38,14 @@ def _known_service_modules() -> list[ModuleType]:
|
|
|
38
38
|
)
|
|
39
39
|
from prefect.server.logs import stream as logs_stream
|
|
40
40
|
from prefect.server.services import (
|
|
41
|
-
foreman,
|
|
42
|
-
late_runs,
|
|
43
|
-
repossessor,
|
|
44
41
|
scheduler,
|
|
45
42
|
task_run_recorder,
|
|
46
|
-
telemetry,
|
|
47
43
|
)
|
|
48
44
|
|
|
49
45
|
return [
|
|
50
46
|
# Orchestration services
|
|
51
|
-
foreman,
|
|
52
|
-
late_runs,
|
|
53
|
-
repossessor,
|
|
54
47
|
scheduler,
|
|
55
48
|
task_run_recorder,
|
|
56
|
-
telemetry,
|
|
57
49
|
# Events services
|
|
58
50
|
event_logger,
|
|
59
51
|
event_persister,
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Foreman
|
|
2
|
+
The Foreman service. Monitors workers and marks stale resources as offline/not ready.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import logging
|
|
5
8
|
from datetime import timedelta
|
|
6
|
-
from typing import Any, Optional
|
|
7
9
|
|
|
8
10
|
import sqlalchemy as sa
|
|
11
|
+
from docket import Perpetual
|
|
9
12
|
|
|
13
|
+
from prefect.logging import get_logger
|
|
10
14
|
from prefect.server import models
|
|
11
|
-
from prefect.server.database import PrefectDBInterface,
|
|
15
|
+
from prefect.server.database import PrefectDBInterface, provide_database_interface
|
|
12
16
|
from prefect.server.models.deployments import mark_deployments_not_ready
|
|
13
17
|
from prefect.server.models.work_queues import mark_work_queues_not_ready
|
|
14
18
|
from prefect.server.models.workers import emit_work_pool_status_event
|
|
@@ -18,224 +22,194 @@ from prefect.server.schemas.statuses import (
|
|
|
18
22
|
WorkerStatus,
|
|
19
23
|
WorkPoolStatus,
|
|
20
24
|
)
|
|
21
|
-
from prefect.server.services.
|
|
22
|
-
from prefect.settings import
|
|
23
|
-
PREFECT_API_SERVICES_FOREMAN_DEPLOYMENT_LAST_POLLED_TIMEOUT_SECONDS,
|
|
24
|
-
PREFECT_API_SERVICES_FOREMAN_FALLBACK_HEARTBEAT_INTERVAL_SECONDS,
|
|
25
|
-
PREFECT_API_SERVICES_FOREMAN_INACTIVITY_HEARTBEAT_MULTIPLE,
|
|
26
|
-
PREFECT_API_SERVICES_FOREMAN_LOOP_SECONDS,
|
|
27
|
-
PREFECT_API_SERVICES_FOREMAN_WORK_QUEUE_LAST_POLLED_TIMEOUT_SECONDS,
|
|
28
|
-
get_current_settings,
|
|
29
|
-
)
|
|
30
|
-
from prefect.settings.models.server.services import ServicesBaseSetting
|
|
25
|
+
from prefect.server.services.perpetual_services import perpetual_service
|
|
26
|
+
from prefect.settings.context import get_current_settings
|
|
31
27
|
from prefect.types._datetime import now
|
|
32
28
|
|
|
29
|
+
logger: logging.Logger = get_logger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@perpetual_service(
|
|
33
|
+
enabled_getter=lambda: get_current_settings().server.services.foreman.enabled,
|
|
34
|
+
)
|
|
35
|
+
async def monitor_worker_health(
|
|
36
|
+
perpetual: Perpetual = Perpetual(
|
|
37
|
+
automatic=False,
|
|
38
|
+
every=timedelta(
|
|
39
|
+
seconds=get_current_settings().server.services.foreman.loop_seconds
|
|
40
|
+
),
|
|
41
|
+
),
|
|
42
|
+
) -> None:
|
|
43
|
+
"""
|
|
44
|
+
Monitor workers and mark stale resources as offline/not ready.
|
|
33
45
|
|
|
34
|
-
|
|
46
|
+
Iterates over workers currently marked as online. Marks workers as offline
|
|
47
|
+
if they have an old last_heartbeat_time. Marks work pools as not ready
|
|
48
|
+
if they do not have any online workers and are currently marked as ready.
|
|
49
|
+
Marks deployments as not ready if they have a last_polled time that is
|
|
50
|
+
older than the configured deployment last polled timeout.
|
|
35
51
|
"""
|
|
36
|
-
|
|
52
|
+
settings = get_current_settings().server.services.foreman
|
|
53
|
+
db = provide_database_interface()
|
|
54
|
+
|
|
55
|
+
await _mark_online_workers_without_recent_heartbeat_as_offline(
|
|
56
|
+
db=db,
|
|
57
|
+
inactivity_heartbeat_multiple=settings.inactivity_heartbeat_multiple,
|
|
58
|
+
fallback_heartbeat_interval_seconds=settings.fallback_heartbeat_interval_seconds,
|
|
59
|
+
)
|
|
60
|
+
await _mark_work_pools_as_not_ready(db=db)
|
|
61
|
+
await _mark_deployments_as_not_ready(
|
|
62
|
+
db=db,
|
|
63
|
+
deployment_last_polled_timeout_seconds=settings.deployment_last_polled_timeout_seconds,
|
|
64
|
+
)
|
|
65
|
+
await _mark_work_queues_as_not_ready(
|
|
66
|
+
db=db,
|
|
67
|
+
work_queue_last_polled_timeout_seconds=settings.work_queue_last_polled_timeout_seconds,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
async def _mark_online_workers_without_recent_heartbeat_as_offline(
|
|
72
|
+
db: PrefectDBInterface,
|
|
73
|
+
inactivity_heartbeat_multiple: int,
|
|
74
|
+
fallback_heartbeat_interval_seconds: int,
|
|
75
|
+
) -> None:
|
|
37
76
|
"""
|
|
77
|
+
Updates the status of workers that have an old last heartbeat time to OFFLINE.
|
|
38
78
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
self._inactivity_heartbeat_multiple = (
|
|
58
|
-
PREFECT_API_SERVICES_FOREMAN_INACTIVITY_HEARTBEAT_MULTIPLE.value()
|
|
59
|
-
if inactivity_heartbeat_multiple is None
|
|
60
|
-
else inactivity_heartbeat_multiple
|
|
61
|
-
)
|
|
62
|
-
self._fallback_heartbeat_interval_seconds = (
|
|
63
|
-
PREFECT_API_SERVICES_FOREMAN_FALLBACK_HEARTBEAT_INTERVAL_SECONDS.value()
|
|
64
|
-
if fallback_heartbeat_interval_seconds is None
|
|
65
|
-
else fallback_heartbeat_interval_seconds
|
|
66
|
-
)
|
|
67
|
-
self._deployment_last_polled_timeout_seconds = (
|
|
68
|
-
PREFECT_API_SERVICES_FOREMAN_DEPLOYMENT_LAST_POLLED_TIMEOUT_SECONDS.value()
|
|
69
|
-
if deployment_last_polled_timeout_seconds is None
|
|
70
|
-
else deployment_last_polled_timeout_seconds
|
|
79
|
+
An old heartbeat is one that is more than the worker's heartbeat interval
|
|
80
|
+
multiplied by the inactivity_heartbeat_multiple seconds ago.
|
|
81
|
+
"""
|
|
82
|
+
async with db.session_context(begin_transaction=True) as session:
|
|
83
|
+
worker_update_stmt = (
|
|
84
|
+
sa.update(db.Worker)
|
|
85
|
+
.values(status=WorkerStatus.OFFLINE)
|
|
86
|
+
.where(
|
|
87
|
+
sa.func.date_diff_seconds(db.Worker.last_heartbeat_time)
|
|
88
|
+
> (
|
|
89
|
+
sa.func.coalesce(
|
|
90
|
+
db.Worker.heartbeat_interval_seconds,
|
|
91
|
+
sa.bindparam("default_interval", sa.Integer),
|
|
92
|
+
)
|
|
93
|
+
* sa.bindparam("multiplier", sa.Integer)
|
|
94
|
+
),
|
|
95
|
+
db.Worker.status == WorkerStatus.ONLINE,
|
|
96
|
+
)
|
|
71
97
|
)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
98
|
+
|
|
99
|
+
result = await session.execute(
|
|
100
|
+
worker_update_stmt,
|
|
101
|
+
{
|
|
102
|
+
"multiplier": inactivity_heartbeat_multiple,
|
|
103
|
+
"default_interval": fallback_heartbeat_interval_seconds,
|
|
104
|
+
},
|
|
76
105
|
)
|
|
77
106
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
Iterate over workers current marked as online. Mark workers as offline
|
|
82
|
-
if they have an old last_heartbeat_time. Marks work pools as not ready
|
|
83
|
-
if they do not have any online workers and are currently marked as ready.
|
|
84
|
-
Mark deployments as not ready if they have a last_polled time that is
|
|
85
|
-
older than the configured deployment last polled timeout.
|
|
86
|
-
"""
|
|
87
|
-
await self._mark_online_workers_without_a_recent_heartbeat_as_offline()
|
|
88
|
-
await self._mark_work_pools_as_not_ready()
|
|
89
|
-
await self._mark_deployments_as_not_ready()
|
|
90
|
-
await self._mark_work_queues_as_not_ready()
|
|
91
|
-
|
|
92
|
-
@db_injector
|
|
93
|
-
async def _mark_online_workers_without_a_recent_heartbeat_as_offline(
|
|
94
|
-
self, db: PrefectDBInterface
|
|
95
|
-
) -> None:
|
|
96
|
-
"""
|
|
97
|
-
Updates the status of workers that have an old last heartbeat time
|
|
98
|
-
to OFFLINE.
|
|
99
|
-
|
|
100
|
-
An old heartbeat last heartbeat that is one more than
|
|
101
|
-
their heartbeat interval multiplied by the
|
|
102
|
-
INACTIVITY_HEARTBEAT_MULTIPLE seconds ago.
|
|
103
|
-
|
|
104
|
-
Args:
|
|
105
|
-
session (AsyncSession): The session to use for the database operation.
|
|
106
|
-
"""
|
|
107
|
-
async with db.session_context(begin_transaction=True) as session:
|
|
108
|
-
worker_update_stmt = (
|
|
109
|
-
sa.update(db.Worker)
|
|
110
|
-
.values(status=WorkerStatus.OFFLINE)
|
|
111
|
-
.where(
|
|
112
|
-
sa.func.date_diff_seconds(db.Worker.last_heartbeat_time)
|
|
113
|
-
> (
|
|
114
|
-
sa.func.coalesce(
|
|
115
|
-
db.Worker.heartbeat_interval_seconds,
|
|
116
|
-
sa.bindparam("default_interval", sa.Integer),
|
|
117
|
-
)
|
|
118
|
-
* sa.bindparam("multiplier", sa.Integer)
|
|
119
|
-
),
|
|
120
|
-
db.Worker.status == WorkerStatus.ONLINE,
|
|
121
|
-
)
|
|
122
|
-
)
|
|
107
|
+
if result.rowcount:
|
|
108
|
+
logger.info(f"Marked {result.rowcount} workers as offline.")
|
|
109
|
+
|
|
123
110
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
111
|
+
async def _mark_work_pools_as_not_ready(db: PrefectDBInterface) -> None:
|
|
112
|
+
"""
|
|
113
|
+
Marks work pools as not ready if they have no online workers.
|
|
114
|
+
|
|
115
|
+
Emits an event and updates any bookkeeping fields on the work pool.
|
|
116
|
+
"""
|
|
117
|
+
async with db.session_context(begin_transaction=True) as session:
|
|
118
|
+
work_pools_select_stmt = (
|
|
119
|
+
sa.select(db.WorkPool)
|
|
120
|
+
.filter(db.WorkPool.status == "READY")
|
|
121
|
+
.outerjoin(
|
|
122
|
+
db.Worker,
|
|
123
|
+
sa.and_(
|
|
124
|
+
db.Worker.work_pool_id == db.WorkPool.id,
|
|
125
|
+
db.Worker.status == "ONLINE",
|
|
126
|
+
),
|
|
130
127
|
)
|
|
128
|
+
.group_by(db.WorkPool.id)
|
|
129
|
+
.having(sa.func.count(db.Worker.id) == 0)
|
|
130
|
+
)
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
Args:
|
|
143
|
-
work_pool (db.WorkPool): The work pool to mark as not ready.
|
|
144
|
-
"""
|
|
145
|
-
async with db.session_context(begin_transaction=True) as session:
|
|
146
|
-
work_pools_select_stmt = (
|
|
147
|
-
sa.select(db.WorkPool)
|
|
148
|
-
.filter(db.WorkPool.status == "READY")
|
|
149
|
-
.outerjoin(
|
|
150
|
-
db.Worker,
|
|
151
|
-
sa.and_(
|
|
152
|
-
db.Worker.work_pool_id == db.WorkPool.id,
|
|
153
|
-
db.Worker.status == "ONLINE",
|
|
154
|
-
),
|
|
155
|
-
)
|
|
156
|
-
.group_by(db.WorkPool.id)
|
|
157
|
-
.having(sa.func.count(db.Worker.id) == 0)
|
|
132
|
+
result = await session.execute(work_pools_select_stmt)
|
|
133
|
+
work_pools = result.scalars().all()
|
|
134
|
+
|
|
135
|
+
for work_pool in work_pools:
|
|
136
|
+
await models.workers.update_work_pool(
|
|
137
|
+
session=session,
|
|
138
|
+
work_pool_id=work_pool.id,
|
|
139
|
+
work_pool=InternalWorkPoolUpdate(status=WorkPoolStatus.NOT_READY),
|
|
140
|
+
emit_status_change=emit_work_pool_status_event,
|
|
158
141
|
)
|
|
159
142
|
|
|
160
|
-
|
|
161
|
-
work_pools = result.scalars().all()
|
|
143
|
+
logger.info(f"Marked work pool {work_pool.id} as NOT_READY.")
|
|
162
144
|
|
|
163
|
-
for work_pool in work_pools:
|
|
164
|
-
await models.workers.update_work_pool(
|
|
165
|
-
session=session,
|
|
166
|
-
work_pool_id=work_pool.id,
|
|
167
|
-
work_pool=InternalWorkPoolUpdate(status=WorkPoolStatus.NOT_READY),
|
|
168
|
-
emit_status_change=emit_work_pool_status_event,
|
|
169
|
-
)
|
|
170
145
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
),
|
|
205
|
-
)
|
|
146
|
+
async def _mark_deployments_as_not_ready(
|
|
147
|
+
db: PrefectDBInterface,
|
|
148
|
+
deployment_last_polled_timeout_seconds: int,
|
|
149
|
+
) -> None:
|
|
150
|
+
"""
|
|
151
|
+
Marks deployments as NOT_READY based on their last_polled field.
|
|
152
|
+
|
|
153
|
+
Emits an event and updates any bookkeeping fields on the deployment.
|
|
154
|
+
"""
|
|
155
|
+
async with db.session_context(begin_transaction=True) as session:
|
|
156
|
+
status_timeout_threshold = now("UTC") - timedelta(
|
|
157
|
+
seconds=deployment_last_polled_timeout_seconds
|
|
158
|
+
)
|
|
159
|
+
deployment_id_select_stmt = (
|
|
160
|
+
sa.select(db.Deployment.id)
|
|
161
|
+
.outerjoin(db.WorkQueue, db.WorkQueue.id == db.Deployment.work_queue_id)
|
|
162
|
+
.filter(db.Deployment.status == DeploymentStatus.READY)
|
|
163
|
+
.filter(db.Deployment.last_polled.isnot(None))
|
|
164
|
+
.filter(
|
|
165
|
+
sa.or_(
|
|
166
|
+
# if work_queue.last_polled doesn't exist, use only deployment's
|
|
167
|
+
# last_polled
|
|
168
|
+
sa.and_(
|
|
169
|
+
db.WorkQueue.last_polled.is_(None),
|
|
170
|
+
db.Deployment.last_polled < status_timeout_threshold,
|
|
171
|
+
),
|
|
172
|
+
# if work_queue.last_polled exists, both times should be less than
|
|
173
|
+
# the threshold
|
|
174
|
+
sa.and_(
|
|
175
|
+
db.WorkQueue.last_polled.isnot(None),
|
|
176
|
+
db.Deployment.last_polled < status_timeout_threshold,
|
|
177
|
+
db.WorkQueue.last_polled < status_timeout_threshold,
|
|
178
|
+
),
|
|
206
179
|
)
|
|
207
180
|
)
|
|
208
|
-
|
|
181
|
+
)
|
|
182
|
+
result = await session.execute(deployment_id_select_stmt)
|
|
209
183
|
|
|
210
|
-
|
|
184
|
+
deployment_ids_to_mark_unready = result.scalars().all()
|
|
211
185
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
186
|
+
await mark_deployments_not_ready(
|
|
187
|
+
deployment_ids=deployment_ids_to_mark_unready,
|
|
188
|
+
)
|
|
215
189
|
|
|
216
|
-
@db_injector
|
|
217
|
-
async def _mark_work_queues_as_not_ready(self, db: PrefectDBInterface):
|
|
218
|
-
"""
|
|
219
|
-
Marks work queues as NOT_READY based on their last_polled field.
|
|
220
|
-
|
|
221
|
-
Args:
|
|
222
|
-
session (AsyncSession): The session to use for the database operation.
|
|
223
|
-
"""
|
|
224
|
-
async with db.session_context(begin_transaction=True) as session:
|
|
225
|
-
status_timeout_threshold = now("UTC") - timedelta(
|
|
226
|
-
seconds=self._work_queue_last_polled_timeout_seconds
|
|
227
|
-
)
|
|
228
|
-
id_select_stmt = (
|
|
229
|
-
sa.select(db.WorkQueue.id)
|
|
230
|
-
.outerjoin(db.WorkPool, db.WorkPool.id == db.WorkQueue.work_pool_id)
|
|
231
|
-
.filter(db.WorkQueue.status == "READY")
|
|
232
|
-
.filter(db.WorkQueue.last_polled.isnot(None))
|
|
233
|
-
.filter(db.WorkQueue.last_polled < status_timeout_threshold)
|
|
234
|
-
.order_by(db.WorkQueue.last_polled.asc())
|
|
235
|
-
)
|
|
236
|
-
result = await session.execute(id_select_stmt)
|
|
237
|
-
unready_work_queue_ids = result.scalars().all()
|
|
238
190
|
|
|
239
|
-
|
|
240
|
-
|
|
191
|
+
async def _mark_work_queues_as_not_ready(
|
|
192
|
+
db: PrefectDBInterface,
|
|
193
|
+
work_queue_last_polled_timeout_seconds: int,
|
|
194
|
+
) -> None:
|
|
195
|
+
"""
|
|
196
|
+
Marks work queues as NOT_READY based on their last_polled field.
|
|
197
|
+
"""
|
|
198
|
+
async with db.session_context(begin_transaction=True) as session:
|
|
199
|
+
status_timeout_threshold = now("UTC") - timedelta(
|
|
200
|
+
seconds=work_queue_last_polled_timeout_seconds
|
|
241
201
|
)
|
|
202
|
+
id_select_stmt = (
|
|
203
|
+
sa.select(db.WorkQueue.id)
|
|
204
|
+
.outerjoin(db.WorkPool, db.WorkPool.id == db.WorkQueue.work_pool_id)
|
|
205
|
+
.filter(db.WorkQueue.status == "READY")
|
|
206
|
+
.filter(db.WorkQueue.last_polled.isnot(None))
|
|
207
|
+
.filter(db.WorkQueue.last_polled < status_timeout_threshold)
|
|
208
|
+
.order_by(db.WorkQueue.last_polled.asc())
|
|
209
|
+
)
|
|
210
|
+
result = await session.execute(id_select_stmt)
|
|
211
|
+
unready_work_queue_ids = result.scalars().all()
|
|
212
|
+
|
|
213
|
+
await mark_work_queues_not_ready(
|
|
214
|
+
work_queue_ids=unready_work_queue_ids,
|
|
215
|
+
)
|