# Buggy code
def run_function_on_all_workers(self, function, run_on_other_drivers=False):
    """Run arbitrary code on all of the workers.

    This function will first be run on the driver, and then it will be
    exported to all of the workers to be run. It will also be run on any
    new workers that register later. If ray.init has not been called yet,
    then cache the function and export it later.

    Args:
        function (Callable): The function to run on all of the workers. It
            takes only one argument, a worker info dict. If it returns
            anything, its return values will not be used.
        run_on_other_drivers: The boolean that indicates whether we want to
            run this function on other drivers. One case is we may need to
            share objects across drivers.
    """
    # If ray.init has not been called yet, then cache the function and
    # export it when connect is called. Otherwise, run the function on all
    # workers.
    if self.mode is None:
        self.cached_functions_to_run.append(function)
    else:
        # Attempt to pickle the function before we need it. This could
        # fail, and it is more convenient if the failure happens before we
        # actually run the function locally.
        pickled_function = pickle.dumps(function)

        function_to_run_id = hashlib.sha1(pickled_function).digest()
        key = b"FunctionsToRun:" + function_to_run_id
        # First run the function on the driver.
        # We always run the task locally.
        function({"worker": self})
        # Check if the function has already been put into redis.
        function_exported = self.redis_client.setnx(b"Lock:" + key, 1)
        if not function_exported:
            # In this case, the function has already been exported, so
            # we don't need to export it again.
            return

        check_oversized_pickle(pickled_function, function.__name__, "function", self)

        # Run the function on all workers.
        self.redis_client.hset(
            key,
            mapping={
                "job_id": self.current_job_id.binary(),
                "function_id": function_to_run_id,
                "function": pickled_function,
                "run_on_other_drivers": str(run_on_other_drivers),
            },
        )
        self.redis_client.rpush("Exports", key)


# Traceback (ray.exceptions.RayTaskError)
2020-10-05 01:55:09,393\u0009ERROR trial_runner.py:567 -- Trial PPO_QbertNoFrameskip-v4_b43b9_00027: Error processing event.
Traceback (most recent call last):
File "/usr/local/lib/python3.6/dist-packages/ray/tune/trial_runner.py", line 515, in _process_trial
result = self.trial_executor.fetch_result(trial)
File "/usr/local/lib/python3.6/dist-packages/ray/tune/ray_trial_executor.py", line 488, in fetch_result
result = ray.get(trial_future[0], timeout=DEFAULT_GET_TIMEOUT)
File "/usr/local/lib/python3.6/dist-packages/ray/worker.py", line 1428, in get
raise value.as_instanceof_cause()
ray.exceptions.RayTaskError: \u001b[36mray::PPO.train()\u001b[39m (pid=4251, ip=172.30.96.106)
File "python/ray/_raylet.pyx", line 484, in ray._raylet.execute_task
File "python/ray/_raylet.pyx", line 438, in ray._raylet.execute_task.function_executor
File "/usr/local/lib/python3.6/dist-packages/ray/rllib/agents/trainer.py", line 516, in train
raise e
File "/usr/local/lib/python3.6/dist-packages/ray/rllib/agents/trainer.py", line 505, in train
result = Trainable.train(self)
File "/usr/local/lib/python3.6/dist-packages/ray/tune/trainable.py", line 336, in train
result = self.step()
File "/usr/local/lib/python3.6/dist-packages/ray/rllib/agents/trainer_template.py", line 134, in step
res = next(self.train_exec_impl)
File "/usr/local/lib/python3.6/dist-packages/ray/util/iter.py", line 756, in __next__
return next(self.built_iterator)
File "/usr/local/lib/python3.6/dist-packages/ray/util/iter.py", line 783, in apply_foreach
for item in it:
File "/usr/local/lib/python3.6/dist-packages/ray/util/iter.py", line 791, in apply_foreach
result = fn(item)
File "/usr/local/lib/python3.6/dist-packages/ray/rllib/execution/metric_ops.py", line 79, in __call__
timeout_seconds=self.timeout_seconds)
File "/usr/local/lib/python3.6/dist-packages/ray/rllib/evaluation/metrics.py", line 75, in collect_episodes
metric_lists = ray.get(collected)
ray.exceptions.RayActorError: The actor died unexpectedly before finishing this task.

# Fixed code
def run_function_on_all_workers(self, function, run_on_other_drivers=False):
    """Run arbitrary code on all of the workers.

    This function will first be run on the driver, and then it will be
    exported to all of the workers to be run. It will also be run on any
    new workers that register later. If ray.init has not been called yet,
    then cache the function and export it later.

    Args:
        function (Callable): The function to run on all of the workers. It
            takes only one argument, a worker info dict. If it returns
            anything, its return values will not be used.
        run_on_other_drivers: The boolean that indicates whether we want to
            run this function on other drivers. One case is we may need to
            share objects across drivers.
    """
    # If ray.init has not been called yet, then cache the function and
    # export it when connect is called. Otherwise, run the function on all
    # workers.
    if self.mode is None:
        self.cached_functions_to_run.append(function)
    else:
        # Attempt to pickle the function before we need it. This could
        # fail, and it is more convenient if the failure happens before we
        # actually run the function locally.
        pickled_function = pickle.dumps(function)

        function_to_run_id = hashlib.shake_128(pickled_function).digest(
            ray_constants.ID_SIZE
        )
        key = b"FunctionsToRun:" + function_to_run_id
        # First run the function on the driver.
        # We always run the task locally.
        function({"worker": self})
        # Check if the function has already been put into redis.
        function_exported = self.redis_client.setnx(b"Lock:" + key, 1)
        if not function_exported:
            # In this case, the function has already been exported, so
            # we don't need to export it again.
            return

        check_oversized_pickle(pickled_function, function.__name__, "function", self)

        # Run the function on all workers.
        self.redis_client.hset(
            key,
            mapping={
                "job_id": self.current_job_id.binary(),
                "function_id": function_to_run_id,
                "function": pickled_function,
                "run_on_other_drivers": str(run_on_other_drivers),
            },
        )
        self.redis_client.rpush("Exports", key)