deltacat 1.1.1__py3-none-any.whl → 1.1.3__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.3"
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,
@@ -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,502 @@
1
+ import unittest
2
+ from unittest.mock import MagicMock, ANY, call
3
+ from deltacat.utils.metrics import (
4
+ metrics,
5
+ success_metric,
6
+ failure_metric,
7
+ latency_metric,
8
+ MetricsConfigCache,
9
+ MetricsConfig,
10
+ MetricsTarget,
11
+ METRICS_TARGET_TO_EMITTER_DICT,
12
+ )
13
+ from deltacat.utils.performance import timed_invocation
14
+
15
+
16
+ def method_not_annotated(mock_func):
17
+ mock_func("called")
18
+
19
+
20
+ @metrics
21
+ def metrics_annotated_method(mock_func):
22
+ mock_func("called")
23
+
24
+
25
+ @metrics
26
+ def metrics_annotated_method_error(mock_func):
27
+ raise ValueError()
28
+
29
+
30
+ @metrics(prefix="test_prefix")
31
+ def metrics_with_prefix_annotated_method(mock_func):
32
+ mock_func("called")
33
+
34
+
35
+ @metrics(prefix="test_prefix")
36
+ def metrics_with_prefix_annotated_method_error(mock_func):
37
+ raise ValueError()
38
+
39
+
40
+ class TestMetricsAnnotation(unittest.TestCase):
41
+ def test_metrics_annotation_sanity(self):
42
+ mock, mock_target = MagicMock(), MagicMock()
43
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
44
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
45
+ MetricsConfigCache.metrics_config = config
46
+
47
+ # action
48
+ metrics_annotated_method(mock)
49
+
50
+ mock.assert_called_once()
51
+ self.assertEqual(2, mock_target.call_count)
52
+ mock_target.assert_has_calls(
53
+ [
54
+ call(
55
+ metrics_name="metrics_annotated_method_time",
56
+ metrics_config=ANY,
57
+ value=ANY,
58
+ ),
59
+ call(
60
+ metrics_name="metrics_annotated_method_success_count",
61
+ metrics_config=ANY,
62
+ value=ANY,
63
+ ),
64
+ ],
65
+ any_order=True,
66
+ )
67
+
68
+ def test_metrics_annotation_when_error(self):
69
+ mock, mock_target = MagicMock(), MagicMock()
70
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
71
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
72
+ MetricsConfigCache.metrics_config = config
73
+
74
+ # action
75
+ self.assertRaises(ValueError, lambda: metrics_annotated_method_error(mock))
76
+
77
+ mock.assert_not_called()
78
+ self.assertEqual(3, mock_target.call_count)
79
+ mock_target.assert_has_calls(
80
+ [
81
+ call(
82
+ metrics_name="metrics_annotated_method_error_time",
83
+ metrics_config=ANY,
84
+ value=ANY,
85
+ ),
86
+ call(
87
+ metrics_name="metrics_annotated_method_error_failure_count",
88
+ metrics_config=ANY,
89
+ value=ANY,
90
+ ),
91
+ call(
92
+ metrics_name="metrics_annotated_method_error_failure_count.ValueError",
93
+ metrics_config=ANY,
94
+ value=ANY,
95
+ ),
96
+ ],
97
+ any_order=True,
98
+ )
99
+
100
+ def test_metrics_with_prefix_annotation_sanity(self):
101
+ mock, mock_target = MagicMock(), MagicMock()
102
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
103
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
104
+ MetricsConfigCache.metrics_config = config
105
+
106
+ # action
107
+ metrics_with_prefix_annotated_method(mock)
108
+
109
+ mock.assert_called_once()
110
+ self.assertEqual(2, mock_target.call_count)
111
+ mock_target.assert_has_calls(
112
+ [
113
+ call(metrics_name="test_prefix_time", metrics_config=ANY, value=ANY),
114
+ call(
115
+ metrics_name="test_prefix_success_count",
116
+ metrics_config=ANY,
117
+ value=ANY,
118
+ ),
119
+ ],
120
+ any_order=True,
121
+ )
122
+
123
+ def test_metrics_annotation_performance(self):
124
+ mock, mock_target = MagicMock(), MagicMock()
125
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
126
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
127
+ MetricsConfigCache.metrics_config = config
128
+
129
+ # action with annotation
130
+ _, actual_latency = timed_invocation(metrics_annotated_method, mock)
131
+ _, second_call_latency = timed_invocation(metrics_annotated_method, mock)
132
+
133
+ mock.assert_called()
134
+ self.assertEqual(4, mock_target.call_count)
135
+ self.assertLess(
136
+ second_call_latency,
137
+ actual_latency,
138
+ "Second call to actor must be much faster",
139
+ )
140
+
141
+ def test_metrics_with_prefix_annotation_when_error(self):
142
+ mock, mock_target = MagicMock(), MagicMock()
143
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
144
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
145
+ MetricsConfigCache.metrics_config = config
146
+
147
+ # action
148
+ self.assertRaises(
149
+ ValueError, lambda: metrics_with_prefix_annotated_method_error(mock)
150
+ )
151
+
152
+ mock.assert_not_called()
153
+ self.assertEqual(3, mock_target.call_count)
154
+ mock_target.assert_has_calls(
155
+ [
156
+ call(metrics_name="test_prefix_time", metrics_config=ANY, value=ANY),
157
+ call(
158
+ metrics_name="test_prefix_failure_count",
159
+ metrics_config=ANY,
160
+ value=ANY,
161
+ ),
162
+ call(
163
+ metrics_name="test_prefix_failure_count.ValueError",
164
+ metrics_config=ANY,
165
+ value=ANY,
166
+ ),
167
+ ],
168
+ any_order=True,
169
+ )
170
+
171
+ def test_metrics_with_prefix_annotation_without_config(self):
172
+ mock, mock_target = MagicMock(), MagicMock()
173
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
174
+ MetricsConfigCache.metrics_config = None
175
+
176
+ # action
177
+ self.assertRaises(
178
+ ValueError, lambda: metrics_with_prefix_annotated_method_error(mock)
179
+ )
180
+
181
+ mock.assert_not_called()
182
+ mock_target.assert_not_called()
183
+
184
+
185
+ @latency_metric
186
+ def latency_metric_annotated_method(mock_func):
187
+ mock_func("called")
188
+
189
+
190
+ @latency_metric
191
+ def latency_metric_annotated_method_error(mock_func):
192
+ raise ValueError()
193
+
194
+
195
+ @latency_metric(name="test")
196
+ def latency_metric_with_name_annotated_method(mock_func):
197
+ mock_func("called")
198
+
199
+
200
+ @latency_metric(name="test")
201
+ def latency_metric_with_name_annotated_method_error(mock_func):
202
+ raise ValueError()
203
+
204
+
205
+ class TestLatencyMetricAnnotation(unittest.TestCase):
206
+ def test_annotation_sanity(self):
207
+ mock, mock_target = MagicMock(), MagicMock()
208
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
209
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
210
+ MetricsConfigCache.metrics_config = config
211
+
212
+ # action
213
+ latency_metric_annotated_method(mock)
214
+
215
+ mock.assert_called_once()
216
+ self.assertEqual(1, mock_target.call_count)
217
+ mock_target.assert_has_calls(
218
+ [
219
+ call(
220
+ metrics_name="latency_metric_annotated_method_time",
221
+ metrics_config=ANY,
222
+ value=ANY,
223
+ )
224
+ ],
225
+ any_order=True,
226
+ )
227
+
228
+ def test_annotation_when_error(self):
229
+ mock, mock_target = MagicMock(), MagicMock()
230
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
231
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
232
+ MetricsConfigCache.metrics_config = config
233
+
234
+ # action
235
+ self.assertRaises(
236
+ ValueError, lambda: latency_metric_annotated_method_error(mock)
237
+ )
238
+
239
+ mock.assert_not_called()
240
+ self.assertEqual(1, mock_target.call_count)
241
+ mock_target.assert_has_calls(
242
+ [
243
+ call(
244
+ metrics_name="latency_metric_annotated_method_error_time",
245
+ metrics_config=ANY,
246
+ value=ANY,
247
+ )
248
+ ],
249
+ any_order=True,
250
+ )
251
+
252
+ def test_annotation_with_args_sanity(self):
253
+ mock, mock_target = MagicMock(), MagicMock()
254
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
255
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
256
+ MetricsConfigCache.metrics_config = config
257
+
258
+ # action
259
+ latency_metric_with_name_annotated_method(mock)
260
+
261
+ mock.assert_called_once()
262
+ self.assertEqual(1, mock_target.call_count)
263
+ mock_target.assert_has_calls(
264
+ [call(metrics_name="test", metrics_config=ANY, value=ANY)], any_order=True
265
+ )
266
+
267
+ def test_annotation_with_args_when_error(self):
268
+ mock, mock_target = MagicMock(), MagicMock()
269
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
270
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
271
+ MetricsConfigCache.metrics_config = config
272
+
273
+ # action
274
+ self.assertRaises(
275
+ ValueError, lambda: latency_metric_with_name_annotated_method_error(mock)
276
+ )
277
+
278
+ mock.assert_not_called()
279
+ self.assertEqual(1, mock_target.call_count)
280
+ mock_target.assert_has_calls(
281
+ [call(metrics_name="test", metrics_config=ANY, value=ANY)], any_order=True
282
+ )
283
+
284
+ def test_annotation_without_config(self):
285
+ mock, mock_target = MagicMock(), MagicMock()
286
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
287
+ MetricsConfigCache.metrics_config = None
288
+
289
+ # action
290
+ self.assertRaises(
291
+ ValueError, lambda: latency_metric_with_name_annotated_method_error(mock)
292
+ )
293
+
294
+ mock.assert_not_called()
295
+ mock_target.assert_not_called()
296
+
297
+
298
+ @success_metric
299
+ def success_metric_annotated_method(mock_func):
300
+ mock_func("called")
301
+
302
+
303
+ @success_metric
304
+ def success_metric_annotated_method_error(mock_func):
305
+ raise ValueError()
306
+
307
+
308
+ @success_metric(name="test")
309
+ def success_metric_with_name_annotated_method(mock_func):
310
+ mock_func("called")
311
+
312
+
313
+ @success_metric(name="test")
314
+ def success_metric_with_name_annotated_method_error(mock_func):
315
+ raise ValueError()
316
+
317
+
318
+ class TestSuccessMetricAnnotation(unittest.TestCase):
319
+ def test_annotation_sanity(self):
320
+ mock, mock_target = MagicMock(), MagicMock()
321
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
322
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
323
+ MetricsConfigCache.metrics_config = config
324
+
325
+ # action
326
+ success_metric_annotated_method(mock)
327
+
328
+ mock.assert_called_once()
329
+ self.assertEqual(1, mock_target.call_count)
330
+ mock_target.assert_has_calls(
331
+ [
332
+ call(
333
+ metrics_name="success_metric_annotated_method_success_count",
334
+ metrics_config=ANY,
335
+ value=ANY,
336
+ )
337
+ ],
338
+ any_order=True,
339
+ )
340
+
341
+ def test_annotation_when_error(self):
342
+ mock, mock_target = MagicMock(), MagicMock()
343
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
344
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
345
+ MetricsConfigCache.metrics_config = config
346
+
347
+ # action
348
+ self.assertRaises(
349
+ ValueError, lambda: success_metric_annotated_method_error(mock)
350
+ )
351
+
352
+ mock.assert_not_called()
353
+ mock_target.assert_not_called()
354
+
355
+ def test_annotation_with_args_sanity(self):
356
+ mock, mock_target = MagicMock(), MagicMock()
357
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
358
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
359
+ MetricsConfigCache.metrics_config = config
360
+
361
+ # action
362
+ success_metric_with_name_annotated_method(mock)
363
+
364
+ mock.assert_called_once()
365
+ self.assertEqual(1, mock_target.call_count)
366
+ mock_target.assert_has_calls(
367
+ [call(metrics_name="test", metrics_config=ANY, value=ANY)], any_order=True
368
+ )
369
+
370
+ def test_annotation_with_args_when_error(self):
371
+ mock, mock_target = MagicMock(), MagicMock()
372
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
373
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
374
+ MetricsConfigCache.metrics_config = config
375
+
376
+ # action
377
+ self.assertRaises(
378
+ ValueError, lambda: success_metric_with_name_annotated_method_error(mock)
379
+ )
380
+
381
+ mock.assert_not_called()
382
+ mock_target.assert_not_called()
383
+
384
+ def test_annotation_without_config(self):
385
+ mock, mock_target = MagicMock(), MagicMock()
386
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
387
+ MetricsConfigCache.metrics_config = None
388
+
389
+ # action
390
+ success_metric_annotated_method(mock)
391
+
392
+ mock.assert_called_once()
393
+ mock_target.assert_not_called()
394
+
395
+
396
+ @failure_metric
397
+ def failure_metric_annotated_method(mock_func):
398
+ mock_func("called")
399
+
400
+
401
+ @failure_metric
402
+ def failure_metric_annotated_method_error(mock_func):
403
+ raise ValueError()
404
+
405
+
406
+ @failure_metric(name="test")
407
+ def failure_metric_with_name_annotated_method(mock_func):
408
+ mock_func("called")
409
+
410
+
411
+ @failure_metric(name="test")
412
+ def failure_metric_with_name_annotated_method_error(mock_func):
413
+ raise ValueError()
414
+
415
+
416
+ class TestFailureMetricAnnotation(unittest.TestCase):
417
+ def test_annotation_sanity(self):
418
+ mock, mock_target = MagicMock(), MagicMock()
419
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
420
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
421
+ MetricsConfigCache.metrics_config = config
422
+
423
+ # action
424
+ failure_metric_annotated_method(mock)
425
+
426
+ mock.assert_called_once()
427
+ mock_target.assert_not_called()
428
+
429
+ def test_annotation_when_error(self):
430
+ mock, mock_target = MagicMock(), MagicMock()
431
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
432
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
433
+ MetricsConfigCache.metrics_config = config
434
+
435
+ # action
436
+ self.assertRaises(
437
+ ValueError, lambda: failure_metric_annotated_method_error(mock)
438
+ )
439
+
440
+ mock.assert_not_called()
441
+ self.assertEqual(2, mock_target.call_count)
442
+ mock_target.assert_has_calls(
443
+ [
444
+ call(
445
+ metrics_name="failure_metric_annotated_method_error_failure_count.ValueError",
446
+ metrics_config=ANY,
447
+ value=ANY,
448
+ ),
449
+ call(
450
+ metrics_name="failure_metric_annotated_method_error_failure_count",
451
+ metrics_config=ANY,
452
+ value=ANY,
453
+ ),
454
+ ],
455
+ any_order=True,
456
+ )
457
+
458
+ def test_annotation_with_args_sanity(self):
459
+ mock, mock_target = MagicMock(), MagicMock()
460
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
461
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
462
+ MetricsConfigCache.metrics_config = config
463
+
464
+ # action
465
+ failure_metric_with_name_annotated_method(mock)
466
+
467
+ mock.assert_called_once()
468
+ mock_target.assert_not_called()
469
+
470
+ def test_annotation_with_args_when_error(self):
471
+ mock, mock_target = MagicMock(), MagicMock()
472
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
473
+ config = MetricsConfig("us-east-1", MetricsTarget.NOOP)
474
+ MetricsConfigCache.metrics_config = config
475
+
476
+ # action
477
+ self.assertRaises(
478
+ ValueError, lambda: failure_metric_with_name_annotated_method_error(mock)
479
+ )
480
+
481
+ mock.assert_not_called()
482
+ self.assertEqual(2, mock_target.call_count)
483
+ mock_target.assert_has_calls(
484
+ [
485
+ call(metrics_name="test.ValueError", metrics_config=ANY, value=ANY),
486
+ call(metrics_name="test", metrics_config=ANY, value=ANY),
487
+ ],
488
+ any_order=True,
489
+ )
490
+
491
+ def test_annotation_without_config(self):
492
+ mock, mock_target = MagicMock(), MagicMock()
493
+ METRICS_TARGET_TO_EMITTER_DICT[MetricsTarget.NOOP] = mock_target
494
+ MetricsConfigCache.metrics_config = None
495
+
496
+ # action
497
+ self.assertRaises(
498
+ ValueError, lambda: failure_metric_with_name_annotated_method_error(mock)
499
+ )
500
+
501
+ mock.assert_not_called()
502
+ mock_target.assert_not_called()
deltacat/utils/metrics.py CHANGED
@@ -1,6 +1,9 @@
1
1
  from dataclasses import dataclass
