-
Notifications
You must be signed in to change notification settings - Fork 8k
[Data] Warn when configured memory is below 1.25x max USS #65512
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
3ae5a49
3f53b30
a6ce8be
55f0fd6
5fba41e
a32d693
f641860
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ | |
| TaskExecDriverStats, | ||
| ) | ||
| from ray.data._internal.execution.interfaces.ref_bundle import BlockEntry | ||
| from ray.data._internal.execution.operators.hash_shuffle import HashShuffleOperator | ||
| from ray.data._internal.execution.operators.input_data_buffer import ( | ||
| InputDataBuffer, | ||
| ) | ||
|
|
@@ -26,6 +27,10 @@ | |
| HangingExecutionIssueDetector, | ||
| HangingExecutionIssueDetectorConfig, | ||
| ) | ||
| from ray.data._internal.issue_detection.detectors.hash_shuffle_detector import ( | ||
| HashShuffleAggregatorIssueDetector, | ||
| HashShuffleAggregatorIssueDetectorConfig, | ||
| ) | ||
| from ray.data._internal.issue_detection.detectors.high_memory_detector import ( | ||
| HighMemoryIssueDetector, | ||
| ) | ||
|
|
@@ -201,6 +206,21 @@ def test_hanging_detector_detects_issues( | |
| assert "has been running or stuck in scheduling for" in issues[0].message | ||
| assert "longer than the average task duration" in issues[0].message | ||
|
|
||
| with patch.object(type(op), "is_shut_down", return_value=True): | ||
| assert detector.detect() == [] | ||
|
|
||
|
|
||
| def test_hash_shuffle_detector_skips_shutdown_operator(): | ||
| hash_shuffle_operator = MagicMock(spec=HashShuffleOperator) | ||
| hash_shuffle_operator.is_shut_down.return_value = True | ||
| hash_shuffle_detector = HashShuffleAggregatorIssueDetector( | ||
| dataset_id="id", | ||
| operators=[hash_shuffle_operator], | ||
| config=HashShuffleAggregatorIssueDetectorConfig(), | ||
| ) | ||
|
|
||
| assert hash_shuffle_detector.detect() == [] | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| "configured_memory, actual_memory, should_return_issue", | ||
|
|
@@ -226,6 +246,7 @@ def test_high_memory_detection( | |
| data_context=ctx, | ||
| ray_remote_args={"memory": configured_memory}, | ||
| ) | ||
| map_operator.has_completed = MagicMock(return_value=False) | ||
| map_operator._metrics = MagicMock(average_max_uss_per_task=actual_memory) | ||
| topology = {input_data_buffer: MagicMock(), map_operator: MagicMock()} | ||
|
|
||
|
|
@@ -239,6 +260,68 @@ def test_high_memory_detection( | |
| issues = detector.detect() | ||
|
|
||
| assert should_return_issue == bool(issues) | ||
| if should_return_issue: | ||
| normalized_message = " ".join(issues[0].message.split()) | ||
| assert "`memory=10737418240` (10.0GiB)" in normalized_message | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| "configured_memory, max_memory, expected_memory_configuration, expected_memory, operator_termination", | ||
| [ | ||
| (10 * GiB, 8 * GiB, None, None, "completed"), | ||
| ( | ||
| 10 * GiB - 1, | ||
| 8 * GiB, | ||
| "The configured logical memory was 10.0GiB.", | ||
| 10 * GiB, | ||
| "completed", | ||
| ), | ||
| (None, 8 * GiB, None, None, "completed"), | ||
| (0, 1, "The configured logical memory was 0.0B.", 2, "completed"), | ||
| (1, None, None, None, "completed"), | ||
| (1, 1, "The configured logical memory was 1.0B.", 2, "shutdown"), | ||
| ], | ||
| ) | ||
| def test_high_memory_detection_on_operator_termination( | ||
| configured_memory, | ||
| max_memory, | ||
| expected_memory_configuration, | ||
| expected_memory, | ||
| operator_termination, | ||
| restore_data_context, | ||
| ): | ||
| ctx = DataContext.get_current() | ||
| input_data_buffer = InputDataBuffer(ctx, input_data=[]) | ||
| map_operator = MapOperator.create( | ||
| map_transformer=MagicMock(), | ||
| input_op=input_data_buffer, | ||
| data_context=ctx, | ||
| ray_remote_args={"memory": configured_memory}, | ||
| ) | ||
| if max_memory is not None: | ||
| map_operator.metrics.max_uss_bytes.add_sample(max_memory // 2) | ||
| map_operator.metrics.max_uss_bytes.add_sample(max_memory) | ||
| if operator_termination == "completed": | ||
| map_operator.has_completed = MagicMock(return_value=True) | ||
| else: | ||
| map_operator.is_shut_down = MagicMock(return_value=True) | ||
|
|
||
| detector = HighMemoryIssueDetector( | ||
| dataset_id="id", | ||
| operators=[input_data_buffer, map_operator], | ||
| config=ctx.issue_detectors_config.high_memory_detector_config, | ||
| ) | ||
|
|
||
| issues = detector.detect() | ||
|
|
||
| assert (expected_memory_configuration is not None) == bool(issues) | ||
| if expected_memory_configuration is not None: | ||
| normalized_message = " ".join(issues[0].message.split()) | ||
| assert map_operator.name in normalized_message | ||
| assert expected_memory_configuration in normalized_message | ||
| assert f"`memory={expected_memory}`" in normalized_message | ||
| # Completion checks are one-shot to avoid duplicate warnings. | ||
| assert detector.detect() == [] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the intent for this second assertion?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It verifies completion detection is one-shot, so repeated detector passes don't emit duplicate warnings or events. I've added comment to make that intent clear. |
||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.