mlrun 1.7.0rc39__py3-none-any.whl → 1.7.0rc41__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.

Files changed (44) hide show
  1. mlrun/common/constants.py +3 -0
  2. mlrun/common/helpers.py +0 -1
  3. mlrun/common/schemas/model_monitoring/model_endpoints.py +0 -1
  4. mlrun/config.py +1 -1
  5. mlrun/data_types/to_pandas.py +9 -9
  6. mlrun/datastore/alibaba_oss.py +1 -0
  7. mlrun/datastore/azure_blob.py +1 -6
  8. mlrun/datastore/base.py +12 -0
  9. mlrun/datastore/dbfs_store.py +1 -5
  10. mlrun/datastore/filestore.py +1 -3
  11. mlrun/datastore/google_cloud_storage.py +1 -9
  12. mlrun/datastore/redis.py +1 -0
  13. mlrun/datastore/s3.py +1 -0
  14. mlrun/datastore/storeytargets.py +147 -0
  15. mlrun/datastore/targets.py +67 -69
  16. mlrun/datastore/v3io.py +1 -0
  17. mlrun/model_monitoring/api.py +1 -2
  18. mlrun/model_monitoring/applications/_application_steps.py +25 -43
  19. mlrun/model_monitoring/applications/context.py +206 -70
  20. mlrun/model_monitoring/controller.py +0 -1
  21. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +17 -8
  22. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +14 -4
  23. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +11 -3
  24. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +35 -23
  25. mlrun/model_monitoring/helpers.py +38 -1
  26. mlrun/model_monitoring/stream_processing.py +8 -26
  27. mlrun/projects/project.py +17 -16
  28. mlrun/runtimes/nuclio/api_gateway.py +9 -0
  29. mlrun/runtimes/nuclio/application/application.py +131 -55
  30. mlrun/runtimes/nuclio/function.py +4 -10
  31. mlrun/runtimes/nuclio/serving.py +2 -2
  32. mlrun/runtimes/utils.py +16 -0
  33. mlrun/serving/routers.py +1 -1
  34. mlrun/serving/server.py +19 -5
  35. mlrun/serving/states.py +8 -0
  36. mlrun/serving/v2_serving.py +34 -26
  37. mlrun/utils/helpers.py +12 -2
  38. mlrun/utils/version/version.json +2 -2
  39. {mlrun-1.7.0rc39.dist-info → mlrun-1.7.0rc41.dist-info}/METADATA +2 -2
  40. {mlrun-1.7.0rc39.dist-info → mlrun-1.7.0rc41.dist-info}/RECORD +44 -43
  41. {mlrun-1.7.0rc39.dist-info → mlrun-1.7.0rc41.dist-info}/WHEEL +1 -1
  42. {mlrun-1.7.0rc39.dist-info → mlrun-1.7.0rc41.dist-info}/LICENSE +0 -0
  43. {mlrun-1.7.0rc39.dist-info → mlrun-1.7.0rc41.dist-info}/entry_points.txt +0 -0
  44. {mlrun-1.7.0rc39.dist-info → mlrun-1.7.0rc41.dist-info}/top_level.txt +0 -0
@@ -11,19 +11,16 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+
14
15
  import json
15
- import typing
16
- from typing import Optional
16
+ from typing import Any, Optional, Union
17
17
 
18
- import mlrun.common.helpers
19
- import mlrun.common.model_monitoring.helpers
20
18
  import mlrun.common.schemas.alert as alert_objects
21
19
  import mlrun.common.schemas.model_monitoring.constants as mm_constant
22
20
  import mlrun.datastore
23
- import mlrun.serving
24
- import mlrun.utils.helpers
25
- import mlrun.utils.v3io_clients
21
+ import mlrun.model_monitoring
26
22
  from mlrun.model_monitoring.helpers import get_stream_path
23
+ from mlrun.serving import GraphContext
27
24
  from mlrun.serving.utils import StepToDict
28
25
  from mlrun.utils import logger
29
26
 
@@ -62,7 +59,7 @@ class _PushToMonitoringWriter(StepToDict):
62
59
  self,
