Skip to content

Commit 73d8bee

Browse files
Add a new DAG that tests orbax emergency checkpointing manager saving feature to GCS Bucket feature. (GoogleCloudPlatform#870)
This change adds a new DAG which test and verifies the Orbax Emergency Checkpointer manager saving functionality of checkpoints to GCS bucket. It checks `checkpoint_steps` are stored into GCS bucket. This DAG automate the installation of the mtc driver, but requires an existing cluster and an GCS bucket with HNS enabled. --------- Co-authored-by: Alfred Yu <alfred.yu@cienet.com>
1 parent 4e23af0 commit 73d8bee

7 files changed

Lines changed: 232 additions & 32 deletions

dags/orbax/maxtext_emc_restore_local.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,15 @@
88

99
from dags import composer_env
1010
from dags.common import test_owner
11-
from dags.common.vm_resource import DockerImage
1211
from dags.common.vm_resource import XpkClusters
1312
from dags.multipod.configs import gke_config
14-
from dags.multipod.configs.common import SetupMode
1513
from dags.orbax.util import checkpoint_util, test_config_util, validation_util
1614
from xlml.utils.gke import zone_to_region
1715
from xlml.utils.xpk import BRANCH_ABHINAV_MTC
1816

1917
DAG_TEST_NAME = "maxtext_emc_orbax_res_local"
2018
SCHEDULE = "0 11 * * *" if composer_env.is_prod_env() else None
2119

22-
# Only one version of the Docker image is supported at the moment.
23-
# Other versions (e.g., "stable") may be introduced later.
24-
DOCKER_IMAGES = [(
25-
SetupMode.NIGHTLY,
26-
DockerImage.MAXTEXT_TPU_JAX_ORBAX_HEAD,
27-
)]
2820

2921
with models.DAG(
3022
dag_id=DAG_TEST_NAME,
@@ -90,7 +82,7 @@
9082

9183
step_to_interrupt = 40
9284

93-
for mode, image in DOCKER_IMAGES:
85+
for mode, image in test_config_util.DOCKER_IMAGES:
9486
for test_config in test_configs:
9587
for slice_num in test_config.slices:
9688
wait_delete_cpc = checkpoint_util.wait_for_cpc_deletion.override(

dags/orbax/maxtext_emc_save_gcs.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
"""
2+
A DAG to run MaxText multi-tier checkpointing with replicator enabled
3+
validates the local checkpoints are replicated (copy) to bucket
4+
with HNS (Hierarchical Namespace)
5+
"""
6+
7+
import datetime
8+
9+
from airflow import models
10+
11+
from dags import composer_env
12+
from dags.common import test_owner
13+
from dags.common.vm_resource import XpkClusters
14+
from dags.multipod.configs import gke_config
15+
from dags.orbax.util import checkpoint_util
16+
from dags.orbax.util import validation_util
17+
from xlml.utils.xpk import BRANCH_ABHINAV_MTC
18+
from xlml.utils.gke import zone_to_region
19+
from dags.orbax.util import test_config_util
20+
21+
22+
SCHEDULE = "0 14 * * *" if composer_env.is_prod_env() else None
23+
DAG_TEST_NAME = "maxtext_emc_save_gcs"
24+
25+
26+
with models.DAG(
27+
dag_id=DAG_TEST_NAME,
28+
start_date=datetime.datetime(2025, 6, 30),
29+
schedule_interval=SCHEDULE,
30+
catchup=False,
31+
tags=[
32+
"multipod_team",
33+
"maxtext",
34+
"emergency_checkpointing",
35+
"nightly",
36+
"orbax",
37+
],
38+
description="DAG that verifies the orbax multi-tier checkpointing saving functionality with replicator to GCS bucket",
39+
doc_md="""
40+
# Multi-tier Checkpoint Validation DAG
41+
42+
### Description
43+
This DAG (Directed Acyclic Graph) automates the process of validating
44+
checkpoint saving when using **Emergency Checkpointer Manager** features.
45+
It will check that the checkpoints are being stored in the GCS bucket.
46+
Also the steps flag controls how many steps the job will run.
47+
48+
### Prerequisites
49+
To run this test, you need an existing cluster with the Multi-tier
50+
Checkpointing configuration enabled, as well as a bucket with HNS
51+
(Hierarchical Namespace) enabled.
52+
53+
### Procedures
54+
1. **Apply Configuration:** A Checkpoint Configuration YAML file is
55+
applied to the cluster, enabling Multi-tier Checkpoint (MTC) features.
56+
2. **Run Maxtext Jobsets:** The DAG runs a Maxtext jobset.
57+
3. The DAG validates that **GCS checkpoints** are being saved correctly
58+
in the `GCS bucket` by checking bucket and pod logs.
59+
""",
60+
concurrency=2,
61+
) as dag:
62+
checkpointing = test_config_util.Checkpointing(
63+
name="emc", enable_multi_tier_checkpointing=False
64+
)
65+
test_configs = [
66+
test_config_util.TestConfig(
67+
cluster=XpkClusters.TPU_V5P_128_CLUSTER,
68+
machine_type="ct5p-hightpu-4t",
69+
accelerator="v5p-128",
70+
slices=[2],
71+
model_name="llama2-7b",
72+
short_id="max-sv-gcs",
73+
replicator_backup_time=30,
74+
step=75,
75+
local_checkpoint_step=20,
76+
checkpoint_step=25,
77+
base_dir=test_config_util.DEFAULT_BUCKET,
78+
),
79+
]
80+
for mode, image in test_config_util.DOCKER_IMAGES:
81+
for test_config in test_configs:
82+
for slice_num in test_config.slices:
83+
# We conditionally set the trigger_rule on the first task.
84+
# If first task group failed the next one can execute.
85+
wait_delete_cpc = checkpoint_util.wait_for_cpc_deletion.override(
86+
trigger_rule="all_done"
87+
)(test_config.cpc_config)
88+
apply_cpc = checkpoint_util.apply_cpc(test_config.cpc_config)
89+
90+
# Generate consistent run name for both training phases
91+
run_name = validation_util.generate_run_name(
92+
short_id=test_config.short_id,
93+
checkpointing_type=checkpointing.name,
94+
slice_number=slice_num,
95+
accelerator=test_config.accelerator,
96+
)
97+
98+
workload_command = test_config.generate_workload_command(
99+
checkpoint_dir=test_config_util.DEFAULT_RAM_DISK,
100+
run_name=run_name,
101+
out_folder=f"maxtext_emc_orbax_save_gcs",
102+
enable_multi_tier_checkp=checkpointing.enable_multi_tier_checkpointing,
103+
slice_num=slice_num,
104+
)
105+
106+
start_time = validation_util.generate_timestamp()
107+
108+
maxtext_chkpt_run_test = gke_config.get_gke_config(
109+
num_slices=slice_num,
110+
cluster=test_config.cluster,
111+
time_out_in_min=60,
112+
test_name=f"{test_config.short_id}-emc",
113+
run_model_cmds=workload_command,
114+
docker_image=image.value,
115+
test_owner=test_owner.CAMILO_Q,
116+
).run(
117+
ramdisk_directory=test_config_util.DEFAULT_RAM_DISK,
118+
mtc_enabled=True,
119+
xpk_branch=BRANCH_ABHINAV_MTC,
120+
skip_post_process=True,
121+
)
122+
123+
end_time = validation_util.generate_timestamp()
124+
125+
steps_to_validate = test_config.generate_step_to_validate(
126+
is_local=False
127+
)
128+
129+
validate_steps = validation_util.validate_checkpoint_at_steps_are_saved(
130+
project_id=test_config.cluster.project,
131+
location=zone_to_region(test_config.cluster.zone),
132+
cluster_name=test_config.cluster.name,
133+
ram_disk="gcs",
134+
pod_pattern=".*-0-0",
135+
start_time=start_time,
136+
end_time=end_time,
137+
steps_to_validate=steps_to_validate,
138+
)
139+
140+
# Validate that GCS restore happened during the second training run
141+
validate_checkpoints_steps_gcs = validation_util.validate_gcs_checkpoint_files(
142+
bucket_path=f"{test_config_util.DEFAULT_BUCKET}/maxtext_emc_orbax_save_gcs/{run_name}",
143+
steps_to_validate=steps_to_validate,
144+
)
145+
146+
# Final CPC cleanup to ensure symmetric start/end
147+
wait_delete_cpc_final = checkpoint_util.wait_for_cpc_deletion.override(
148+
trigger_rule="all_done", task_id="wait_delete_cpc_final"
149+
)(test_config.cpc_config).as_teardown(setups=apply_cpc)
150+
151+
(
152+
wait_delete_cpc
153+
>> apply_cpc
154+
>> run_name
155+
>> start_time
156+
>> maxtext_chkpt_run_test
157+
>> end_time
158+
>> validate_steps
159+
>> validate_checkpoints_steps_gcs
160+
>> wait_delete_cpc_final
161+
)

dags/orbax/maxtext_mtc_emergency_save_local.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@
1414

1515
from dags import composer_env
1616
from dags.common import test_owner
17-
from dags.common.vm_resource import DockerImage, XpkClusters
17+
from dags.common.vm_resource import XpkClusters
1818
from dags.multipod.configs import gke_config
19-
from dags.multipod.configs.common import SetupMode
2019
from dags.orbax.util import validation_util
2120
from dags.orbax.util import checkpoint_util
2221
from xlml.utils.xpk import BRANCH_ABHINAV_MTC
@@ -27,13 +26,6 @@
2726
SCHEDULE = "0 13 * * *" if composer_env.is_prod_env() else None
2827
DAG_TEST_NAME = "maxtext_emc_and_mtc_orbax_save_local"
2928

30-
# Only one version of the Docker image is supported at the moment.
31-
# Other versions (e.g., "stable") may be introduced later.
32-
DOCKER_IMAGES = [(
33-
SetupMode.NIGHTLY,
34-
DockerImage.MAXTEXT_TPU_JAX_ORBAX_HEAD,
35-
)]
36-
3729

3830
with models.DAG(
3931
dag_id=DAG_TEST_NAME,
@@ -112,7 +104,7 @@
112104
with TaskGroup(
113105
group_id=f"maxtext_{checkpointing.name}_orbax_save_local",
114106
) as group:
115-
for mode, image in DOCKER_IMAGES:
107+
for mode, image in test_config_util.DOCKER_IMAGES:
116108
for test_config in test_configs:
117109
for slice_num in test_config.slices:
118110
# We conditionally set the trigger_rule on the first task.

dags/orbax/maxtext_mtc_save_gcs.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010

1111
from dags import composer_env
1212
from dags.common import test_owner
13-
from dags.common.vm_resource import DockerImage, XpkClusters
13+
from dags.common.vm_resource import XpkClusters
1414
from dags.multipod.configs import gke_config
15-
from dags.multipod.configs.common import SetupMode
1615
from dags.orbax.util import checkpoint_util
1716
from dags.orbax.util import validation_util
1817
from xlml.utils.xpk import BRANCH_ABHINAV_MTC
@@ -23,13 +22,6 @@
2322
SCHEDULE = "0 15 * * *" if composer_env.is_prod_env() else None
2423
DAG_TEST_NAME = "maxtext_mtc_orbax_save_gcs"
2524

26-
# Only one version of the Docker image is supported at the moment.
27-
# Other versions (e.g., "stable") may be introduced later.
28-
DOCKER_IMAGES = [(
29-
SetupMode.NIGHTLY,
30-
DockerImage.MAXTEXT_TPU_JAX_ORBAX_HEAD,
31-
)]
32-
3325

3426
with models.DAG(
3527
dag_id=DAG_TEST_NAME,
@@ -87,7 +79,7 @@
8779
base_dir=test_config_util.DEFAULT_BUCKET,
8880
),
8981
]
90-
for mode, image in DOCKER_IMAGES:
82+
for mode, image in test_config_util.DOCKER_IMAGES:
9183
for test_config in test_configs:
9284
for slice_num in test_config.slices:
9385
# We conditionally set the trigger_rule on the first task.

dags/orbax/util/checkpoint_util.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ def load_yaml_and_parse_body(
5353
full parsed YAML body as a dictionary.
5454
"""
5555

56-
logging.info(f"RAMDISK ==> {self.ramdisk_memory_in_mi}")
5756
cpc_yaml_template = f"""
5857
apiVersion: checkpointing.gke.io/v1
5958
kind: CheckpointConfiguration

dags/orbax/util/test_config_util.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,21 @@
1111

1212
from dags import gcs_bucket
1313
from xlml.utils.gke import zone_to_region
14-
from dags.common.vm_resource import XpkClusters
14+
from dags.common.vm_resource import XpkClusters, DockerImage
1515
from dags.orbax.util import checkpoint_util
16+
from dags.multipod.configs.common import SetupMode
1617

1718

1819
DEFAULT_BUCKET = gcs_bucket.MTC_AUTOMATION_BUCKET
1920
DEFAULT_RAM_DISK = "/local"
2021

22+
# Only one version of the Docker image is supported at the moment.
23+
# Other versions (e.g., "stable") may be introduced later.
24+
DOCKER_IMAGES = [(
25+
SetupMode.NIGHTLY,
26+
DockerImage.MAXTEXT_TPU_JAX_ORBAX_HEAD,
27+
)]
28+
2129
# Valid models and sizes for current Maxtext Repository.
2230
MODELS = {
2331
"deepseek2",
@@ -111,7 +119,7 @@ def __init__(
111119
self.step = step
112120
self.local_checkpoint_step = local_checkpoint_step
113121
self.checkpoint_step = checkpoint_step
114-
self.ram_disk_size = f"{self._get_disk_size(slice_num=max(self.slices), mode='mib',multiplier=30)}Mi"
122+
self.ram_disk_size = f"{self._get_disk_size(slice_num=max(self.slices), mode='mib',multiplier=60)}Mi"
115123
self.base_dir = base_dir
116124
self.cpc_config = checkpoint_util.CheckpointConfiguration(
117125
project_id=self.cluster.project,

dags/orbax/util/validation_util.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,62 @@ def validate_log_with_gcs(
248248
return max(gcs_save_step_list), max(gcs_save_step_list_bucket)
249249

250250

251+
@task
252+
def validate_gcs_checkpoint_files(
253+
bucket_path: str,
254+
steps_to_validate: Optional[list] = None,
255+
) -> None:
256+
"""
257+
Validates that checkpoint files exist in GCS bucket for expected steps.
258+
This function uses the GCS utility to check that checkpoint files
259+
are properly saved in the bucket for each expected step.
260+
Args:
261+
bucket_path: The full gs:// path to the GCS bucket
262+
vali_step_list: Optional list of steps to validate
263+
Returns:
264+
None: Raises AirflowFailException if checkpoint validation fails
265+
"""
266+
if steps_to_validate is None:
267+
logging.info(
268+
"No validation steps provided, skipping GCS checkpoint validation"
269+
)
270+
return
271+
272+
try:
273+
checkpoint_files = get_gcs_checkpoint(bucket_path)
274+
logging.info(f"Found checkpoint files in GCS: {checkpoint_files}")
275+
276+
# Extract step directories from checkpoint files
277+
found_steps = set()
278+
for file_path in checkpoint_files:
279+
# Extract directory names that are numeric (step numbers)
280+
path_parts = file_path.split("/")
281+
for part in path_parts:
282+
if part.isdigit():
283+
found_steps.add(int(part))
284+
285+
expected_steps = set(steps_to_validate)
286+
missing_steps = expected_steps - found_steps
287+
288+
logging.info(f"Expected steps: {sorted(expected_steps)}")
289+
logging.info(f"Found steps: {sorted(found_steps)}")
290+
291+
if missing_steps:
292+
raise AirflowFailException(
293+
f"GCS checkpoint validation failed: Missing checkpoint files for steps {sorted(missing_steps)}. "
294+
f"Expected steps: {sorted(steps_to_validate)}, Found steps: {sorted(found_steps)}"
295+
)
296+
297+
logging.info(f"GCS checkpoint validation successful!")
298+
logging.info(
299+
f"All {len(steps_to_validate)} expected checkpoint files found in GCS"
300+
)
301+
logging.info(f"Validated steps: {sorted(found_steps)}")
302+
303+
except Exception as e:
304+
raise AirflowFailException(f"Error validating GCS checkpoints: {str(e)}")
305+
306+
251307
def list_log_entries(
252308
project_id: str,
253309
location: str,

0 commit comments

Comments
 (0)