chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:17:40 +08:00
commit f1825c8ceb
10096 changed files with 2364182 additions and 0 deletions
+293
View File
@@ -0,0 +1,293 @@
import asyncio
import logging
import random
from collections import deque
from typing import List, Optional, Tuple
import grpc
from grpc import aio as aiogrpc
import ray._private.gcs_utils as gcs_utils
from ray._common.utils import get_or_create_event_loop
from ray.core.generated import (
gcs_pb2,
gcs_service_pb2,
gcs_service_pb2_grpc,
pubsub_pb2,
)
logger = logging.getLogger(__name__)
_OBSERVABILITY_PUBSUB_CHANNELS = (
pubsub_pb2.RAY_ERROR_INFO_CHANNEL,
pubsub_pb2.RAY_LOG_CHANNEL,
pubsub_pb2.RAY_NODE_RESOURCE_USAGE_CHANNEL,
)
class _SubscriberBase:
def __init__(self, worker_id: bytes = None):
self._worker_id = worker_id
# self._subscriber_id needs to match the binary format of a random
# SubscriberID / UniqueID, which is 28 (kUniqueIDSize) random bytes.
self._subscriber_id = bytes(bytearray(random.getrandbits(8) for _ in range(28)))
self._last_batch_size = 0
self._max_processed_sequence_id = 0
self._publisher_id = b""
# Batch size of the result from last poll. Used to indicate whether the
# subscriber can keep up.
@property
def last_batch_size(self):
return self._last_batch_size
def _subscribe_request(self, channel):
cmd = pubsub_pb2.Command(channel_type=channel, subscribe_message={})
req = gcs_service_pb2.GcsSubscriberCommandBatchRequest(
subscriber_id=self._subscriber_id, sender_id=self._worker_id, commands=[cmd]
)
return req
def _poll_request(self):
return gcs_service_pb2.GcsSubscriberPollRequest(
subscriber_id=self._subscriber_id,
max_processed_sequence_id=self._max_processed_sequence_id,
publisher_id=self._publisher_id,
)
def _unsubscribe_request(self, channels):
req = gcs_service_pb2.GcsSubscriberCommandBatchRequest(
subscriber_id=self._subscriber_id, sender_id=self._worker_id, commands=[]
)
for channel in channels:
req.commands.append(
pubsub_pb2.Command(channel_type=channel, unsubscribe_message={})
)
return req
@staticmethod
def _should_terminate_polling(e: grpc.RpcError) -> None:
# Caller only expects polling to be terminated after deadline exceeded.
if e.code() == grpc.StatusCode.DEADLINE_EXCEEDED:
return True
# Could be a temporary connection issue. Suppress error.
# TODO: reconnect GRPC channel?
if e.code() == grpc.StatusCode.UNAVAILABLE:
return True
return False
class _AioSubscriber(_SubscriberBase):
"""Async io subscriber to GCS.
Usage example common to Aio subscribers:
subscriber = GcsAioXxxSubscriber(address="...")
await subscriber.subscribe()
while running:
...... = await subscriber.poll()
......
await subscriber.close()
"""
def __init__(
self,
pubsub_channel_type,
worker_id: bytes = None,
address: str = None,
channel: aiogrpc.Channel = None,
):
super().__init__(worker_id)
if address:
assert channel is None, "address and channel cannot both be specified"
channel = gcs_utils.create_gcs_channel(address, aio=True)
else:
assert channel is not None, "One of address and channel must be specified"
if pubsub_channel_type in _OBSERVABILITY_PUBSUB_CHANNELS:
self._stub = gcs_service_pb2_grpc.ObservabilityPubSubServiceStub(channel)
else:
self._stub = gcs_service_pb2_grpc.ControlPlanePubSubGcsServiceStub(channel)
# Type of the channel.
self._channel = pubsub_channel_type
# A queue of received PubMessage.
self._queue = deque()
# Indicates whether the subscriber has closed.
self._close = asyncio.Event()
async def subscribe(self) -> None:
"""Registers a subscription for the subscriber's channel type.
Before the registration, published messages in the channel will not be
saved for the subscriber.
"""
if self._close.is_set():
return
req = self._subscribe_request(self._channel)
await self._stub.GcsSubscriberCommandBatch(req, timeout=30)
async def _poll_call(self, req, timeout=None):
# Wrap GRPC _AioCall as a coroutine.
return await self._stub.GcsSubscriberPoll(req, timeout=timeout)
async def _poll(self, timeout=None) -> None:
while len(self._queue) == 0:
req = self._poll_request()
poll = get_or_create_event_loop().create_task(
self._poll_call(req, timeout=timeout)
)
close = get_or_create_event_loop().create_task(self._close.wait())
done, others = await asyncio.wait(
[poll, close], timeout=timeout, return_when=asyncio.FIRST_COMPLETED
)
# Cancel the other task if needed to prevent memory leak.
other_task = others.pop()
if not other_task.done():
other_task.cancel()
if poll not in done or close in done:
# Request timed out or subscriber closed.
break
try:
self._last_batch_size = len(poll.result().pub_messages)
if poll.result().publisher_id != self._publisher_id:
if self._publisher_id != b"":
logger.debug(
f"replied publisher_id {poll.result().publisher_id} "
f"different from {self._publisher_id}, this should "
"only happen during gcs failover."
)
self._publisher_id = poll.result().publisher_id
self._max_processed_sequence_id = 0
for msg in poll.result().pub_messages:
if msg.sequence_id <= self._max_processed_sequence_id:
logger.warning(f"Ignoring out of order message {msg}")
continue
self._max_processed_sequence_id = msg.sequence_id
self._queue.append(msg)
except grpc.RpcError as e:
if self._should_terminate_polling(e):
return
raise
async def close(self) -> None:
"""Closes the subscriber and its active subscription."""
# Mark close to terminate inflight polling and prevent future requests.
if self._close.is_set():
return
self._close.set()
req = self._unsubscribe_request(channels=[self._channel])
try:
await self._stub.GcsSubscriberCommandBatch(req, timeout=5)
except Exception:
pass
self._stub = None
class GcsAioResourceUsageSubscriber(_AioSubscriber):
def __init__(
self,
worker_id: bytes = None,
address: str = None,
channel: grpc.Channel = None,
):
super().__init__(
pubsub_pb2.RAY_NODE_RESOURCE_USAGE_CHANNEL, worker_id, address, channel
)
async def poll(self, timeout: Optional[float] = None) -> Tuple[bytes, str]:
"""Polls for new resource usage message.
Args:
timeout: Optional timeout in seconds for the poll request.
Returns:
A tuple of string reporter ID and resource usage json string.
"""
await self._poll(timeout=timeout)
return self._pop_resource_usage(self._queue)
@staticmethod
def _pop_resource_usage(queue):
if len(queue) == 0:
return None, None
msg = queue.popleft()
return msg.key_id.decode(), msg.node_resource_usage_message.json
class GcsAioActorSubscriber(_AioSubscriber):
def __init__(
self,
worker_id: bytes = None,
address: str = None,
channel: grpc.Channel = None,
):
super().__init__(pubsub_pb2.GCS_ACTOR_CHANNEL, worker_id, address, channel)
@property
def queue_size(self):
return len(self._queue)
async def poll(
self, batch_size: int, timeout: Optional[float] = None
) -> List[Tuple[bytes, gcs_pb2.ActorTableData]]:
"""Polls for new actor message.
Args:
batch_size: Maximum number of messages to return.
timeout: Optional timeout in seconds for the poll request.
Returns:
A list of tuples of binary actor ID and actor table data.
"""
await self._poll(timeout=timeout)
return self._pop_actors(self._queue, batch_size=batch_size)
@staticmethod
def _pop_actors(queue, batch_size):
if len(queue) == 0:
return []
popped = 0
msgs = []
while len(queue) > 0 and popped < batch_size:
msg = queue.popleft()
msgs.append((msg.key_id, msg.actor_message))
popped += 1
return msgs
class GcsAioNodeInfoSubscriber(_AioSubscriber):
def __init__(
self,
worker_id: bytes = None,
address: str = None,
channel: grpc.Channel = None,
):
super().__init__(pubsub_pb2.GCS_NODE_INFO_CHANNEL, worker_id, address, channel)
async def poll(
self, batch_size: int, timeout: Optional[float] = None
) -> List[Tuple[bytes, gcs_pb2.GcsNodeInfo]]:
"""Polls for new node info message.
Args:
batch_size: Maximum number of messages to return.
timeout: Optional timeout in seconds for the poll request.
Returns:
A list of tuples of (node_id, GcsNodeInfo).
"""
await self._poll(timeout=timeout)
return self._pop_node_infos(self._queue, batch_size=batch_size)
@staticmethod
def _pop_node_infos(queue, batch_size):
if len(queue) == 0:
return []
popped = 0
msgs = []
while len(queue) > 0 and popped < batch_size:
msg = queue.popleft()
msgs.append((msg.key_id, msg.node_info_message))
popped += 1
return msgs