Skip to content

Commit a3e5bec

Browse files
committed
Fix: Add validation of success status
1 parent 498bef3 commit a3e5bec

2 files changed

Lines changed: 59 additions & 25 deletions

File tree

dags/tpu_observability/jobset_healthiness_validation.py

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from dags import composer_env
2525
from dags.tpu_observability.utils import jobset_util as jobset
2626
from dags.tpu_observability.utils import node_pool_util as node_pool
27-
from dags.tpu_observability.utils.jobset_util import Workload, ReplicatedJobStatus
27+
from dags.tpu_observability.utils.jobset_util import Workload, JobSetHealthiness
2828
from dags.tpu_observability.configs.common import (
2929
MachineConfigMap,
3030
GCS_CONFIG_PATH,
@@ -38,6 +38,7 @@
3838
SCHEDULE = SchedulingHelper.arrange_schedule_time(DAG_ID)
3939

4040
FAIL_WORKLOAD = "python3 -c 'import logging; import sys; logging.error(\"Simulating Failure\"); sys.exit(1)'"
41+
SUCCESS_WORKLOAD = "python3 -c 'import logging; import sys; logging.info(\"Simulating Success\"); sys.exit(0)'"
4142

4243
# Keyword arguments are generated dynamically at runtime (pylint does not
4344
# know this signature).
@@ -63,18 +64,20 @@
6364
doc_md="""
6465
# JobSet Healthiness Test For the "Suspended" Status
6566
### Description
66-
This DAG automates the process of creating node-pools, ensuring the
67-
correct number of "Suspended" replicas appear, then launching a jobset on
68-
multiple replicas to ensure the correct number begin running.
67+
This DAG automates node-pool creation and validates JobSet healthiness
68+
by examining replica-based metrics: Specified, Active, Ready,
69+
Suspended, Succeeded, and Failed. It ensures the JobSet controller
70+
accurately reports these states during startup, maintenance,
71+
and failure scenarios.
6972
### Prerequisites
7073
This test requires an existing cluster to run.
7174
### Procedures
72-
First a node-pool is created. The validation test is then run to
73-
check if the number of "Suspended" replicas is 0. Once the jobset is
74-
running the jobs should quickly enter the "Ready" state. Then using
75-
command to suspend entire jobset. The number of found replicas is
76-
tested against the number of replicas which should be "Suspended".
77-
If they match the DAG is a success.
75+
First a node-pool is created. This test uses a State-Trigger-Observe pattern:
76+
it triggers lifecycle transitions (e.g., suspension, failure or succeeded)
77+
and verifies that GKE telemetry reflects these shifts. Using sensors,
78+
the DAG polls for eventual consistency to account for ingestion latency,
79+
dynamically matching runtime JobSet configurations against normalized monitoring
80+
data types to ensure accurate state validation.
7881
""",
7982
) as dag:
8083
for machine in MachineConfigMap:
@@ -110,20 +113,20 @@
110113
node_pool=cluster_info,
111114
)
112115

