Skip to content

Commit 8d2e62b

Browse files
abhishek002002Orbax Authors
authored andcommitted
Remove emergency directory from Orbax copybara exclusions.
PiperOrigin-RevId: 932263086
1 parent 3813648 commit 8d2e62b

14 files changed

Lines changed: 1318 additions & 16 deletions

File tree

checkpoint/orbax/checkpoint/_src/testing/oss/multiprocess_test.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from absl.testing import absltest
3333
import jax
3434
from jax import config
35+
from orbax.checkpoint._src.futures import synchronization
3536
from orbax.checkpoint._src.multihost import multihost
3637
import portpicker
3738

@@ -299,6 +300,22 @@ def _main(argv):
299300
assert retval == 0, f"process {i} failed, return value: {retval}"
300301

301302

303+
def _sync_operation_id(client, sync_key: str):
304+
"""Synchronizes the OperationIdGenerator across processes."""
305+
if jax.process_index() == 0:
306+
client.key_value_set(
307+
sync_key,
308+
synchronization.OperationIdGenerator.get_current_operation_id(),
309+
allow_overwrite=True,
310+
)
311+
target = int(client.blocking_key_value_get(sync_key, 10000))
312+
while (
313+
int(synchronization.OperationIdGenerator.get_current_operation_id())
314+
< target
315+
):
316+
synchronization.OperationIdGenerator.next_operation_id()
317+
318+
302319
class MultiProcessTest(absltest.TestCase):
303320
# TODO(b/378138653) Support TPUless MultiProcessTest.
304321

@@ -318,6 +335,8 @@ def setUp(self):
318335
f"multiprocess_test_ensure_all_processes_arrive_at_test_case_{self._testMethodName}",
319336
10000,
320337
)
338+
sync_key = f"sync_op_id_{self._testMethodName}"
339+
_sync_operation_id(client, sync_key)
321340

322341
def multiprocess_create_tempdir(self, name: str | None = None) -> str:
323342
"""Creates a temporary directory for the test."""

checkpoint/orbax/checkpoint/_src/testing/oss/run_tests.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
from absl import app
2323
from absl import flags
2424
from absl import logging
25+
import jax
26+
from orbax.checkpoint._src.futures import synchronization
27+
from orbax.checkpoint._src.multihost import multihost
2528
import pytest
2629
import yaml
2730

@@ -80,6 +83,29 @@ def _find_test_path(test_file_yaml):
8083
return None
8184

8285

86+
def _sync_op_id_generator(test_file_yaml: str) -> None:
87+
"""Synchronizes the OperationIdGenerator across processes."""
88+
try:
89+
client = multihost.get_jax_distributed_client()
90+
if client is not None:
91+
normalized_name = test_file_yaml.replace('/', '_').replace(':', '_')
92+
sync_key = f'sync_op_id_file_{normalized_name}'
93+
operation_id_generator = synchronization.OperationIdGenerator
94+
if jax.process_index() == 0:
95+
client.key_value_set(
96+
sync_key,
97+
operation_id_generator.get_current_operation_id(),
98+
allow_overwrite=True,
99+
)
100+
target = int(client.blocking_key_value_get(sync_key, 10000))
101+
while int(operation_id_generator.get_current_operation_id()) < target:
102+
operation_id_generator.next_operation_id()
103+
except Exception as sync_e: # pylint: disable=broad-exception-caught
104+
logging.warning(
105+
'Could not synchronize OperationIdGenerator for file: %s', sync_e
106+
)
107+
108+
83109
def main(argv: Sequence[str]) -> None:
84110
if len(argv) > 1:
85111
raise app.UsageError('Too many command-line arguments.')
@@ -130,7 +156,9 @@ def main(argv: Sequence[str]) -> None:
130156

131157
logging.info('Running test: %s (found from %s)', test_path, test_file_yaml)
132158
try:
133-
exit_code = pytest.main([test_path])
159+
_sync_op_id_generator(test_file_yaml)
160+
161+
exit_code = pytest.main(['--import-mode=importlib', test_path])
134162
if exit_code == 0:
135163
results[test_file_yaml] = 'PASSED'
136164
logging.info('%s: PASSED', test_path)

