pathwaysutils 0.1.10__tar.gz → 0.1.11__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/PKG-INFO +2 -2
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/__init__.py +1 -1
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/debug/watchdog.py +1 -1
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/elastic/elastic.py +8 -7
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/elastic/manager.py +178 -107
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/gke/jobset.py +368 -24
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/shared_pathways_service/deploy_pathways_service.py +51 -50
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/shared_pathways_service/metrics_collector.py +1 -1
- pathwaysutils-0.1.11/pathwaysutils/experimental/shared_pathways_service/tpu_specs.py +109 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/split_by_mesh_axis.py +1 -1
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/lru_cache.py +3 -3
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/persistence/helper.py +6 -6
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/persistence/orbax_handler.py +5 -5
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/plugin_executable.py +2 -2
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/profiling.py +10 -8
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/reshard.py +1 -1
- pathwaysutils-0.1.11/pathwaysutils/test/experimental/gke/jobset_test.py +977 -0
- pathwaysutils-0.1.10/pathwaysutils/experimental/shared_pathways_service/yamls/pw-service.yaml → pathwaysutils-0.1.11/pathwaysutils/test/experimental/gke/testdata/model_lite_tpuv5e_4x8.yaml +106 -73
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/test/profiling_test.py +2 -2
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pyproject.toml +1 -1
- pathwaysutils-0.1.10/pathwaysutils/test/experimental/gke/jobset_test.py +0 -210
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/LICENSE +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/README.md +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/_initialize.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/collect_profile.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/debug/__init__.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/debug/timing.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/elastic/__init__.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/__init__.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/concatenate_by_mesh_axis.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/gke/__init__.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/profiling.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/reshard.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/shared_pathways_service/README.md +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/shared_pathways_service/__init__.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/shared_pathways_service/dockerfiles/sample_sidecar.dockerfile +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/shared_pathways_service/gke_utils.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/shared_pathways_service/isc_pathways.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/shared_pathways_service/run_connect_example.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/shared_pathways_service/run_workload.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/shared_pathways_service/start_vscode_on_cpu_np.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/shared_pathways_service/validators.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/shared_pathways_service/yamls/code-server.yaml +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/experimental/shared_pathways_service/yamls/pw-proxy.yaml +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/jax/__init__.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/persistence/__init__.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/proxy_backend.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/test/__init__.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/test/debug/__init__.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/test/debug/timing_test.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/test/debug/watchdog_test.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/test/experimental/reshard_test.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/test/initialize_test.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/test/lru_cache_test.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/test/persistence_test.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/test/plugin_executable_test.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/test/proxy_backend_test.py +0 -0
- {pathwaysutils-0.1.10 → pathwaysutils-0.1.11}/pathwaysutils/test/reshard_test.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pathwaysutils
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.11
|
|
4
4
|
Summary: Package of Pathways-on-Cloud utilities.
|
|
5
5
|
Keywords:
|
|
6
6
|
Author-email: Pathways-on-Cloud Utilities Developers <pathwaysutils-dev@google.com>
|
|
7
|
-
Requires-Python: >=3.
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
10
10
|
License-File: LICENSE
|
|
@@ -34,7 +34,7 @@ def _log_thread_stack(thread: threading.Thread):
|
|
|
34
34
|
_logger.debug(
|
|
35
35
|
"".join(
|
|
36
36
|
traceback.format_stack(
|
|
37
|
-
sys._current_frames().get( # pylint: disable=protected-access
|
|
37
|
+
sys._current_frames().get( # pylint: disable=protected-access # pyrefly: ignore[no-matching-overload]
|
|
38
38
|
thread.ident, []
|
|
39
39
|
)
|
|
40
40
|
)
|
|
@@ -121,9 +121,9 @@ class DefaultSliceHealthChecker(SliceHealthChecker):
|
|
|
121
121
|
_logger.debug(
|
|
122
122
|
"Caught JaxRuntimeError for slice_index=%s: %s", slice_index, error
|
|
123
123
|
)
|
|
124
|
-
if not is_error_due_to_slice_down(error):
|
|
124
|
+
if not is_error_due_to_slice_down(error, log_traceback=False):
|
|
125
125
|
raise
|
|
126
|
-
return active_slice_indices
|
|
126
|
+
return frozenset(active_slice_indices)
|
|
127
127
|
|
|
128
128
|
|
|
129
129
|
|
|
@@ -249,7 +249,9 @@ def wait_for_slices(
|
|
|
249
249
|
time.sleep(time_to_sleep)
|
|
250
250
|
|
|
251
251
|
|
|
252
|
-
def is_error_due_to_slice_down(
|
|
252
|
+
def is_error_due_to_slice_down(
|
|
253
|
+
error: Exception, log_traceback: bool = True
|
|
254
|
+
) -> bool:
|
|
253
255
|
"""Returns True if the error is due to slice down.
|
|
254
256
|
|
|
255
257
|
The error types that are considered due to slice down are
|
|
@@ -261,6 +263,7 @@ def is_error_due_to_slice_down(error: Exception) -> bool:
|
|
|
261
263
|
|
|
262
264
|
Args:
|
|
263
265
|
error: The error to check.
|
|
266
|
+
log_traceback: If True, log the traceback of the error.
|
|
264
267
|
"""
|
|
265
268
|
error_due_to_slice_down = False
|
|
266
269
|
traceback_logging_level = logging.DEBUG
|
|
@@ -290,9 +293,7 @@ def is_error_due_to_slice_down(error: Exception) -> bool:
|
|
|
290
293
|
|
|
291
294
|
error_due_to_slice_down = True
|
|
292
295
|
|
|
293
|
-
if not error_due_to_slice_down:
|
|
294
|
-
_logger.
|
|
295
|
-
|
|
296
|
-
_logger.log(traceback_logging_level, "Error details:", exc_info=True)
|
|
296
|
+
if not error_due_to_slice_down and log_traceback:
|
|
297
|
+
_logger.log(traceback_logging_level, "Error details:", exc_info=True)
|
|
297
298
|
|
|
298
299
|
return error_due_to_slice_down
|
|
@@ -86,17 +86,24 @@ class Manager:
|
|
|
86
86
|
Attributes:
|
|
87
87
|
slice_to_devices: A mapping from slice index to a sequence of `jax.Device`
|
|
88
88
|
objects for that slice.
|
|
89
|
-
all_slice_indices: A set of all possible slice indices.
|
|
90
|
-
active_slice_indices: A set of indices of the currently
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
all_slice_indices: A set of all possible slice indices in the allocation.
|
|
90
|
+
active_slice_indices: A set of indices of the slices currently participating
|
|
91
|
+
in the JAX computation mesh.
|
|
92
|
+
inactive_slice_indices: A set of indices of the slices in the allocation
|
|
93
|
+
that are not currently participating in the JAX computation mesh (e.g.
|
|
94
|
+
because they are down or have not joined yet).
|
|
95
|
+
available_inactive_slices: A set of indices of the inactive slices that
|
|
96
|
+
have been detected as up and healthy by the background monitor thread,
|
|
97
|
+
but have not yet been joined to the active JAX computation mesh.
|
|
93
98
|
"""
|
|
94
99
|
|
|
95
100
|
slice_to_devices: Mapping[int, Sequence[jax.Device]]
|
|
96
101
|
all_slice_indices: Set[int]
|
|
97
102
|
active_slice_indices: Set[int]
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
inactive_slice_indices: Set[int]
|
|
104
|
+
available_inactive_slices: Set[int]
|
|
105
|
+
_stop_event: threading.Event | None
|
|
106
|
+
_monitor_thread: threading.Thread | None
|
|
100
107
|
def __init__(self, devices: Sequence[jax.Device] | None = None) -> None:
|
|
101
108
|
"""Initializes the manager.
|
|
102
109
|
|
|
@@ -107,12 +114,55 @@ class Manager:
|
|
|
107
114
|
devices = jax.devices()
|
|
108
115
|
self.slice_to_devices = elastic.get_slice_to_devices(devices)
|
|
109
116
|
|
|
110
|
-
self.all_slice_indices =
|
|
117
|
+
self.all_slice_indices = frozenset(self.slice_to_devices.keys())
|
|
111
118
|
|
|
112
119
|
self.active_slice_indices = elastic.get_active_slice_indices(
|
|
113
120
|
slice_to_devices=self.slice_to_devices
|
|
114
121
|
)
|
|
115
|
-
self.
|
|
122
|
+
self.inactive_slice_indices = self.all_slice_indices - self.active_slice_indices
|
|
123
|
+
self.available_inactive_slices = frozenset()
|
|
124
|
+
|
|
125
|
+
self._stop_event = None
|
|
126
|
+
self._monitor_thread = None
|
|
127
|
+
|
|
128
|
+
def start_monitoring(self, poll_interval: float | int = 10) -> None:
|
|
129
|
+
"""Starts the background monitor thread.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
poll_interval: The number of seconds to wait between activity checks.
|
|
133
|
+
"""
|
|
134
|
+
if self._monitor_thread is not None and self._monitor_thread.is_alive():
|
|
135
|
+
_logger.warning("Monitor thread is already running.")
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
self._stop_event = threading.Event()
|
|
139
|
+
self._monitor_thread = threading.Thread(
|
|
140
|
+
target=self._monitor_new_slices,
|
|
141
|
+
args=(self._stop_event, poll_interval),
|
|
142
|
+
daemon=True,
|
|
143
|
+
)
|
|
144
|
+
self._monitor_thread.start()
|
|
145
|
+
_logger.info("Elastic monitor thread started with interval %s.", poll_interval)
|
|
146
|
+
|
|
147
|
+
def close(self) -> None:
|
|
148
|
+
"""Stops the background monitor thread."""
|
|
149
|
+
if self._stop_event is not None:
|
|
150
|
+
self._stop_event.set()
|
|
151
|
+
if self._monitor_thread is not None:
|
|
152
|
+
_logger.info("Closing manager, waiting for monitor thread to stop...")
|
|
153
|
+
try:
|
|
154
|
+
self._monitor_thread.join(timeout=5)
|
|
155
|
+
if self._monitor_thread.is_alive():
|
|
156
|
+
_logger.warning(
|
|
157
|
+
"Elastic monitor thread failed to stop within 5s timeout."
|
|
158
|
+
)
|
|
159
|
+
except RuntimeError as e:
|
|
160
|
+
if "cannot join thread" in str(e):
|
|
161
|
+
pass
|
|
162
|
+
else:
|
|
163
|
+
raise
|
|
164
|
+
self._monitor_thread = None
|
|
165
|
+
self._stop_event = None
|
|
116
166
|
|
|
117
167
|
@functools.cached_property
|
|
118
168
|
def total_slice_count(self) -> int:
|
|
@@ -136,9 +186,15 @@ class Manager:
|
|
|
136
186
|
return len(self.active_slice_indices)
|
|
137
187
|
|
|
138
188
|
@property
|
|
139
|
-
def
|
|
140
|
-
"""
|
|
141
|
-
|
|
189
|
+
def new_slice_event(self) -> threading.Event:
|
|
190
|
+
"""Deprecated compatibility property for un-updated MaxText code.
|
|
191
|
+
|
|
192
|
+
TODO: b/527183831 - Remove this property once MaxText CL 2 is submitted.
|
|
193
|
+
"""
|
|
194
|
+
event = threading.Event()
|
|
195
|
+
if self.available_inactive_slices:
|
|
196
|
+
event.set()
|
|
197
|
+
return event
|
|
142
198
|
|
|
143
199
|
def scale_by_active_slices(self, x: int | float) -> int | float:
|
|
144
200
|
"""Scale x by the number of active slices."""
|
|
@@ -171,37 +227,57 @@ class Manager:
|
|
|
171
227
|
for array in jax.live_arrays():
|
|
172
228
|
array.delete()
|
|
173
229
|
|
|
174
|
-
def
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
_logger.debug("No inactive slices to check.")
|
|
182
|
-
continue
|
|
230
|
+
def _check_inactive_slices(self) -> None:
|
|
231
|
+
"""Checks inactive slices and updates available_inactive_slices."""
|
|
232
|
+
if not self.inactive_slice_indices:
|
|
233
|
+
_logger.debug("No inactive slices to check.")
|
|
234
|
+
if self.available_inactive_slices:
|
|
235
|
+
self.available_inactive_slices = frozenset()
|
|
236
|
+
return
|
|
183
237
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
238
|
+
_logger.debug(
|
|
239
|
+
"Now checking inactive slices %s", self.inactive_slice_indices
|
|
240
|
+
)
|
|
241
|
+
inactive_slice_to_devices = {
|
|
242
|
+
i: self.slice_to_devices[i] for i in self.inactive_slice_indices
|
|
243
|
+
}
|
|
244
|
+
found_slices = elastic.get_active_slice_indices(
|
|
245
|
+
inactive_slice_to_devices
|
|
246
|
+
)
|
|
193
247
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
248
|
+
_logger.debug(
|
|
249
|
+
"Found available and inactive slices %s", found_slices
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
# Filter against active_slice_indices in case the main thread initiated scale-up
|
|
253
|
+
# and claimed these slices while this background health check was running.
|
|
254
|
+
found_slices = found_slices - self.active_slice_indices
|
|
255
|
+
|
|
256
|
+
if found_slices != self.available_inactive_slices:
|
|
257
|
+
_logger.info(
|
|
258
|
+
"Newly available but inactive slices %s", found_slices
|
|
259
|
+
)
|
|
260
|
+
self.available_inactive_slices = frozenset(found_slices)
|
|
201
261
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
262
|
+
def _monitor_new_slices(
|
|
263
|
+
self, stop_event: threading.Event, poll_interval: float | int
|
|
264
|
+
) -> None:
|
|
265
|
+
"""Monitors for new slices and updates available_inactive_slices."""
|
|
266
|
+
_logger.info("Elastic monitor thread started.")
|
|
267
|
+
try:
|
|
268
|
+
while not stop_event.wait(poll_interval):
|
|
269
|
+
try:
|
|
270
|
+
self._check_inactive_slices()
|
|
271
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
272
|
+
_logger.exception("Error in monitor thread loop")
|
|
273
|
+
except BaseException as e:
|
|
274
|
+
_logger.critical(
|
|
275
|
+
"Catastrophic error in monitor thread, thread is dying!",
|
|
276
|
+
exc_info=True,
|
|
277
|
+
)
|
|
278
|
+
raise
|
|
279
|
+
finally:
|
|
280
|
+
_logger.info("Elastic monitor thread stopped.")
|
|
205
281
|
|
|
206
282
|
def elastic_retry(
|
|
207
283
|
self,
|
|
@@ -227,10 +303,10 @@ class Manager:
|
|
|
227
303
|
(i.e., it waits for all slices to be active).
|
|
228
304
|
|
|
229
305
|
When `minimum_slice_count` is less than the total number of slices, a
|
|
230
|
-
background thread will monitor for
|
|
231
|
-
`self.
|
|
232
|
-
a `ScaleUpSignalError` to gracefully interrupt the
|
|
233
|
-
trigger a retry.
|
|
306
|
+
background thread will monitor for newly joined inactive slices and populate
|
|
307
|
+
`self.available_inactive_slices`. User code can check this set (e.g. at step
|
|
308
|
+
boundaries) and raise a `ScaleUpSignalError` to gracefully interrupt the
|
|
309
|
+
current execution and trigger a retry with the expanded hardware.
|
|
234
310
|
|
|
235
311
|
Often, the function will dispatch JAX operations and wait for them to
|
|
236
312
|
complete while creating a log message. If using Python logging, it is
|
|
@@ -286,6 +362,7 @@ class Manager:
|
|
|
286
362
|
def decorator(func: _F) -> _F:
|
|
287
363
|
@functools.wraps(func)
|
|
288
364
|
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
365
|
+
self.start_monitoring(poll_interval)
|
|
289
366
|
|
|
290
367
|
def attempt_execution(attempt: int) -> Any:
|
|
291
368
|
_logger.info("Elastic attempt %d", attempt)
|
|
@@ -295,75 +372,69 @@ class Manager:
|
|
|
295
372
|
poll_interval=poll_interval,
|
|
296
373
|
timeout=timeout,
|
|
297
374
|
)
|
|
375
|
+
self.inactive_slice_indices = (
|
|
376
|
+
self.all_slice_indices - self.active_slice_indices
|
|
377
|
+
)
|
|
378
|
+
# Reset available_inactive_slices at attempt start since
|
|
379
|
+
# active_slice_indices has just been updated by wait_for_slices.
|
|
380
|
+
self.available_inactive_slices = frozenset()
|
|
298
381
|
if pre_callback is not None:
|
|
299
382
|
pre_callback()
|
|
300
383
|
|
|
301
384
|
with jax.default_device(self.default_device):
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
385
|
+
return func(*args, **kwargs)
|
|
386
|
+
|
|
387
|
+
def handle_scale_up_error(attempt: int, error: ScaleUpSignalError) -> None:
|
|
388
|
+
_logger.info("Scale up requested.")
|
|
389
|
+
_elastic_event_cleanup()
|
|
390
|
+
# Reset available_inactive_slices before retry callback and next attempt.
|
|
391
|
+
self.available_inactive_slices = frozenset()
|
|
392
|
+
|
|
393
|
+
if on_elastic_event_callback is not None:
|
|
394
|
+
on_elastic_event_callback()
|
|
395
|
+
|
|
396
|
+
if not retry_policy(attempt, error):
|
|
397
|
+
_logger.info("Retry policy rejected retry after ScaleUpSignalError.")
|
|
398
|
+
raise ElasticRuntimeError(
|
|
399
|
+
f"Elastic attempt {attempt} failed."
|
|
400
|
+
) from error
|
|
401
|
+
|
|
402
|
+
def handle_slice_down_error(
|
|
403
|
+
attempt: int, error: jax.errors.JaxRuntimeError
|
|
404
|
+
) -> None:
|
|
405
|
+
if not elastic.is_error_due_to_slice_down(error):
|
|
406
|
+
raise
|
|
407
|
+
|
|
408
|
+
_logger.exception("Elastic event detected")
|
|
409
|
+
_elastic_event_cleanup()
|
|
410
|
+
# Reset available_inactive_slices on slice-down failure before retry.
|
|
411
|
+
self.available_inactive_slices = frozenset()
|
|
412
|
+
|
|
413
|
+
if on_elastic_event_callback is not None:
|
|
414
|
+
on_elastic_event_callback()
|
|
415
|
+
|
|
416
|
+
if not retry_policy(attempt, error):
|
|
417
|
+
_logger.info(
|
|
418
|
+
"Retry policy rejected retry after jax.errors.JaxRuntimeError."
|
|
419
|
+
)
|
|
420
|
+
raise ElasticRuntimeError(
|
|
421
|
+
f"Elastic attempt {attempt} failed."
|
|
422
|
+
) from error
|
|
423
|
+
|
|
424
|
+
try:
|
|
425
|
+
attempt = 1
|
|
426
|
+
while True:
|
|
315
427
|
try:
|
|
316
|
-
return
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
except ScaleUpSignalError as error:
|
|
327
|
-
_logger.info("Scale up requested.")
|
|
328
|
-
_elastic_event_cleanup()
|
|
329
|
-
|
|
330
|
-
if on_elastic_event_callback is not None:
|
|
331
|
-
on_elastic_event_callback()
|
|
332
|
-
|
|
333
|
-
if not retry_policy(attempt, error):
|
|
334
|
-
_logger.info(
|
|
335
|
-
"Retry policy rejected retry after ScaleUpSignalError."
|
|
336
|
-
)
|
|
337
|
-
raise ElasticRuntimeError(
|
|
338
|
-
f"Elastic attempt {attempt} failed."
|
|
339
|
-
) from error
|
|
340
|
-
|
|
341
|
-
_logger.info("Retrying.")
|
|
342
|
-
except jax.errors.JaxRuntimeError as error:
|
|
343
|
-
if not elastic.is_error_due_to_slice_down(error):
|
|
344
|
-
raise
|
|
345
|
-
|
|
346
|
-
if self.new_slice_event.is_set():
|
|
347
|
-
_logger.info("Slice down event and new slice available detected.")
|
|
348
|
-
else:
|
|
349
|
-
_logger.info("Slice down event detected.")
|
|
350
|
-
|
|
351
|
-
_elastic_event_cleanup()
|
|
352
|
-
|
|
353
|
-
if on_elastic_event_callback is not None:
|
|
354
|
-
on_elastic_event_callback()
|
|
355
|
-
|
|
356
|
-
if not retry_policy(attempt, error):
|
|
357
|
-
_logger.info("Retry policy rejected retry after JaxRuntimeError.")
|
|
358
|
-
raise ElasticRuntimeError(
|
|
359
|
-
f"Elastic attempt {attempt} failed."
|
|
360
|
-
) from error
|
|
361
|
-
|
|
362
|
-
_logger.info("Retrying.")
|
|
363
|
-
|
|
364
|
-
attempt += 1
|
|
365
|
-
|
|
366
|
-
return wrapper
|
|
428
|
+
return attempt_execution(attempt)
|
|
429
|
+
except ScaleUpSignalError as error:
|
|
430
|
+
handle_scale_up_error(attempt, error)
|
|
431
|
+
except jax.errors.JaxRuntimeError as error:
|
|
432
|
+
handle_slice_down_error(attempt, error)
|
|
433
|
+
attempt += 1
|
|
434
|
+
finally:
|
|
435
|
+
self.close()
|
|
436
|
+
|
|
437
|
+
return wrapper # pyrefly: ignore[bad-return]
|
|
367
438
|
|
|
368
439
|
return decorator
|
|
369
440
|
|