113-
startup = jobset.create_jobset_startup_group(
116+
startup = jobset.create_jobset_startup_tasks(
114117
node_pool=cluster_info,
115118
jobset_config=jobset_config,
116119
workload_type=Workload.JAX_TPU_BENCHMARK,
117120
)
118121

119122
with TaskGroup(group_id="validate_running_metrics") as validate_running:
120123
running_metrics = [
121-
(ReplicatedJobStatus.SPECIFIED, "USE_CONFIG_REPLICAS"),
122-
(ReplicatedJobStatus.ACTIVE, "USE_CONFIG_REPLICAS"),
123-
(ReplicatedJobStatus.READY, "USE_CONFIG_REPLICAS"),
124-
(ReplicatedJobStatus.FAILED, 0),
125-
(ReplicatedJobStatus.SUCCEEDED, 0),
126-
(ReplicatedJobStatus.SUSPENDED, 0),
124+
(JobSetHealthiness.SPECIFIED, "USE_CONFIG_REPLICAS"),
125+
(JobSetHealthiness.ACTIVE, "USE_CONFIG_REPLICAS"),
126+
(JobSetHealthiness.READY, "USE_CONFIG_REPLICAS"),
127+
(JobSetHealthiness.FAILED, 0),
128+
(JobSetHealthiness.SUCCEEDED, 0),
129+
(JobSetHealthiness.SUSPENDED, 0),
127130
]
128131
for status, expected in running_metrics:
129132
jobset.wait_for_jobset_metrics.override(
@@ -146,8 +149,8 @@
146149
group_id="validate_suspended_metrics"
147150
) as validate_suspended:
148151
suspended_metrics = [
149-
(ReplicatedJobStatus.ACTIVE, 0),
150-
(ReplicatedJobStatus.SUSPENDED, "USE_CONFIG_REPLICAS"),
152+
(JobSetHealthiness.ACTIVE, 0),
153+
(JobSetHealthiness.SUSPENDED, "USE_CONFIG_REPLICAS"),
151154
]
152155
for status, expected in suspended_metrics:
153156
jobset.wait_for_jobset_metrics.override(
@@ -164,6 +167,33 @@
164167
jobset_config=jobset_config,
165168
)
166169

170+
with TaskGroup(group_id="inject_and_validate_success") as success_test:
171+
cleanup_for_success = jobset.end_workload.override(
172+
task_id="cleanup_before_success_injection"
173+
)(
174+
node_pool=cluster_info,
175+
jobset_config=jobset_config,
176+
)
177+
178+
start_success_job = jobset.run_workload.override(
179+
task_id="start_success_job"
180+
)(
181+
node_pool=cluster_info,
182+
jobset_config=jobset_config,
183+
workload_type=SUCCESS_WORKLOAD,
184+
)
185+
186+
validate_succeeded_metric = jobset.wait_for_jobset_metrics.override(
187+
task_id="wait_for_succeeded_count"
188+
)(
189+
metric_name=JobSetHealthiness.SUCCEEDED,
190+
expected_value="USE_CONFIG_REPLICAS",
191+
node_pool=cluster_info,
192+
jobset_config=jobset_config,
193+
)
194+
195+
chain(cleanup_for_success, start_success_job, validate_succeeded_metric)
196+
167197
with TaskGroup(group_id="inject_and_validate_failure") as failure_test:
168198
cleanup_for_failure = jobset.end_workload.override(
169199
task_id="cleanup_before_failure_injection"
@@ -181,7 +211,7 @@
181211
validate_failed_metric = jobset.wait_for_jobset_metrics.override(
182212
task_id="wait_for_failed_count"
183213
)(
184-
metric_name=ReplicatedJobStatus.FAILED,
214+
metric_name=JobSetHealthiness.FAILED,
185215
expected_value="USE_CONFIG_REPLICAS",
186216
node_pool=cluster_info,
187217
jobset_config=jobset_config,
@@ -209,11 +239,12 @@
209239
jobset_config,
210240
cluster_info,
211241
create_node_pool,
212-
startup.task_group,
242+
*startup.tasks,
213243
validate_running,
214244
suspend_action,
215245
validate_suspended,
216246
resume_action,
247+
success_test,
217248
failure_test,
218249
cleanup_workload,
219250
cleanup_node_pool,

dags/tpu_observability/utils/jobset_util.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,8 +325,11 @@ class JobSetStartupOutput:
325325
jobset_start_time: XComArg
326326

327327

328-
class ReplicatedJobStatus(enum.Enum):
329-
"""Defines status of a replicated job."""
328+
class JobSetHealthiness(enum.Enum):
329+
"""Enumeration of JobSet healthiness metrics based on replica states.
330+
The values represent the metric name suffix used in Prometheus/GKE
331+
observability to track the count of replicas in each lifecycle state.
332+
"""
330333

331334
READY = "ready"
332335
SUSPENDED = "suspended"
@@ -907,7 +910,7 @@ def wait_for_jobset_started(
907910

908911
@task.sensor(poke_interval=60, timeout=3600, mode="poke")
909912
def wait_for_jobset_metrics(
910-
metric_name: ReplicatedJobStatus,
913+
metric_name: JobSetHealthiness,
911914
expected_value: any,
912915
node_pool: node_pool_info,
913916
jobset_config: JobSet,
@@ -921,7 +924,7 @@ def wait_for_jobset_metrics(
921924
types returned by the Prometheus exporter.
922925
923926
Args:
924-
metric_name (ReplicatedJobStatus): The status of the replicated job to
927+
metric_name (JobSetHealthiness): The status of the replicated job to
925928
monitor.
926929
expected_value (any): The target value to wait for. If set to
927930
"USE_CONFIG_REPLICAS", the sensor will dynamically retrieve the

0 commit comments

Comments
 (0)