2
+ from typing import Optional
2
3
 
3
4
  import logging
5
+ import time
6
+ import functools
4
7
 
5
8
  from aws_embedded_metrics import metric_scope
6
9
  from aws_embedded_metrics.config import get_config
@@ -23,6 +26,7 @@ DEFAULT_DELTACAT_LOG_STREAM_CALLABLE = get_node_ip_address
23
26
  class MetricsTarget(str, Enum):
24
27
  CLOUDWATCH = "cloudwatch"
25
28
  CLOUDWATCH_EMF = "cloudwatch_emf"
29
+ NOOP = "noop"
26
30
 
27
31
 
28
32
  @dataclass
@@ -42,27 +46,16 @@ class MetricsConfig:
42
46
  self.metrics_kwargs = metrics_kwargs
43
47
 
44
48
 
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
49
  def _build_cloudwatch_metrics(
55
50
  metrics_name: str,
56
- metrics_type: Enum,
57
51
  value: str,
58
52
  metrics_dimensions: List[Dict[str, str]],
59
53
  timestamp: datetime,
60
54
  **kwargs,
61
55
  ) -> Dict[str, Any]:
62
- metrics_name_with_type = _build_metrics_name(metrics_type, metrics_name)
63
56
  return [
64
57
  {
65
- "MetricName": f"{metrics_name_with_type}",
58
+ "MetricName": metrics_name,
66
59
  "Dimensions": metrics_dimensions,
67
60
  "Timestamp": timestamp,
68
61
  "Value": value,
@@ -73,7 +66,6 @@ def _build_cloudwatch_metrics(
73
66
 
74
67
  def _emit_metrics(
75
68
  metrics_name: str,
76
- metrics_type: Enum,
77
69
  metrics_config: MetricsConfig,
78
70
  value: str,
79
71
  **kwargs,
@@ -85,7 +77,6 @@ def _emit_metrics(
85
77
  if metrics_target in METRICS_TARGET_TO_EMITTER_DICT:
86
78
  METRICS_TARGET_TO_EMITTER_DICT.get(metrics_target)(
87
79
  metrics_name=metrics_name,
88
- metrics_type=metrics_type,
89
80
  metrics_config=metrics_config,
90
81
  value=value,
91
82
  **kwargs,
@@ -94,9 +85,15 @@ def _emit_metrics(
94
85
  logger.warning(f"{metrics_target} is not a supported metrics target type.")
95
86
 
96
87
 
88
+ def _emit_noop_metrics(
89
+ *args,
90
+ **kwargs,
91
+ ) -> None:
92
+ pass
93
+
94
+
97
95
  def _emit_cloudwatch_metrics(
98
96
  metrics_name: str,
99
- metrics_type: Enum,
100
97
  metrics_config: MetricsConfig,
101
98
  value: str,
102
99
  **kwargs,
@@ -111,7 +108,6 @@ def _emit_cloudwatch_metrics(
111
108
 
112
109
  metrics_data = _build_cloudwatch_metrics(
113
110
  metrics_name,
114
- metrics_type,
115
111
  value,
116
112
  metrics_dimensions,
117
113
  current_time,
@@ -128,14 +124,13 @@ def _emit_cloudwatch_metrics(
128
124
  except Exception as e:
129
125
  logger.warning(
130
126
  f"Failed to publish Cloudwatch metrics with name: {metrics_name}, "
131
- f"type: {metrics_type}, with exception: {e}, response: {response}"
127
+ f"with exception: {e}, response: {response}"
132
128
  )
133
129
 
134
130
 
135
131
  @metric_scope
136
132
  def _emit_cloudwatch_emf_metrics(
137
133
  metrics_name: str,
138
- metrics_type: Enum,
139
134
  metrics_config: MetricsConfig,
140
135
  value: str,
141
136
  metrics: MetricsLogger,
@@ -155,7 +150,6 @@ def _emit_cloudwatch_emf_metrics(
155
150
  dimensions = dict(
156
151
  [(dim["Name"], dim["Value"]) for dim in metrics_config.metrics_dimensions]
157
152
  )
158
- metrics_name_with_type = _build_metrics_name(metrics_type, metrics_name)
159
153
  try:
160
154
  metrics.set_timestamp(current_time)
161
155
  metrics.set_dimensions(dimensions)
@@ -177,25 +171,183 @@ def _emit_cloudwatch_emf_metrics(
177
171
  else DEFAULT_DELTACAT_LOG_STREAM_CALLABLE()
178
172
  )
179
173
 
180
- metrics.put_metric(metrics_name_with_type, value)
174
+ metrics.put_metric(metrics_name, value)
181
175
  except Exception as e:
182
176
  logger.warning(
183
- f"Failed to publish Cloudwatch EMF metrics with name: {metrics_name_with_type}, "
184
- f"type: {metrics_type}, with exception: {e}"
177
+ f"Failed to publish Cloudwatch EMF metrics with name: {metrics_name}", e
185
178
  )
186
179
 
187
180
 
188
181
  METRICS_TARGET_TO_EMITTER_DICT: Dict[str, Callable] = {
189
182
  MetricsTarget.CLOUDWATCH: _emit_cloudwatch_metrics,
190
183
  MetricsTarget.CLOUDWATCH_EMF: _emit_cloudwatch_emf_metrics,
184
+ MetricsTarget.NOOP: _emit_noop_metrics,
191
185
  }
192
186
 
193
187
 
188
+ class MetricsConfigCache:
189
+ metrics_config: MetricsConfig = None
190
+
191
+
192
+ def _emit_ignore_exceptions(name: Optional[str], value: Any):
193
+ try:
194
+ config = MetricsConfigCache.metrics_config
195
+ if config:
196
+ _emit_metrics(metrics_name=name, value=value, metrics_config=config)
197
+ except BaseException as ex:
198
+ logger.warning("Emitting metrics failed", ex)
199
+
200
+
194
201
  def emit_timer_metrics(metrics_name, value, metrics_config, **kwargs):
195
202
  _emit_metrics(
196
203
  metrics_name=metrics_name,
197
- metrics_type=MetricsType.TIMER,
198
204
  metrics_config=metrics_config,
199
205
  value=value,
200
206
  **kwargs,
201
207
  )
208
+
209
+
210
+ def latency_metric(original_func=None, name: Optional[str] = None):
211
+ """
212
+ A decorator that emits latency metrics of a function
213
+ based on configured metrics config. Hence, make sure to set it in all worker processes:
214
+
215
+ def setup_cache():
216
+ from deltacat.utils.metrics import MetricsConfigCache
217
+ MetricsConfigCache.metrics_config = metrics_config
218
+
219
+ setup_cache();
220
+ ray.init(address="auto", runtime_env={"worker_process_setup_hook": setup_cache})
221
+ """
222
+
223
+ def _decorate(func):
224
+ metrics_name = name or f"{func.__name__}_time"
225
+
226
+ @functools.wraps(func)
227
+ def wrapper(*args, **kwargs):
228
+ start = time.monotonic()
229
+ try:
230
+ return func(*args, **kwargs)
231
+ finally:
232
+ end = time.monotonic()
233
+ _emit_ignore_exceptions(name=metrics_name, value=end - start)
234
+
235
+ return wrapper
236
+
237
+ if original_func:
238
+ return _decorate(original_func)
239
+
240
+ return _decorate
241
+
242
+
243
+ def success_metric(original_func=None, name: Optional[str] = None):
244
+ """
245
+ A decorator that emits success metrics of a function
246
+ based on configured metrics config. Hence, make sure to set it in all worker processes:
247
+
248
+ def setup_cache():
249
+ from deltacat.utils.metrics import MetricsConfigCache
250
+ MetricsConfigCache.metrics_config = metrics_config
251
+
252
+ setup_cache();
253
+ ray.init(address="auto", runtime_env={"worker_process_setup_hook": setup_cache})
254
+ """
255
+
256
+ def _decorate(func):
257
+ metrics_name = name or f"{func.__name__}_success_count"
258
+
259
+ @functools.wraps(func)
260
+ def wrapper(*args, **kwargs):
261
+ result = func(*args, **kwargs)
262
+ _emit_ignore_exceptions(name=metrics_name, value=1)
263
+ return result
264
+
265
+ return wrapper
266
+
267
+ if original_func:
268
+ return _decorate(original_func)
269
+
270
+ return _decorate
271
+
272
+
273
+ def failure_metric(original_func=None, name: Optional[str] = None):
274
+ """
275
+ A decorator that emits failure metrics of a function
276
+ based on configured metrics config. Hence, make sure to set it in all worker processes:
277
+
278
+ def setup_cache():
279
+ from deltacat.utils.metrics import MetricsConfigCache
280
+ MetricsConfigCache.metrics_config = metrics_config
281
+
282
+ setup_cache();
283
+ ray.init(address="auto", runtime_env={"worker_process_setup_hook": setup_cache})
284
+ """
285
+
286
+ def _decorate(func):
287
+ metrics_name = name or f"{func.__name__}_failure_count"
288
+
289
+ @functools.wraps(func)
290
+ def wrapper(*args, **kwargs):
291
+ try:
292
+ return func(*args, **kwargs)
293
+ except BaseException as ex:
294
+ exception_name = type(ex).__name__
295
+ # We emit two metrics, one for exception class and
296
+ # another for just the specified metric name.
297
+ _emit_ignore_exceptions(
298
+ name=f"{metrics_name}.{exception_name}", value=1
299
+ )
300
+ _emit_ignore_exceptions(name=metrics_name, value=1)
301
+ raise ex
302
+
303
+ return wrapper
304
+
305
+ if original_func:
306
+ return _decorate(original_func)
307
+
308
+ return _decorate
309
+
310
+
311
+ def metrics(original_func=None, prefix: Optional[str] = None):
312
+ """
313
+ A decorator that emits all metrics for a function. This decorator depends
314
+ on a metrics config. Hence, make sure to set it in all worker processes:
315
+
316
+ def setup_cache():
317
+ from deltacat.utils.metrics import MetricsConfigCache
318
+ MetricsConfigCache.metrics_config = metrics_config
319
+
320
+ setup_cache();
321
+ ray.init(address="auto", runtime_env={"worker_process_setup_hook": setup_cache})
322
+ """
323
+
324
+ def _decorate(func):
325
+ name = prefix or func.__name__
326
+ failure_metrics_name = f"{name}_failure_count"
327
+ success_metrics_name = f"{name}_success_count"
328
+ latency_metrics_name = f"{name}_time"
329
+
330
+ @functools.wraps(func)
331
+ def wrapper(*args, **kwargs):
332
+ start = time.monotonic()
333
+ try:
334
+ result = func(*args, **kwargs)
335
+ _emit_ignore_exceptions(name=success_metrics_name, value=1)
336
+ return result
337
+ except BaseException as ex:
338
+ exception_name = type(ex).__name__
339
+ _emit_ignore_exceptions(
340
+ name=f"{failure_metrics_name}.{exception_name}", value=1
341
+ )
342
+ _emit_ignore_exceptions(name=failure_metrics_name, value=1)
343
+ raise ex
344
+ finally:
345
+ end = time.monotonic()
346
+ _emit_ignore_exceptions(name=latency_metrics_name, value=end - start)
347
+
348
+ return wrapper
349
+
350
+ if original_func:
351
+ return _decorate(original_func)
352
+
353
+ 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.3
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=eO5VKENAHF-beW-5X9eJHbQk9EvjaQ09PiyxhQNUuxo,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
@@ -51,13 +51,13 @@ deltacat/compute/compactor/utils/sort_key.py,sha256=oK6otg-CSsma6zlGPaKg-KNEvcZR
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
53
  deltacat/compute/compactor_v2/compaction_session.py,sha256=xAxiTOxHffRvPqkv4ObGM-NWdpxRRMyrS8WWZWrgTFQ,25141
54
- deltacat/compute/compactor_v2/constants.py,sha256=yZgzFD59wiXbXiTVgYPWRodZGpngiSBNFB2jmoZ4fps,1471
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=Ym9nOz1EtB180pLmvugihj1sDTNDMb5opIjjr5Nmcls,16339
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=HYKyZSrtVLu8gXezg_TMNUKJp4h1WWI0VEzn0Xlzf-I,10778
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.3.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
207
+ deltacat-1.1.3.dist-info/METADATA,sha256=uj1xTijXxJlOZQy38MbY6Axj7cMjsT75xlKjWZPJyzw,1780
208
+ deltacat-1.1.3.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
209
+ deltacat-1.1.3.dist-info/top_level.txt,sha256=RWdIcid4Bv2i2ozLVh-70kJpyB61xEKXod9XXGpiono,9
210
+ deltacat-1.1.3.dist-info/RECORD,,