Skip to content

Commit 8dd534e

Browse files
authored
test: stop slow CI Health V2 suite from timing out (#6391)
- jumpstart: run gated training jobs through fit_estimator_with_capacity_xfail so a job stuck in Pending waiting for ml.g5 capacity becomes an xfail after 30 minutes instead of blocking the build; let the helper forward fit() kwargs (accept_eula) - clarify monitor: upload the model under a per-run key, and move the abalone model uploads off the shared integ-test-data/xgboost/model key, so concurrently running tests cannot swap in a model the xgboost:1 image cannot load (Clarify shadow endpoint ping failures) - clarify monitor: cut reruns of the scheduled-execution tests from 5 to 1, since each attempt waits for the next hourly execution - feature processor: serialize local Spark runs across xdist workers with a file lock, so concurrent spark-submit --packages downloads do not corrupt the shared ivy cache
1 parent 4afbb5a commit 8dd534e

10 files changed

Lines changed: 65 additions & 26 deletions

File tree

‎tests/integ/sagemaker/experiments/test_run.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ def test_run_from_transform_job(
502502
exp_name = unique_name_from_base(_EXP_NAME_BASE_IN_SCRIPT)
503503
xgb_model_data_s3 = sagemaker_session.upload_data(
504504
path=os.path.join(_TRANSFORM_MATERIALS, "xgb_model.tar.gz"),
505-
key_prefix="integ-test-data/xgboost/model",
505+
key_prefix="integ-test-data/xgboost_abalone/model",
506506
)
507507
env = _update_env_with_client_config(
508508
env={

‎tests/integ/sagemaker/feature_store/feature_processor/test_feature_processor_integ.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
import os
1717
import subprocess
1818
import sys
19+
import tempfile
1920
import time
2021
from typing import Dict
2122
from datetime import datetime
23+
from filelock import FileLock
2224
from pyspark.sql import DataFrame
2325
import pytz
2426

@@ -109,10 +111,28 @@
109111

110112
SCHEDULE_EXPRESSION_TIMESTAMP_FORMAT = "%Y-%m-%dT%H:%M:%S" # 2023-01-01T07:00:00
111113

114+
# Local (non-@remote) feature_processor runs start a Spark JVM whose spark-submit resolves the
115+
# hadoop-aws/hadoop-common Maven packages into the shared ~/.ivy2 cache. Ivy is not safe for
116+
# concurrent processes: when two pytest-xdist workers on the same host start Spark at the same
117+
# time against a cold cache, their downloads collide (0-byte jars) and the JVM exits with
118+
# "Java gateway process exited before sending its port number". Run those tests one at a time.
119+
_LOCAL_SPARK_LOCK_PATH = os.path.join(
120+
tempfile.gettempdir(), "sagemaker-integ-feature-processor-local-spark.lock"
121+
)
122+
_LOCAL_SPARK_LOCK_TIMEOUT_SECONDS = 30 * 60
123+
124+
125+
@pytest.fixture
126+
def local_spark_lock():
127+
"""Hold a host-wide lock so only one test runs a local Spark session at a time."""
128+
with FileLock(_LOCAL_SPARK_LOCK_PATH, timeout=_LOCAL_SPARK_LOCK_TIMEOUT_SECONDS):
129+
yield
130+
112131

113132
@pytest.mark.slow_test
114133
def test_feature_processor_transform_online_only_store_ingestion(
115134
sagemaker_session,
135+
local_spark_lock,
116136
):
117137
car_data_feature_group_name = get_car_data_feature_group_name()
118138
car_data_aggregated_feature_group_name = get_car_data_aggregated_feature_group_name()
@@ -234,6 +254,7 @@ def transform(raw_s3_data_as_df):
234254
@pytest.mark.slow_test
235255
def test_feature_processor_transform_with_customized_data_source(
236256
sagemaker_session,
257+
local_spark_lock,
237258
):
238259
car_data_feature_group_name = get_car_data_feature_group_name()
239260
car_data_aggregated_feature_group_name = get_car_data_aggregated_feature_group_name()
@@ -377,6 +398,7 @@ def transform(raw_s3_data_as_df):
377398
@pytest.mark.flaky(reruns=5, reruns_delay=2)
378399
def test_feature_processor_transform_offline_only_store_ingestion(
379400
sagemaker_session,
401+
local_spark_lock,
380402
):
381403
car_data_feature_group_name = get_car_data_feature_group_name()
382404
car_data_aggregated_feature_group_name = get_car_data_aggregated_feature_group_name()

‎tests/integ/sagemaker/jumpstart/estimator/test_jumpstart_estimator.py‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,14 @@ def test_gated_model_training_v1(setup):
121121
hyperparameters={"max_steps": "1"},
122122
)
123123

124-
# uses ml.g5.12xlarge instance
125-
estimator.fit(
124+
# uses ml.g5.12xlarge instance, which can sit in Pending for hours waiting for
125+
# capacity; fail fast as a CapacityError instead of blocking the whole build.
126+
fit_estimator_with_capacity_xfail(
127+
estimator,
126128
{
127129
"training": f"s3://{get_jumpstart_content_bucket(JUMPSTART_DEFAULT_REGION_NAME)}/"
128130
f"{get_training_dataset_for_model_and_version(model_id, model_version)}",
129-
}
131+
},
130132
)
131133

132134
# uses ml.g5.2xlarge instance
@@ -167,12 +169,14 @@ def test_gated_model_training_v2(setup):
167169
hyperparameters={"max_steps": "1"},
168170
)
169171

170-
# uses ml.g5.12xlarge instance
171-
estimator.fit(
172+
# uses ml.g5.12xlarge instance, which can sit in Pending for hours waiting for
173+
# capacity; fail fast as a CapacityError instead of blocking the whole build.
174+
fit_estimator_with_capacity_xfail(
175+
estimator,
172176
{
173177
"training": f"s3://{get_jumpstart_content_bucket(JUMPSTART_DEFAULT_REGION_NAME)}/"
174178
f"{get_training_dataset_for_model_and_version(model_id, model_version)}",
175-
}
179+
},
176180
)
177181

178182
# test that we can create a JumpStartEstimator from existing job with `attach`

‎tests/integ/sagemaker/jumpstart/private_hub/estimator/test_jumpstart_private_hub_estimator.py‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
get_sm_session,
3131
get_training_dataset_for_model_and_version,
3232
add_model_references_to_hub,
33+
fit_estimator_with_capacity_xfail,
3334
x_fail_if_ice,
3435
)
3536

@@ -151,12 +152,15 @@ def test_jumpstart_hub_gated_estimator_with_eula(setup, add_model_references):
151152
hyperparameters={"max_steps": "1"},
152153
)
153154

