116 lines
4.1 KiB
Python
116 lines
4.1 KiB
Python
import tempfile
|
|
import time
|
|
import unittest
|
|
|
|
import ray
|
|
from ray import tune
|
|
from ray.rllib.algorithms.ppo import PPOConfig
|
|
from ray.rllib.callbacks.callbacks import RLlibCallback
|
|
from ray.rllib.examples.envs.classes.cartpole_crashing import CartPoleCrashing
|
|
from ray.rllib.utils.test_utils import check
|
|
|
|
|
|
def on_env_runners_recreated(
|
|
algorithm,
|
|
env_runner_group,
|
|
env_runner_indices,
|
|
is_evaluation,
|
|
**kwargs,
|
|
):
|
|
# Store in the algorithm object's counters the number of times, this worker
|
|
# (ID'd by index and whether eval or not) has been recreated/restarted.
|
|
for id_ in env_runner_indices:
|
|
key = f"{'eval_' if is_evaluation else ''}worker_{id_}_recreated"
|
|
# Increase the counter.
|
|
algorithm.metrics.log_value(key, 1, reduce="lifetime_sum")
|
|
print(f"changed {key} to {algorithm._counters[key]}")
|
|
|
|
# Execute some dummy code on each of the recreated workers.
|
|
results = env_runner_group.foreach_env_runner(lambda w: w.ping())
|
|
print(results) # should print "pong" n times (one for each recreated worker).
|
|
|
|
|
|
class InitAndCheckpointRestoredCallbacks(RLlibCallback):
|
|
def on_algorithm_init(self, *, algorithm, metrics_logger, **kwargs):
|
|
self._on_init_was_called = True
|
|
|
|
def on_checkpoint_loaded(self, *, algorithm, **kwargs):
|
|
self._on_checkpoint_loaded_was_called = True
|
|
|
|
|
|
class TestCallbacks(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
ray.init()
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
ray.shutdown()
|
|
|
|
def test_on_env_runners_recreated_callback(self):
|
|
tune.register_env("env", lambda cfg: CartPoleCrashing(cfg))
|
|
|
|
config = (
|
|
PPOConfig()
|
|
.environment("env", env_config={"p_crash": 1.0})
|
|
.callbacks(on_env_runners_recreated=on_env_runners_recreated)
|
|
.env_runners(num_env_runners=3)
|
|
.fault_tolerance(
|
|
restart_failed_env_runners=True,
|
|
delay_between_env_runner_restarts_s=0,
|
|
)
|
|
)
|
|
|
|
algo = config.build()
|
|
original_env_runner_ids = algo.env_runner_group.healthy_worker_ids()
|
|
for id_ in original_env_runner_ids:
|
|
check(algo.metrics.peek(f"worker_{id_}_recreated", default=0), 0)
|
|
check(algo.metrics.peek("total_num_workers_recreated", default=0), 0)
|
|
|
|
# After building the algorithm, we should have 2 healthy (remote) workers.
|
|
self.assertTrue(len(original_env_runner_ids) == 3)
|
|
|
|
# Train a bit (and have the envs/workers crash).
|
|
for _ in range(3):
|
|
print(algo.train())
|
|
time.sleep(15.0)
|
|
|
|
algo.restore_env_runners(algo.env_runner_group)
|
|
# After training, the `on_workers_recreated` callback should have captured
|
|
# the exact worker IDs recreated (the exact number of times) as the actor
|
|
# manager itself. This confirms that the callback is triggered correctly,
|
|
# always.
|
|
new_worker_ids = algo.env_runner_group.healthy_worker_ids()
|
|
self.assertEqual(len(new_worker_ids), 3)
|
|
for id_ in new_worker_ids:
|
|
# num_restored = algo.env_runner_group.restored_actors_history[id_]
|
|
self.assertTrue(algo.metrics.peek(f"worker_{id_}_recreated") > 1)
|
|
algo.stop()
|
|
|
|
def test_on_init_and_checkpoint_loaded(self):
|
|
config = (
|
|
PPOConfig()
|
|
.environment("CartPole-v1")
|
|
.callbacks(InitAndCheckpointRestoredCallbacks)
|
|
)
|
|
algo = config.build()
|
|
callbacks = algo.callbacks[0]
|
|
self.assertTrue(callbacks._on_init_was_called)
|
|
self.assertTrue(not hasattr(callbacks, "_on_checkpoint_loaded_was_called"))
|
|
algo.train()
|
|
# Save algo and restore.
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
algo.save(checkpoint_dir=tmpdir)
|
|
self.assertTrue(not hasattr(callbacks, "_on_checkpoint_loaded_was_called"))
|
|
algo.load_checkpoint(checkpoint_dir=tmpdir)
|
|
self.assertTrue(callbacks._on_checkpoint_loaded_was_called)
|
|
algo.stop()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
import pytest
|
|
|
|
sys.exit(pytest.main(["-v", __file__]))
|