checkpoint/orbax/checkpoint/_src/testing/oss/tagged_tests_presubmit.yaml

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,10 @@ processes:1:
1111
- orbax/checkpoint/_src/serialization:serialization_test
1212
- orbax/checkpoint/experimental/emergency/multi_tier_checkpointing:pathways_process_metadata_checkpoint_handler_test
1313
- orbax/checkpoint/experimental/emergency/multi_tier_checkpointing:pathways_replicator_checkpoint_manager_test
14+
- orbax/checkpoint/experimental/v1/_src/emergency:deleter_test
15+
- orbax/checkpoint/experimental/v1/_src/emergency:path_utils_test
1416
- orbax/checkpoint:single_host_test
1517
processes:2:
1618
- orbax/checkpoint/_src/handlers:array_checkpoint_handler_test
17-
- orbax/checkpoint/_src/handlers:pytree_checkpoint_handler_test
18-
- orbax/checkpoint/_src/handlers:standard_checkpoint_handler_test
19-
- orbax/checkpoint/_src/serialization:local_type_handlers_test
20-
- orbax/checkpoint/_src/serialization:type_handlers_test
21-
- orbax/checkpoint/experimental/emergency/p2p:checkpoint_manager_multiprocess_test
22-
- orbax/checkpoint/experimental/emergency/p2p:local_multiprocess_test
23-
- orbax/checkpoint/experimental/emergency/p2p:persistent_multiprocess_test
2419
processes:4:
2520
- orbax/checkpoint/_src/multihost:multihost_test
26-
- orbax/checkpoint/_src/testing/tree_verity:checkpoint_manager_test
27-
- orbax/checkpoint/experimental/emergency/multi_tier_checkpointing:process_metadata_checkpoint_handler_test
28-
- orbax/checkpoint/experimental/emergency:local_checkpoint_data_debugging_test
29-
- orbax/checkpoint/experimental/emergency:local_checkpoint_manager_test
30-
- orbax/checkpoint/experimental/emergency:single_slice_checkpoint_manager_test
31-
- orbax/checkpoint/testing:local_path_test
32-
- orbax/checkpoint:checkpoint_manager_slice_test
33-
- orbax/checkpoint:checkpoint_manager_test
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Copyright 2026 The Orbax Authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import time
16+
17+
from absl import flags
18+
from absl.testing import flagsaver
19+
from absl.testing import parameterized
20+
from etils import epath
21+
import jax
22+
import numpy as np
23+
from orbax.checkpoint import args
24+
from orbax.checkpoint import checkpoint_manager
25+
from orbax.checkpoint import checkpoint_utils
26+
from orbax.checkpoint import test_utils
27+
from orbax.checkpoint import utils
28+
from orbax.checkpoint._src.handlers import handler_registration
29+
from orbax.checkpoint._src.handlers import pytree_checkpoint_handler
30+
from orbax.checkpoint._src.metadata import array_metadata_store as array_metadata_store_lib
31+
from orbax.checkpoint._src.multihost import multihost
32+
from orbax.checkpoint._src.serialization import type_handler_registry
33+
from orbax.checkpoint._src.serialization import type_handlers
34+
from orbax.checkpoint._src.testing import multiprocess_test
35+
36+
37+
FLAGS = flags.FLAGS
38+
PyTreeCheckpointHandler = pytree_checkpoint_handler.PyTreeCheckpointHandler
39+
CheckpointManager = checkpoint_manager.CheckpointManager
40+
CheckpointManagerOptions = checkpoint_manager.CheckpointManagerOptions
41+
42+
43+
@test_utils.barrier_compatible_test
44+
class CheckpointManagerSliceTest(
45+
parameterized.TestCase, multiprocess_test.MultiProcessTest
46+
):
47+
"""Structure allows test to run as subclasses, not base class."""
48+
49+
def setUp(self):
50+
super().setUp()
51+
52+
if not multihost.is_runtime_to_distributed_ids_initialized():
53+
multihost.initialize_runtime_to_distributed_ids()
54+
55+
self.assertEqual(jax.device_count(), 8)
56+
self.assertEqual(jax.process_count(), 4)
57+
self.assertEqual(jax.local_device_count(), 2)
58+
59+
self.directory = epath.Path(
60+
self.multiprocess_create_tempdir(name='checkpoint_manager_slice_test')
61+
)
62+
test_utils.set_tensorstore_driver_for_test()
63+
64+
test_utils.sync_global_processes(
65+
'CheckpointManagerSliceTest:setup_complete'
66+
)
67+
68+
def tearDown(self):
69+
test_utils.sync_global_processes(
70+
'CheckpointManagerSliceTest:tests_complete'
71+
)
72+
super().tearDown()
73+
74+
def wait_if_async(self, manager):
75+
manager.wait_until_finished() # no-op if no async checkpointers.
76+
77+
@parameterized.product(
78+
enable_async_checkpointing=(False, True),
79+
array_metadata_store=(None, array_metadata_store_lib.Store()),
80+
)
81+
def test_slice(
82+
self,
83+
enable_async_checkpointing: bool,
84+
array_metadata_store: array_metadata_store_lib.Store | None,
85+
):
86+
"""Test slice."""
87+
self.enter_context(
88+
flagsaver.flagsaver(experimental_orbax_use_distributed_process_id=True)
89+
)
90+
global_mesh = test_utils.get_fake_global_mesh_for_slices([{0, 1}, {2, 3}])
91+
92+
mesh_axes = jax.sharding.PartitionSpec('data')
93+
arrays = [
94+
test_utils.create_sharded_array(arr, global_mesh, mesh_axes)
95+
for arr in [np.arange(8), np.arange(16)]
96+
]
97+
assert len(global_mesh.devices[0]) == 4
98+
assert jax.process_count() == 4
99+
active_processes = {0, 1}
100+
primary_host = 0
101+
if multihost.process_index() in active_processes:
102+
single_slice_arrays = test_utils.select_single_replica(
103+
arrays, global_mesh
104+
)
105+
options = CheckpointManagerOptions(
106+
create=False,
107+
enable_async_checkpointing=enable_async_checkpointing,
108+
multiprocessing_options=checkpoint_manager.MultiprocessingOptions(
109+
primary_host=primary_host,
110+
active_processes=active_processes,
111+
),
112+
)
113+
registry = type_handler_registry.create_type_handler_registry(
114+
(
115+
jax.Array,
116+
type_handlers.ArrayHandler(
117+
primary_host=None,
118+
replica_id=None,
119+
use_replica_parallel=False,
120+
array_metadata_store=array_metadata_store,
121+
),
122+
),
123+
)
124+
handler = PyTreeCheckpointHandler(
125+
multiprocessing_options=options.multiprocessing_options,
126+
type_handler_registry=registry,
127+
)
128+
registry = handler_registration.DefaultCheckpointHandlerRegistry()
129+
registry.add(None, args.PyTreeSave, handler)
130+
registry.add(None, args.PyTreeRestore, handler)
131+
with CheckpointManager(
132+
self.directory,
133+
options=options,
134+
handler_registry=registry,
135+
) as manager:
136+
self.assertTrue(manager.save(0, args=args.PyTreeSave(arrays)))
137+
time.sleep(10)
138+
self.wait_if_async(manager)
139+
abstract_target = jax.tree.map(
140+
utils.to_shape_dtype_struct, single_slice_arrays
141+
)
142+
restore_args = checkpoint_utils.construct_restore_args(abstract_target)
143+
restored = manager.restore(
144+
0, args=args.PyTreeRestore(restore_args=restore_args)
145+
)
146+
test_utils.assert_tree_equal(self, single_slice_arrays, restored)
147+
148+
149+
if __name__ == '__main__':
150+
multiprocess_test.main()

