mlrun 1.10.0rc40__py3-none-any.whl → 1.11.0rc16__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.
Potentially problematic release.
This version of mlrun might be problematic. Click here for more details.
- mlrun/__init__.py +3 -2
- mlrun/__main__.py +0 -4
- mlrun/artifacts/dataset.py +2 -2
- mlrun/artifacts/plots.py +1 -1
- mlrun/{model_monitoring/db/tsdb/tdengine → auth}/__init__.py +2 -3
- mlrun/auth/nuclio.py +89 -0
- mlrun/auth/providers.py +429 -0
- mlrun/auth/utils.py +415 -0
- mlrun/common/constants.py +7 -0
- mlrun/common/model_monitoring/helpers.py +41 -4
- mlrun/common/runtimes/constants.py +28 -0
- mlrun/common/schemas/__init__.py +13 -3
- mlrun/common/schemas/alert.py +2 -2
- mlrun/common/schemas/api_gateway.py +3 -0
- mlrun/common/schemas/auth.py +10 -10
- mlrun/common/schemas/client_spec.py +4 -0
- mlrun/common/schemas/constants.py +25 -0
- mlrun/common/schemas/frontend_spec.py +1 -8
- mlrun/common/schemas/function.py +24 -0
- mlrun/common/schemas/hub.py +3 -2
- mlrun/common/schemas/model_monitoring/__init__.py +1 -1
- mlrun/common/schemas/model_monitoring/constants.py +2 -2
- mlrun/common/schemas/secret.py +17 -2
- mlrun/common/secrets.py +95 -1
- mlrun/common/types.py +10 -10
- mlrun/config.py +53 -15
- mlrun/data_types/infer.py +2 -2
- mlrun/datastore/__init__.py +2 -3
- mlrun/datastore/base.py +274 -10
- mlrun/datastore/datastore.py +1 -1
- mlrun/datastore/datastore_profile.py +49 -17
- mlrun/datastore/model_provider/huggingface_provider.py +6 -2
- mlrun/datastore/model_provider/model_provider.py +2 -2
- mlrun/datastore/model_provider/openai_provider.py +2 -2
- mlrun/datastore/s3.py +15 -16
- mlrun/datastore/sources.py +1 -1
- mlrun/datastore/store_resources.py +4 -4
- mlrun/datastore/storeytargets.py +16 -10
- mlrun/datastore/targets.py +1 -1
- mlrun/datastore/utils.py +16 -3
- mlrun/datastore/v3io.py +1 -1
- mlrun/db/base.py +36 -12
- mlrun/db/httpdb.py +316 -101
- mlrun/db/nopdb.py +29 -11
- mlrun/errors.py +4 -2
- mlrun/execution.py +11 -12
- mlrun/feature_store/api.py +1 -1
- mlrun/feature_store/common.py +1 -1
- mlrun/feature_store/feature_vector_utils.py +1 -1
- mlrun/feature_store/steps.py +8 -6
- mlrun/frameworks/_common/utils.py +3 -3
- mlrun/frameworks/_dl_common/loggers/logger.py +1 -1
- mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +2 -1
- mlrun/frameworks/_ml_common/loggers/mlrun_logger.py +1 -1
- mlrun/frameworks/_ml_common/utils.py +2 -1
- mlrun/frameworks/auto_mlrun/auto_mlrun.py +4 -3
- mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +2 -1
- mlrun/frameworks/onnx/dataset.py +2 -1
- mlrun/frameworks/onnx/mlrun_interface.py +2 -1
- mlrun/frameworks/pytorch/callbacks/logging_callback.py +5 -4
- mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +2 -1
- mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +2 -1
- mlrun/frameworks/pytorch/utils.py +2 -1
- mlrun/frameworks/sklearn/metric.py +2 -1
- mlrun/frameworks/tf_keras/callbacks/logging_callback.py +5 -4
- mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +2 -1
- mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +2 -1
- mlrun/hub/__init__.py +37 -0
- mlrun/hub/base.py +142 -0
- mlrun/hub/module.py +67 -76
- mlrun/hub/step.py +113 -0
- mlrun/launcher/base.py +2 -1
- mlrun/launcher/local.py +2 -1
- mlrun/model.py +12 -2
- mlrun/model_monitoring/__init__.py +0 -1
- mlrun/model_monitoring/api.py +2 -2
- mlrun/model_monitoring/applications/base.py +20 -6
- mlrun/model_monitoring/applications/context.py +1 -0
- mlrun/model_monitoring/controller.py +7 -17
- mlrun/model_monitoring/db/_schedules.py +2 -16
- mlrun/model_monitoring/db/_stats.py +2 -13
- mlrun/model_monitoring/db/tsdb/__init__.py +9 -7
- mlrun/model_monitoring/db/tsdb/base.py +2 -4
- mlrun/model_monitoring/db/tsdb/preaggregate.py +234 -0
- mlrun/model_monitoring/db/tsdb/stream_graph_steps.py +63 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/queries/timescaledb_metrics_queries.py +414 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/queries/timescaledb_predictions_queries.py +376 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/queries/timescaledb_results_queries.py +590 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_connection.py +434 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_connector.py +541 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_operations.py +808 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_schema.py +502 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_stream.py +163 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_stream_graph_steps.py +60 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/utils/timescaledb_dataframe_processor.py +141 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/utils/timescaledb_query_builder.py +585 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/writer_graph_steps.py +73 -0
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +4 -6
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +147 -79
- mlrun/model_monitoring/features_drift_table.py +2 -1
- mlrun/model_monitoring/helpers.py +2 -1
- mlrun/model_monitoring/stream_processing.py +18 -16
- mlrun/model_monitoring/writer.py +4 -3
- mlrun/package/__init__.py +2 -1
- mlrun/platforms/__init__.py +0 -44
- mlrun/platforms/iguazio.py +1 -1
- mlrun/projects/operations.py +11 -10
- mlrun/projects/project.py +81 -82
- mlrun/run.py +4 -7
- mlrun/runtimes/__init__.py +2 -204
- mlrun/runtimes/base.py +89 -21
- mlrun/runtimes/constants.py +225 -0
- mlrun/runtimes/daskjob.py +4 -2
- mlrun/runtimes/databricks_job/databricks_runtime.py +2 -1
- mlrun/runtimes/mounts.py +5 -0
- mlrun/runtimes/nuclio/__init__.py +12 -8
- mlrun/runtimes/nuclio/api_gateway.py +36 -6
- mlrun/runtimes/nuclio/application/application.py +200 -32
- mlrun/runtimes/nuclio/function.py +154 -49
- mlrun/runtimes/nuclio/serving.py +55 -42
- mlrun/runtimes/pod.py +59 -10
- mlrun/secrets.py +46 -2
- mlrun/serving/__init__.py +2 -0
- mlrun/serving/remote.py +5 -5
- mlrun/serving/routers.py +3 -3
- mlrun/serving/server.py +46 -43
- mlrun/serving/serving_wrapper.py +6 -2
- mlrun/serving/states.py +554 -207
- mlrun/serving/steps.py +1 -1
- mlrun/serving/system_steps.py +42 -33
- mlrun/track/trackers/mlflow_tracker.py +29 -31
- mlrun/utils/helpers.py +89 -16
- mlrun/utils/http.py +9 -2
- mlrun/utils/notifications/notification/git.py +1 -1
- mlrun/utils/notifications/notification/mail.py +39 -16
- mlrun/utils/notifications/notification_pusher.py +2 -2
- mlrun/utils/version/version.json +2 -2
- mlrun/utils/version/version.py +3 -4
- {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/METADATA +39 -49
- {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/RECORD +144 -130
- mlrun/db/auth_utils.py +0 -152
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +0 -343
- mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +0 -75
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connection.py +0 -281
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +0 -1368
- mlrun/model_monitoring/db/tsdb/tdengine/writer_graph_steps.py +0 -51
- {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/WHEEL +0 -0
- {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/entry_points.txt +0 -0
- {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/licenses/LICENSE +0 -0
- {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
# Copyright 2025 Iguazio
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from typing import Optional, Union
|
|
17
|
+
|
|
18
|
+
import pandas as pd
|
|
19
|
+
|
|
20
|
+
import mlrun
|
|
21
|
+
import mlrun.common.schemas.model_monitoring as mm_schemas
|
|
22
|
+
import mlrun.errors
|
|
23
|
+
from mlrun.model_monitoring.db.tsdb.timescaledb.timescaledb_connection import (
|
|
24
|
+
Statement,
|
|
25
|
+
)
|
|
26
|
+
from mlrun.model_monitoring.db.tsdb.timescaledb.utils.timescaledb_query_builder import (
|
|
27
|
+
TimescaleDBQueryBuilder,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TimescaleDBMetricsQueries:
|
|
32
|
+
"""
|
|
33
|
+
Query class containing metrics-related query methods for TimescaleDB.
|
|
34
|
+
|
|
35
|
+
Can be used as a mixin or standalone instance with proper initialization.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
project: Optional[str] = None,
|
|
41
|
+
connection=None,
|
|
42
|
+
pre_aggregate_manager=None,
|
|
43
|
+
tables: Optional[dict] = None,
|
|
44
|
+
):
|
|
45
|
+
"""
|
|
46
|
+
Initialize TimescaleDB metrics query handler.
|
|
47
|
+
|
|
48
|
+
:param project: Project name
|
|
49
|
+
:param connection: TimescaleDB connection instance
|
|
50
|
+
:param pre_aggregate_manager: PreAggregateManager instance
|
|
51
|
+
:param tables: Dictionary of table schemas
|
|
52
|
+
"""
|
|
53
|
+
self.project = project
|
|
54
|
+
self._connection = connection
|
|
55
|
+
self._pre_aggregate_manager = pre_aggregate_manager
|
|
56
|
+
self.tables = tables
|
|
57
|
+
|
|
58
|
+
def get_model_endpoint_real_time_metrics(
|
|
59
|
+
self,
|
|
60
|
+
endpoint_id: str,
|
|
61
|
+
metrics: list[str],
|
|
62
|
+
start: str,
|
|
63
|
+
end: str,
|
|
64
|
+
interval: Optional[str] = None,
|
|
65
|
+
agg_function: Optional[str] = None,
|
|
66
|
+
) -> dict[str, list[tuple[str, float]]]:
|
|
67
|
+
"""Get real-time metrics with optional pre-aggregate optimization."""
|
|
68
|
+
|
|
69
|
+
# Validate that metrics are provided
|
|
70
|
+
if not metrics:
|
|
71
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
72
|
+
"Metric names must be provided"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Prepare time range with validation and ISO conversion using helper
|
|
76
|
+
start_dt, end_dt, interval = (
|
|
77
|
+
TimescaleDBQueryBuilder.prepare_time_range_with_validation(
|
|
78
|
+
self._pre_aggregate_manager, start, end, interval, agg_function
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
table_schema = self.tables[mm_schemas.TimescaleDBTables.METRICS]
|
|
83
|
+
|
|
84
|
+
# Build query including metric names for filtering and grouping
|
|
85
|
+
columns = [
|
|
86
|
+
table_schema.time_column,
|
|
87
|
+
mm_schemas.MetricData.METRIC_NAME,
|
|
88
|
+
mm_schemas.MetricData.METRIC_VALUE,
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
# Build filters: endpoint + requested metrics
|
|
92
|
+
endpoint_filter = TimescaleDBQueryBuilder.build_endpoint_filter(endpoint_id)
|
|
93
|
+
metrics_filter = TimescaleDBQueryBuilder.build_metrics_filter_from_names(
|
|
94
|
+
metrics
|
|
95
|
+
)
|
|
96
|
+
combined_filter = TimescaleDBQueryBuilder.combine_filters(
|
|
97
|
+
[endpoint_filter, metrics_filter]
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Use fallback pattern for potential pre-aggregate compatibility issues
|
|
101
|
+
def build_pre_agg_query():
|
|
102
|
+
return table_schema._get_records_query(
|
|
103
|
+
start=start_dt,
|
|
104
|
+
end=end_dt,
|
|
105
|
+
columns_to_filter=columns,
|
|
106
|
+
filter_query=combined_filter,
|
|
107
|
+
interval=interval,
|
|
108
|
+
agg_funcs=[agg_function] if agg_function else None,
|
|
109
|
+
use_pre_aggregates=True,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def build_raw_query():
|
|
113
|
+
return table_schema._get_records_query(
|
|
114
|
+
start=start_dt,
|
|
115
|
+
end=end_dt,
|
|
116
|
+
columns_to_filter=columns,
|
|
117
|
+
filter_query=combined_filter,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Column mapping rules for pre-aggregate results (if needed)
|
|
121
|
+
column_mapping_rules = {
|
|
122
|
+
mm_schemas.MetricData.METRIC_NAME: [mm_schemas.MetricData.METRIC_NAME],
|
|
123
|
+
mm_schemas.MetricData.METRIC_VALUE: [mm_schemas.MetricData.METRIC_VALUE],
|
|
124
|
+
table_schema.time_column: [table_schema.time_column],
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
df = self._connection.execute_with_fallback(
|
|
128
|
+
self._pre_aggregate_manager,
|
|
129
|
+
build_pre_agg_query,
|
|
130
|
+
build_raw_query,
|
|
131
|
+
interval=interval,
|
|
132
|
+
agg_funcs=[agg_function] if agg_function else None,
|
|
133
|
+
column_mapping_rules=column_mapping_rules,
|
|
134
|
+
debug_name="get_model_endpoint_real_time_metrics",
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# Process DataFrame result into expected format: {metric_name: [(timestamp, value), ...]}
|
|
138
|
+
metrics_data = {metric_name: [] for metric_name in metrics}
|
|
139
|
+
|
|
140
|
+
if not df.empty:
|
|
141
|
+
for _, row in df.iterrows():
|
|
142
|
+
timestamp = row[table_schema.time_column]
|
|
143
|
+
metric_name = row[mm_schemas.MetricData.METRIC_NAME]
|
|
144
|
+
value = row[mm_schemas.MetricData.METRIC_VALUE]
|
|
145
|
+
|
|
146
|
+
# Only include requested metrics
|
|
147
|
+
if metric_name in metrics_data:
|
|
148
|
+
metrics_data[metric_name].append(
|
|
149
|
+
(timestamp.isoformat(), float(value))
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
return metrics_data
|
|
153
|
+
|
|
154
|
+
def read_metrics_data_impl(
|
|
155
|
+
self,
|
|
156
|
+
*,
|
|
157
|
+
endpoint_id: Optional[str] = None,
|
|
158
|
+
start: datetime,
|
|
159
|
+
end: datetime,
|
|
160
|
+
metrics: Optional[list[mm_schemas.ModelEndpointMonitoringMetric]] = None,
|
|
161
|
+
timestamp_column: Optional[str] = None,
|
|
162
|
+
) -> pd.DataFrame:
|
|
163
|
+
"""Read metrics data from TimescaleDB (metrics table only) - returns DataFrame.
|
|
164
|
+
|
|
165
|
+
:param endpoint_id: Endpoint ID to filter by, or None to get all endpoints
|
|
166
|
+
:param start: Start time
|
|
167
|
+
:param end: End time
|
|
168
|
+
:param metrics: List of metrics to filter by, or None to get all metrics
|
|
169
|
+
:param timestamp_column: Optional timestamp column to use for time filtering
|
|
170
|
+
:return: DataFrame with metrics data
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
table_schema = self.tables[mm_schemas.TimescaleDBTables.METRICS]
|
|
174
|
+
name_column = mm_schemas.MetricData.METRIC_NAME
|
|
175
|
+
value_column = mm_schemas.MetricData.METRIC_VALUE
|
|
176
|
+
columns = [
|
|
177
|
+
table_schema.time_column,
|
|
178
|
+
mm_schemas.WriterEvent.START_INFER_TIME,
|
|
179
|
+
mm_schemas.WriterEvent.ENDPOINT_ID,
|
|
180
|
+
mm_schemas.WriterEvent.APPLICATION_NAME,
|
|
181
|
+
name_column,
|
|
182
|
+
value_column,
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
# Build metrics condition using query builder utilities (accepts None)
|
|
186
|
+
metrics_condition = TimescaleDBQueryBuilder.build_metrics_filter(metrics)
|
|
187
|
+
endpoint_filter = TimescaleDBQueryBuilder.build_endpoint_filter(endpoint_id)
|
|
188
|
+
|
|
189
|
+
# Combine filters using query builder utilities
|
|
190
|
+
filters = [endpoint_filter, metrics_condition]
|
|
191
|
+
filter_query = TimescaleDBQueryBuilder.combine_filters(filters)
|
|
192
|
+
|
|
193
|
+
# Use shared utility for consistent query building with fallback
|
|
194
|
+
df = TimescaleDBQueryBuilder.build_read_data_with_fallback(
|
|
195
|
+
connection=self._connection,
|
|
196
|
+
pre_aggregate_manager=self._pre_aggregate_manager,
|
|
197
|
+
table_schema=table_schema,
|
|
198
|
+
start=start,
|
|
199
|
+
end=end,
|
|
200
|
+
columns=columns,
|
|
201
|
+
filter_query=filter_query,
|
|
202
|
+
name_column=name_column,
|
|
203
|
+
value_column=value_column,
|
|
204
|
+
debug_name="read_metrics_data",
|
|
205
|
+
timestamp_column=timestamp_column,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
if not df.empty:
|
|
209
|
+
df[table_schema.time_column] = pd.to_datetime(df[table_schema.time_column])
|
|
210
|
+
df.set_index(table_schema.time_column, inplace=True)
|
|
211
|
+
|
|
212
|
+
return df
|
|
213
|
+
|
|
214
|
+
def get_metrics_metadata(
|
|
215
|
+
self,
|
|
216
|
+
endpoint_id: Union[str, list[str]],
|
|
217
|
+
start: Optional[datetime] = None,
|
|
218
|
+
end: Optional[datetime] = None,
|
|
219
|
+
interval: Optional[str] = None,
|
|
220
|
+
) -> pd.DataFrame:
|
|
221
|
+
"""Get metrics metadata with optional pre-aggregate optimization."""
|
|
222
|
+
|
|
223
|
+
# Prepare time range and interval (no auto-determination since interval passed in)
|
|
224
|
+
start, end, interval = TimescaleDBQueryBuilder.prepare_time_range_and_interval(
|
|
225
|
+
self._pre_aggregate_manager,
|
|
226
|
+
start,
|
|
227
|
+
end,
|
|
228
|
+
interval,
|
|
229
|
+
auto_determine_interval=False,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
table_schema = self.tables[mm_schemas.TimescaleDBTables.METRICS]
|
|
233
|
+
filter_query = TimescaleDBQueryBuilder.build_endpoint_filter(endpoint_id)
|
|
234
|
+
|
|
235
|
+
columns = [
|
|
236
|
+
mm_schemas.WriterEvent.APPLICATION_NAME,
|
|
237
|
+
mm_schemas.MetricData.METRIC_NAME,
|
|
238
|
+
mm_schemas.WriterEvent.ENDPOINT_ID,
|
|
239
|
+
]
|
|
240
|
+
|
|
241
|
+
# Use fallback pattern for potential pre-aggregate compatibility issues
|
|
242
|
+
def build_pre_agg_query():
|
|
243
|
+
return table_schema._get_records_query(
|
|
244
|
+
start=start,
|
|
245
|
+
end=end,
|
|
246
|
+
columns_to_filter=columns,
|
|
247
|
+
filter_query=filter_query,
|
|
248
|
+
interval=interval,
|
|
249
|
+
use_pre_aggregates=True,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
def build_raw_query():
|
|
253
|
+
return table_schema._get_records_query(
|
|
254
|
+
start=start,
|
|
255
|
+
end=end,
|
|
256
|
+
columns_to_filter=columns,
|
|
257
|
+
filter_query=filter_query,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Column mapping rules for pre-aggregate results (if needed)
|
|
261
|
+
column_mapping_rules = {
|
|
262
|
+
mm_schemas.WriterEvent.APPLICATION_NAME: [
|
|
263
|
+
mm_schemas.WriterEvent.APPLICATION_NAME
|
|
264
|
+
],
|
|
265
|
+
mm_schemas.MetricData.METRIC_NAME: [mm_schemas.MetricData.METRIC_NAME],
|
|
266
|
+
mm_schemas.WriterEvent.ENDPOINT_ID: [mm_schemas.WriterEvent.ENDPOINT_ID],
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
df = self._connection.execute_with_fallback(
|
|
270
|
+
self._pre_aggregate_manager,
|
|
271
|
+
build_pre_agg_query,
|
|
272
|
+
build_raw_query,
|
|
273
|
+
interval=interval,
|
|
274
|
+
agg_funcs=None,
|
|
275
|
+
column_mapping_rules=column_mapping_rules,
|
|
276
|
+
debug_name="get_metrics_metadata",
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# Get distinct values
|
|
280
|
+
if not df.empty:
|
|
281
|
+
df = df.drop_duplicates()
|
|
282
|
+
|
|
283
|
+
return df
|
|
284
|
+
|
|
285
|
+
def calculate_latest_metrics(
|
|
286
|
+
self,
|
|
287
|
+
start: Optional[datetime] = None,
|
|
288
|
+
end: Optional[datetime] = None,
|
|
289
|
+
application_names: Optional[list[str]] = None,
|
|
290
|
+
) -> list[
|
|
291
|
+
Union[mm_schemas.ApplicationResultRecord, mm_schemas.ApplicationMetricRecord]
|
|
292
|
+
]:
|
|
293
|
+
"""
|
|
294
|
+
Calculate the latest metrics and results across applications.
|
|
295
|
+
|
|
296
|
+
Returns a list of ApplicationResultRecord and ApplicationMetricRecord objects.
|
|
297
|
+
|
|
298
|
+
:param start: The start time of the query. Last 24 hours is used by default.
|
|
299
|
+
:param end: The end time of the query. The current time is used by default.
|
|
300
|
+
:param application_names: A list of application names to filter the results by. If not provided, all
|
|
301
|
+
applications are included.
|
|
302
|
+
:return: A list containing the latest metrics and results for each application.
|
|
303
|
+
"""
|
|
304
|
+
if not application_names:
|
|
305
|
+
return []
|
|
306
|
+
|
|
307
|
+
start, end = self._pre_aggregate_manager.get_start_end(start, end)
|
|
308
|
+
|
|
309
|
+
metric_objects = []
|
|
310
|
+
|
|
311
|
+
for app_name in application_names:
|
|
312
|
+
# Get latest results for this application
|
|
313
|
+
results_records = self._get_latest_results_for_application(
|
|
314
|
+
app_name, start, end
|
|
315
|
+
)
|
|
316
|
+
metric_objects.extend(results_records)
|
|
317
|
+
# Get latest metrics for this application
|
|
318
|
+
metrics_records = self._get_latest_metrics_for_application(
|
|
319
|
+
app_name, start, end
|
|
320
|
+
)
|
|
321
|
+
metric_objects.extend(metrics_records)
|
|
322
|
+
return metric_objects
|
|
323
|
+
|
|
324
|
+
def _get_latest_metrics_for_application(
|
|
325
|
+
self, application_name: str, start: datetime, end: datetime
|
|
326
|
+
) -> list[mm_schemas.ApplicationMetricRecord]:
|
|
327
|
+
"""Get the latest metrics for a specific application."""
|
|
328
|
+
table_schema = self.tables[mm_schemas.TimescaleDBTables.METRICS]
|
|
329
|
+
|
|
330
|
+
# Build filters using query builder utilities
|
|
331
|
+
app_filter = TimescaleDBQueryBuilder.build_application_filter(application_name)
|
|
332
|
+
time_filter = TimescaleDBQueryBuilder.build_time_range_filter(
|
|
333
|
+
start, end, mm_schemas.WriterEvent.END_INFER_TIME
|
|
334
|
+
)
|
|
335
|
+
where_clause = TimescaleDBQueryBuilder.combine_filters(
|
|
336
|
+
[app_filter, time_filter]
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
# DISTINCT ON is PostgreSQL-specific, keep as specialized query
|
|
340
|
+
query = f"""
|
|
341
|
+
SELECT DISTINCT ON (metric_name)
|
|
342
|
+
{mm_schemas.WriterEvent.END_INFER_TIME},
|
|
343
|
+
{mm_schemas.WriterEvent.APPLICATION_NAME},
|
|
344
|
+
{mm_schemas.MetricData.METRIC_NAME},
|
|
345
|
+
{mm_schemas.MetricData.METRIC_VALUE}
|
|
346
|
+
FROM {table_schema.full_name()}
|
|
347
|
+
WHERE {where_clause}
|
|
348
|
+
ORDER BY metric_name, {mm_schemas.WriterEvent.END_INFER_TIME} DESC
|
|
349
|
+
"""
|
|
350
|
+
|
|
351
|
+
stmt = Statement(query)
|
|
352
|
+
result = self._connection.run(query=stmt)
|
|
353
|
+
|
|
354
|
+
if not result or not result.data:
|
|
355
|
+
return []
|
|
356
|
+
|
|
357
|
+
# Work directly with raw result data instead of constructing DataFrame
|
|
358
|
+
# Fields order: end_infer_time, application_name, metric_name, metric_value
|
|
359
|
+
return [
|
|
360
|
+
mm_schemas.ApplicationMetricRecord(
|
|
361
|
+
time=row[0], # end_infer_time
|
|
362
|
+
value=row[3], # metric_value
|
|
363
|
+
metric_name=row[2], # metric_name
|
|
364
|
+
)
|
|
365
|
+
for row in result.data
|
|
366
|
+
]
|
|
367
|
+
|
|
368
|
+
def _get_latest_results_for_application(
|
|
369
|
+
self, application_name: str, start: datetime, end: datetime
|
|
370
|
+
) -> list[mm_schemas.ApplicationResultRecord]:
|
|
371
|
+
"""Get the latest results for a specific application."""
|
|
372
|
+
table_schema = self.tables[mm_schemas.TimescaleDBTables.APP_RESULTS]
|
|
373
|
+
|
|
374
|
+
# Build filters using query builder utilities
|
|
375
|
+
app_filter = TimescaleDBQueryBuilder.build_application_filter(application_name)
|
|
376
|
+
time_filter = TimescaleDBQueryBuilder.build_time_range_filter(
|
|
377
|
+
start, end, mm_schemas.WriterEvent.END_INFER_TIME
|
|
378
|
+
)
|
|
379
|
+
where_clause = TimescaleDBQueryBuilder.combine_filters(
|
|
380
|
+
[app_filter, time_filter]
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
# DISTINCT ON is PostgreSQL-specific, keep as specialized query
|
|
384
|
+
query = f"""
|
|
385
|
+
SELECT DISTINCT ON (result_name)
|
|
386
|
+
{mm_schemas.WriterEvent.END_INFER_TIME},
|
|
387
|
+
{mm_schemas.WriterEvent.APPLICATION_NAME},
|
|
388
|
+
{mm_schemas.ResultData.RESULT_NAME},
|
|
389
|
+
{mm_schemas.ResultData.RESULT_VALUE},
|
|
390
|
+
{mm_schemas.ResultData.RESULT_STATUS},
|
|
391
|
+
{mm_schemas.ResultData.RESULT_KIND}
|
|
392
|
+
FROM {table_schema.full_name()}
|
|
393
|
+
WHERE {where_clause}
|
|
394
|
+
ORDER BY result_name, {mm_schemas.WriterEvent.END_INFER_TIME} DESC
|
|
395
|
+
"""
|
|
396
|
+
|
|
397
|
+
stmt = Statement(query)
|
|
398
|
+
result = self._connection.run(query=stmt)
|
|
399
|
+
|
|
400
|
+
if not result or not result.data:
|
|
401
|
+
return []
|
|
402
|
+
|
|
403
|
+
# Work directly with raw result data instead of constructing DataFrame
|
|
404
|
+
# Fields order: end_infer_time, application_name, result_name, result_value, result_status, result_kind
|
|
405
|
+
return [
|
|
406
|
+
mm_schemas.ApplicationResultRecord(
|
|
407
|
+
time=row[0], # end_infer_time
|
|
408
|
+
value=row[3], # result_value
|
|
409
|
+
kind=mm_schemas.ResultKindApp(row[5]), # result_kind
|
|
410
|
+
status=mm_schemas.ResultStatusApp(row[4]), # result_status
|
|
411
|
+
result_name=row[2], # result_name
|
|
412
|
+
)
|
|
413
|
+
for row in result.data
|
|
414
|
+
]
|