Files
2026-07-13 13:17:40 +08:00

156 lines
6.1 KiB
Python

import abc
from typing import List, Optional
import ray
from ray.air.execution.resources.request import AcquiredResources, ResourceRequest
from ray.util.annotations import DeveloperAPI
@DeveloperAPI
class ResourceManager(abc.ABC):
"""Resource manager interface.
A resource manager can be used to request resources from a Ray cluster and
allocate them to remote Ray tasks or actors.
Resources have to be requested before they can be acquired.
Resources managed by the resource manager can be in three states:
1. "Requested": The resources have been requested but are not yet available to
schedule remote Ray objects. The resource request may trigger autoscaling,
and can be cancelled if no longer needed.
2. "Ready": The requested resources are now available to schedule remote Ray
objects. They can be acquired and subsequently used remote Ray objects.
The resource request can still be cancelled if no longer needed.
3. "Acquired": The resources have been acquired by a caller to use for scheduling
remote Ray objects. Note that it is the responsibility of the caller to
schedule the Ray objects with these resources.
The associated resource request has been completed and can no longer be
cancelled. The acquired resources can be freed by the resource manager when
they are no longer used.
The flow is as follows:
.. code-block:: python
# Create resource manager
resource_manager = ResourceManager()
# Create resource request
resource_request = ResourceRequest([{"CPU": 4}])
# Pass to resource manager
resource_manager.request_resources(resource_request)
# Wait until ready
while not resource_manager.has_resources_ready(resource_request):
time.sleep(1)
# Once ready, acquire resources
acquired_resource = resource_manager.acquire_resources(resource_request)
# Bind to remote task or actor
annotated_remote_fn = acquired_resource.annotate_remote_entities(
[remote_fn])
# Run remote function. This will use the acquired resources
ray.get(annotated_remote_fn.remote())
# After using the resources, free
resource_manager.free_resources(annotated_resources)
"""
def request_resources(self, resource_request: ResourceRequest):
"""Request resources.
Depending on the backend, resources can trigger autoscaling. Requested
resources can be ready or not ready. Once they are "ready", they can
be acquired and used by remote Ray objects.
Resource requests can be cancelled anytime using ``cancel_resource_request()``.
Once acquired, the resource request is removed. Acquired resources can be
freed with ``free_resources()``.
"""
raise NotImplementedError
def cancel_resource_request(self, resource_request: ResourceRequest):
"""Cancel resource request.
Resource requests can be cancelled anytime before a resource is acquired.
Acquiring a resource will remove the associated resource request.
Acquired resources can be freed with ``free_resources()``.
"""
raise NotImplementedError
def has_resources_ready(self, resource_request: ResourceRequest) -> bool:
"""Returns True if resources for the given request are ready to be acquired."""
raise NotImplementedError
def acquire_resources(
self, resource_request: ResourceRequest
) -> Optional[AcquiredResources]:
"""Acquire resources. Returns None if resources are not ready to be acquired.
Acquiring resources will remove the associated resource request.
Acquired resources can be returned with ``free_resources()``.
"""
raise NotImplementedError
def free_resources(self, acquired_resource: AcquiredResources):
"""Free acquired resources from usage and return them to the resource manager.
Freeing resources will return the resources to the manager, but there are
no guarantees about the tasks and actors scheduled on the resources. The caller
should make sure that any references to tasks or actors scheduled on the
resources have been removed before calling ``free_resources()``.
"""
raise NotImplementedError
def get_resource_futures(self) -> List[ray.ObjectRef]:
"""Return futures for resources to await.
Depending on the backend, we use resource futures to determine availability
of resources (e.g. placement groups) or resolution of requests.
In this case, the futures can be awaited externally by the caller.
When a resource future resolved, the caller may call ``update_state()``
to force the resource manager to update its internal state immediately.
"""
return []
def update_state(self):
"""Update internal state of the resource manager.
The resource manager may have internal state that needs periodic updating.
For instance, depending on the backend, resource futures can be awaited
externally (with ``get_resource_futures()``).
If such a future resolved, the caller can instruct the resource
manager to update its internal state immediately.
"""
pass
def clear(self):
"""Reset internal state and clear all resources.
Calling this method will reset the resource manager to its initialization state.
All resources will be removed.
Clearing the state will remove tracked resources from the manager, but there are
no guarantees about the tasks and actors scheduled on the resources. The caller
should make sure that any references to tasks or actors scheduled on the
resources have been removed before calling ``clear()``.
"""
raise NotImplementedError
def __reduce__(self):
"""We disallow serialization.
Shared resource managers should live on an actor.
"""
raise ValueError(
f"Resource managers cannot be serialized. Resource manager: {str(self)}"
)