154-
estimator.fit(
155-
accept_eula=True,
156-
inputs={
155+
# Fail fast as a CapacityError if ml.g5.2xlarge capacity is not granted, instead of
156+
# letting the training job sit in Pending until the build times out.
157+
fit_estimator_with_capacity_xfail(
158+
estimator,
159+
{
157160
"training": f"s3://{get_jumpstart_content_bucket(JUMPSTART_DEFAULT_REGION_NAME)}/"
158161
f"{get_training_dataset_for_model_and_version(model_id, model_version)}",
159162
},
163+
accept_eula=True,
160164
)
161165

162166
predictor = estimator.deploy(

‎tests/integ/sagemaker/jumpstart/utils.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ def fit_estimator_with_capacity_xfail(
104104
estimator,
105105
inputs,
106106
capacity_wait_timeout_seconds: int = DEFAULT_CAPACITY_WAIT_TIMEOUT_SECONDS,
107+
**fit_kwargs,
107108
):
108109
"""Submit a training job and wait for it, but fail fast on capacity queueing.
109110
@@ -122,8 +123,10 @@ def fit_estimator_with_capacity_xfail(
122123
inputs: The training inputs passed through to ``estimator.fit()``.
123124
capacity_wait_timeout_seconds: Max time to allow the job to remain in
124125
"waiting for capacity" before declaring a capacity shortage.
126+
**fit_kwargs: Extra keyword arguments passed through to ``estimator.fit()``
127+
(e.g. ``accept_eula=True``).
125128
"""
126-
estimator.fit(inputs, wait=False)
129+
estimator.fit(inputs, wait=False, **fit_kwargs)
127130

128131
training_job_name = estimator.latest_training_job.name
129132
sagemaker_client = estimator.sagemaker_session.sagemaker_client

‎tests/integ/sagemaker/workflow/test_model_steps.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,7 @@ def test_xgboost_model_register_and_deploy_with_runtime_repack(
704704
model_package_group_name = f"{pipeline_name}TestModelPackageGroup"
705705
xgb_model_data_s3 = pipeline_session.upload_data(
706706
path=os.path.join(_XGBOOST_PATH, "xgb_model.tar.gz"),
707-
key_prefix="integ-test-data/xgboost/model",
707+
key_prefix="integ-test-data/xgboost_abalone/model",
708708
)
709709
xgb_model_data_param = ParameterString(name="ModelData", default_value=xgb_model_data_s3)
710710
xgb_model = XGBoostModel(

‎tests/integ/sagemaker/workflow/test_monitor_batch_transform_step.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ def step_model_create(
205205

206206
xgb_model_data_s3 = pipeline_session.upload_data(
207207
path=os.path.join(os.path.join(DATA_DIR, "xgboost_abalone"), "xgb_model.tar.gz"),
208-
key_prefix="integ-test-data/xgboost/model",
208+
key_prefix="integ-test-data/xgboost_abalone/model",
209209
)
210210

211211
xgb_model = XGBoostModel(

‎tests/integ/test_clarify_model_monitor.py‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,12 @@
8585
@pytest.yield_fixture(scope="module")
8686
def endpoint_name(sagemaker_session):
8787
endpoint_name = unique_name_from_base("clarify-xgb-integ")
88+
# Upload under a key unique to this run. Clarify re-downloads the model artifact when it
89+
# creates its shadow endpoint for every monitoring execution, long after this upload, so
90+
# a shared key can by then hold a different model written by a concurrently running test.
8891
xgb_model_data = sagemaker_session.upload_data(
8992
path=os.path.join(XGBOOST_DATA_PATH, "xgb_model.tar.gz"),
90-
key_prefix="integ-test-data/xgboost/model",
93+
key_prefix=f"integ-test-data/xgboost/model/{endpoint_name}",
9194
)
9295

9396
xgb_image = image_uris.retrieve(
@@ -360,7 +363,9 @@ def test_bias_monitor(sagemaker_session, scheduled_bias_monitor, endpoint_name,
360363
tests.integ.test_region() in tests.integ.NO_MODEL_MONITORING_REGIONS,
361364
reason="ModelMonitoring is not yet supported in this region.",
362365
)
363-
@pytest.mark.flaky(reruns=5, reruns_delay=2)
366+
# Each attempt waits for the next hourly scheduled execution, so every rerun costs up to an
367+
# hour of build time; more than one rerun can push the slow suite past its build timeout.
368+
@pytest.mark.flaky(reruns=1, reruns_delay=2)
364369
def test_run_bias_monitor(
365370
scheduled_bias_monitor, sagemaker_session, endpoint_name, ground_truth_input, upload_actual_data
366371
):
@@ -470,7 +475,8 @@ def test_explainability_monitor(sagemaker_session, scheduled_explainability_moni
470475
tests.integ.test_region() in tests.integ.NO_MODEL_MONITORING_REGIONS,
471476
reason="ModelMonitoring is not yet supported in this region.",
472477
)
473-
@pytest.mark.flaky(reruns=5, reruns_delay=2)
478+
# See test_run_bias_monitor: each rerun waits for another hourly execution.
479+
@pytest.mark.flaky(reruns=1, reruns_delay=2)
474480
def test_run_explainability_monitor(
475481
scheduled_explainability_monitor,
476482
sagemaker_session,

‎tests/integ/test_model_package.py‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def test_update_approval_model_package(sagemaker_session):
4444

4545
xgb_model_data_s3 = sagemaker_session.upload_data(
4646
path=os.path.join(_XGBOOST_PATH, "xgb_model.tar.gz"),
47-
key_prefix="integ-test-data/xgboost/model",
47+
key_prefix="integ-test-data/xgboost_abalone/model",
4848
)
4949
model = XGBoostModel(
5050
model_data=xgb_model_data_s3, framework_version="1.3-1", sagemaker_session=sagemaker_session
@@ -86,7 +86,7 @@ def test_update_model_life_cycle_model_package(sagemaker_session):
8686

8787
xgb_model_data_s3 = sagemaker_session.upload_data(
8888
path=os.path.join(_XGBOOST_PATH, "xgb_model.tar.gz"),
89-
key_prefix="integ-test-data/xgboost/model",
89+
key_prefix="integ-test-data/xgboost_abalone/model",
9090
)
9191
model = XGBoostModel(
9292
model_data=xgb_model_data_s3, framework_version="1.3-1", sagemaker_session=sagemaker_session
@@ -142,7 +142,7 @@ def test_inference_specification_addition(sagemaker_session):
142142

143143
xgb_model_data_s3 = sagemaker_session.upload_data(
144144
path=os.path.join(_XGBOOST_PATH, "xgb_model.tar.gz"),
145-
key_prefix="integ-test-data/xgboost/model",
145+
key_prefix="integ-test-data/xgboost_abalone/model",
146146
)
147147
model = XGBoostModel(
148148
model_data=xgb_model_data_s3, framework_version="1.3-1", sagemaker_session=sagemaker_session
@@ -223,7 +223,7 @@ def test_update_source_uri(sagemaker_session):
223223

224224
xgb_model_data_s3 = sagemaker_session.upload_data(
225225
path=os.path.join(_XGBOOST_PATH, "xgb_model.tar.gz"),
226-
key_prefix="integ-test-data/xgboost/model",
226+
key_prefix="integ-test-data/xgboost_abalone/model",
227227
)
228228
model = XGBoostModel(
229229
model_data=xgb_model_data_s3, framework_version="1.3-1", sagemaker_session=sagemaker_session
@@ -287,7 +287,7 @@ def test_update_model_card_with_model_card_object(sagemaker_session):
287287

288288
xgb_model_data_s3 = sagemaker_session.upload_data(
289289
path=os.path.join(_XGBOOST_PATH, "xgb_model.tar.gz"),
290-
key_prefix="integ-test-data/xgboost/model",
290+
key_prefix="integ-test-data/xgboost_abalone/model",
291291
)
292292
model = XGBoostModel(
293293
model_data=xgb_model_data_s3, framework_version="1.3-1", sagemaker_session=sagemaker_session
@@ -384,7 +384,7 @@ def test_update_model_card_with_model_card_json(sagemaker_session):
384384

385385
xgb_model_data_s3 = sagemaker_session.upload_data(
386386
path=os.path.join(_XGBOOST_PATH, "xgb_model.tar.gz"),
387-
key_prefix="integ-test-data/xgboost/model",
387+
key_prefix="integ-test-data/xgboost_abalone/model",
388388
)
389389
model = XGBoostModel(
390390
model_data=xgb_model_data_s3, framework_version="1.3-1", sagemaker_session=sagemaker_session
@@ -468,7 +468,7 @@ def test_clone_model_package_using_source_uri(sagemaker_session):
468468

469469
xgb_model_data_s3 = sagemaker_session.upload_data(
470470
path=os.path.join(_XGBOOST_PATH, "xgb_model.tar.gz"),
471-
key_prefix="integ-test-data/xgboost/model",
471+
key_prefix="integ-test-data/xgboost_abalone/model",
472472
)
473473
model = XGBoostModel(
474474
model_data=xgb_model_data_s3, framework_version="1.3-1", sagemaker_session=sagemaker_session
@@ -545,7 +545,7 @@ def test_register_model_using_source_uri(sagemaker_session):
545545

546546
xgb_model_data_s3 = sagemaker_session.upload_data(
547547
path=os.path.join(_XGBOOST_PATH, "xgb_model.tar.gz"),
548-
key_prefix="integ-test-data/xgboost/model",
548+
key_prefix="integ-test-data/xgboost_abalone/model",
549549
)
550550

551551
model = XGBoostModel(

‎tests/integ/test_transformer.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,7 @@ def test_transformer_and_monitoring_job(
666666
):
667667
xgb_model_data_s3 = pipeline_session.upload_data(
668668
path=os.path.join(os.path.join(DATA_DIR, "xgboost_abalone"), "xgb_model.tar.gz"),
669-
key_prefix="integ-test-data/xgboost/model",
669+
key_prefix="integ-test-data/xgboost_abalone/model",
670670
)
671671
data_bias_supplied_baseline_constraints = Constraints.from_file_path(
672672
constraints_file_path=os.path.join(
@@ -729,7 +729,7 @@ def test_transformer_and_monitoring_job_to_pass_with_no_failure_in_violation(
729729
):
730730
xgb_model_data_s3 = pipeline_session.upload_data(
731731
path=os.path.join(os.path.join(DATA_DIR, "xgboost_abalone"), "xgb_model.tar.gz"),
732-
key_prefix="integ-test-data/xgboost/model",
732+
key_prefix="integ-test-data/xgboost_abalone/model",
733733
)
734734
data_bias_supplied_baseline_constraints = Constraints.from_file_path(
735735
constraints_file_path=os.path.join(

0 commit comments

Comments
 (0)