dagster-cloud 1.8.2__py3-none-any.whl → 1.12.6__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.
- dagster_cloud/__init__.py +3 -3
- dagster_cloud/agent/__init__.py +4 -4
- dagster_cloud/agent/cli/__init__.py +56 -17
- dagster_cloud/agent/dagster_cloud_agent.py +360 -172
- dagster_cloud/agent/instrumentation/__init__.py +0 -0
- dagster_cloud/agent/instrumentation/constants.py +2 -0
- dagster_cloud/agent/instrumentation/run_launch.py +23 -0
- dagster_cloud/agent/instrumentation/schedule.py +34 -0
- dagster_cloud/agent/instrumentation/sensor.py +34 -0
- dagster_cloud/anomaly_detection/__init__.py +2 -2
- dagster_cloud/anomaly_detection/defs.py +17 -12
- dagster_cloud/anomaly_detection/types.py +3 -3
- dagster_cloud/api/dagster_cloud_api.py +209 -293
- dagster_cloud/auth/constants.py +21 -5
- dagster_cloud/batching/__init__.py +1 -0
- dagster_cloud/batching/batcher.py +210 -0
- dagster_cloud/dagster_insights/__init__.py +12 -6
- dagster_cloud/dagster_insights/bigquery/bigquery_utils.py +3 -2
- dagster_cloud/dagster_insights/bigquery/dbt_wrapper.py +39 -12
- dagster_cloud/dagster_insights/bigquery/insights_bigquery_resource.py +8 -6
- dagster_cloud/dagster_insights/insights_utils.py +18 -8
- dagster_cloud/dagster_insights/metrics_utils.py +12 -12
- dagster_cloud/dagster_insights/snowflake/dagster_snowflake_insights.py +5 -12
- dagster_cloud/dagster_insights/snowflake/dbt_wrapper.py +34 -8
- dagster_cloud/dagster_insights/snowflake/definitions.py +38 -12
- dagster_cloud/dagster_insights/snowflake/insights_snowflake_resource.py +11 -23
- dagster_cloud/definitions/__init__.py +0 -0
- dagster_cloud/definitions/job_selection.py +36 -0
- dagster_cloud/execution/cloud_run_launcher/k8s.py +1 -1
- dagster_cloud/execution/cloud_run_launcher/process.py +3 -3
- dagster_cloud/execution/monitoring/__init__.py +27 -33
- dagster_cloud/execution/utils/process.py +3 -3
- dagster_cloud/instance/__init__.py +125 -38
- dagster_cloud/instrumentation/__init__.py +32 -0
- dagster_cloud/metadata/source_code.py +13 -8
- dagster_cloud/metrics/__init__.py +0 -0
- dagster_cloud/metrics/tracer.py +59 -0
- dagster_cloud/opentelemetry/__init__.py +0 -0
- dagster_cloud/opentelemetry/config/__init__.py +73 -0
- dagster_cloud/opentelemetry/config/exporter.py +81 -0
- dagster_cloud/opentelemetry/config/log_record_processor.py +40 -0
- dagster_cloud/opentelemetry/config/logging_handler.py +14 -0
- dagster_cloud/opentelemetry/config/meter_provider.py +9 -0
- dagster_cloud/opentelemetry/config/metric_reader.py +39 -0
- dagster_cloud/opentelemetry/controller.py +319 -0
- dagster_cloud/opentelemetry/enum.py +58 -0
- dagster_cloud/opentelemetry/factories/__init__.py +1 -0
- dagster_cloud/opentelemetry/factories/logs.py +113 -0
- dagster_cloud/opentelemetry/factories/metrics.py +121 -0
- dagster_cloud/opentelemetry/metrics/__init__.py +0 -0
- dagster_cloud/opentelemetry/metrics/meter.py +140 -0
- dagster_cloud/opentelemetry/observers/__init__.py +0 -0
- dagster_cloud/opentelemetry/observers/dagster_exception_handler.py +40 -0
- dagster_cloud/opentelemetry/observers/execution_observer.py +178 -0
- dagster_cloud/pex/grpc/__generated__/multi_pex_api_pb2.pyi +175 -0
- dagster_cloud/pex/grpc/__init__.py +2 -2
- dagster_cloud/pex/grpc/client.py +4 -4
- dagster_cloud/pex/grpc/compile.py +2 -2
- dagster_cloud/pex/grpc/server/__init__.py +2 -2
- dagster_cloud/pex/grpc/server/cli/__init__.py +31 -19
- dagster_cloud/pex/grpc/server/manager.py +60 -42
- dagster_cloud/pex/grpc/server/registry.py +28 -21
- dagster_cloud/pex/grpc/server/server.py +23 -14
- dagster_cloud/pex/grpc/types.py +5 -5
- dagster_cloud/py.typed +0 -0
- dagster_cloud/secrets/__init__.py +1 -1
- dagster_cloud/secrets/loader.py +3 -3
- dagster_cloud/serverless/__init__.py +1 -1
- dagster_cloud/serverless/io_manager.py +36 -53
- dagster_cloud/storage/client.py +54 -17
- dagster_cloud/storage/compute_logs/__init__.py +3 -1
- dagster_cloud/storage/compute_logs/compute_log_manager.py +22 -17
- dagster_cloud/storage/defs_state/__init__.py +3 -0
- dagster_cloud/storage/defs_state/queries.py +15 -0
- dagster_cloud/storage/defs_state/storage.py +113 -0
- dagster_cloud/storage/event_logs/__init__.py +3 -1
- dagster_cloud/storage/event_logs/queries.py +102 -4
- dagster_cloud/storage/event_logs/storage.py +266 -73
- dagster_cloud/storage/event_logs/utils.py +88 -7
- dagster_cloud/storage/runs/__init__.py +1 -1
- dagster_cloud/storage/runs/queries.py +17 -2
- dagster_cloud/storage/runs/storage.py +88 -42
- dagster_cloud/storage/schedules/__init__.py +1 -1
- dagster_cloud/storage/schedules/storage.py +6 -8
- dagster_cloud/storage/tags.py +66 -1
- dagster_cloud/util/__init__.py +10 -12
- dagster_cloud/util/errors.py +49 -64
- dagster_cloud/version.py +1 -1
- dagster_cloud/workspace/config_schema/__init__.py +55 -13
- dagster_cloud/workspace/docker/__init__.py +76 -25
- dagster_cloud/workspace/docker/utils.py +1 -1
- dagster_cloud/workspace/ecs/__init__.py +1 -1
- dagster_cloud/workspace/ecs/client.py +51 -33
- dagster_cloud/workspace/ecs/launcher.py +76 -22
- dagster_cloud/workspace/ecs/run_launcher.py +3 -3
- dagster_cloud/workspace/ecs/utils.py +14 -5
- dagster_cloud/workspace/kubernetes/__init__.py +1 -1
- dagster_cloud/workspace/kubernetes/launcher.py +61 -29
- dagster_cloud/workspace/kubernetes/utils.py +34 -22
- dagster_cloud/workspace/user_code_launcher/__init__.py +5 -3
- dagster_cloud/workspace/user_code_launcher/process.py +16 -14
- dagster_cloud/workspace/user_code_launcher/user_code_launcher.py +552 -172
- dagster_cloud/workspace/user_code_launcher/utils.py +105 -1
- {dagster_cloud-1.8.2.dist-info → dagster_cloud-1.12.6.dist-info}/METADATA +48 -42
- dagster_cloud-1.12.6.dist-info/RECORD +134 -0
- {dagster_cloud-1.8.2.dist-info → dagster_cloud-1.12.6.dist-info}/WHEEL +1 -1
- dagster_cloud-1.8.2.dist-info/RECORD +0 -100
- {dagster_cloud-1.8.2.dist-info → dagster_cloud-1.12.6.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
from
|
|
1
|
+
from collections.abc import Iterable, Iterator
|
|
2
|
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
|
2
3
|
|
|
3
4
|
import yaml
|
|
4
5
|
from dagster import (
|
|
6
|
+
AssetCheckEvaluation,
|
|
5
7
|
AssetCheckResult,
|
|
6
8
|
AssetExecutionContext,
|
|
7
9
|
AssetKey,
|
|
@@ -11,8 +13,15 @@ from dagster import (
|
|
|
11
13
|
Output,
|
|
12
14
|
)
|
|
13
15
|
|
|
14
|
-
from
|
|
15
|
-
|
|
16
|
+
from dagster_cloud.dagster_insights.insights_utils import (
|
|
17
|
+
extract_asset_info_from_event,
|
|
18
|
+
handle_raise_on_error,
|
|
19
|
+
)
|
|
20
|
+
from dagster_cloud.dagster_insights.snowflake.snowflake_utils import (
|
|
21
|
+
OPAQUE_ID_SQL_SIGIL,
|
|
22
|
+
build_opaque_id_metadata,
|
|
23
|
+
marker_asset_key_for_job,
|
|
24
|
+
)
|
|
16
25
|
|
|
17
26
|
if TYPE_CHECKING:
|
|
18
27
|
from dagster_dbt import DbtCliInvocation
|
|
@@ -23,11 +32,21 @@ def dbt_with_snowflake_insights(
|
|
|
23
32
|
context: Union[OpExecutionContext, AssetExecutionContext],
|
|
24
33
|
dbt_cli_invocation: "DbtCliInvocation",
|
|
25
34
|
dagster_events: Optional[
|
|
26
|
-
Iterable[
|
|
35
|
+
Iterable[
|
|
36
|
+
Union[
|
|
37
|
+
Output,
|
|
38
|
+
AssetMaterialization,
|
|
39
|
+
AssetObservation,
|
|
40
|
+
AssetCheckResult,
|
|
41
|
+
AssetCheckEvaluation,
|
|
42
|
+
]
|
|
43
|
+
]
|
|
27
44
|
] = None,
|
|
28
45
|
skip_config_check=False,
|
|
29
46
|
record_observation_usage: bool = True,
|
|
30
|
-
) -> Iterator[
|
|
47
|
+
) -> Iterator[
|
|
48
|
+
Union[Output, AssetMaterialization, AssetObservation, AssetCheckResult, AssetCheckEvaluation]
|
|
49
|
+
]:
|
|
31
50
|
"""Wraps a dagster-dbt invocation to associate each Snowflake query with the produced
|
|
32
51
|
asset materializations. This allows the cost of each query to be associated with the asset
|
|
33
52
|
materialization that it produced.
|
|
@@ -38,7 +57,7 @@ def dbt_with_snowflake_insights(
|
|
|
38
57
|
Args:
|
|
39
58
|
context (AssetExecutionContext): The context of the asset that is being materialized.
|
|
40
59
|
dbt_cli_invocation (DbtCliInvocation): The invocation of the dbt CLI to wrap.
|
|
41
|
-
dagster_events (Optional[Iterable[Union[Output, AssetObservation, AssetCheckResult]]]):
|
|
60
|
+
dagster_events (Optional[Iterable[Union[Output, AssetObservation, AssetCheckResult, AssetCheckEvaluation]]]):
|
|
42
61
|
The events that were produced by the dbt CLI invocation. If not provided, it is assumed
|
|
43
62
|
that the dbt CLI invocation has not yet been run, and it will be run and the events
|
|
44
63
|
will be streamed.
|
|
@@ -89,10 +108,17 @@ def dbt_with_snowflake_insights(
|
|
|
89
108
|
if dagster_events is None:
|
|
90
109
|
dagster_events = dbt_cli_invocation.stream()
|
|
91
110
|
|
|
92
|
-
asset_and_partition_key_to_unique_id:
|
|
111
|
+
asset_and_partition_key_to_unique_id: list[tuple[AssetKey, Optional[str], Any]] = []
|
|
93
112
|
for dagster_event in dagster_events:
|
|
94
113
|
if isinstance(
|
|
95
|
-
dagster_event,
|
|
114
|
+
dagster_event,
|
|
115
|
+
(
|
|
116
|
+
AssetMaterialization,
|
|
117
|
+
AssetObservation,
|
|
118
|
+
Output,
|
|
119
|
+
AssetCheckResult,
|
|
120
|
+
AssetCheckEvaluation,
|
|
121
|
+
),
|
|
96
122
|
):
|
|
97
123
|
unique_id = dagster_event.metadata["unique_id"].value
|
|
98
124
|
asset_key, partition = extract_asset_info_from_event(
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import warnings
|
|
2
|
+
from collections.abc import Sequence
|
|
2
3
|
from dataclasses import dataclass
|
|
3
4
|
from datetime import date, datetime, timedelta, timezone
|
|
4
|
-
from
|
|
5
|
-
from typing import TYPE_CHECKING, Optional, Sequence, Union
|
|
5
|
+
from typing import TYPE_CHECKING, Optional, Union
|
|
6
6
|
|
|
7
7
|
from dagster import (
|
|
8
8
|
AssetExecutionContext,
|
|
9
|
+
AssetKey,
|
|
9
10
|
AssetsDefinition,
|
|
10
11
|
AssetSelection,
|
|
11
12
|
HourlyPartitionsDefinition,
|
|
13
|
+
RunRequest,
|
|
12
14
|
ScheduleDefinition,
|
|
13
15
|
ScheduleEvaluationContext,
|
|
14
16
|
TimeWindow,
|
|
@@ -18,9 +20,16 @@ from dagster import (
|
|
|
18
20
|
fs_io_manager,
|
|
19
21
|
schedule,
|
|
20
22
|
)
|
|
23
|
+
from dagster._core.definitions.unresolved_asset_job_definition import UnresolvedAssetJobDefinition
|
|
24
|
+
from dagster._core.storage.tags import (
|
|
25
|
+
ASSET_PARTITION_RANGE_END_TAG,
|
|
26
|
+
ASSET_PARTITION_RANGE_START_TAG,
|
|
27
|
+
)
|
|
21
28
|
|
|
22
|
-
from
|
|
23
|
-
from .dagster_snowflake_insights import
|
|
29
|
+
from dagster_cloud.dagster_insights.metrics_utils import put_cost_information
|
|
30
|
+
from dagster_cloud.dagster_insights.snowflake.dagster_snowflake_insights import (
|
|
31
|
+
get_cost_data_for_hour,
|
|
32
|
+
)
|
|
24
33
|
|
|
25
34
|
if TYPE_CHECKING:
|
|
26
35
|
from dagster_snowflake import SnowflakeConnection
|
|
@@ -34,6 +43,22 @@ class SnowflakeInsightsDefinitions:
|
|
|
34
43
|
schedule: ScheduleDefinition
|
|
35
44
|
|
|
36
45
|
|
|
46
|
+
def _build_run_request_for_partition_key_range(
|
|
47
|
+
job: UnresolvedAssetJobDefinition,
|
|
48
|
+
asset_keys: Sequence[AssetKey],
|
|
49
|
+
partition_range_start: str,
|
|
50
|
+
partition_range_end: str,
|
|
51
|
+
) -> RunRequest:
|
|
52
|
+
tags = {
|
|
53
|
+
ASSET_PARTITION_RANGE_START_TAG: partition_range_start,
|
|
54
|
+
ASSET_PARTITION_RANGE_END_TAG: partition_range_end,
|
|
55
|
+
}
|
|
56
|
+
partition_key = partition_range_start if partition_range_start == partition_range_end else None
|
|
57
|
+
return RunRequest(
|
|
58
|
+
job_name=job.name, asset_selection=asset_keys, partition_key=partition_key, tags=tags
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
37
62
|
def create_snowflake_insights_asset_and_schedule(
|
|
38
63
|
start_date: Union[datetime, date, str],
|
|
39
64
|
name: Optional[str] = None,
|
|
@@ -97,7 +122,7 @@ def create_snowflake_insights_asset_and_schedule(
|
|
|
97
122
|
def poll_snowflake_query_history_hour(
|
|
98
123
|
context: AssetExecutionContext,
|
|
99
124
|
) -> None:
|
|
100
|
-
snowflake:
|
|
125
|
+
snowflake: SnowflakeConnection = getattr(context.resources, snowflake_resource_key)
|
|
101
126
|
|
|
102
127
|
start_hour = context.partition_time_window.start
|
|
103
128
|
end_hour = context.partition_time_window.end
|
|
@@ -130,7 +155,7 @@ def create_snowflake_insights_asset_and_schedule(
|
|
|
130
155
|
)
|
|
131
156
|
|
|
132
157
|
if dry_run:
|
|
133
|
-
|
|
158
|
+
pass
|
|
134
159
|
else:
|
|
135
160
|
context.log.info(
|
|
136
161
|
f"Submitting cost information for {len(costs)} queries to Dagster Insights"
|
|
@@ -168,13 +193,14 @@ def create_snowflake_insights_asset_and_schedule(
|
|
|
168
193
|
n_hours_ago = timestamp - timedelta(hours=schedule_batch_size_hrs)
|
|
169
194
|
window = TimeWindow(start=n_hours_ago, end=timestamp)
|
|
170
195
|
|
|
171
|
-
|
|
196
|
+
partition_key_range = partitions_def.get_partition_key_range_for_time_window(window)
|
|
172
197
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
198
|
+
yield _build_run_request_for_partition_key_range(
|
|
199
|
+
insights_job,
|
|
200
|
+
[poll_snowflake_query_history_hour.key],
|
|
201
|
+
partition_key_range.start,
|
|
202
|
+
partition_key_range.end,
|
|
203
|
+
)
|
|
178
204
|
|
|
179
205
|
insights_schedule = _insights_schedule
|
|
180
206
|
|
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
import warnings
|
|
2
|
+
from collections.abc import Generator, Iterable, Iterator, Mapping, Sequence
|
|
2
3
|
from contextlib import contextmanager
|
|
3
4
|
from io import StringIO
|
|
4
|
-
from typing import
|
|
5
|
-
Any,
|
|
6
|
-
Generator,
|
|
7
|
-
Iterable,
|
|
8
|
-
Iterator,
|
|
9
|
-
Mapping,
|
|
10
|
-
Optional,
|
|
11
|
-
Sequence,
|
|
12
|
-
Tuple,
|
|
13
|
-
Type,
|
|
14
|
-
Union,
|
|
15
|
-
cast,
|
|
16
|
-
)
|
|
5
|
+
from typing import Any, Optional, Union, cast
|
|
17
6
|
|
|
18
7
|
import snowflake.connector
|
|
19
8
|
from dagster import (
|
|
@@ -23,7 +12,7 @@ from dagster import (
|
|
|
23
12
|
OpExecutionContext,
|
|
24
13
|
get_dagster_logger,
|
|
25
14
|
)
|
|
26
|
-
from dagster._annotations import
|
|
15
|
+
from dagster._annotations import beta, public
|
|
27
16
|
from dagster._check import CheckError
|
|
28
17
|
from dagster._core.errors import DagsterInvalidPropertyError
|
|
29
18
|
from dagster._core.storage.event_log.sql_event_log import SqlDbConnection
|
|
@@ -31,13 +20,12 @@ from dagster_snowflake import SnowflakeConnection, SnowflakeResource
|
|
|
31
20
|
from snowflake.connector.cursor import SnowflakeCursor
|
|
32
21
|
|
|
33
22
|
from dagster_cloud.dagster_insights.insights_utils import get_current_context_and_asset_key
|
|
34
|
-
|
|
35
|
-
from .snowflake_utils import meter_snowflake_query
|
|
23
|
+
from dagster_cloud.dagster_insights.snowflake.snowflake_utils import meter_snowflake_query
|
|
36
24
|
|
|
37
25
|
|
|
38
|
-
def get_current_context_and_asset_key_or_warn() ->
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
def get_current_context_and_asset_key_or_warn() -> tuple[
|
|
27
|
+
Union[OpExecutionContext, AssetExecutionContext, None], Optional[AssetKey]
|
|
28
|
+
]:
|
|
41
29
|
try:
|
|
42
30
|
return get_current_context_and_asset_key()
|
|
43
31
|
except (DagsterInvalidPropertyError, DagsterInvariantViolationError, CheckError):
|
|
@@ -80,7 +68,7 @@ class WrappedSnowflakeConnection(snowflake.connector.SnowflakeConnection):
|
|
|
80
68
|
sql_text: str,
|
|
81
69
|
remove_comments: bool = False,
|
|
82
70
|
return_cursors: bool = True,
|
|
83
|
-
cursor_class:
|
|
71
|
+
cursor_class: type[SnowflakeCursor] = InsightsSnowflakeCursor,
|
|
84
72
|
**kwargs,
|
|
85
73
|
) -> Iterable[SnowflakeCursor]:
|
|
86
74
|
return super().execute_string(
|
|
@@ -95,14 +83,14 @@ class WrappedSnowflakeConnection(snowflake.connector.SnowflakeConnection):
|
|
|
95
83
|
self,
|
|
96
84
|
stream: StringIO,
|
|
97
85
|
remove_comments: bool = False,
|
|
98
|
-
cursor_class:
|
|
86
|
+
cursor_class: type[SnowflakeCursor] = InsightsSnowflakeCursor,
|
|
99
87
|
**kwargs,
|
|
100
88
|
) -> Generator[SnowflakeCursor, None, None]:
|
|
101
89
|
return super().execute_stream(stream, remove_comments, cursor_class, **kwargs) # type: ignore # (bad stubs)
|
|
102
90
|
|
|
103
91
|
def cursor(self, cursor_class=None) -> SnowflakeCursor:
|
|
104
92
|
if cursor_class is None:
|
|
105
|
-
cursor = cast(InsightsSnowflakeCursor, super().cursor(InsightsSnowflakeCursor))
|
|
93
|
+
cursor = cast("InsightsSnowflakeCursor", super().cursor(InsightsSnowflakeCursor))
|
|
106
94
|
cursor.set_asset_key(self._asset_key)
|
|
107
95
|
else:
|
|
108
96
|
cursor = super().cursor(cursor_class)
|
|
@@ -110,7 +98,7 @@ class WrappedSnowflakeConnection(snowflake.connector.SnowflakeConnection):
|
|
|
110
98
|
return cursor
|
|
111
99
|
|
|
112
100
|
|
|
113
|
-
@
|
|
101
|
+
@beta
|
|
114
102
|
class InsightsSnowflakeResource(SnowflakeResource):
|
|
115
103
|
"""A wrapper around :py:class:`SnowflakeResource` which automatically tags
|
|
116
104
|
Snowflake queries with comments which can be used to attribute Snowflake
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from collections.abc import Sequence
|
|
3
|
+
from typing import AbstractSet # noqa: UP035
|
|
4
|
+
|
|
5
|
+
import dagster._check as check
|
|
6
|
+
from dagster._record import record
|
|
7
|
+
from dagster_shared.serdes import whitelist_for_serdes
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@whitelist_for_serdes
|
|
11
|
+
@record
|
|
12
|
+
class JobSelection(ABC):
|
|
13
|
+
code_location_name: str
|
|
14
|
+
repository_name: str
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def resolve_job_names(self) -> AbstractSet[str]:
|
|
18
|
+
raise NotImplementedError()
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def names(
|
|
22
|
+
names: Sequence[str], code_location_name: str, repository_name: str
|
|
23
|
+
) -> "NamesJobSelection":
|
|
24
|
+
check.invariant(len(names) == 1, "Only one job name is supported at this time")
|
|
25
|
+
return NamesJobSelection(
|
|
26
|
+
job_names=names, code_location_name=code_location_name, repository_name=repository_name
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@whitelist_for_serdes
|
|
31
|
+
@record
|
|
32
|
+
class NamesJobSelection(JobSelection):
|
|
33
|
+
job_names: Sequence[str]
|
|
34
|
+
|
|
35
|
+
def resolve_job_names(self) -> AbstractSet[str]:
|
|
36
|
+
return set(self.job_names)
|
|
@@ -5,5 +5,5 @@ class CloudK8sRunLauncher(K8sRunLauncher):
|
|
|
5
5
|
# Avoid call to `count_resume_run_attempts`, since resuming runs is not currently
|
|
6
6
|
# supported in cloud and the method makes repeated event log calls.
|
|
7
7
|
@property
|
|
8
|
-
def supports_run_worker_crash_recovery(self):
|
|
8
|
+
def supports_run_worker_crash_recovery(self): # pyright: ignore[reportIncompatibleMethodOverride]
|
|
9
9
|
return False
|
|
@@ -6,11 +6,11 @@ from dagster._core.launcher import RunLauncher
|
|
|
6
6
|
from dagster._core.launcher.base import LaunchRunContext
|
|
7
7
|
from dagster._core.utils import parse_env_var
|
|
8
8
|
from dagster._grpc.types import ExecuteRunArgs
|
|
9
|
-
from
|
|
10
|
-
from dagster_cloud_cli.core.workspace import get_instance_ref_for_user_code
|
|
9
|
+
from dagster_shared.ipc import open_ipc_subprocess
|
|
11
10
|
|
|
12
11
|
from dagster_cloud.execution.utils import TaskStatus
|
|
13
12
|
from dagster_cloud.execution.utils.process import check_on_process, kill_process
|
|
13
|
+
from dagster_cloud.workspace.user_code_launcher.utils import get_instance_ref_for_user_code
|
|
14
14
|
|
|
15
15
|
PID_TAG = "process/pid"
|
|
16
16
|
|
|
@@ -59,7 +59,7 @@ class CloudProcessRunLauncher(RunLauncher):
|
|
|
59
59
|
for run_id in self._run_ids
|
|
60
60
|
if (
|
|
61
61
|
self._instance.get_run_by_id(run_id)
|
|
62
|
-
and not self._instance.get_run_by_id(run_id).is_finished
|
|
62
|
+
and not self._instance.get_run_by_id(run_id).is_finished # pyright: ignore[reportOptionalMemberAccess]
|
|
63
63
|
)
|
|
64
64
|
]
|
|
65
65
|
|
|
@@ -2,22 +2,9 @@ import logging
|
|
|
2
2
|
import os
|
|
3
3
|
import sys
|
|
4
4
|
import threading
|
|
5
|
+
from collections.abc import Iterable, Mapping, Sequence
|
|
5
6
|
from enum import Enum
|
|
6
|
-
from typing import
|
|
7
|
-
Any,
|
|
8
|
-
Dict,
|
|
9
|
-
Iterable,
|
|
10
|
-
List,
|
|
11
|
-
Mapping,
|
|
12
|
-
NamedTuple,
|
|
13
|
-
Optional,
|
|
14
|
-
Sequence,
|
|
15
|
-
Set,
|
|
16
|
-
Tuple,
|
|
17
|
-
TypedDict,
|
|
18
|
-
Union,
|
|
19
|
-
cast,
|
|
20
|
-
)
|
|
7
|
+
from typing import Any, NamedTuple, Optional, TypedDict, Union, cast
|
|
21
8
|
|
|
22
9
|
import dagster._check as check
|
|
23
10
|
import grpc
|
|
@@ -96,13 +83,13 @@ class CloudCodeServerHeartbeat(
|
|
|
96
83
|
error: Optional[SerializableErrorInfo] = None,
|
|
97
84
|
metadata: Optional[Mapping[str, Any]] = None,
|
|
98
85
|
):
|
|
99
|
-
return super(
|
|
86
|
+
return super().__new__(
|
|
100
87
|
cls,
|
|
101
88
|
location_name=check.str_param(location_name, "location_name"),
|
|
102
89
|
server_status=check.inst_param(server_status, "server_status", CloudCodeServerStatus),
|
|
103
90
|
error=check.opt_inst_param(error, "error", SerializableErrorInfo),
|
|
104
91
|
metadata=cast(
|
|
105
|
-
CloudCodeServerHeartbeatMetadata, check.opt_mapping_param(metadata, "metadata")
|
|
92
|
+
"CloudCodeServerHeartbeatMetadata", check.opt_mapping_param(metadata, "metadata")
|
|
106
93
|
),
|
|
107
94
|
)
|
|
108
95
|
|
|
@@ -113,7 +100,7 @@ class CloudCodeServerHeartbeat(
|
|
|
113
100
|
return self._replace(
|
|
114
101
|
error=None,
|
|
115
102
|
metadata=cast(
|
|
116
|
-
CloudCodeServerHeartbeatMetadata,
|
|
103
|
+
"CloudCodeServerHeartbeatMetadata",
|
|
117
104
|
{},
|
|
118
105
|
),
|
|
119
106
|
)
|
|
@@ -148,7 +135,7 @@ class CloudRunWorkerStatus(
|
|
|
148
135
|
transient: Optional[bool] = None,
|
|
149
136
|
run_worker_id: Optional[str] = None,
|
|
150
137
|
):
|
|
151
|
-
return super(
|
|
138
|
+
return super().__new__(
|
|
152
139
|
cls,
|
|
153
140
|
run_id=check.str_param(run_id, "run_id"),
|
|
154
141
|
status_type=check.inst_param(status_type, "status_type", WorkerStatus),
|
|
@@ -180,7 +167,7 @@ class CloudRunWorkerStatuses(
|
|
|
180
167
|
NamedTuple(
|
|
181
168
|
"_CloudRunWorkerStatuses",
|
|
182
169
|
[
|
|
183
|
-
("statuses",
|
|
170
|
+
("statuses", list[CloudRunWorkerStatus]),
|
|
184
171
|
("run_worker_monitoring_supported", bool),
|
|
185
172
|
("run_worker_monitoring_thread_alive", Optional[bool]),
|
|
186
173
|
],
|
|
@@ -188,11 +175,11 @@ class CloudRunWorkerStatuses(
|
|
|
188
175
|
):
|
|
189
176
|
def __new__(
|
|
190
177
|
cls,
|
|
191
|
-
statuses: Optional[
|
|
178
|
+
statuses: Optional[list[CloudRunWorkerStatus]],
|
|
192
179
|
run_worker_monitoring_supported: bool,
|
|
193
180
|
run_worker_monitoring_thread_alive: Optional[bool],
|
|
194
181
|
):
|
|
195
|
-
return super(
|
|
182
|
+
return super().__new__(
|
|
196
183
|
cls,
|
|
197
184
|
statuses=check.opt_list_param(statuses, "statuses", of_type=CloudRunWorkerStatus),
|
|
198
185
|
run_worker_monitoring_supported=check.bool_param(
|
|
@@ -229,13 +216,13 @@ def _is_grpc_unknown_error(error: Exception) -> bool:
|
|
|
229
216
|
def get_cloud_run_worker_statuses(
|
|
230
217
|
instance: DagsterCloudAgentInstance, deployment_names: Iterable[str], logger: logging.Logger
|
|
231
218
|
) -> Mapping[str, Sequence[CloudRunWorkerStatus]]:
|
|
232
|
-
statuses:
|
|
219
|
+
statuses: dict[str, Sequence[CloudRunWorkerStatus]] = {}
|
|
233
220
|
|
|
234
221
|
# protected with a lock inside the method
|
|
235
222
|
active_grpc_server_handles = instance.user_code_launcher.get_active_grpc_server_handles()
|
|
236
223
|
active_grpc_server_handle_strings = [str(s) for s in active_grpc_server_handles]
|
|
237
224
|
|
|
238
|
-
active_non_isolated_run_ids_by_server_handle:
|
|
225
|
+
active_non_isolated_run_ids_by_server_handle: dict[
|
|
239
226
|
str, Union[Sequence[str], _GetCurrentRunsError]
|
|
240
227
|
] = {}
|
|
241
228
|
if instance.user_code_launcher.supports_get_current_runs_for_server_handle:
|
|
@@ -276,12 +263,19 @@ def get_cloud_run_worker_statuses(
|
|
|
276
263
|
instance.ref_for_deployment(deployment_name)
|
|
277
264
|
) as scoped_instance:
|
|
278
265
|
runs = scoped_instance.get_runs(RunsFilter(statuses=IN_PROGRESS_RUN_STATUSES))
|
|
279
|
-
statuses_for_deployment:
|
|
266
|
+
statuses_for_deployment: list[CloudRunWorkerStatus] = []
|
|
280
267
|
for run in runs:
|
|
281
268
|
if is_isolated_run(run):
|
|
282
269
|
launcher = scoped_instance.run_launcher
|
|
283
270
|
|
|
284
|
-
|
|
271
|
+
try:
|
|
272
|
+
run_worker_health = launcher.check_run_worker_health(run)
|
|
273
|
+
except Exception:
|
|
274
|
+
logger.exception(
|
|
275
|
+
f"An exception occurred while checking run worker health for run '{run.run_id}'"
|
|
276
|
+
)
|
|
277
|
+
continue
|
|
278
|
+
|
|
285
279
|
run_worker_debug_info = None
|
|
286
280
|
|
|
287
281
|
if run_worker_health.status == WorkerStatus.FAILED:
|
|
@@ -385,8 +379,8 @@ def get_cloud_run_worker_statuses(
|
|
|
385
379
|
|
|
386
380
|
def run_worker_monitoring_thread(
|
|
387
381
|
instance: DagsterCloudAgentInstance,
|
|
388
|
-
deployments_to_check:
|
|
389
|
-
statuses_dict:
|
|
382
|
+
deployments_to_check: set[str],
|
|
383
|
+
statuses_dict: dict[str, Sequence[CloudRunWorkerStatus]],
|
|
390
384
|
run_worker_monitoring_lock: threading.Lock,
|
|
391
385
|
shutdown_event: threading.Event,
|
|
392
386
|
monitoring_interval: Optional[int] = None,
|
|
@@ -409,8 +403,8 @@ def run_worker_monitoring_thread(
|
|
|
409
403
|
|
|
410
404
|
def run_worker_monitoring_thread_iteration(
|
|
411
405
|
instance: DagsterCloudAgentInstance,
|
|
412
|
-
deployments_to_check:
|
|
413
|
-
statuses_dict:
|
|
406
|
+
deployments_to_check: set[str],
|
|
407
|
+
statuses_dict: dict[str, Sequence[CloudRunWorkerStatus]],
|
|
414
408
|
run_worker_monitoring_lock: threading.Lock,
|
|
415
409
|
logger: logging.Logger,
|
|
416
410
|
) -> None:
|
|
@@ -432,11 +426,11 @@ def run_worker_monitoring_thread_iteration(
|
|
|
432
426
|
|
|
433
427
|
def start_run_worker_monitoring_thread(
|
|
434
428
|
instance: DagsterCloudAgentInstance,
|
|
435
|
-
deployments_to_check:
|
|
436
|
-
statuses_dict:
|
|
429
|
+
deployments_to_check: set[str],
|
|
430
|
+
statuses_dict: dict[str, list[CloudRunWorkerStatus]],
|
|
437
431
|
run_worker_monitoring_lock: threading.Lock,
|
|
438
432
|
monitoring_interval: Optional[int] = None,
|
|
439
|
-
) ->
|
|
433
|
+
) -> tuple[threading.Thread, threading.Event]:
|
|
440
434
|
shutdown_event = threading.Event()
|
|
441
435
|
thread = threading.Thread(
|
|
442
436
|
target=run_worker_monitoring_thread,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
|
-
from
|
|
3
|
+
from collections.abc import Sequence
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from dagster_shared.ipc import interrupt_ipc_subprocess_pid, open_ipc_subprocess
|
|
6
6
|
|
|
7
|
-
from . import TaskStatus
|
|
7
|
+
from dagster_cloud.execution.utils import TaskStatus
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def launch_process(args: Sequence[str]) -> int:
|