deltacat 1.1.1__py3-none-any.whl → 1.1.2__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.
deltacat/__init__.py CHANGED
@@ -44,7 +44,7 @@ from deltacat.types.tables import TableWriteMode
44
44
 
45
45
  deltacat.logs.configure_deltacat_logger(logging.getLogger(__name__))
46
46
 
47
- __version__ = "1.1.1"
47
+ __version__ = "1.1.2"
48
48
 
49
49
 
50
50
  __all__ = [
deltacat/aws/clients.py CHANGED
@@ -4,6 +4,7 @@ from typing import Optional
4
4
  from http import HTTPStatus
5
5
 
6
6
  import boto3
7
+ from botocore.exceptions import CredentialRetrievalError
7
8
  from boto3.exceptions import ResourceNotExistsError
8
9
  from boto3.resources.base import ServiceResource
9
10
  from botocore.client import BaseClient
@@ -15,6 +16,8 @@ from tenacity import (
15
16
  wait_fixed,
16
17
  retry_if_exception,
17
18
  stop_after_delay,
19
+ retry_if_exception_type,
20
+ wait_random_exponential,
18
21
  )
19
22
 
20
23
  from deltacat import logs
@@ -37,6 +40,13 @@ RETRYABLE_HTTP_STATUS_CODES = [
37
40
  HTTPStatus.GATEWAY_TIMEOUT,
38
41
  ]
39
42
 
43
+ boto_retry_wrapper = Retrying(
44
+ wait=wait_random_exponential(multiplier=1, max=10),
45
+ stop=stop_after_delay(60 * 5),
46
+ # CredentialRetrievalError can still be thrown due to throttling, even if IMDS health checks succeed.
47
+ retry=retry_if_exception_type(CredentialRetrievalError),
48
+ )
49
+
40
50
 
41
51
  class RetryIfRetryableHTTPStatusCode(retry_if_exception):
42
52
  """
@@ -183,10 +193,10 @@ def _client(name: str, region: Optional[str], **kwargs) -> BaseClient:
183
193
  def resource_cache(name: str, region: Optional[str], **kwargs) -> ServiceResource:
184
194
  # we don't use the @lru_cache decorator because Ray can't pickle it
185
195
  cached_function = lru_cache()(_resource)
186
- return cached_function(name, region, **kwargs)
196
+ return boto_retry_wrapper(cached_function, name, region, **kwargs)
187
197
 
188
198
 
189
199
  def client_cache(name: str, region: Optional[str], **kwargs) -> BaseClient:
190
200
  # we don't use the @lru_cache decorator because Ray can't pickle it
191
201
  cached_function = lru_cache()(_client)
192
- return cached_function(name, region, **kwargs)
202
+ return boto_retry_wrapper(cached_function, name, region, **kwargs)
deltacat/aws/constants.py CHANGED
@@ -3,6 +3,10 @@ from typing import List
3
3
  from deltacat.utils.common import env_integer, env_string
4
4
 
5
5
  DAFT_MAX_S3_CONNECTIONS_PER_FILE = env_integer("DAFT_MAX_S3_CONNECTIONS_PER_FILE", 8)
6
- BOTO_MAX_RETRIES = env_integer("BOTO_MAX_RETRIES", 15)
6
+ BOTO_MAX_RETRIES = env_integer("BOTO_MAX_RETRIES", 5)
7
7
  TIMEOUT_ERROR_CODES: List[str] = ["ReadTimeoutError", "ConnectTimeoutError"]
8
8
  AWS_REGION = env_string("AWS_REGION", "us-east-1")
9
+
10
+ # Metric Names
11
+ DOWNLOAD_MANIFEST_ENTRY_METRIC_PREFIX = "download_manifest_entry"
12
+ UPLOAD_SLICED_TABLE_METRIC_PREFIX = "upload_sliced_table"
deltacat/aws/s3u.py CHANGED
@@ -25,7 +25,11 @@ from tenacity import (
25
25
  from deltacat.utils.ray_utils.concurrency import invoke_parallel
26
26
  import deltacat.aws.clients as aws_utils
27
27
  from deltacat import logs
28
- from deltacat.aws.constants import TIMEOUT_ERROR_CODES
28
+ from deltacat.aws.constants import (
29
+ TIMEOUT_ERROR_CODES,
30
+ DOWNLOAD_MANIFEST_ENTRY_METRIC_PREFIX,
31
+ UPLOAD_SLICED_TABLE_METRIC_PREFIX,
32
+ )
29
33
  from deltacat.exceptions import NonRetryableError, RetryableError
30
34
  from deltacat.storage import (
31
35
  DistributedDataset,
@@ -50,6 +54,7 @@ from deltacat.types.tables import (
50
54
  )
51
55
  from deltacat.types.partial_download import PartialFileDownloadParams
52
56
  from deltacat.utils.common import ReadKwargsProvider
57
+ from deltacat.utils.metrics import metrics
53
58
 
54
59
  logger = logs.configure_deltacat_logger(logging.getLogger(__name__))
55
60
 
@@ -238,6 +243,7 @@ def read_file(
238
243
  raise e
239
244
 
240
245
 
246
+ @metrics(prefix=UPLOAD_SLICED_TABLE_METRIC_PREFIX)
241
247
  def upload_sliced_table(
242
248
  table: Union[LocalTable, DistributedDataset],
243
249
  s3_url_prefix: str,
@@ -346,6 +352,7 @@ def upload_table(
346
352
  return manifest_entries
347
353
 
348
354
 
355
+ @metrics(prefix=DOWNLOAD_MANIFEST_ENTRY_METRIC_PREFIX)
349
356
  def download_manifest_entry(
350
357
  manifest_entry: ManifestEntry,
351
358
  token_holder: Optional[Dict[str, Any]] = None,
@@ -65,6 +65,7 @@ from deltacat.compute.compactor_v2.utils.task_options import (
65
65
  local_merge_resource_options_provider,
66
66
  )
67
67
  from deltacat.compute.compactor.model.compactor_version import CompactorVersion
68
+ from deltacat.utils.metrics import MetricsActor, METRICS_CONFIG_ACTOR_NAME
68
69
 
69
70
  if importlib.util.find_spec("memray"):
70
71
  import memray
@@ -118,6 +119,15 @@ def _execute_compaction(
118
119
  params: CompactPartitionParams, **kwargs
119
120
  ) -> Tuple[Optional[Partition], Optional[RoundCompletionInfo], Optional[str]]:
120
121
 
122
+ if params.metrics_config:
123
+ logger.info(
124
+ f"Setting metrics config with target: {params.metrics_config.metrics_target}"
125
+ )
126
+ metrics_actor = MetricsActor.options(
127
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
128
+ ).remote()
129
+ ray.get(metrics_actor.set_metrics_config.remote(params.metrics_config))
130
+
121
131
  rcf_source_partition_locator = (
122
132
  params.rebase_source_partition_locator or params.source_partition_locator
123
133
  )
@@ -40,3 +40,31 @@ DROP_DUPLICATES = True
40
40
  # This is the observed upper bound inflation for parquet
41
41
  # size in metadata to pyarrow table size.
42
42
  PARQUET_TO_PYARROW_INFLATION = 4
43
+
44
+ # Metric Names
45
+ # Time taken for a hash bucket task
46
+ HASH_BUCKET_TIME_IN_SECONDS = "hash_bucket_time"
47
+
48
+ # Hash bucket success count
49
+ HASH_BUCKET_SUCCESS_COUNT = "hash_bucket_success_count"
50
+
51
+ # Hash bucket failure count
52
+ HASH_BUCKET_FAILURE_COUNT = "hash_bucket_failure_count"
53
+
54
+ # Time taken for a merge task
55
+ MERGE_TIME_IN_SECONDS = "merge_time"
56
+
57
+ # Merge success count
58
+ MERGE_SUCCESS_COUNT = "merge_success_count"
59
+
60
+ # Merge failure count
61
+ MERGE_FAILURE_COUNT = "merge_failure_count"
62
+
63
+ # Metric prefix for discover deltas
64
+ DISCOVER_DELTAS_METRIC_PREFIX = "discover_deltas"
65
+
66
+ # Metric prefix for prepare deletes
67
+ PREPARE_DELETES_METRIC_PREFIX = "prepare_deletes"
68
+
69
+ # Metric prefix for materialize
70
+ MATERIALIZE_METRIC_PREFIX = "delta_materialize"
@@ -23,6 +23,8 @@ from deltacat.storage import (
23
23
  Delta,
24
24
  )
25
25
  from deltacat import logs
26
+ from deltacat.utils.metrics import metrics
27
+ from deltacat.compute.compactor_v2.constants import PREPARE_DELETES_METRIC_PREFIX
26
28
 
27
29
 
28
30
  logger = logs.configure_deltacat_logger(logging.getLogger(__name__))
@@ -115,6 +117,7 @@ def _get_delete_file_envelopes(
115
117
  return delete_file_envelopes
116
118
 
117
119
 
120
+ @metrics(prefix=PREPARE_DELETES_METRIC_PREFIX)
118
121
  def prepare_deletes(
119
122
  params: CompactPartitionParams,
120
123
  input_deltas: List[Delta],
@@ -25,12 +25,17 @@ from deltacat.utils.ray_utils.runtime import (
25
25
  )
26
26
  from deltacat.utils.common import ReadKwargsProvider
27
27
  from deltacat.utils.performance import timed_invocation
28
- from deltacat.utils.metrics import emit_timer_metrics
28
+ from deltacat.utils.metrics import emit_timer_metrics, failure_metric, success_metric
29
29
  from deltacat.utils.resources import (
30
30
  get_current_process_peak_memory_usage_in_bytes,
31
31
  ProcessUtilizationOverTimeRange,
32
32
  )
33
33
  from deltacat.constants import BYTES_PER_GIBIBYTE
34
+ from deltacat.compute.compactor_v2.constants import (
35
+ HASH_BUCKET_TIME_IN_SECONDS,
36
+ HASH_BUCKET_FAILURE_COUNT,
37
+ HASH_BUCKET_SUCCESS_COUNT,
38
+ )
34
39
 
35
40
  if importlib.util.find_spec("memray"):
36
41
  import memray
@@ -91,6 +96,8 @@ def _group_file_records_by_pk_hash_bucket(
91
96
  return hb_to_delta_file_envelopes, total_record_count, total_size_bytes
92
97
 
93
98
 
99
+ @success_metric(name=HASH_BUCKET_SUCCESS_COUNT)
100
+ @failure_metric(name=HASH_BUCKET_FAILURE_COUNT)
94
101
  def _timed_hash_bucket(input: HashBucketInput):
95
102
  task_id = get_current_ray_task_id()
96
103
  worker_id = get_current_ray_worker_id()
@@ -153,7 +160,7 @@ def hash_bucket(input: HashBucketInput) -> HashBucketResult:
153
160
  if input.metrics_config:
154
161
  emit_result, latency = timed_invocation(
155
162
  func=emit_timer_metrics,
156
- metrics_name="hash_bucket",
163
+ metrics_name=HASH_BUCKET_TIME_IN_SECONDS,
157
164
  value=duration,
158
165
  metrics_config=input.metrics_config,
159
166
  )
@@ -24,7 +24,7 @@ from deltacat.utils.ray_utils.runtime import (
24
24
  )
25
25
  from deltacat.compute.compactor.utils import system_columns as sc
26
26
  from deltacat.utils.performance import timed_invocation
27
- from deltacat.utils.metrics import emit_timer_metrics
27
+ from deltacat.utils.metrics import emit_timer_metrics, failure_metric, success_metric
28
28
  from deltacat.utils.resources import (
29
29
  get_current_process_peak_memory_usage_in_bytes,
30
30
  ProcessUtilizationOverTimeRange,
@@ -42,6 +42,11 @@ from deltacat.storage import (
42
42
  )
43
43
  from deltacat.compute.compactor_v2.utils.dedupe import drop_duplicates
44
44
  from deltacat.constants import BYTES_PER_GIBIBYTE
45
+ from deltacat.compute.compactor_v2.constants import (
46
+ MERGE_TIME_IN_SECONDS,
47
+ MERGE_SUCCESS_COUNT,
48
+ MERGE_FAILURE_COUNT,
49
+ )
45
50
 
46
51
 
47
52
  if importlib.util.find_spec("memray"):
@@ -479,6 +484,8 @@ def _copy_manifests_from_hash_bucketing(
479
484
  return materialized_results
480
485
 
481
486
 
487
+ @success_metric(name=MERGE_SUCCESS_COUNT)
488
+ @failure_metric(name=MERGE_FAILURE_COUNT)
482
489
  def _timed_merge(input: MergeInput) -> MergeResult:
483
490
  task_id = get_current_ray_task_id()
484
491
  worker_id = get_current_ray_worker_id()
@@ -578,7 +585,7 @@ def merge(input: MergeInput) -> MergeResult:
578
585
  if input.metrics_config:
579
586
  emit_result, latency = timed_invocation(
580
587
  func=emit_timer_metrics,
581
- metrics_name="merge",
588
+ metrics_name=MERGE_TIME_IN_SECONDS,
582
589
  value=duration,
583
590
  metrics_config=input.metrics_config,
584
591
  )
@@ -23,10 +23,13 @@ from deltacat.compute.compactor_v2.utils.task_options import (
23
23
  from deltacat.compute.compactor_v2.utils.content_type_params import (
24
24
  append_content_type_params,
25
25
  )
26
+ from deltacat.utils.metrics import metrics
27
+ from deltacat.compute.compactor_v2.constants import DISCOVER_DELTAS_METRIC_PREFIX
26
28
 
27
29
  logger = logs.configure_deltacat_logger(logging.getLogger(__name__))
28
30
 
29
31
 
32
+ @metrics(prefix=DISCOVER_DELTAS_METRIC_PREFIX)
30
33
  def discover_deltas(
31
34
  source_partition_locator: PartitionLocator,
32
35
  last_stream_position_to_compact: int,
@@ -31,11 +31,14 @@ from deltacat.compute.compactor_v2.deletes.delete_strategy import (
31
31
  from deltacat.compute.compactor_v2.deletes.delete_file_envelope import (
32
32
  DeleteFileEnvelope,
33
33
  )
34
+ from deltacat.utils.metrics import metrics
35
+ from deltacat.compute.compactor_v2.constants import MATERIALIZE_METRIC_PREFIX
34
36
 
35
37
 
36
38
  logger = logs.configure_deltacat_logger(logging.getLogger(__name__))
37
39
 
38
40
 
41
+ @metrics(prefix=MATERIALIZE_METRIC_PREFIX)
39
42
  def materialize(
40
43
  input: MergeInput,
41
44
  task_index: int,
@@ -0,0 +1,575 @@
1
+ import unittest
2
+ from unittest.mock import MagicMock, ANY, call
3
+ import ray
4
+ from deltacat.utils.metrics import (
5
+ metrics,
6
+ success_metric,
7
+ failure_metric,
8
+ latency_metric,
9
+ MetricsActor,
10
+ METRICS_CONFIG_ACTOR_NAME,
11
+ MetricsConfig,
12
+ MetricsTarget,
13
+ METRICS_TARGET_TO_EMITTER_DICT,
14
+ )
15
+ from deltacat.utils.performance import timed_invocation
16
+
17
+
18
+ def method_not_annotated(mock_func):
19
+ mock_func("called")
20
+
21
+
22
+ @metrics
23
+ def metrics_annotated_method(mock_func):
24
+ mock_func("called")
25
+
26
+
27
+ @metrics
28
+ def metrics_annotated_method_error(mock_func):
29
+ raise ValueError()
30
+
31
+
32
+ @metrics(prefix="test_prefix")
33
+ def metrics_with_prefix_annotated_method(mock_func):
34
+ mock_func("called")
35
+
36
+
37
+ @metrics(prefix="test_prefix")
38
+ def metrics_with_prefix_annotated_method_error(mock_func):
39
+ raise ValueError()
40
+
41
+
42
+ class TestMetricsAnnotation(unittest.TestCase):
43
+ def test_metrics_annotation_sanity(self):
44
+ mock, mock_target = MagicMock(), MagicMock()
45
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
46
+ metrics_actor = MetricsActor.options(
47
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
48
+ ).remote()
49
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
50
+ ray.get(metrics_actor.set_metrics_config.remote(config))
51
+
52
+ # action
53
+ metrics_annotated_method(mock)
54
+
55
+ mock.assert_called_once()
56
+ self.assertEqual(2, mock_target.call_count)
57
+ mock_target.assert_has_calls(
58
+ [
59
+ call(
60
+ metrics_name="metrics_annotated_method_time",
61
+ metrics_config=ANY,
62
+ value=ANY,
63
+ ),
64
+ call(
65
+ metrics_name="metrics_annotated_method_success_count",
66
+ metrics_config=ANY,
67
+ value=ANY,
68
+ ),
69
+ ],
70
+ any_order=True,
71
+ )
72
+
73
+ def test_metrics_annotation_when_error(self):
74
+ mock, mock_target = MagicMock(), MagicMock()
75
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
76
+ metrics_actor = MetricsActor.options(
77
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
78
+ ).remote()
79
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
80
+ ray.get(metrics_actor.set_metrics_config.remote(config))
81
+
82
+ # action
83
+ self.assertRaises(ValueError, lambda: metrics_annotated_method_error(mock))
84
+
85
+ mock.assert_not_called()
86
+ self.assertEqual(3, mock_target.call_count)
87
+ mock_target.assert_has_calls(
88
+ [
89
+ call(
90
+ metrics_name="metrics_annotated_method_error_time",
91
+ metrics_config=ANY,
92
+ value=ANY,
93
+ ),
94
+ call(
95
+ metrics_name="metrics_annotated_method_error_failure_count",
96
+ metrics_config=ANY,
97
+ value=ANY,
98
+ ),
99
+ call(
100
+ metrics_name="metrics_annotated_method_error_failure_count.ValueError",
101
+ metrics_config=ANY,
102
+ value=ANY,
103
+ ),
104
+ ],
105
+ any_order=True,
106
+ )
107
+
108
+ def test_metrics_with_prefix_annotation_sanity(self):
109
+ mock, mock_target = MagicMock(), MagicMock()
110
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
111
+ metrics_actor = MetricsActor.options(
112
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
113
+ ).remote()
114
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
115
+ ray.get(metrics_actor.set_metrics_config.remote(config))
116
+
117
+ # action
118
+ metrics_with_prefix_annotated_method(mock)
119
+
120
+ mock.assert_called_once()
121
+ self.assertEqual(2, mock_target.call_count)
122
+ mock_target.assert_has_calls(
123
+ [
124
+ call(metrics_name="test_prefix_time", metrics_config=ANY, value=ANY),
125
+ call(
126
+ metrics_name="test_prefix_success_count",
127
+ metrics_config=ANY,
128
+ value=ANY,
129
+ ),
130
+ ],
131
+ any_order=True,
132
+ )
133
+
134
+ def test_metrics_annotation_performance(self):
135
+ mock, mock_target = MagicMock(), MagicMock()
136
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
137
+ metrics_actor = MetricsActor.options(
138
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
139
+ ).remote()
140
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
141
+ ray.get(metrics_actor.set_metrics_config.remote(config))
142
+
143
+ # action with annotation
144
+ _, actual_latency = timed_invocation(metrics_annotated_method, mock)
145
+ _, second_call_latency = timed_invocation(metrics_annotated_method, mock)
146
+
147
+ mock.assert_called()
148
+ self.assertEqual(4, mock_target.call_count)
149
+ self.assertLess(
150
+ second_call_latency,
151
+ actual_latency,
152
+ "Second call to actor must be much faster",
153
+ )
154
+
155
+ def test_metrics_with_prefix_annotation_when_error(self):
156
+ mock, mock_target = MagicMock(), MagicMock()
157
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
158
+ metrics_actor = MetricsActor.options(
159
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
160
+ ).remote()
161
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
162
+ ray.get(metrics_actor.set_metrics_config.remote(config))
163
+
164
+ # action
165
+ self.assertRaises(
166
+ ValueError, lambda: metrics_with_prefix_annotated_method_error(mock)
167
+ )
168
+
169
+ mock.assert_not_called()
170
+ self.assertEqual(3, mock_target.call_count)
171
+ mock_target.assert_has_calls(
172
+ [
173
+ call(metrics_name="test_prefix_time", metrics_config=ANY, value=ANY),
174
+ call(
175
+ metrics_name="test_prefix_failure_count",
176
+ metrics_config=ANY,
177
+ value=ANY,
178
+ ),
179
+ call(
180
+ metrics_name="test_prefix_failure_count.ValueError",
181
+ metrics_config=ANY,
182
+ value=ANY,
183
+ ),
184
+ ],
185
+ any_order=True,
186
+ )
187
+
188
+ def test_metrics_with_prefix_annotation_without_actor(self):
189
+ mock, mock_target = MagicMock(), MagicMock()
190
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
191
+ metrics_actor = MetricsActor.options(
192
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
193
+ ).remote()
194
+
195
+ # explicitly making sure actor is killed
196
+ ray.kill(metrics_actor)
197
+
198
+ # action
199
+ self.assertRaises(
200
+ ValueError, lambda: metrics_with_prefix_annotated_method_error(mock)
201
+ )
202
+
203
+ mock.assert_not_called()
204
+ mock_target.assert_not_called()
205
+
206
+
207
+ @latency_metric
208
+ def latency_metric_annotated_method(mock_func):
209
+ mock_func("called")
210
+
211
+
212
+ @latency_metric
213
+ def latency_metric_annotated_method_error(mock_func):
214
+ raise ValueError()
215
+
216
+
217
+ @latency_metric(name="test")
218
+ def latency_metric_with_name_annotated_method(mock_func):
219
+ mock_func("called")
220
+
221
+
222
+ @latency_metric(name="test")
223
+ def latency_metric_with_name_annotated_method_error(mock_func):
224
+ raise ValueError()
225
+
226
+
227
+ class TestLatencyMetricAnnotation(unittest.TestCase):
228
+ def test_annotation_sanity(self):
229
+ mock, mock_target = MagicMock(), MagicMock()
230
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
231
+ metrics_actor = MetricsActor.options(
232
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
233
+ ).remote()
234
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
235
+ ray.get(metrics_actor.set_metrics_config.remote(config))
236
+
237
+ # action
238
+ latency_metric_annotated_method(mock)
239
+
240
+ mock.assert_called_once()
241
+ self.assertEqual(1, mock_target.call_count)
242
+ mock_target.assert_has_calls(
243
+ [
244
+ call(
245
+ metrics_name="latency_metric_annotated_method_time",
246
+ metrics_config=ANY,
247
+ value=ANY,
248
+ )
249
+ ],
250
+ any_order=True,
251
+ )
252
+
253
+ def test_annotation_when_error(self):
254
+ mock, mock_target = MagicMock(), MagicMock()
255
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
256
+ metrics_actor = MetricsActor.options(
257
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
258
+ ).remote()
259
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
260
+ ray.get(metrics_actor.set_metrics_config.remote(config))
261
+
262
+ # action
263
+ self.assertRaises(
264
+ ValueError, lambda: latency_metric_annotated_method_error(mock)
265
+ )
266
+
267
+ mock.assert_not_called()
268
+ self.assertEqual(1, mock_target.call_count)
269
+ mock_target.assert_has_calls(
270
+ [
271
+ call(
272
+ metrics_name="latency_metric_annotated_method_error_time",
273
+ metrics_config=ANY,
274
+ value=ANY,
275
+ )
276
+ ],
277
+ any_order=True,
278
+ )
279
+
280
+ def test_annotation_with_args_sanity(self):
281
+ mock, mock_target = MagicMock(), MagicMock()
282
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
283
+ metrics_actor = MetricsActor.options(
284
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
285
+ ).remote()
286
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
287
+ ray.get(metrics_actor.set_metrics_config.remote(config))
288
+
289
+ # action
290
+ latency_metric_with_name_annotated_method(mock)
291
+
292
+ mock.assert_called_once()
293
+ self.assertEqual(1, mock_target.call_count)
294
+ mock_target.assert_has_calls(
295
+ [call(metrics_name="test", metrics_config=ANY, value=ANY)], any_order=True
296
+ )
297
+
298
+ def test_annotation_with_args_when_error(self):
299
+ mock, mock_target = MagicMock(), MagicMock()
300
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
301
+ metrics_actor = MetricsActor.options(
302
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
303
+ ).remote()
304
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
305
+ ray.get(metrics_actor.set_metrics_config.remote(config))
306
+
307
+ # action
308
+ self.assertRaises(
309
+ ValueError, lambda: latency_metric_with_name_annotated_method_error(mock)
310
+ )
311
+
312
+ mock.assert_not_called()
313
+ self.assertEqual(1, mock_target.call_count)
314
+ mock_target.assert_has_calls(
315
+ [call(metrics_name="test", metrics_config=ANY, value=ANY)], any_order=True
316
+ )
317
+
318
+ def test_annotation_without_actor(self):
319
+ mock, mock_target = MagicMock(), MagicMock()
320
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
321
+ metrics_actor = MetricsActor.options(
322
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
323
+ ).remote()
324
+
325
+ # explicitly making sure actor is killed
326
+ ray.kill(metrics_actor)
327
+
328
+ # action
329
+ self.assertRaises(
330
+ ValueError, lambda: latency_metric_with_name_annotated_method_error(mock)
331
+ )
332
+
333
+ mock.assert_not_called()
334
+ mock_target.assert_not_called()
335
+
336
+
337
+ @success_metric
338
+ def success_metric_annotated_method(mock_func):
339
+ mock_func("called")
340
+
341
+
342
+ @success_metric
343
+ def success_metric_annotated_method_error(mock_func):
344
+ raise ValueError()
345
+
346
+
347
+ @success_metric(name="test")
348
+ def success_metric_with_name_annotated_method(mock_func):
349
+ mock_func("called")
350
+
351
+
352
+ @success_metric(name="test")
353
+ def success_metric_with_name_annotated_method_error(mock_func):
354
+ raise ValueError()
355
+
356
+
357
+ class TestSuccessMetricAnnotation(unittest.TestCase):
358
+ def test_annotation_sanity(self):
359
+ mock, mock_target = MagicMock(), MagicMock()
360
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
361
+ metrics_actor = MetricsActor.options(
362
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
363
+ ).remote()
364
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
365
+ ray.get(metrics_actor.set_metrics_config.remote(config))
366
+
367
+ # action
368
+ success_metric_annotated_method(mock)
369
+
370
+ mock.assert_called_once()
371
+ self.assertEqual(1, mock_target.call_count)
372
+ mock_target.assert_has_calls(
373
+ [
374
+ call(
375
+ metrics_name="success_metric_annotated_method_success_count",
376
+ metrics_config=ANY,
377
+ value=ANY,
378
+ )
379
+ ],
380
+ any_order=True,
381
+ )
382
+
383
+ def test_annotation_when_error(self):
384
+ mock, mock_target = MagicMock(), MagicMock()
385
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
386
+ metrics_actor = MetricsActor.options(
387
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
388
+ ).remote()
389
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
390
+ ray.get(metrics_actor.set_metrics_config.remote(config))
391
+
392
+ # action
393
+ self.assertRaises(
394
+ ValueError, lambda: success_metric_annotated_method_error(mock)
395
+ )
396
+
397
+ mock.assert_not_called()
398
+ mock_target.assert_not_called()
399
+
400
+ def test_annotation_with_args_sanity(self):
401
+ mock, mock_target = MagicMock(), MagicMock()
402
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
403
+ metrics_actor = MetricsActor.options(
404
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
405
+ ).remote()
406
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
407
+ ray.get(metrics_actor.set_metrics_config.remote(config))
408
+
409
+ # action
410
+ success_metric_with_name_annotated_method(mock)
411
+
412
+ mock.assert_called_once()
413
+ self.assertEqual(1, mock_target.call_count)
414
+ mock_target.assert_has_calls(
415
+ [call(metrics_name="test", metrics_config=ANY, value=ANY)], any_order=True
416
+ )
417
+
418
+ def test_annotation_with_args_when_error(self):
419
+ mock, mock_target = MagicMock(), MagicMock()
420
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
421
+ metrics_actor = MetricsActor.options(
422
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
423
+ ).remote()
424
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
425
+ ray.get(metrics_actor.set_metrics_config.remote(config))
426
+
427
+ # action
428
+ self.assertRaises(
429
+ ValueError, lambda: success_metric_with_name_annotated_method_error(mock)
430
+ )
431
+
432
+ mock.assert_not_called()
433
+ mock_target.assert_not_called()
434
+
435
+ def test_annotation_without_actor(self):
436
+ mock, mock_target = MagicMock(), MagicMock()
437
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
438
+ metrics_actor = MetricsActor.options(
439
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
440
+ ).remote()
441
+
442
+ # explicitly making sure actor is killed
443
+ ray.kill(metrics_actor)
444
+
445
+ # action
446
+ success_metric_annotated_method(mock)
447
+
448
+ mock.assert_called_once()
449
+ mock_target.assert_not_called()
450
+
451
+
452
+ @failure_metric
453
+ def failure_metric_annotated_method(mock_func):
454
+ mock_func("called")
455
+
456
+
457
+ @failure_metric
458
+ def failure_metric_annotated_method_error(mock_func):
459
+ raise ValueError()
460
+
461
+
462
+ @failure_metric(name="test")
463
+ def failure_metric_with_name_annotated_method(mock_func):
464
+ mock_func("called")
465
+
466
+
467
+ @failure_metric(name="test")
468
+ def failure_metric_with_name_annotated_method_error(mock_func):
469
+ raise ValueError()
470
+
471
+
472
+ class TestFailureMetricAnnotation(unittest.TestCase):
473
+ def test_annotation_sanity(self):
474
+ mock, mock_target = MagicMock(), MagicMock()
475
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
476
+ metrics_actor = MetricsActor.options(
477
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
478
+ ).remote()
479
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
480
+ ray.get(metrics_actor.set_metrics_config.remote(config))
481
+
482
+ # action
483
+ failure_metric_annotated_method(mock)
484
+
485
+ mock.assert_called_once()
486
+ mock_target.assert_not_called()
487
+
488
+ def test_annotation_when_error(self):
489
+ mock, mock_target = MagicMock(), MagicMock()
490
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
491
+ metrics_actor = MetricsActor.options(
492
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
493
+ ).remote()
494
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
495
+ ray.get(metrics_actor.set_metrics_config.remote(config))
496
+
497
+ # action
498
+ self.assertRaises(
499
+ ValueError, lambda: failure_metric_annotated_method_error(mock)
500
+ )
501
+
502
+ mock.assert_not_called()
503
+ self.assertEqual(2, mock_target.call_count)
504
+ mock_target.assert_has_calls(
505
+ [
506
+ call(
507
+ metrics_name="failure_metric_annotated_method_error_failure_count.ValueError",
508
+ metrics_config=ANY,
509
+ value=ANY,
510
+ ),
511
+ call(
512
+ metrics_name="failure_metric_annotated_method_error_failure_count",
513
+ metrics_config=ANY,
514
+ value=ANY,
515
+ ),
516
+ ],
517
+ any_order=True,
518
+ )
519
+
520
+ def test_annotation_with_args_sanity(self):
521
+ mock, mock_target = MagicMock(), MagicMock()
522
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
523
+ metrics_actor = MetricsActor.options(
524
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
525
+ ).remote()
526
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
527
+ ray.get(metrics_actor.set_metrics_config.remote(config))
528
+
529
+ # action
530
+ failure_metric_with_name_annotated_method(mock)
531
+
532
+ mock.assert_called_once()
533
+ mock_target.assert_not_called()
534
+
535
+ def test_annotation_with_args_when_error(self):
536
+ mock, mock_target = MagicMock(), MagicMock()
537
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
538
+ metrics_actor = MetricsActor.options(
539
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
540
+ ).remote()
541
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
542
+ ray.get(metrics_actor.set_metrics_config.remote(config))
543
+
544
+ # action
545
+ self.assertRaises(
546
+ ValueError, lambda: failure_metric_with_name_annotated_method_error(mock)
547
+ )
548
+
549
+ mock.assert_not_called()
550
+ self.assertEqual(2, mock_target.call_count)
551
+ mock_target.assert_has_calls(
552
+ [
553
+ call(metrics_name="test.ValueError", metrics_config=ANY, value=ANY),
554
+ call(metrics_name="test", metrics_config=ANY, value=ANY),
555
+ ],
556
+ any_order=True,
557
+ )
558
+
559
+ def test_annotation_without_actor(self):
560
+ mock, mock_target = MagicMock(), MagicMock()
561
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
562
+ metrics_actor = MetricsActor.options(
563
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
564
+ ).remote()
565
+
566
+ # explicitly making sure actor is killed
567
+ ray.kill(metrics_actor)
568
+
569
+ # action
570
+ self.assertRaises(
571
+ ValueError, lambda: failure_metric_with_name_annotated_method_error(mock)
572
+ )
573
+
574
+ mock.assert_not_called()
575
+ mock_target.assert_not_called()
deltacat/utils/metrics.py CHANGED
@@ -1,6 +1,10 @@
1
1
  from dataclasses import dataclass
2
+ from typing import Optional
2
3
 
3
4
  import logging
5
+ import ray
6
+ import time
7
+ import functools
4
8
 
5
9
  from aws_embedded_metrics import metric_scope
6
10
  from aws_embedded_metrics.config import get_config
@@ -18,11 +22,13 @@ logger = logs.configure_deltacat_logger(logging.getLogger(__name__))
18
22
  DEFAULT_DELTACAT_METRICS_NAMESPACE = "ray-deltacat-metrics"
19
23
  DEFAULT_DELTACAT_LOG_GROUP_NAME = "ray-deltacat-metrics-EMF-logs"
20
24
  DEFAULT_DELTACAT_LOG_STREAM_CALLABLE = get_node_ip_address
25
+ METRICS_CONFIG_ACTOR_NAME = "metrics-config-actor"
21
26
 
22
27
 
23
28
  class MetricsTarget(str, Enum):
24
29
  CLOUDWATCH = "cloudwatch"
25
30
  CLOUDWATCH_EMF = "cloudwatch_emf"
31
+ NOOP = "noop"
26
32
 
27
33
 
28
34
  @dataclass
@@ -42,27 +48,16 @@ class MetricsConfig:
42
48
  self.metrics_kwargs = metrics_kwargs
43
49
 
44
50
 
45
- class MetricsType(str, Enum):
46
- TIMER = "timer"
47
-
48
-
49
- def _build_metrics_name(metrics_type: Enum, metrics_name: str) -> str:
50
- metrics_name_with_type = f"{metrics_name}_{metrics_type}"
51
- return metrics_name_with_type
52
-
53
-
54
51
  def _build_cloudwatch_metrics(
55
52
  metrics_name: str,
56
- metrics_type: Enum,
57
53
  value: str,
58
54
  metrics_dimensions: List[Dict[str, str]],
59
55
  timestamp: datetime,
60
56
  **kwargs,
61
57
  ) -> Dict[str, Any]:
62
- metrics_name_with_type = _build_metrics_name(metrics_type, metrics_name)
63
58
  return [
64
59
  {
65
- "MetricName": f"{metrics_name_with_type}",
60
+ "MetricName": metrics_name,
66
61
  "Dimensions": metrics_dimensions,
67
62
  "Timestamp": timestamp,
68
63
  "Value": value,
@@ -73,7 +68,6 @@ def _build_cloudwatch_metrics(
73
68
 
74
69
  def _emit_metrics(
75
70
  metrics_name: str,
76
- metrics_type: Enum,
77
71
  metrics_config: MetricsConfig,
78
72
  value: str,
79
73
  **kwargs,
@@ -85,7 +79,6 @@ def _emit_metrics(
85
79
  if metrics_target in METRICS_TARGET_TO_EMITTER_DICT:
86
80
  METRICS_TARGET_TO_EMITTER_DICT.get(metrics_target)(
87
81
  metrics_name=metrics_name,
88
- metrics_type=metrics_type,
89
82
  metrics_config=metrics_config,
90
83
  value=value,
91
84
  **kwargs,
@@ -94,9 +87,15 @@ def _emit_metrics(
94
87
  logger.warning(f"{metrics_target} is not a supported metrics target type.")
95
88
 
96
89
 
90
+ def _emit_noop_metrics(
91
+ *args,
92
+ **kwargs,
93
+ ) -> None:
94
+ pass
95
+
96
+
97
97
  def _emit_cloudwatch_metrics(
98
98
  metrics_name: str,
99
- metrics_type: Enum,
100
99
  metrics_config: MetricsConfig,
101
100
  value: str,
102
101
  **kwargs,
@@ -111,7 +110,6 @@ def _emit_cloudwatch_metrics(
111
110
 
112
111
  metrics_data = _build_cloudwatch_metrics(
113
112
  metrics_name,
114
- metrics_type,
115
113
  value,
116
114
  metrics_dimensions,
117
115
  current_time,
@@ -128,14 +126,13 @@ def _emit_cloudwatch_metrics(
128
126
  except Exception as e:
129
127
  logger.warning(
130
128
  f"Failed to publish Cloudwatch metrics with name: {metrics_name}, "
131
- f"type: {metrics_type}, with exception: {e}, response: {response}"
129
+ f"with exception: {e}, response: {response}"
132
130
  )
133
131
 
134
132
 
135
133
  @metric_scope
136
134
  def _emit_cloudwatch_emf_metrics(
137
135
  metrics_name: str,
138
- metrics_type: Enum,
139
136
  metrics_config: MetricsConfig,
140
137
  value: str,
141
138
  metrics: MetricsLogger,
@@ -155,7 +152,6 @@ def _emit_cloudwatch_emf_metrics(
155
152
  dimensions = dict(
156
153
  [(dim["Name"], dim["Value"]) for dim in metrics_config.metrics_dimensions]
157
154
  )
158
- metrics_name_with_type = _build_metrics_name(metrics_type, metrics_name)
159
155
  try:
160
156
  metrics.set_timestamp(current_time)
161
157
  metrics.set_dimensions(dimensions)
@@ -177,25 +173,164 @@ def _emit_cloudwatch_emf_metrics(
177
173
  else DEFAULT_DELTACAT_LOG_STREAM_CALLABLE()
178
174
  )
179
175
 
180
- metrics.put_metric(metrics_name_with_type, value)
176
+ metrics.put_metric(metrics_name, value)
181
177
  except Exception as e:
182
178
  logger.warning(
183
- f"Failed to publish Cloudwatch EMF metrics with name: {metrics_name_with_type}, "
184
- f"type: {metrics_type}, with exception: {e}"
179
+ f"Failed to publish Cloudwatch EMF metrics with name: {metrics_name}", e
185
180
  )
186
181
 
187
182
 
188
183
  METRICS_TARGET_TO_EMITTER_DICT: Dict[str, Callable] = {
189
184
  MetricsTarget.CLOUDWATCH: _emit_cloudwatch_metrics,
190
185
  MetricsTarget.CLOUDWATCH_EMF: _emit_cloudwatch_emf_metrics,
186
+ MetricsTarget.NOOP: _emit_noop_metrics,
191
187
  }
192
188
 
193
189
 
190
+ @ray.remote
191
+ class MetricsActor:
192
+ metrics_config: MetricsConfig = None
193
+
194
+ def set_metrics_config(self, config: MetricsConfig) -> None:
195
+ self.metrics_config = config
196
+
197
+ def get_metrics_config(self) -> MetricsConfig:
198
+ return self.metrics_config
199
+
200
+
201
+ def _emit_ignore_exceptions(name: Optional[str], value: Any):
202
+ try:
203
+ config_cache: MetricsActor = MetricsActor.options(
204
+ name=METRICS_CONFIG_ACTOR_NAME, get_if_exists=True
205
+ ).remote()
206
+ config = ray.get(config_cache.get_metrics_config.remote())
207
+ if config:
208
+ _emit_metrics(metrics_name=name, value=value, metrics_config=config)
209
+ except BaseException as ex:
210
+ logger.warning("Emitting metrics failed", ex)
211
+
212
+
194
213
  def emit_timer_metrics(metrics_name, value, metrics_config, **kwargs):
195
214
  _emit_metrics(
196
215
  metrics_name=metrics_name,
197
- metrics_type=MetricsType.TIMER,
198
216
  metrics_config=metrics_config,
199
217
  value=value,
200
218
  **kwargs,
201
219
  )
220
+
221
+
222
+ def latency_metric(original_func=None, name: Optional[str] = None):
223
+ """
224
+ A decorator that emits latency metrics of a function
225
+ based on configured metrics config.
226
+ """
227
+
228
+ def _decorate(func):
229
+ metrics_name = name or f"{func.__name__}_time"
230
+
231
+ @functools.wraps(func)
232
+ def wrapper(*args, **kwargs):
233
+ start = time.monotonic()
234
+ try:
235
+ return func(*args, **kwargs)
236
+ finally:
237
+ end = time.monotonic()
238
+ _emit_ignore_exceptions(name=metrics_name, value=end - start)
239
+
240
+ return wrapper
241
+
242
+ if original_func:
243
+ return _decorate(original_func)
244
+
245
+ return _decorate
246
+
247
+
248
+ def success_metric(original_func=None, name: Optional[str] = None):
249
+ """
250
+ A decorator that emits success metrics of a function
251
+ based on configured metrics config.
252
+ """
253
+
254
+ def _decorate(func):
255
+ metrics_name = name or f"{func.__name__}_success_count"
256
+
257
+ @functools.wraps(func)
258
+ def wrapper(*args, **kwargs):
259
+ result = func(*args, **kwargs)
260
+ _emit_ignore_exceptions(name=metrics_name, value=1)
261
+ return result
262
+
263
+ return wrapper
264
+
265
+ if original_func:
266
+ return _decorate(original_func)
267
+
268
+ return _decorate
269
+
270
+
271
+ def failure_metric(original_func=None, name: Optional[str] = None):
272
+ """
273
+ A decorator that emits failure metrics of a function
274
+ based on configured metrics config.
275
+ """
276
+
277
+ def _decorate(func):
278
+ metrics_name = name or f"{func.__name__}_failure_count"
279
+
280
+ @functools.wraps(func)
281
+ def wrapper(*args, **kwargs):
282
+ try:
283
+ return func(*args, **kwargs)
284
+ except BaseException as ex:
285
+ exception_name = type(ex).__name__
286
+ # We emit two metrics, one for exception class and
287
+ # another for just the specified metric name.
288
+ _emit_ignore_exceptions(
289
+ name=f"{metrics_name}.{exception_name}", value=1
290
+ )
291
+ _emit_ignore_exceptions(name=metrics_name, value=1)
292
+ raise ex
293
+
294
+ return wrapper
295
+
296
+ if original_func:
297
+ return _decorate(original_func)
298
+
299
+ return _decorate
300
+
301
+
302
+ def metrics(original_func=None, prefix: Optional[str] = None):
303
+ """
304
+ A decorator that emits all metrics for a function.
305
+ """
306
+
307
+ def _decorate(func):
308
+ name = prefix or func.__name__
309
+ failure_metrics_name = f"{name}_failure_count"
310
+ success_metrics_name = f"{name}_success_count"
311
+ latency_metrics_name = f"{name}_time"
312
+
313
+ @functools.wraps(func)
314
+ def wrapper(*args, **kwargs):
315
+ start = time.monotonic()
316
+ try:
317
+ result = func(*args, **kwargs)
318
+ _emit_ignore_exceptions(name=success_metrics_name, value=1)
319
+ return result
320
+ except BaseException as ex:
321
+ exception_name = type(ex).__name__
322
+ _emit_ignore_exceptions(
323
+ name=f"{failure_metrics_name}.{exception_name}", value=1
324
+ )
325
+ _emit_ignore_exceptions(name=failure_metrics_name, value=1)
326
+ raise ex
327
+ finally:
328
+ end = time.monotonic()
329
+ _emit_ignore_exceptions(name=latency_metrics_name, value=end - start)
330
+
331
+ return wrapper
332
+
333
+ if original_func:
334
+ return _decorate(original_func)
335
+
336
+ return _decorate
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: deltacat
3
- Version: 1.1.1
3
+ Version: 1.1.2
4
4
  Summary: A scalable, fast, ACID-compliant Data Catalog powered by Ray.
5
5
  Home-page: https://github.com/ray-project/deltacat
6
6
  Author: Ray Team
@@ -1,11 +1,11 @@
1
- deltacat/__init__.py,sha256=PeDnE7G_w2Gwkfg1VVp1mGIZm0_-WoyYbu7SlmoCFHs,1777
1
+ deltacat/__init__.py,sha256=1qjk3DyuuYXNfuzWlEqKVNNPDDpBkV8gwsSgJiMCq1s,1777
2
2
  deltacat/constants.py,sha256=_6oRI-3yp5c8J1qKGQZrt89I9-ttT_gSSvVsJ0h8Duc,1939
3
3
  deltacat/exceptions.py,sha256=xqZf8CwysNYP2d39pf27OnXGStPREgBgIM-e2Tts-TI,199
4
4
  deltacat/logs.py,sha256=YT26oj-oKZReyBEhiBQU5jc246aSu_d4B2bZcUVBHmE,6550
5
5
  deltacat/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- deltacat/aws/clients.py,sha256=WKgbgaq8CacH81nvvcqrjdshGtYvQicvBeOLg1m7wCA,6501
7
- deltacat/aws/constants.py,sha256=luXWMO_8eatq8f9NlFjNM7q362j77JwzTM2BEVS_8-8,353
8
- deltacat/aws/s3u.py,sha256=aK1_pyfipd9Jq1ZiaOC-gszyIIfc1TSbch6YmuZmjt0,23878
6
+ deltacat/aws/clients.py,sha256=VgddlV3AEjlBGIFmhhHxokYzwJ-lXnmHAeprVyADduI,6948
7
+ deltacat/aws/constants.py,sha256=Ti8D92AFY3lY2eoOwYBSI1eqS8gMykX5DHf4w3xPybE,492
8
+ deltacat/aws/s3u.py,sha256=7-ZNz8fui_S5nprxtMO8PGa-G1YOXfidqcguzTTOvL8,24118
9
9
  deltacat/aws/redshift/__init__.py,sha256=7SvjG-dqox8zZUhFicTsUvpG5vXYDl_QQ3ohlHOgTKc,342
10
10
  deltacat/aws/redshift/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  deltacat/aws/redshift/model/manifest.py,sha256=ThgpdwzaWz493Zz9e8HSWwuxEheA1nDuypM3pe4vozk,12987
@@ -50,14 +50,14 @@ deltacat/compute/compactor/utils/round_completion_file.py,sha256=DmZfHeAXlQn0DDd
50
50
  deltacat/compute/compactor/utils/sort_key.py,sha256=oK6otg-CSsma6zlGPaKg-KNEvcZRG2NqBlCw1X3_FBc,2397
51
51
  deltacat/compute/compactor/utils/system_columns.py,sha256=CNIgAGos0xAGEpdaQIH7KfbSRrGZgjRbItXMararqXQ,9399
52
52
  deltacat/compute/compactor_v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
- deltacat/compute/compactor_v2/compaction_session.py,sha256=xAxiTOxHffRvPqkv4ObGM-NWdpxRRMyrS8WWZWrgTFQ,25141
54
- deltacat/compute/compactor_v2/constants.py,sha256=yZgzFD59wiXbXiTVgYPWRodZGpngiSBNFB2jmoZ4fps,1471
53
+ deltacat/compute/compactor_v2/compaction_session.py,sha256=lXLoOiVjvsG-VEFD9afeKMjcExmOMg9I4-JTrTCVSo4,25576
54
+ deltacat/compute/compactor_v2/constants.py,sha256=R5qvsaX-rDrSmSqUl0yLp6A3hVrAD4whYkV2NBkDR9Y,2199
55
55
  deltacat/compute/compactor_v2/deletes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
56
  deltacat/compute/compactor_v2/deletes/delete_file_envelope.py,sha256=AeuH9JRMwp6mvQf6P2cqL92hUEtResQq6qUTS0kIKac,3111
57
57
  deltacat/compute/compactor_v2/deletes/delete_strategy.py,sha256=SMEJOxR-5r92kvKNqtu2w6HmwtmhljcZX1wcNEuS-4w,2833
58
58
  deltacat/compute/compactor_v2/deletes/delete_strategy_equality_delete.py,sha256=U4zxVECXSPs1Nj3iPf_tiRRCs12CF8CHmRt4s_GDzq8,6503
59
59
  deltacat/compute/compactor_v2/deletes/model.py,sha256=kW7kfRe4jVNMnsWQrl0nyKdDpvB9mbJND-MVzAajbAI,558
60
- deltacat/compute/compactor_v2/deletes/utils.py,sha256=g63imGECPpPK9eHdde-6qyCg8_RjXCLF2UdfckPk0h4,6468
60
+ deltacat/compute/compactor_v2/deletes/utils.py,sha256=9CchSw1_caWGWtRHa4ycy58t5T422EN6UB9XYa1zpbk,6640
61
61
  deltacat/compute/compactor_v2/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
62
  deltacat/compute/compactor_v2/model/hash_bucket_input.py,sha256=iJy8kLi1dIpFIyfoAjkaAtZvg8Np1z7BsUNGAcWfFm4,3042
63
63
  deltacat/compute/compactor_v2/model/hash_bucket_result.py,sha256=EsY9BPPywhmxlcLKn3kGWzAX4s4BTR2vYyPUB-wAEOc,309
@@ -65,14 +65,14 @@ deltacat/compute/compactor_v2/model/merge_file_group.py,sha256=1o86t9lc3K6ZvtViV
65
65
  deltacat/compute/compactor_v2/model/merge_input.py,sha256=ITRBR8gMbJpeRkZhRaVzGEEk3F2WqS2LwEkjd5zcaMQ,5416
66
66
  deltacat/compute/compactor_v2/model/merge_result.py,sha256=_IZTCStpb4UKiRCJYA3g6EhAqjrw0t9vmoDAN8kIK-Y,436
67
67
  deltacat/compute/compactor_v2/steps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
- deltacat/compute/compactor_v2/steps/hash_bucket.py,sha256=Kuhgh1y12t4V9rP67bdalQF6_sZiobfAK3tvOka8IWA,6324
69
- deltacat/compute/compactor_v2/steps/merge.py,sha256=dNGE_UtQmptYWj59_XQBoFrB6iNWYE37iup81HqWcAU,21394
68
+ deltacat/compute/compactor_v2/steps/hash_bucket.py,sha256=be61umFucTfzeZ03xii1P_NFqXsGmhl55hm_ULuCFPc,6617
69
+ deltacat/compute/compactor_v2/steps/merge.py,sha256=xjLpv93o9bMjihLXXO1j4XCXhyARc5GQI5eWxQRy0XI,21657
70
70
  deltacat/compute/compactor_v2/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
71
  deltacat/compute/compactor_v2/utils/content_type_params.py,sha256=rNKZisxGrLQOkwX8eHUQiFoTR1V-E66pMqWigtrs618,2156
72
72
  deltacat/compute/compactor_v2/utils/dedupe.py,sha256=62tFCY2iRP7I3-45GCIYs6_SJsQl8C5lBEr8gbNfbsw,1932
73
73
  deltacat/compute/compactor_v2/utils/delta.py,sha256=8hjkDeIIkSX-gAQ2utQSp2sZcO2tWZHMTxpFusZwBHw,3635
74
- deltacat/compute/compactor_v2/utils/io.py,sha256=jgIfwrfH2mTFUx1M0TgwZGGfrS4IXjP1PmqwaQmNAJM,5092
75
- deltacat/compute/compactor_v2/utils/merge.py,sha256=hK4Y7acrtgfvWWTz-fAGznEg6qn6dBYu8blQUQVHhs0,5244
74
+ deltacat/compute/compactor_v2/utils/io.py,sha256=autXlE3uHICdCCuJoS7mfdeJbRRiz2_xlz-3izlccB4,5264
75
+ deltacat/compute/compactor_v2/utils/merge.py,sha256=7p8lEmepxNekFFP5uVxgVLvAQRQnscOmUlSj8kUvW2c,5408
76
76
  deltacat/compute/compactor_v2/utils/primary_key_index.py,sha256=MAscmL35WfwN7Is72aFlD_cGhxtZgjRwwR5kS9Yn2uU,11393
77
77
  deltacat/compute/compactor_v2/utils/task_options.py,sha256=MCY0Sz5NCgNMaY92W8p87FvvDB91mnPQ4AhL8ix3BiA,13780
78
78
  deltacat/compute/merge_on_read/__init__.py,sha256=ckbgngmqPjYBYz_NySsR1vNTOb_hNpeL1sYkZKvBI9M,214
@@ -174,6 +174,7 @@ deltacat/tests/test_utils/utils.py,sha256=a32qEwcSSd1lvRi0aJJ4ZLnc1ZyXmoQF_K95za
174
174
  deltacat/tests/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
175
175
  deltacat/tests/utils/test_cloudpickle.py,sha256=J0pnBY3-PxlUh6MamZAN1PuquKQPr2iyzjiJ7-Rcl0o,1506
176
176
  deltacat/tests/utils/test_daft.py,sha256=Xal84zR42rXsWQI3lImdDYWOzewomKmhmiUQ59m67V0,6488
177
+ deltacat/tests/utils/test_metrics.py,sha256=kznfueuChhTG3kKnfbVOtxks2uwIWrXlDjY3b2qQaNc,19429
177
178
  deltacat/tests/utils/test_pyarrow.py,sha256=eZAuYp9MUf8lmpIilH57JkURuNsTGZ3IAGC4Gm5hdrM,17307
178
179
  deltacat/tests/utils/test_record_batch_tables.py,sha256=AkG1WyljQmjnl-AxhbFWyo5LnMIKRyLScfgC2B_ES-s,11321
179
180
  deltacat/tests/utils/test_resources.py,sha256=HtpvDrfPZQNtGDXUlsIzc_yd7Vf1cDscZ3YbN0oTvO8,2560
@@ -187,7 +188,7 @@ deltacat/utils/arguments.py,sha256=5y1Xz4HSAD8M8Jt83i6gOEKoYjy_fMQe1V43IhIE4hY,1
187
188
  deltacat/utils/cloudpickle.py,sha256=XE7YDmQe56ksfl3NdYZkzOAhbHSuhNcBZGOehQpgZr0,1187
188
189
  deltacat/utils/common.py,sha256=RG_-enXNpLKaYrqyx1ne2lL10lxN9vK7F631oJP6SE8,1375
189
190
  deltacat/utils/daft.py,sha256=heg6J8NHnTI1UbcjXvsjGBbFKU6f7mvAio-KZuTSuFI,5506
190
- deltacat/utils/metrics.py,sha256=Ob-RXGoNnfTMRXaNbSHoqW8y-n8KfRA9nLuo9AvsReI,6201
191
+ deltacat/utils/metrics.py,sha256=PZeBV4rhlA_7WjucXDe2DyZj5A8pYCQsYoKDwgyqb7E,9937
191
192
  deltacat/utils/numpy.py,sha256=ZiGREobTVT6IZXgPxkSUpLJFN2Hn8KEZcrqybLDXCIA,2027
192
193
  deltacat/utils/pandas.py,sha256=GfwjYb8FUSEeoBdXZI1_NJkdjxPMbCCUhlyRfGbDkn8,9562
193
194
  deltacat/utils/performance.py,sha256=7ZLaMkS1ehPSIhT5uOQVBHvjC70iKHzoFquFo-KL0PI,645
@@ -202,8 +203,8 @@ deltacat/utils/ray_utils/concurrency.py,sha256=JDVwMiQWrmuSlyCWAoiq9ctoJ0XADEfDD
202
203
  deltacat/utils/ray_utils/dataset.py,sha256=SIljK3UkSqQ6Ntit_iSiYt9yYjN_gGrCTX6_72XdQ3w,3244
203
204
  deltacat/utils/ray_utils/performance.py,sha256=d7JFM7vTXHzkGx9qNQcZzUWajnqINvYRwaM088_FpsE,464
204
205
  deltacat/utils/ray_utils/runtime.py,sha256=5eaBWTDm0IXVoc5Y6aacoVB-f0Mnv-K2ewyTSjHKHwM,5009
205
- deltacat-1.1.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
206
- deltacat-1.1.1.dist-info/METADATA,sha256=XeXOc863rqN4olrgF3bK3Hm3Cozw7nlshK-hHILg-o4,1780
207
- deltacat-1.1.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
208
- deltacat-1.1.1.dist-info/top_level.txt,sha256=RWdIcid4Bv2i2ozLVh-70kJpyB61xEKXod9XXGpiono,9
209
- deltacat-1.1.1.dist-info/RECORD,,
206
+ deltacat-1.1.2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
207
+ deltacat-1.1.2.dist-info/METADATA,sha256=r16g6Lc01Nlsb0Dr5DPona2hiOsWLIcscEXkQyfpD3o,1780
208
+ deltacat-1.1.2.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
209
+ deltacat-1.1.2.dist-info/top_level.txt,sha256=RWdIcid4Bv2i2ozLVh-70kJpyB61xEKXod9XXGpiono,9
210
+ deltacat-1.1.2.dist-info/RECORD,,