Skip to content

Commit aea57e1

Browse files
committed
-Created "validate_jobset_replica_number" funtion in jobset_util.py
-Changed workload name to "IDLE_READY_TPU_20M" -Changed schedule time and added statement to only run on PROD environment Changed to new nodepool creation format Remove unused DAG flow
1 parent b93a74c commit aea57e1

2 files changed

Lines changed: 84 additions & 104 deletions

File tree

dags/tpu_observability/jobset_healthiness_ready.py

Lines changed: 68 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -16,41 +16,24 @@
1616

1717
import datetime
1818

19-
from airflow.decorators import task
2019
from airflow import models
20+
from airflow.decorators import task
2121
from airflow.utils.trigger_rule import TriggerRule
2222
from airflow.utils.task_group import TaskGroup
2323

24-
from dags.common.vm_resource import Region, Zone
24+
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
2727
from dags.tpu_observability.utils.jobset_util import JobSet, Workload
28-
from dags.tpu_observability.configs.common import MachineConfigMap
29-
from dags import composer_env
30-
31-
# Can be moved to jobset_util.py
32-
@task.sensor(poke_interval=30, timeout=900, mode="reschedule")
33-
def validate_replica_number(
34-
node_pool: node_pool.Info,
35-
jobset_config: JobSet,
36-
replica_type: str,
37-
replica_num: int
38-
):
39-
found_replia_num = jobset.get_replica_num(
40-
replica_type=replica_type,
41-
job_name=jobset_config.replicated_job_name,
42-
node_pool=node_pool
43-
)
44-
45-
return (found_replia_num == replica_num)
28+
from dags.tpu_observability.configs.common import MachineConfigMap, GCS_CONFIG_PATH
4629

4730