checkpoint/orbax/checkpoint/experimental/emergency/checkpoint_manager_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ def setUp(self):
9999
)
100100
if not multihost.is_runtime_to_distributed_ids_initialized():
101101
multihost.initialize_runtime_to_distributed_ids()
102+
if not multihost.is_distributed_to_device_ids_initialized():
102103
multihost.initialize_distributed_to_device_ids()
103104

104105
# make sure each process is working on different directories

checkpoint/orbax/checkpoint/experimental/emergency/multi_tier_checkpointing/replicator_checkpoint_manager_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ def setUp(self):
209209
)
210210
if not multihost.is_runtime_to_distributed_ids_initialized():
211211
multihost.initialize_runtime_to_distributed_ids()
212+
if not multihost.is_distributed_to_device_ids_initialized():
212213
multihost.initialize_distributed_to_device_ids()
213214

214215
self.global_mesh = self.make_global_mesh()

checkpoint/orbax/checkpoint/experimental/emergency/single_slice_checkpoint_manager_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ def setUp(self):
5858
)
5959
if not multihost.is_runtime_to_distributed_ids_initialized():
6060
multihost.initialize_runtime_to_distributed_ids()
61+
if not multihost.is_distributed_to_device_ids_initialized():
6162
multihost.initialize_distributed_to_device_ids()
6263