63
60
  event: tuple[
64
61
  list[
65
- typing.Union[
62
+ Union[
66
63
  ModelMonitoringApplicationResult, ModelMonitoringApplicationMetric
67
64
  ]
68
65
  ],
@@ -121,50 +118,35 @@ class _PushToMonitoringWriter(StepToDict):
121
118
 
122
119
 
123
120
  class _PrepareMonitoringEvent(StepToDict):
124
- def __init__(self, application_name: str):
121
+ def __init__(self, context: GraphContext, application_name: str) -> None:
125
122
  """
126
123
  Class for preparing the application event for the application step.
127
124
 
128
125
  :param application_name: Application name.
129
126
  """
127
+ self.graph_context = context
128
+ self.application_name = application_name
129
+ self.model_endpoints: dict[str, mlrun.model_monitoring.ModelEndpoint] = {}
130
130
 
131
- self.context = self._create_mlrun_context(application_name)
132
- self.model_endpoints = {}
133
-
134
- def do(self, event: dict[str, dict]) -> MonitoringApplicationContext:
131
+ def do(self, event: dict[str, Any]) -> MonitoringApplicationContext:
135
132
  """
136
133
  Prepare the application event for the application step.
137
134
 
138
135
  :param event: Application event.
139
- :return: Application event.
136
+ :return: Application context.
140
137
  """
141
- if not event.get("mlrun_context"):
142
- application_context = MonitoringApplicationContext().from_dict(
143
- event,
144
- context=self.context,
145
- model_endpoint_dict=self.model_endpoints,
146
- )
147
- else:
148
- application_context = MonitoringApplicationContext().from_dict(event)
138
+ application_context = MonitoringApplicationContext(
139
+ graph_context=self.graph_context,
140
+ application_name=self.application_name,
141
+ event=event,
142
+ model_endpoint_dict=self.model_endpoints,
143
+ )
144
+
149
145
  self.model_endpoints.setdefault(
150
146
  application_context.endpoint_id, application_context.model_endpoint
151
147
  )
152
- return application_context
153
148
 
154
- @staticmethod
155
- def _create_mlrun_context(app_name: str):
156
- artifact_path = mlrun.utils.helpers.template_artifact_path(
157
- mlrun.mlconf.artifact_path, mlrun.mlconf.default_project
158
- )
159
- context = mlrun.get_or_create_ctx(
160
- f"{app_name}-logger",
161
- spec={
162
- "metadata": {"labels": {"kind": mlrun.runtimes.RuntimeKinds.serving}},
163
- "spec": {mlrun.utils.helpers.RunKeys.output_path: artifact_path},
164
- },
165
- )
166
- context.__class__ = MonitoringApplicationContext
167
- return context
149
+ return application_context
168
150
 
169
151
 
170
152
  class _ApplicationErrorHandler(StepToDict):
@@ -181,13 +163,13 @@ class _ApplicationErrorHandler(StepToDict):
181
163
 
182
164
  logger.error(f"Error in application step: {event}")
183
165
 
184
- event_data = mlrun.common.schemas.Event(
166
+ event_data = alert_objects.Event(
185
167
  kind=alert_objects.EventKind.MM_APP_FAILED,
186
- entity={
187
- "kind": alert_objects.EventEntityKind.MODEL_MONITORING_APPLICATION,
188
- "project": self.project,
189
- "ids": [f"{self.project}_{event.body.application_name}"],
190
- },
168
+ entity=alert_objects.EventEntities(
169
+ kind=alert_objects.EventEntityKind.MODEL_MONITORING_APPLICATION,
170
+ project=self.project,
171
+ ids=[f"{self.project}_{event.body.application_name}"],
172
+ ),
191
173
  value_dict={
192
174
  "Error": event.error,
193
175
  "Timestamp": event.timestamp,
@@ -11,19 +11,22 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+
14
15
  import json
15
- import typing
16
+ import socket
17
+ from typing import Any, Optional, cast
16
18
 
17
19
  import numpy as np
18
20
  import pandas as pd
19
21
 
20
- import mlrun.common.helpers
21
- import mlrun.common.model_monitoring.helpers
22
+ import mlrun.common.constants as mlrun_constants
22
23
  import mlrun.common.schemas.model_monitoring.constants as mm_constants
23
24
  import mlrun.feature_store as fstore
24
- from mlrun.artifacts.model import ModelArtifact, get_model
25
+ import mlrun.features
26
+ import mlrun.serving
27
+ import mlrun.utils
28
+ from mlrun.artifacts import Artifact, DatasetArtifact, ModelArtifact, get_model
25
29
  from mlrun.common.model_monitoring.helpers import FeatureStats, pad_features_hist
26
- from mlrun.execution import MLClientCtx
27
30
  from mlrun.model_monitoring.helpers import (
28
31
  calculate_inputs_statistics,
29
32
  get_endpoint_record,
@@ -31,13 +34,17 @@ from mlrun.model_monitoring.helpers import (
31
34
  from mlrun.model_monitoring.model_endpoint import ModelEndpoint
32
35
 
33
36
 
34
- class MonitoringApplicationContext(MLClientCtx):
37
+ class MonitoringApplicationContext:
35
38
  """
36
39
  The monitoring context holds all the relevant information for the monitoring application,
37
40
  and also it can be used for logging artifacts and results.
38
41
  The monitoring context has the following attributes:
39
42
 
40
- :param application_name: (str) the app name
43
+ :param application_name: (str) The model monitoring application name.
44
+ :param project_name: (str) The project name.
45
+ :param project: (MlrunProject) The project object.
46
+ :param logger: (mlrun.utils.Logger) MLRun logger.
47
+ :param nuclio_logger: (nuclio.request.Logger) Nuclio logger.
41
48
  :param sample_df_stats: (FeatureStats) The new sample distribution dictionary.
42
49
  :param feature_stats: (FeatureStats) The train sample distribution dictionary.
43
50
  :param sample_df: (pd.DataFrame) The new sample DataFrame.
@@ -49,79 +56,89 @@ class MonitoringApplicationContext(MLClientCtx):
49
56
  :param model_endpoint: (ModelEndpoint) The model endpoint object.
50
57
  :param feature_names: (list[str]) List of models feature names.
51
58
  :param label_names: (list[str]) List of models label names.
52
- :param model: (tuple[str, ModelArtifact, dict]) The model file, model spec object, and list of
53
-
59
+ :param model: (tuple[str, ModelArtifact, dict]) The model file, model spec object,
60
+ and a list of extra data items.
54
61
  """
55
62
 
56
- def __init__(self, **kwargs):
57
- super().__init__(**kwargs)
58
-
59
- def _enrich_data(self):
60
- self.application_name: typing.Optional[str] = None
61
- self.start_infer_time: typing.Optional[pd.Timestamp] = None
62
- self.end_infer_time: typing.Optional[pd.Timestamp] = None
63
- self.latest_request: typing.Optional[pd.Timestamp] = None
64
- self.endpoint_id: typing.Optional[str] = None
65
- self.output_stream_uri: typing.Optional[str] = None
66
-
67
- self._sample_df: typing.Optional[pd.DataFrame] = None
68
- self._model_endpoint: typing.Optional[ModelEndpoint] = None
69
- self._feature_stats: typing.Optional[FeatureStats] = None
70
- self._sample_df_stats: typing.Optional[FeatureStats] = None
71
-
72
- @classmethod
73
- def from_dict(
74
- cls,
75
- attrs: dict,
76
- context=None,
77
- model_endpoint_dict=None,
78
- **kwargs,
79
- ) -> "MonitoringApplicationContext":
63
+ def __init__(
64
+ self,
65
+ *,
66
+ graph_context: mlrun.serving.GraphContext,
67
+ application_name: str,
68
+ event: dict[str, Any],
69
+ model_endpoint_dict: dict[str, ModelEndpoint],
70
+ ) -> None:
80
71
  """
81
- Create an instance of the MonitoringApplicationContext from a dictionary.
72
+ Initialize a `MonitoringApplicationContext` object.
73
+ Note: this object should not be instantiated manually.
82
74
 
83
- :param attrs: The instance data dictionary.
84
- :param context: The current application context.
75
+ :param application_name: The application name.
76
+ :param event: The instance data dictionary.
85
77
  :param model_endpoint_dict: Dictionary of model endpoints.
86
-
87
78
  """
79
+ self.application_name = application_name
88
80
 
89
- if not context:
90
- ctx = (
91
- super().from_dict(
92
- attrs=attrs.get(mm_constants.ApplicationEvent.MLRUN_CONTEXT, {}),
93
- **kwargs,
94
- ),
95
- )
96
- else:
97
- ctx = context
98
- cls._enrich_data(ctx)
81
+ self.project_name = graph_context.project
82
+ self.project = mlrun.load_project(url=self.project_name)
83
+
84
+ # MLRun Logger
85
+ self.logger = mlrun.utils.create_logger(
86
+ level=mlrun.mlconf.log_level,
87
+ formatter_kind=mlrun.mlconf.log_formatter,
88
+ name="monitoring-application",
89
+ )
90
+ # Nuclio logger - `nuclio.request.Logger`.
91
+ # Note: this logger does not accept keyword arguments.
92
+ self.nuclio_logger = graph_context.logger
99
93
 
100
- ctx.start_infer_time = pd.Timestamp(
101
- attrs.get(mm_constants.ApplicationEvent.START_INFER_TIME)
94
+ # event data
95
+ self.start_infer_time = pd.Timestamp(
96
+ cast(str, event.get(mm_constants.ApplicationEvent.START_INFER_TIME))
102
97
  )
103
- ctx.end_infer_time = pd.Timestamp(
104
- attrs.get(mm_constants.ApplicationEvent.END_INFER_TIME)
98
+ self.end_infer_time = pd.Timestamp(
99
+ cast(str, event.get(mm_constants.ApplicationEvent.END_INFER_TIME))
105
100
  )
106
- ctx.latest_request = pd.Timestamp(
107
- attrs.get(mm_constants.ApplicationEvent.LAST_REQUEST)
101
+ self.latest_request = pd.Timestamp(
102
+ cast(str, event.get(mm_constants.ApplicationEvent.LAST_REQUEST))
108
103
  )
109
- ctx.application_name = attrs.get(mm_constants.ApplicationEvent.APPLICATION_NAME)
110
- ctx._feature_stats = json.loads(
111
- attrs.get(mm_constants.ApplicationEvent.FEATURE_STATS, "{}")
104
+ self.endpoint_id = cast(
105
+ str, event.get(mm_constants.ApplicationEvent.ENDPOINT_ID)
112
106
  )
113
- ctx._sample_df_stats = json.loads(
114
- attrs.get(mm_constants.ApplicationEvent.CURRENT_STATS, "{}")
107
+ self.output_stream_uri = cast(
108
+ str, event.get(mm_constants.ApplicationEvent.OUTPUT_STREAM_URI)
115
109
  )
116
110
 
117
- ctx.endpoint_id = attrs.get(mm_constants.ApplicationEvent.ENDPOINT_ID)
118
- ctx._model_endpoint = model_endpoint_dict.get(ctx.endpoint_id)
111
+ self._feature_stats: Optional[FeatureStats] = json.loads(
112
+ event.get(mm_constants.ApplicationEvent.FEATURE_STATS, "{}")
113
+ )
114
+ self._sample_df_stats: Optional[FeatureStats] = json.loads(
115
+ event.get(mm_constants.ApplicationEvent.CURRENT_STATS, "{}")
116
+ )
119
117
 
120
- return ctx
118
+ # Default labels for the artifacts
119
+ self._default_labels = self._get_default_labels()
120
+
121
+ # Persistent data - fetched when needed
122
+ self._sample_df: Optional[pd.DataFrame] = None
123
+ self._model_endpoint: Optional[ModelEndpoint] = model_endpoint_dict.get(
124
+ self.endpoint_id
125
+ )
126
+
127
+ def _get_default_labels(self) -> dict[str, str]:
128
+ return {
129
+ mlrun_constants.MLRunInternalLabels.runner_pod: socket.gethostname(),
130
+ mlrun_constants.MLRunInternalLabels.producer_type: "model-monitoring-app",
131
+ mlrun_constants.MLRunInternalLabels.app_name: self.application_name,
132
+ mlrun_constants.MLRunInternalLabels.endpoint_id: self.endpoint_id,
133
+ }
134
+
135
+ def _add_default_labels(self, labels: Optional[dict[str, str]]) -> dict[str, str]:
136
+ """Add the default labels to logged artifacts labels"""
137
+ return (labels or {}) | self._default_labels
121
138
 
122
139
  @property
123
140
  def sample_df(self) -> pd.DataFrame:
124
- if not hasattr(self, "_sample_df") or self._sample_df is None:
141
+ if self._sample_df is None:
125
142
  feature_set = fstore.get_feature_set(
126
143
  self.model_endpoint.status.monitoring_feature_set_uri
127
144
  )
@@ -144,15 +161,15 @@ class MonitoringApplicationContext(MLClientCtx):
144
161
 
145
162
  @property
146
163
  def model_endpoint(self) -> ModelEndpoint:
147
- if not hasattr(self, "_model_endpoint") or not self._model_endpoint:
164
+ if not self._model_endpoint:
148
165
  self._model_endpoint = ModelEndpoint.from_flat_dict(
149
- get_endpoint_record(self.project, self.endpoint_id)
166
+ get_endpoint_record(self.project_name, self.endpoint_id)
150
167
  )
151
168
  return self._model_endpoint
152
169
 
153
170
  @property
154
171
  def feature_stats(self) -> FeatureStats:
155
- if not hasattr(self, "_feature_stats") or not self._feature_stats:
172
+ if not self._feature_stats:
156
173
  self._feature_stats = json.loads(self.model_endpoint.status.feature_stats)
157
174
  pad_features_hist(self._feature_stats)
158
175
  return self._feature_stats
@@ -160,7 +177,7 @@ class MonitoringApplicationContext(MLClientCtx):
160
177
  @property
161
178
  def sample_df_stats(self) -> FeatureStats:
162
179
  """statistics of the sample dataframe"""
163
- if not hasattr(self, "_sample_df_stats") or not self._sample_df_stats:
180
+ if not self._sample_df_stats:
164
181
  self._sample_df_stats = calculate_inputs_statistics(
165
182
  self.feature_stats, self.sample_df
166
183
  )
@@ -184,13 +201,11 @@ class MonitoringApplicationContext(MLClientCtx):
184
201
 
185
202
  @property
186
203
  def model(self) -> tuple[str, ModelArtifact, dict]:
187
- """return model file, model spec object, and list of extra data items"""
204
+ """The model file, model spec object, and a list of extra data items"""
188
205
  return get_model(self.model_endpoint.spec.model_uri)
189
206
 
190
207
  @staticmethod
191
- def dict_to_histogram(
192
- histogram_dict: mlrun.common.model_monitoring.helpers.FeatureStats,
193
- ) -> pd.DataFrame:
208
+ def dict_to_histogram(histogram_dict: FeatureStats) -> pd.DataFrame:
194
209
  """
195
210
  Convert histogram dictionary to pandas DataFrame with feature histograms as columns
196
211
 
@@ -210,3 +225,124 @@ class MonitoringApplicationContext(MLClientCtx):
210
225
  histograms = pd.DataFrame(histograms)
211
226
 
212
227
  return histograms
228
+
229
+ def log_artifact(
230
+ self,
231
+ item,
232
+ body=None,
233
+ tag: str = "",
234
+ local_path: str = "",
235
+ artifact_path: Optional[str] = None,
236
+ format: Optional[str] = None,
237
+ upload: Optional[bool] = None,
238
+ labels: Optional[dict[str, str]] = None,
239
+ target_path: Optional[str] = None,
240
+ **kwargs,
241
+ ) -> Artifact:
242
+ """
243
+ Log an artifact.
244
+ See :func:`~mlrun.projects.MlrunProject.log_artifact` for the documentation.
245
+ """
246
+ labels = self._add_default_labels(labels)
247
+ return self.project.log_artifact(
248
+ item,
249
+ body=body,
250
+ tag=tag,
251
+ local_path=local_path,
252
+ artifact_path=artifact_path,
253
+ format=format,
254
+ upload=upload,
255
+ labels=labels,
256
+ target_path=target_path,
257
+ **kwargs,
258
+ )
259
+
260
+ def log_dataset(
261
+ self,
262
+ key,
263
+ df,
264
+ tag="",
265
+ local_path=None,
266
+ artifact_path=None,
267
+ upload=None,
268
+ labels=None,
269
+ format="",
270
+ preview=None,
271
+ stats=None,
272
+ target_path="",
273
+ extra_data=None,
274
+ label_column: Optional[str] = None,
275
+ **kwargs,
276
+ ) -> DatasetArtifact:
277
+ """
278
+ Log a dataset artifact.
279
+ See :func:`~mlrun.projects.MlrunProject.log_dataset` for the documentation.
280
+ """
281
+ labels = self._add_default_labels(labels)
282
+ return self.project.log_dataset(
283
+ key,
284
+ df,
285
+ tag=tag,
286
+ local_path=local_path,
287
+ artifact_path=artifact_path,
288
+ upload=upload,
289
+ labels=labels,
290
+ format=format,
291
+ preview=preview,
292
+ stats=stats,
293
+ target_path=target_path,
294
+ extra_data=extra_data,
295
+ label_column=label_column,
296
+ **kwargs,
297
+ )
298
+
299
+ def log_model(
300
+ self,
301
+ key,
302
+ body=None,
303
+ framework="",
304
+ tag="",
305
+ model_dir=None,
306
+ model_file=None,
307
+ algorithm=None,
308
+ metrics=None,
309
+ parameters=None,
310
+ artifact_path=None,
311
+ upload=None,
312
+ labels=None,
313
+ inputs: Optional[list[mlrun.features.Feature]] = None,
314
+ outputs: Optional[list[mlrun.features.Feature]] = None,
315
+ feature_vector: Optional[str] = None,
316
+ feature_weights: Optional[list] = None,
317
+ training_set=None,
318
+ label_column=None,
319
+ extra_data=None,
320
+ **kwargs,
321
+ ) -> ModelArtifact:
322
+ """
323
+ Log a model artifact.
324
+ See :func:`~mlrun.projects.MlrunProject.log_model` for the documentation.
325
+ """
326
+ labels = self._add_default_labels(labels)
327
+ return self.project.log_model(
328
+ key,
329
+ body=body,
330
+ framework=framework,
331
+ tag=tag,
332
+ model_dir=model_dir,
333
+ model_file=model_file,
334
+ algorithm=algorithm,
335
+ metrics=metrics,
336
+ parameters=parameters,
337
+ artifact_path=artifact_path,
338
+ upload=upload,
339
+ labels=labels,
340
+ inputs=inputs,
341
+ outputs=outputs,
342
+ feature_vector=feature_vector,
343
+ feature_weights=feature_weights,
344
+ training_set=training_set,
345
+ label_column=label_column,
346
+ extra_data=extra_data,
347
+ **kwargs,
348
+ )
@@ -596,7 +596,6 @@ class MonitoringApplicationController:
596
596
  project=project,
597
597
  function_name=mm_constants.MonitoringFunctionNames.WRITER,
598
598
  ),
599
- mm_constants.ApplicationEvent.MLRUN_CONTEXT: {}, # TODO : for future use by ad-hoc batch infer
600
599
  }
601
600
  for app_name in applications_names:
602
601
  data.update({mm_constants.ApplicationEvent.APPLICATION_NAME: app_name})
@@ -20,7 +20,7 @@ import pandas as pd
20
20
  import sqlalchemy
21
21
  import sqlalchemy.exc
22
22
  import sqlalchemy.orm
23
- from sqlalchemy.engine import make_url
23
+ from sqlalchemy.engine import Engine, make_url
24
24
  from sqlalchemy.sql.elements import BinaryExpression
25
25
 
26
26
  import mlrun.common.model_monitoring.helpers
@@ -61,9 +61,15 @@ class SQLStoreBase(StoreBase):
61
61
  )
62
62
 
63
63
  self._sql_connection_string = kwargs.get("store_connection_string")
64
- self._engine = get_engine(dsn=self._sql_connection_string)
64
+ self._engine = None
65
65
  self._init_tables()
66
66
 
67
+ @property
68
+ def engine(self) -> Engine:
69
+ if not self._engine:
70
+ self._engine = get_engine(dsn=self._sql_connection_string)
71
+ return self._engine
72
+
67
73
  def create_tables(self):
68
74
  self._create_tables_if_not_exist()
69
75
 
@@ -116,7 +122,7 @@ class SQLStoreBase(StoreBase):
116
122
  :param table_name: Target table name.
117
123
  :param event: Event dictionary that will be written into the DB.
118
124
  """
119
- with self._engine.connect() as connection:
125
+ with self.engine.connect() as connection:
120
126
  # Convert the result into a pandas Dataframe and write it into the database
121
127
  event_df = pd.DataFrame([event])
122
128
  event_df.to_sql(table_name, con=connection, index=False, if_exists="append")
@@ -177,7 +183,7 @@ class SQLStoreBase(StoreBase):
177
183
  param table: SQLAlchemy declarative table.
178
184
  :param criteria: A list of binary expressions that filter the query.
179
185
  """
180
- if not self._engine.has_table(table.__tablename__):
186
+ if not self.engine.has_table(table.__tablename__):
181
187
  logger.debug(
182
188
  f"Table {table.__tablename__} does not exist in the database. Skipping deletion."
183
189
  )
@@ -524,9 +530,9 @@ class SQLStoreBase(StoreBase):
524
530
  for table in self._tables:
525
531
  # Create table if not exist. The `metadata` contains the `ModelEndpointsTable`
526
532
  db_name = make_url(self._sql_connection_string).database
527
- if not self._engine.has_table(table):
533
+ if not self.engine.has_table(table):
528
534
  logger.info(f"Creating table {table} on {db_name} db.")
529
- self._tables[table].metadata.create_all(bind=self._engine)
535
+ self._tables[table].metadata.create_all(bind=self.engine)
530
536
  else:
531
537
  logger.info(f"Table {table} already exists on {db_name} db.")
532
538
 
@@ -574,8 +580,11 @@ class SQLStoreBase(StoreBase):
574
580
  """
575
581
  Delete all the model monitoring resources of the project in the SQL tables.
576
582
  """
583
+ logger.debug(
584
+ "Deleting model monitoring endpoints resources from the SQL tables",
585
+ project=self.project,
586
+ )
577
587
  endpoints = self.list_model_endpoints()
578
- logger.debug("Deleting model monitoring resources", project=self.project)
579
588
 
580
589
  for endpoint_dict in endpoints:
581
590
  endpoint_id = endpoint_dict[mm_schemas.EventFieldType.UID]
@@ -612,7 +621,7 @@ class SQLStoreBase(StoreBase):
612
621
 
613
622
  # Note: the block below does not use self._get, as we need here all the
614
623
  # results, not only `one_or_none`.
615
- with sqlalchemy.orm.Session(self._engine) as session:
624
+ with sqlalchemy.orm.Session(self.engine) as session:
616
625
  metric_rows = (
617
626
  session.query(table) # pyright: ignore[reportOptionalCall]
618
627
  .filter(table.endpoint_id == endpoint_id)
@@ -20,6 +20,7 @@ from http import HTTPStatus
20
20
  import v3io.dataplane
21
21
  import v3io.dataplane.output
22
22
  import v3io.dataplane.response
23
+ from v3io.dataplane import Client as V3IOClient
23
24
 
24
25
  import mlrun.common.model_monitoring.helpers
25
26
  import mlrun.common.schemas.model_monitoring as mm_schemas
@@ -100,13 +101,18 @@ class KVStoreBase(StoreBase):
100
101
  project: str,
101
102
  ) -> None:
102
103
  super().__init__(project=project)
103
- # Initialize a V3IO client instance
104
- self.client = mlrun.utils.v3io_clients.get_v3io_client(
105
- endpoint=mlrun.mlconf.v3io_api,
106
- )
104
+ self._client = None
107
105
  # Get the KV table path and container
108
106
  self.path, self.container = self._get_path_and_container()
109
107
 
108
+ @property
109
+ def client(self) -> V3IOClient:
110
+ if not self._client:
111
+ self._client = mlrun.utils.v3io_clients.get_v3io_client(
112
+ endpoint=mlrun.mlconf.v3io_api,
113
+ )
114
+ return self._client
115
+
110
116
  def write_model_endpoint(self, endpoint: dict[str, typing.Any]):
111
117
  """
112
118
  Create a new endpoint record in the KV table.
@@ -285,6 +291,10 @@ class KVStoreBase(StoreBase):
285
291
  """
286
292
  Delete all model endpoints resources in V3IO KV.
287
293
  """
294
+ logger.debug(
295
+ "Deleting model monitoring endpoints resources in V3IO KV",
296
+ project=self.project,
297
+ )
288
298
 
289
299
  endpoints = self.list_model_endpoints()
290
300
 
@@ -47,10 +47,17 @@ class TDEngineConnector(TSDBConnector):
47
47
  )
48
48
  self._tdengine_connection_string = kwargs.get("connection_string")
49
49
  self.database = database
50
- self._connection = self._create_connection()
50
+
51
+ self._connection = None
51
52
  self._init_super_tables()
52
53
 
53
- def _create_connection(self):
54
+ @property
55
+ def connection(self) -> taosws.Connection:
56
+ if not self._connection:
57
+ self._connection = self._create_connection()
58
+ return self._connection
59
+
60
+ def _create_connection(self) -> taosws.Connection:
54
61
  """Establish a connection to the TSDB server."""
55
62
  conn = taosws.connect(self._tdengine_connection_string)
56
63
  try:
@@ -176,7 +183,8 @@ class TDEngineConnector(TSDBConnector):
176
183
  mm_schemas.EventFieldType.PROJECT,
177
184
  mm_schemas.EventFieldType.ENDPOINT_ID,
178
185
  ],
179
- max_events=10,
186
+ max_events=1000,
187
+ flush_after_seconds=30,
180
188
  )
181
189
 
182
190
  apply_process_before_tsdb()