4831
# Keyword arguments are generated dynamically at runtime (pylint does not
4932
# know this signature).
5033
with models.DAG( # pylint: disable=unexpected-keyword-arg
5134
dag_id="jobset_healthiness_ready",
5235
start_date=datetime.datetime(2025, 8, 10),
53-
schedule="00 03 * * *",
36+
schedule="30 19 * * *" if composer_env.is_prod_env() else None,
5437
catchup=False,
5538
tags=[
5639
"cloud-ml-auto-solutions",
@@ -85,54 +68,15 @@ def validate_replica_number(
8568
match the DAG is a success.
8669
""",
8770
) as dag:
88-
cluster_name = "tpu-observability-automation"
89-
cluster_name += "-prod" if composer_env.is_prod_env() else "-dev"
90-
9171
for machine in MachineConfigMap:
9272
config = machine.value
93-
cluster_info = node_pool.Info(
94-
project_id=models.Variable.get("PROJECT_ID", default_var="cienet-cmcs"),
95-
cluster_name=models.Variable.get(
96-
"CLUSTER_NAME", default_var=cluster_name
97-
),
98-
node_pool_name=models.Variable.get(
99-
"NODE_POOL_NAME", default_var="jobset-healthiness-ready-v6e"
100-
),
101-
region=models.Variable.get(
102-
"REGION", default_var=Region.US_CENTRAL1.value
103-
),
104-
location=models.Variable.get(
105-
"LOCATION", default_var=Region.US_CENTRAL1.value
106-
),
107-
node_locations=models.Variable.get(
108-
"LOCATIONS", default_var=Zone.US_CENTRAL1_B.value
109-
),
110-
num_nodes=models.Variable.get("NUM_NODES", default_var=4),
111-
machine_type=config.machine_version.value,
112-
tpu_topology=config.tpu_topology,
113-
)
11473

115-
cluster_info_2 = node_pool.Info(
116-
project_id=models.Variable.get("PROJECT_ID", default_var="cienet-cmcs"),
117-
cluster_name=models.Variable.get(
118-
"CLUSTER_NAME", default_var=cluster_name
119-
),
120-
node_pool_name=models.Variable.get(
121-
"NODE_POOL_NAME", default_var="jobset-healthiness-ready-v6e-2"
122-
),
123-
region=models.Variable.get(
124-
"REGION", default_var=Region.US_CENTRAL1.value
125-
),
126-
location=models.Variable.get(
127-
"LOCATION", default_var=Region.US_CENTRAL1.value
128-
),
129-
node_locations=models.Variable.get(
130-
"LOCATIONS", default_var=Zone.US_CENTRAL1_B.value
131-
),
132-
num_nodes=models.Variable.get("NUM_NODES", default_var=4),
133-
machine_type=config.machine_version.value,
134-
tpu_topology=config.tpu_topology,
135-
)
74+
@task
75+
def generate_second_node_pool_name(
76+
node_pool_info: node_pool.Info,
77+
) -> str:
78+
"""Generates a second node pool name."""
79+
return f"{node_pool_info.node_pool_name}-2"
13680

13781
jobset_config = JobSet(
13882
jobset_name="jobset-healthiness-ready",
@@ -155,36 +99,60 @@ def validate_replica_number(
15599
with TaskGroup( # pylint: disable=unexpected-keyword-arg
156100
group_id=f"v{config.tpu_version.value}"
157101
):
158-
create_node_pool = node_pool.create(
159-
node_pool=cluster_info,
160-
reservation="cloudtpu-20251107233000-1246578561",
102+
cluster_info = node_pool.build_node_pool_info_from_gcs_yaml.override(
103+
task_id="build_node_pool_info_from_gcs_yaml"
104+
)(
105+
gcs_path=GCS_CONFIG_PATH,
106+
dag_name="jobset_healthiness_ready",
107+
is_prod=composer_env.is_prod_env(),
108+
machine_type=config.machine_version.value,
109+
tpu_topology=config.tpu_topology,
161110
)
162111

163-
create_node_pool_2 = node_pool.create(
164-
node_pool=cluster_info_2,
165-
reservation="cloudtpu-20251107233000-1246578561",
112+
cluster_info_2 = node_pool.copy_node_pool_info_with_override(
113+
info=cluster_info,
114+
node_pool_name=generate_second_node_pool_name(cluster_info),
166115
)
167116

168-
validate_zero_replicas = validate_replica_number(
117+
# Keyword arguments are generated dynamically at runtime (pylint does not
118+
# know this signature).
119+
with TaskGroup( # pylint: disable=unexpected-keyword-arg
120+
group_id="create_node_pool"
121+
) as create_node_pool:
122+
create_first_node_pool = node_pool.create.override(
123+
task_id="node_pool_1",
124+
retries=2,
125+
)(
126+
node_pool=cluster_info,
127+
)
128+
129+
create_second_node_pool = node_pool.create.override(
130+
task_id="node_pool_2",
131+
retries=2,
132+
)(
133+
node_pool=cluster_info_2,
134+
)
135+
136+
validate_zero_replicas = jobset.validate_jobset_replica_number(
169137
node_pool=cluster_info,
170138
jobset_config=jobset_config,
171139
replica_type="ready",
172-
replica_num=0
140+
correct_replica_num=0,
173141
)
174142

175143
start_workload = jobset.run_workload(
176144
node_pool=cluster_info,
177145
yaml_config=jobset_config.generate_yaml(
178-
workload_script=Workload.READY_TPU
146+
workload_script=Workload.IDLE_READY_TPU_20M
179147
),
180148
namespace=jobset_config.namespace,
181149
)
182150

183-
validate_ready_replicas = validate_replica_number(
151+
validate_ready_replicas = jobset.validate_jobset_replica_number(
184152
node_pool=cluster_info,
185153
jobset_config=jobset_config,
186154
replica_type="ready",
187-
replica_num=jobset_config.replicas
155+
correct_replica_num=jobset_config.replicas,
188156
)
189157

190158
cleanup_workload = jobset.end_workload.override(
@@ -197,29 +165,38 @@ def validate_replica_number(
197165
setups=start_workload
198166
)
199167

200-
cleanup_node_pool = node_pool.delete.override(
201-
task_id="cleanup_node_pool", trigger_rule=TriggerRule.ALL_DONE
202-
)(node_pool=cluster_info).as_teardown(
203-
setups=create_node_pool,
204-
)
205-
206-
cleanup_node_pool_2 = node_pool.delete.override(
207-
task_id="cleanup_node_pool", trigger_rule=TriggerRule.ALL_DONE
208-
)(node_pool=cluster_info_2).as_teardown(
209-
setups=create_node_pool_2,
210-
)
168+
# Keyword arguments are generated dynamically at runtime (pylint does not
169+
# know this signature).
170+
with TaskGroup( # pylint: disable=unexpected-keyword-arg
171+
group_id="cleanup_node_pool"
172+
) as cleanup_node_pool:
173+
cleanup_first_node_pool = node_pool.delete.override(
174+
task_id="cleanup_node_pool_1",
175+
trigger_rule=TriggerRule.ALL_DONE,
176+
retries=2,
177+
)(node_pool=cluster_info).as_teardown(
178+
setups=create_node_pool,
179+
)
180+
181+
cleanup_second_node_pool = node_pool.delete.override(
182+
task_id="cleanup_node_pool_2",
183+
trigger_rule=TriggerRule.ALL_DONE,
184+
retries=2,
185+
)(node_pool=cluster_info_2).as_teardown(
186+
setups=create_node_pool,
187+
)
211188

212189
# Airflow uses >> for task chaining, which is pointless for pylint.
213190
# pylint: disable=pointless-statement
214191
(
215-
create_node_pool
216-
>> create_node_pool_2
192+
cluster_info
193+
>> cluster_info_2
194+
>> create_node_pool
217195
>> validate_zero_replicas
218196
>> start_workload
219197
>> validate_ready_replicas
220198
>> cleanup_workload
221199
>> cleanup_node_pool
222-
>> cleanup_node_pool_2
223200

224201
)
225202
# pylint: enable=pointless-statement

dags/tpu_observability/utils/jobset_util.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ class Workload:
4343
Each workload is a JSON-escaped string, ready to be used as a shell argument.
4444
"""
4545

46-
READY_TPU = json.dumps(
46+
IDLE_READY_TPU_20M = json.dumps(
4747
textwrap.dedent(
4848
"""
49-
sleep 1000
49+
sleep 1200
5050
"""
5151
),
52-
ensure_ascii=False
52+
ensure_ascii=False,
5353
)
5454

5555
JAX_TPU_BENCHMARK = json.dumps(
@@ -560,26 +560,29 @@ def wait_for_jobset_ttr_to_be_found(node_pool: node_pool.Info) -> bool:
560560
return len(time_series) > 0
561561

562562

563-
@task.sensor(poke_interval=30, timeout=600, mode="reschedule")
564-
def wait_for_jobset_status_occurrence(
565-
replica_type: str, job_name: str, node_pool: node_pool.Info
563+
@task.sensor(poke_interval=30, timeout=900, mode="reschedule")
564+
def validate_jobset_replica_number(
565+
node_pool: node_pool.Info,
566+
jobset_config: JobSet,
567+
replica_type: str,
568+
correct_replica_num: int,
566569
):
567570
"""
568-
A sensor which checks if are any jobset replicas in a status type.
571+
A sensor which checks if the correct number jobset replicas in a status type.
569572
570573
Args:
571-
replica_type(str): The type of status being checked for.
572-
job_name(str): The name of the job replica which is run from the jobset.
573-
node_pool(Info): The Info object containing the cluster information needed
574-
for the kubernetes API to connect to it.
574+
node_pool: Configuration object with cluster details.
575+
jobset_config: Configuration of JobSet which is being run.
576+
replica_type(str): The name of the type of status being checked for.
577+
correct_replica_num(int): The expected number of replicas to be found.
575578
"""
576579
logging.info("Checking for number of replicas of type: %s", replica_type)
577580
ready_replicas = get_replica_num(
578581
replica_type=replica_type,
579-
job_name=job_name,
582+
job_name=jobset_config.replicated_job_name,
580583
node_pool=node_pool,
581584
)
582-
return ready_replicas > 0
585+
return ready_replicas == correct_replica_num
583586

584587

585588
@task.sensor(poke_interval=30, timeout=600, mode="reschedule")

0 commit comments

Comments
 (0)