6364
self.local_directory = epath.Path(
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Copyright 2026 The Orbax Authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Deleter that dispatches to Pathways workers with Remote Python."""
16+
17+
from typing import Sequence
18+
import jax
19+
from orbax.checkpoint._src.multihost import dispatchers
20+
from orbax.checkpoint._src.path import deleter as deleter_lib
21+
from orbax.checkpoint._src.path import step as step_lib
22+
from orbax.checkpoint.experimental.v1._src.path import types as path_types
23+
24+
25+
CheckpointDeleter = deleter_lib.CheckpointDeleter
26+
27+
28+
class _PathwaysDeleter(deleter_lib.CheckpointDeleter):
29+
"""Deleter that dispatches to Pathways workers with Remote Python."""
30+
31+
def __init__(
32+
self,
33+
deleter: deleter_lib.StandardCheckpointDeleter,
34+
global_mesh: jax.sharding.Mesh | None,
35+
):
36+
self._global_mesh = global_mesh or jax.sharding.Mesh(jax.devices(), 'x')
37+
self._deleter = deleter
38+
self._dispatcher = dispatchers.RemotePythonDispatcher()
39+
40+
def delete(self, step: int) -> None:
41+
"""Deletes a step.
42+
43+
Args:
44+
step: The step to delete.
45+
"""
46+
47+
def _delete(
48+
input_arrays: jax.Array,
49+
step: int,
50+
):
51+
del input_arrays
52+
self._deleter.delete(step)
53+
54+
jax.block_until_ready(
55+
self._dispatcher.dispatch(
56+
_delete,
57+
input_arrays=dispatchers.get_dummy_input_array(
58+
self._global_mesh.devices.flatten().tolist(),
59+
),
60+
func_kwargs={'step': step},
61+
)
62+
)
63+
64+
def delete_steps(self, steps: Sequence[int]) -> None:
65+
"""Deletes a sequence of steps.
66+
67+
Args:
68+
steps: The steps to delete.
69+
"""
70+
def _delete(
71+
input_arrays: jax.Array,
72+
steps: Sequence[int],
73+
):
74+
del input_arrays
75+
self._deleter.delete_steps(steps)
76+
77+
jax.block_until_ready(
78+
self._dispatcher.dispatch(
79+
_delete,
80+
input_arrays=dispatchers.get_dummy_input_array(
81+
self._global_mesh.devices.flatten().tolist(),
82+
),
83+
func_kwargs={'steps': steps},
84+
)
85+
)
86+
87+
def close(self) -> None:
88+
"""Performs any cleanup before closing this deleter."""
89+
self._deleter.close()
90+
91+
92+
def create_checkpoint_deleter(
93+
directory: path_types.Path,
94+
*,
95+
global_mesh: jax.sharding.Mesh | None = None,
96+
name_format: step_lib.NameFormat[step_lib.Metadata],
97+
todelete_subdir: str | None = None,
98+
) -> CheckpointDeleter:
99+
return _PathwaysDeleter(
100+
deleter_lib.StandardCheckpointDeleter(
101+
directory,
102+
name_format=name_format,
103+
todelete_subdir=todelete_subdir,
104+
primary_host=None,
105+
),
106+
global_mesh,
107+
)

0 commit comments

Comments
 (0)