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
View File
+6
View File
@@ -0,0 +1,6 @@
from libc.stddef cimport size_t
from libcpp.string cimport string
cdef extern from "<array>" namespace "std":
cdef cppclass array_string_2 "std::array<std::string, 2>":
string& operator[](size_t) except +
+74
View File
@@ -0,0 +1,74 @@
from cpython cimport Py_buffer, PyBytes_FromStringAndSize
from libc.stdint cimport int64_t, uintptr_t
from libc.stdio cimport printf
from libcpp.memory cimport shared_ptr
from ray.includes.common cimport CBuffer
cdef class Buffer:
"""Cython wrapper class of C++ `ray::Buffer`.
This class implements the Python 'buffer protocol', which allows
us to use it for calls into Python libraries without having to
copy the data.
See https://docs.python.org/3/c-api/buffer.html for details.
"""
@staticmethod
cdef make(const shared_ptr[CBuffer]& buffer):
cdef Buffer self = Buffer.__new__(Buffer)
self.buffer = buffer
self.shape = <Py_ssize_t>self.size
self.strides = <Py_ssize_t>(1)
return self
def __len__(self):
return self.size
@property
def size(self):
"""
The buffer size in bytes.
"""
return self.buffer.get().Size()
def to_pybytes(self):
"""
Return this buffer as a Python bytes object. Memory is copied.
"""
return PyBytes_FromStringAndSize(
<const char*>self.buffer.get().Data(),
self.buffer.get().Size())
def __getbuffer__(self, Py_buffer* buffer, int flags):
buffer.readonly = 0
buffer.buf = <char *>self.buffer.get().Data()
buffer.format = 'B'
buffer.internal = NULL
buffer.itemsize = 1
buffer.len = self.size
buffer.ndim = 1
buffer.obj = self
buffer.shape = &self.shape
buffer.strides = &self.strides
buffer.suboffsets = NULL
def __getsegcount__(self, Py_ssize_t *len_out):
if len_out != NULL:
len_out[0] = <Py_ssize_t>self.size
return 1
def __getreadbuffer__(self, Py_ssize_t idx, void **p):
if idx != 0:
raise SystemError("accessing non-existent buffer segment")
if p != NULL:
p[0] = <void*> self.buffer.get().Data()
return self.size
def __getwritebuffer__(self, Py_ssize_t idx, void **p):
if idx != 0:
raise SystemError("accessing non-existent buffer segment")
if p != NULL:
p[0] = <void*> self.buffer.get().Data()
return self.size
+900
View File
@@ -0,0 +1,900 @@
from libcpp cimport bool as c_bool
from libcpp.memory cimport shared_ptr, unique_ptr
from libcpp.string cimport string as c_string
from libc.stdint cimport uint8_t, int32_t, uint64_t, int64_t, uint32_t
from libcpp.unordered_map cimport unordered_map
from libcpp.vector cimport vector as c_vector
from libcpp.pair cimport pair as c_pair
from ray.includes.optional cimport (
optional,
)
from ray.includes.unique_ids cimport (
CActorID,
CJobID,
CClusterID,
CWorkerID,
CObjectID,
CTaskID,
CPlacementGroupID,
CNodeID,
)
from ray.includes.function_descriptor cimport (
CFunctionDescriptor,
)
cdef extern from * namespace "polyfill" nogil:
"""
namespace polyfill {
template <typename T>
inline typename std::remove_reference<T>::type&& move(T& t) {
return std::move(t);
}
template <typename T>
inline typename std::remove_reference<T>::type&& move(T&& t) {
return std::move(t);
}
} // namespace polyfill
"""
cdef T move[T](T)
cdef extern from "ray/common/status.h" namespace "ray" nogil:
cdef enum class CStatusCode "ray::StatusCode":
pass
c_bool operator==(CStatusCode lhs, CStatusCode rhs)
cdef cppclass CRayStatus "ray::Status":
CRayStatus()
CRayStatus(CStatusCode code, const c_string &msg)
CRayStatus(CStatusCode code, const c_string &msg, int rpc_code)
CRayStatus(const CRayStatus &s)
@staticmethod
CRayStatus OK()
@staticmethod
CRayStatus OutOfMemory(const c_string &msg)
@staticmethod
CRayStatus KeyError(const c_string &msg)
@staticmethod
CRayStatus Invalid(const c_string &msg)
@staticmethod
CRayStatus IOError(const c_string &msg)
@staticmethod
CRayStatus TypeError(const c_string &msg)
@staticmethod
CRayStatus UnknownError(const c_string &msg)
@staticmethod
CRayStatus NotImplemented(const c_string &msg)
@staticmethod
CRayStatus ObjectStoreFull(const c_string &msg)
@staticmethod
CRayStatus RedisError(const c_string &msg)
@staticmethod
CRayStatus TimedOut(const c_string &msg)
@staticmethod
CRayStatus InvalidArgument(const c_string &msg)
@staticmethod
CRayStatus Interrupted(const c_string &msg)
@staticmethod
CRayStatus IntentionalSystemExit(const c_string &msg)
@staticmethod
CRayStatus UnexpectedSystemExit(const c_string &msg)
@staticmethod
CRayStatus CreationTaskError(const c_string &msg)
@staticmethod
CRayStatus NotFound()
@staticmethod
CRayStatus ObjectRefEndOfStream()
c_bool ok()
c_bool IsOutOfMemory()
c_bool IsKeyError()
c_bool IsInvalid()
c_bool IsIOError()
c_bool IsTypeError()
c_bool IsUnknownError()
c_bool IsNotImplemented()
c_bool IsObjectStoreFull()
c_bool IsAlreadyExists()
c_bool IsOutOfDisk()
c_bool IsRedisError()
c_bool IsTimedOut()
c_bool IsInvalidArgument()
c_bool IsInterrupted()
c_bool IsObjectNotFound()
c_bool IsNotFound()
c_bool IsObjectUnknownOwner()
c_bool IsRpcError()
c_bool IsOutOfResource()
c_bool IsObjectRefEndOfStream()
c_bool IsIntentionalSystemExit()
c_bool IsUnexpectedSystemExit()
c_bool IsChannelError()
c_bool IsChannelTimeoutError()
c_bool IsUnauthenticated()
c_string ToString()
c_string CodeAsString()
CStatusCode code()
c_string message()
int rpc_code()
# We can later add more of the common status factory methods as needed
cdef CRayStatus RayStatus_OK "Status::OK"()
cdef CRayStatus RayStatus_Invalid "Status::Invalid"()
cdef CRayStatus RayStatus_NotImplemented "Status::NotImplemented"()
cdef extern from "ray/common/status_or.h" namespace "ray" nogil:
cdef cppclass CStatusOr "ray::StatusOr"[T]:
c_bool ok()
const CRayStatus &status() const
T &value()
cdef extern from "ray/common/status.h" namespace "ray::StatusT" nogil:
cdef cppclass CStatusTIOError "ray::StatusT::IOError":
CStatusTIOError(const c_string &msg)
c_string message() const
cdef cppclass CStatusTTimedOut "ray::StatusT::TimedOut":
CStatusTTimedOut(const c_string &msg)
c_string message() const
cdef cppclass CStatusTInvalid "ray::StatusT::Invalid":
CStatusTInvalid(const c_string &msg)
c_string message() const
cdef extern from "ray/common/status.h" namespace "ray" nogil:
cdef cppclass CWaitForPersistedPortResult "ray::StatusSetOr<int, ray::StatusT::IOError, ray::StatusT::TimedOut, ray::StatusT::Invalid>":
c_bool has_value()
c_bool has_error()
int &value()
c_string message()
cdef extern from "ray/util/port_persistence.h" namespace "ray" nogil:
c_string GetPortFileName "ray::GetPortFileName"(
const CNodeID &node_id,
const c_string &port_name)
CRayStatus PersistPort "ray::PersistPort"(
const c_string &dir,
const CNodeID &node_id,
const c_string &port_name,
int port)
CWaitForPersistedPortResult WaitForPersistedPort "ray::WaitForPersistedPort"(
const c_string &dir,
const CNodeID &node_id,
const c_string &port_name,
int timeout_ms,
int poll_interval_ms)
cdef extern from "ray/common/id.h" namespace "ray" nogil:
const CTaskID GenerateTaskId(const CJobID &job_id,
const CTaskID &parent_task_id,
int parent_task_counter)
cdef extern from "src/ray/protobuf/common.pb.h" nogil:
cdef cppclass CLanguage "ray::Language":
pass
cdef cppclass CWorkerType "ray::core::WorkerType":
pass
cdef cppclass CWorkerExitType "ray::rpc::WorkerExitType":
pass
cdef cppclass CTaskType "ray::TaskType":
pass
cdef cppclass CPlacementStrategy "ray::core::PlacementStrategy":
pass
cdef cppclass CDefaultSchedulingStrategy "ray::rpc::DefaultSchedulingStrategy": # noqa: E501
CDefaultSchedulingStrategy()
cdef cppclass CSpreadSchedulingStrategy "ray::rpc::SpreadSchedulingStrategy": # noqa: E501
CSpreadSchedulingStrategy()
cdef cppclass CPlacementGroupSchedulingStrategy "ray::rpc::PlacementGroupSchedulingStrategy": # noqa: E501
CPlacementGroupSchedulingStrategy()
void set_placement_group_id(const c_string& placement_group_id)
void set_placement_group_bundle_index(int64_t placement_group_bundle_index) # noqa: E501
void set_placement_group_capture_child_tasks(c_bool placement_group_capture_child_tasks) # noqa: E501
cdef cppclass CNodeAffinitySchedulingStrategy "ray::rpc::NodeAffinitySchedulingStrategy": # noqa: E501
CNodeAffinitySchedulingStrategy()
void set_node_id(const c_string& node_id)
void set_soft(c_bool soft)
void set_spill_on_unavailable(c_bool spill_on_unavailable)
void set_fail_on_unavailable(c_bool fail_on_unavailable)
cdef cppclass CSchedulingStrategy "ray::rpc::SchedulingStrategy":
CSchedulingStrategy()
void clear_scheduling_strategy()
CSpreadSchedulingStrategy* mutable_spread_scheduling_strategy()
CDefaultSchedulingStrategy* mutable_default_scheduling_strategy()
CPlacementGroupSchedulingStrategy* mutable_placement_group_scheduling_strategy() # noqa: E501
CNodeAffinitySchedulingStrategy* mutable_node_affinity_scheduling_strategy()
CNodeLabelSchedulingStrategy* mutable_node_label_scheduling_strategy()
cdef cppclass CAddress "ray::rpc::Address":
CAddress()
const c_string &SerializeAsString() const
void ParseFromString(const c_string &serialized)
void CopyFrom(const CAddress& address)
const c_string &worker_id()
cdef cppclass CObjectReference "ray::rpc::ObjectReference":
CObjectReference()
CAddress owner_address() const
const c_string &object_id() const
const c_string &call_site() const
c_bool has_tensor_transport() const
const c_string &tensor_transport() const
cdef cppclass CNodeLabelSchedulingStrategy "ray::rpc::NodeLabelSchedulingStrategy": # noqa: E501
CNodeLabelSchedulingStrategy()
CLabelMatchExpressions* mutable_hard()
CLabelMatchExpressions* mutable_soft()
cdef cppclass CLabelMatchExpressions "ray::rpc::LabelMatchExpressions": # noqa: E501
CLabelMatchExpressions()
CLabelMatchExpression* add_expressions()
cdef cppclass CLabelMatchExpression "ray::rpc::LabelMatchExpression": # noqa: E501
CLabelMatchExpression()
void set_key(const c_string &key)
CLabelOperator* mutable_operator_()
cdef cppclass CLabelIn "ray::rpc::LabelIn": # noqa: E501
CLabelIn()
void add_values(const c_string &value)
cdef cppclass CLabelNotIn "ray::rpc::LabelNotIn": # noqa: E501
CLabelNotIn()
void add_values(const c_string &value)
cdef cppclass CLabelExists "ray::rpc::LabelExists": # noqa: E501
CLabelExists()
cdef cppclass CLabelDoesNotExist "ray::rpc::LabelDoesNotExist": # noqa: E501
CLabelDoesNotExist()
cdef cppclass CLabelNotIn "ray::rpc::LabelNotIn": # noqa: E501
CLabelNotIn()
void add_values(const c_string &value)
cdef cppclass CLabelOperator "ray::rpc::LabelOperator": # noqa: E501
CLabelOperator()
CLabelIn* mutable_label_in()
CLabelNotIn* mutable_label_not_in()
CLabelExists* mutable_label_exists()
CLabelDoesNotExist* mutable_label_does_not_exist()
cdef cppclass CLineageReconstructionTask "ray::rpc::LineageReconstructionTask":
CLineageReconstructionTask()
const c_string &SerializeAsString() const
cdef extern from "ray/common/scheduling/cluster_resource_data.h" namespace "ray" nogil:
cdef cppclass CNodeResources "ray::NodeResources":
CNodeResources()
unordered_map[c_string, c_string] labels
c_bool HasRequiredLabels(const CLabelSelector &label_selector) const
void SetNodeResourcesLabels(CNodeResources& resources, const unordered_map[c_string, c_string]& labels)
cdef extern from "ray/common/scheduling/label_selector.h" namespace "ray":
cdef cppclass CLabelSelector "ray::LabelSelector":
CLabelSelector() nogil except +
void AddConstraint(const c_string& key, const c_string& value) nogil except +
cdef extern from "ray/common/scheduling/fallback_strategy.h" namespace "ray":
cdef cppclass CFallbackOption "ray::FallbackOption":
CLabelSelector label_selector
CFallbackOption() nogil except +
CFallbackOption(CLabelSelector) nogil except +
# This is a workaround for C++ enum class since Cython has no corresponding
# representation.
cdef extern from "src/ray/protobuf/common.pb.h" nogil:
cdef CLanguage LANGUAGE_PYTHON "ray::Language::PYTHON"
cdef CLanguage LANGUAGE_CPP "ray::Language::CPP"
cdef CLanguage LANGUAGE_JAVA "ray::Language::JAVA"
cdef extern from "src/ray/protobuf/common.pb.h" nogil:
cdef CWorkerType WORKER_TYPE_WORKER "ray::core::WorkerType::WORKER"
cdef CWorkerType WORKER_TYPE_DRIVER "ray::core::WorkerType::DRIVER"
cdef CWorkerType WORKER_TYPE_SPILL_WORKER "ray::core::WorkerType::SPILL_WORKER" # noqa: E501
cdef CWorkerType WORKER_TYPE_RESTORE_WORKER "ray::core::WorkerType::RESTORE_WORKER" # noqa: E501
cdef CWorkerType WORKER_TYPE_UTIL_WORKER "ray::core::WorkerType::UTIL_WORKER" # noqa: E501
cdef CWorkerExitType WORKER_EXIT_TYPE_USER_ERROR "ray::rpc::WorkerExitType::USER_ERROR" # noqa: E501
cdef CWorkerExitType WORKER_EXIT_TYPE_SYSTEM_ERROR "ray::rpc::WorkerExitType::SYSTEM_ERROR" # noqa: E501
cdef CWorkerExitType WORKER_EXIT_TYPE_INTENTIONAL_SYSTEM_ERROR "ray::rpc::WorkerExitType::INTENDED_SYSTEM_EXIT" # noqa: E501
cdef extern from "src/ray/protobuf/common.pb.h" nogil:
cdef CTaskType TASK_TYPE_NORMAL_TASK "ray::TaskType::NORMAL_TASK"
cdef CTaskType TASK_TYPE_ACTOR_CREATION_TASK "ray::TaskType::ACTOR_CREATION_TASK" # noqa: E501
cdef CTaskType TASK_TYPE_ACTOR_TASK "ray::TaskType::ACTOR_TASK"
cdef extern from "src/ray/protobuf/common.pb.h" nogil:
cdef CPlacementStrategy PLACEMENT_STRATEGY_PACK \
"ray::core::PlacementStrategy::PACK"
cdef CPlacementStrategy PLACEMENT_STRATEGY_SPREAD \
"ray::core::PlacementStrategy::SPREAD"
cdef CPlacementStrategy PLACEMENT_STRATEGY_STRICT_PACK \
"ray::core::PlacementStrategy::STRICT_PACK"
cdef CPlacementStrategy PLACEMENT_STRATEGY_STRICT_SPREAD \
"ray::core::PlacementStrategy::STRICT_SPREAD"
cdef extern from "ray/common/buffer.h" namespace "ray" nogil:
cdef cppclass CBuffer "ray::Buffer":
uint8_t *Data() const
size_t Size() const
c_bool IsPlasmaBuffer() const
cdef cppclass LocalMemoryBuffer(CBuffer):
LocalMemoryBuffer(uint8_t *data, size_t size, c_bool copy_data)
LocalMemoryBuffer(size_t size)
cdef cppclass SharedMemoryBuffer(CBuffer):
SharedMemoryBuffer(
const shared_ptr[CBuffer] &buffer,
int64_t offset,
int64_t size)
c_bool IsPlasmaBuffer() const
cdef extern from "ray/common/ray_object.h" nogil:
cdef cppclass CRayObject "ray::RayObject":
CRayObject(const shared_ptr[CBuffer] &data,
const shared_ptr[CBuffer] &metadata,
const c_vector[CObjectReference] &nested_refs)
c_bool HasData() const
c_bool HasMetadata() const
const size_t DataSize() const
const shared_ptr[CBuffer] &GetData()
const shared_ptr[CBuffer] &GetMetadata() const
c_bool IsInPlasmaError() const
optional[c_string] GetTensorTransport() const
void SetDirectTransportMetadata(c_string direct_transport_metadata)
cdef extern from "ray/core_worker/common.h" nogil:
cdef cppclass CRayFunction "ray::core::RayFunction":
CRayFunction()
CRayFunction(CLanguage language,
const CFunctionDescriptor &function_descriptor)
CLanguage GetLanguage()
const CFunctionDescriptor GetFunctionDescriptor()
cdef cppclass CTaskArg "ray::TaskArg":
pass
cdef cppclass CTaskArgByReference "ray::TaskArgByReference":
CTaskArgByReference(const CObjectID &object_id,
const CAddress &owner_address,
const c_string &call_site,
optional[c_string] tensor_transport)
cdef cppclass CTaskArgByValue "ray::TaskArgByValue":
CTaskArgByValue(const shared_ptr[CRayObject] &data)
cdef cppclass CTaskOptions "ray::core::TaskOptions":
CTaskOptions()
CTaskOptions(c_string name, int num_returns,
unordered_map[c_string, double] &resources,
c_string concurrency_group_name,
int64_t generator_backpressure_num_objects,
int64_t num_objects_per_yield)
CTaskOptions(c_string name, int num_returns,
unordered_map[c_string, double] &resources,
c_string concurrency_group_name,
int64_t generator_backpressure_num_objects,
int64_t num_objects_per_yield,
c_string serialized_runtime_env)
CTaskOptions(c_string name, int num_returns,
unordered_map[c_string, double] &resources,
c_string concurrency_group_name,
int64_t generator_backpressure_num_objects,
int64_t num_objects_per_yield,
c_string serialized_runtime_env,
c_bool enable_task_events,
const unordered_map[c_string, c_string] &labels,
CLabelSelector label_selector,
optional[c_string] tensor_transport,
c_vector[CFallbackOption] fallback_strategy)
cdef cppclass CActorCreationOptions "ray::core::ActorCreationOptions":
CActorCreationOptions()
CActorCreationOptions(
int64_t max_restarts,
int64_t max_task_retries,
int32_t max_concurrency,
const unordered_map[c_string, double] &resources,
const unordered_map[c_string, double] &placement_resources,
const c_vector[c_string] &dynamic_worker_options,
optional[c_bool] is_detached, c_string &name, c_string &ray_namespace,
c_bool is_asyncio,
const CSchedulingStrategy &scheduling_strategy,
c_string serialized_runtime_env,
const c_vector[CConcurrencyGroup] &concurrency_groups,
c_bool allow_out_of_order_execution,
int32_t max_pending_calls,
c_bool enable_tensor_transport,
c_bool enable_task_events,
const unordered_map[c_string, c_string] &labels,
CLabelSelector label_selector,
c_vector[CFallbackOption] fallback_strategy,
int64_t actor_generator_backpressure_num_objects)
cdef cppclass CPlacementGroupCreationOptions \
"ray::core::PlacementGroupCreationOptions":
CPlacementGroupCreationOptions()
CPlacementGroupCreationOptions(
const c_string &name,
CPlacementStrategy strategy,
const c_vector[unordered_map[c_string, double]] &bundles,
c_bool is_detached,
CNodeID soft_target_node_id,
const c_vector[unordered_map[c_string, c_string]] &bundle_label_selector,
const unordered_map[c_string, CPlacementStrategy] &topology_strategy,
)
cdef cppclass CObjectLocation "ray::core::ObjectLocation":
const int64_t GetObjectSize() const
const c_vector[CNodeID] &GetNodeIDs() const
c_bool IsSpilled() const
const c_string &GetSpilledURL() const
const CNodeID &GetSpilledNodeID() const
const c_bool GetDidSpill() const
cdef extern from "ray/common/python_callbacks.h" namespace "ray":
cdef cppclass MultiItemPyCallback[T]:
MultiItemPyCallback(
object (*)(CRayStatus, c_vector[T]) nogil,
void (object, object) nogil,
object) nogil
cdef cppclass OptionalItemPyCallback[T]:
OptionalItemPyCallback(
object (*)(CRayStatus, optional[T]) nogil,
void (object, object) nogil,
object) nogil
cdef cppclass StatusPyCallback:
StatusPyCallback(
object (*)(CRayStatus) nogil,
void (object, object) nogil,
object) nogil
cdef extern from "ray/gcs_rpc_client/accessors/actor_info_accessor_interface.h" nogil:
cdef cppclass CActorInfoAccessorInterface "ray::gcs::ActorInfoAccessorInterface":
void AsyncGetAllByFilter(
const optional[CActorID] &actor_id,
const optional[CJobID] &job_id,
const optional[c_string] &actor_state_name,
const MultiItemPyCallback[CActorTableData] &callback,
int64_t timeout_ms)
cdef extern from "ray/gcs_rpc_client/accessor.h" nogil:
cdef cppclass CJobInfoAccessor "ray::gcs::JobInfoAccessor":
CRayStatus GetAll(
const optional[c_string] &job_or_submission_id,
c_bool skip_submission_job_info_field,
c_bool skip_is_running_tasks_field,
c_vector[CJobTableData] &result,
int64_t timeout_ms)
void AsyncGetAll(
const optional[c_string] &job_or_submission_id,
c_bool skip_submission_job_info_field,
c_bool skip_is_running_tasks_field,
const MultiItemPyCallback[CJobTableData] &callback,
int64_t timeout_ms)
cdef cppclass CNodeInfoAccessor "ray::gcs::NodeInfoAccessor":
CRayStatus CheckAlive(
const c_vector[CNodeID] &node_ids,
int64_t timeout_ms,
c_vector[c_bool] &result)
void AsyncCheckAlive(
const c_vector[CNodeID] &node_ids,
int64_t timeout_ms,
const MultiItemPyCallback[c_bool] &callback)
CRayStatus DrainNodes(
const c_vector[CNodeID] &node_ids,
int64_t timeout_ms,
c_vector[c_string] &drained_node_ids)
CStatusOr[c_vector[CGcsNodeInfo]] GetAllNoCache(
int64_t timeout_ms,
optional[CGcsNodeState] state_filter,
const c_vector[CNodeSelector] &node_selectors)
void AsyncGetAll(
const OptionalItemPyCallback[c_pair[c_vector[CGcsNodeInfo], int64_t]] &callback,
int64_t timeout_ms,
optional[CGcsNodeState] state_filter,
const c_vector[CNodeSelector] &node_selectors,
optional[int64_t] limit) const
cdef cppclass CNodeResourceInfoAccessor "ray::gcs::NodeResourceInfoAccessor":
CRayStatus GetAllResourceUsage(
int64_t timeout_ms,
CGetAllResourceUsageReply &serialized_reply)
cdef cppclass CInternalKVAccessor "ray::gcs::InternalKVAccessor":
CRayStatus Keys(
const c_string &ns,
const c_string &prefix,
int64_t timeout_ms,
c_vector[c_string] &value)
CRayStatus Put(
const c_string &ns,
const c_string &key,
const c_string &value,
c_bool overwrite,
int64_t timeout_ms,
c_bool &added)
CRayStatus Get(
const c_string &ns,
const c_string &key,
int64_t timeout_ms,
c_string &value)
CRayStatus MultiGet(
const c_string &ns,
const c_vector[c_string] &keys,
int64_t timeout_ms,
unordered_map[c_string, c_string] &values)
CRayStatus Del(
const c_string &ns,
const c_string &key,
c_bool del_by_prefix,
int64_t timeout_ms,
int& num_deleted)
CRayStatus Exists(
const c_string &ns,
const c_string &key,
int64_t timeout_ms,
c_bool &exists)
void AsyncInternalKVKeys(
const c_string &ns,
const c_string &prefix,
int64_t timeout_ms,
const OptionalItemPyCallback[c_vector[c_string]] &callback)
void AsyncInternalKVGet(
const c_string &ns,
const c_string &key,
int64_t timeout_ms,
const OptionalItemPyCallback[c_string] &callback)
void AsyncInternalKVMultiGet(
const c_string &ns,
const c_vector[c_string] &keys,
int64_t timeout_ms,
const OptionalItemPyCallback[unordered_map[c_string, c_string]] &callback)
void AsyncInternalKVPut(
const c_string &ns,
const c_string &key,
const c_string &value,
c_bool overwrite,
int64_t timeout_ms,
const OptionalItemPyCallback[c_bool] &callback)
void AsyncInternalKVExists(
const c_string &ns,
const c_string &key,
int64_t timeout_ms,
const OptionalItemPyCallback[c_bool] &callback)
void AsyncInternalKVDel(
const c_string &ns,
const c_string &key,
c_bool del_by_prefix,
int64_t timeout_ms,
const OptionalItemPyCallback[int] &callback)
cdef cppclass CRuntimeEnvAccessor "ray::gcs::RuntimeEnvAccessor":
CRayStatus PinRuntimeEnvUri(
const c_string &uri,
int expiration_s,
int64_t timeout_ms)
cdef cppclass CAutoscalerStateAccessor "ray::gcs::AutoscalerStateAccessor":
CRayStatus RequestClusterResourceConstraint(
int64_t timeout_ms,
const c_vector[unordered_map[c_string, double]] &bundles,
const c_vector[unordered_map[c_string, c_string]] &label_selectors,
const c_vector[int64_t] &count_array,
)
CRayStatus GetClusterResourceState(
int64_t timeout_ms,
c_string &serialized_reply
)
CRayStatus GetClusterStatus(
int64_t timeout_ms,
c_string &serialized_reply
)
void AsyncGetClusterStatus(
int64_t timeout_ms,
const OptionalItemPyCallback[CGetClusterStatusReply] &callback)
CRayStatus ReportAutoscalingState(
int64_t timeout_ms,
const c_string &serialized_state
)
CRayStatus ReportClusterConfig(
int64_t timeout_ms,
const c_string &serialized_cluster_config
)
CRayStatus DrainNode(
const c_string &node_id,
int32_t reason,
const c_string &reason_message,
int64_t deadline_timestamp_ms,
int64_t timeout_ms,
c_bool &is_accepted,
c_string &rejection_reason_message
)
CRayStatus ResizeRayletResourceInstances(
const c_string &node_id,
const unordered_map[c_string, double] &resources,
int64_t timeout_ms,
unordered_map[c_string, double] &total_resources
)
cdef cppclass CPublisherAccessor "ray::gcs::PublisherAccessor":
CRayStatus PublishError(
c_string key_id,
CErrorTableData data,
int64_t timeout_ms)
CRayStatus PublishLogs(
c_string key_id,
CLogBatch data,
int64_t timeout_ms)
void AsyncPublishNodeResourceUsage(
c_string key_id,
c_string node_resource_usage,
const StatusPyCallback &callback
)
cdef cppclass CTaskInfoAccessor "ray::gcs::TaskInfoAccessor":
void AsyncAddEvents(
CAddEventsRequest &&request,
const StatusPyCallback &callback,
int64_t timeout_ms)
cdef extern from "ray/gcs_rpc_client/gcs_client.h" nogil:
cdef enum CGrpcStatusCode "grpc::StatusCode":
UNAVAILABLE "grpc::StatusCode::UNAVAILABLE",
UNKNOWN "grpc::StatusCode::UNKNOWN",
DEADLINE_EXCEEDED "grpc::StatusCode::DEADLINE_EXCEEDED",
RESOURCE_EXHAUSTED "grpc::StatusCode::RESOURCE_EXHAUSTED",
UNIMPLEMENTED "grpc::StatusCode::UNIMPLEMENTED",
cdef cppclass CGcsClientOptions "ray::gcs::GcsClientOptions":
CGcsClientOptions(
c_string gcs_address, int port, CClusterID cluster_id,
c_bool allow_cluster_id_nil, c_bool fetch_cluster_id_if_nil)
cdef cppclass CGcsClient "ray::gcs::GcsClient":
CGcsClient(CGcsClientOptions options)
c_pair[c_string, int] GetGcsServerAddress() const
CClusterID GetClusterId() const
CActorInfoAccessorInterface& Actors()
CJobInfoAccessor& Jobs()
CInternalKVAccessor& InternalKV()
CNodeInfoAccessor& Nodes()
CNodeResourceInfoAccessor& NodeResources()
CRuntimeEnvAccessor& RuntimeEnvs()
CAutoscalerStateAccessor& Autoscaler()
CPublisherAccessor& Publisher()
CTaskInfoAccessor& Tasks()
CGcsRpcClient& GetGcsRpcClient()
cdef CRayStatus ConnectOnSingletonIoContext(CGcsClient &gcs_client, int timeout_ms)
cdef extern from "ray/gcs_rpc_client/rpc_client.h" namespace "ray::rpc::events" nogil:
cdef cppclass CAddEventsRequest "ray::rpc::events::AddEventsRequest":
bint ParseFromString(const c_string &data)
cdef cppclass CAddEventsReply "ray::rpc::events::AddEventsReply":
pass
cdef extern from "ray/gcs_rpc_client/rpc_client.h" namespace "ray::rpc" nogil:
cdef cppclass CGcsRpcClient "ray::rpc::GcsRpcClient":
pass
cdef extern from "ray/gcs_rpc_client/gcs_client.h" namespace "ray::gcs" nogil:
unordered_map[c_string, double] PythonGetResourcesTotal(
const CGcsNodeInfo& node_info)
cdef extern from "ray/pubsub/python_gcs_subscriber.h" nogil:
cdef cppclass CPythonGcsSubscriber "ray::pubsub::PythonGcsSubscriber":
CPythonGcsSubscriber(
const c_string& gcs_address, int gcs_port, CChannelType channel_type,
const c_string& subscriber_id, const c_string& worker_id)
CRayStatus Subscribe()
int64_t last_batch_size()
CRayStatus PollError(
c_string* key_id, int64_t timeout_ms, CErrorTableData* data)
CRayStatus PollLogs(
c_string* key_id, int64_t timeout_ms, CLogBatch* data)
CRayStatus Close()
cdef extern from "ray/pubsub/python_gcs_subscriber.h" namespace "ray::pubsub" nogil:
c_vector[c_string] PythonGetLogBatchLines(CLogBatch log_batch)
cdef extern from "ray/gcs_rpc_client/gcs_client.h" namespace "ray::gcs" nogil:
unordered_map[c_string, c_string] PythonGetNodeLabels(
const CGcsNodeInfo& node_info)
cdef extern from "src/ray/protobuf/gcs.pb.h" nogil:
cdef enum CChannelType "ray::rpc::ChannelType":
RAY_ERROR_INFO_CHANNEL "ray::rpc::ChannelType::RAY_ERROR_INFO_CHANNEL",
RAY_LOG_CHANNEL "ray::rpc::ChannelType::RAY_LOG_CHANNEL",
GCS_ACTOR_CHANNEL "ray::rpc::ChannelType::GCS_ACTOR_CHANNEL",
cdef cppclass CJobConfig "ray::rpc::JobConfig":
c_string ray_namespace() const
const c_string &SerializeAsString() const
cdef cppclass CNodeDeathInfo "ray::rpc::NodeDeathInfo":
int reason() const
c_string reason_message() const
cdef cppclass CGcsNodeInfo "ray::rpc::GcsNodeInfo":
c_string node_id() const
c_string node_name() const
int state() const
c_string node_manager_address() const
c_string node_manager_hostname() const
int node_manager_port() const
int object_manager_port() const
c_string object_store_socket_name() const
c_string raylet_socket_name() const
int metrics_export_port() const
int metrics_agent_port() const
int dashboard_agent_listen_port() const
int runtime_env_agent_port() const
CNodeDeathInfo death_info() const
void ParseFromString(const c_string &serialized)
const c_string& SerializeAsString() const
cdef enum CGcsNodeState "ray::rpc::GcsNodeInfo_GcsNodeState":
ALIVE "ray::rpc::GcsNodeInfo_GcsNodeState_ALIVE",
cdef cppclass CNodeSelector "ray::rpc::GetAllNodeInfoRequest::NodeSelector":
CNodeSelector()
void set_node_id(const c_string &node_id)
void set_node_name(const c_string &node_name)
void set_node_ip_address(const c_string &node_ip_address)
void set_is_head_node(c_bool is_head_node)
void ParseFromString(const c_string &serialized)
cdef cppclass CJobTableData "ray::rpc::JobTableData":
c_string job_id() const
c_bool is_dead() const
CJobConfig config() const
const c_string &SerializeAsString() const
cdef cppclass CGetAllResourceUsageReply "ray::rpc::GetAllResourceUsageReply":
const c_string& SerializeAsString() const
cdef cppclass CPythonFunction "ray::rpc::PythonFunction":
void set_key(const c_string &key)
c_string key() const
cdef cppclass CErrorTableData "ray::rpc::ErrorTableData":
c_string job_id() const
c_string type() const
c_string error_message() const
double timestamp() const
void set_job_id(const c_string &job_id)
void set_type(const c_string &type)
void set_error_message(const c_string &error_message)
void set_timestamp(double timestamp)
cdef cppclass CLogBatch "ray::rpc::LogBatch":
c_string ip() const
c_string pid() const
c_string job_id() const
c_bool is_error() const
c_string actor_name() const
c_string task_name() const
void set_ip(const c_string &ip)
void set_pid(const c_string &pid)
void set_job_id(const c_string &job_id)
void set_is_error(c_bool is_error)
void add_lines(const c_string &line)
void set_actor_name(const c_string &actor_name)
void set_task_name(const c_string &task_name)
cdef cppclass CActorTableData "ray::rpc::ActorTableData":
CAddress address() const
void ParseFromString(const c_string &serialized)
const c_string &SerializeAsString() const
cdef extern from "src/ray/protobuf/autoscaler.pb.h" nogil:
cdef cppclass CGetClusterStatusReply "ray::rpc::autoscaler::GetClusterStatusReply":
c_string serialized_cluster_status() const
void ParseFromString(const c_string &serialized)
const c_string &SerializeAsString() const
cdef extern from "ray/raylet_rpc_client/raylet_client_with_io_context.h" nogil:
cdef cppclass CRayletClientWithIoContext "ray::rpc::RayletClientWithIoContext":
CRayletClientWithIoContext(const c_string &ip_address, int port)
CRayStatus GetWorkerPIDs(const OptionalItemPyCallback[c_vector[int32_t]] &callback,
int64_t timeout_ms)
CRayStatus GetAgentPIDs(const OptionalItemPyCallback[c_vector[int32_t]] &callback,
int64_t timeout_ms)
cdef extern from "ray/common/task/task_spec.h" nogil:
cdef cppclass CConcurrencyGroup "ray::ConcurrencyGroup":
CConcurrencyGroup(
c_string name,
uint32_t max_concurrency,
c_vector[CFunctionDescriptor] c_fds)
CConcurrencyGroup()
c_string GetName() const
uint32_t GetMaxConcurrency() const
c_vector[CFunctionDescriptor] GetFunctionDescriptors() const
cdef extern from "ray/common/constants.h" nogil:
cdef const char[] kWorkerSetupHookKeyName
cdef int kResourceUnitScaling
cdef const char[] kImplicitResourcePrefix
cdef int kStreamingGeneratorReturn
cdef const char[] kGcsAutoscalerStateNamespace
cdef const char[] kGcsAutoscalerV2EnabledKey
cdef const char[] kGcsAutoscalerClusterConfigKey
cdef const char[] kGcsPidKey
cdef const char[] kNodeTypeNameEnv
cdef const char[] kNodeMarketTypeEnv
cdef const char[] kNodeRegionEnv
cdef const char[] kNodeZoneEnv
cdef const char[] kLabelKeyNodeID
cdef const char[] kLabelKeyNodeAcceleratorType
cdef const char[] kLabelKeyNodeMarketType
cdef const char[] kLabelKeyNodeRegion
cdef const char[] kLabelKeyNodeZone
cdef const char[] kLabelKeyNodeGroup
cdef const char[] kLabelKeyTpuTopology
# Port names for local port discovery
cdef const char[] kRuntimeEnvAgentPortName
cdef const char[] kMetricsAgentPortName
cdef const char[] kMetricsExportPortName
cdef const char[] kDashboardAgentListenPortName
cdef const char[] kGcsServerPortName
cdef const char[] kLabelKeyTpuSliceName
cdef const char[] kLabelKeyTpuWorkerId
cdef const char[] kLabelKeyTpuPodType
cdef const char[] kRayInternalNamespacePrefix
+216
View File
@@ -0,0 +1,216 @@
from libcpp cimport bool as c_bool
from libcpp.string cimport string as c_string
from libcpp.vector cimport vector as c_vector
from ray.includes.common cimport (
CObjectLocation,
CGcsClientOptions,
CPythonGcsSubscriber,
kWorkerSetupHookKeyName,
kResourceUnitScaling,
kImplicitResourcePrefix,
kStreamingGeneratorReturn,
kGcsAutoscalerStateNamespace,
kGcsAutoscalerV2EnabledKey,
kGcsAutoscalerClusterConfigKey,
kGcsPidKey,
kNodeTypeNameEnv,
kNodeMarketTypeEnv,
kNodeRegionEnv,
kNodeZoneEnv,
kLabelKeyNodeAcceleratorType,
kLabelKeyNodeMarketType,
kLabelKeyNodeRegion,
kLabelKeyNodeZone,
kLabelKeyNodeID,
kLabelKeyNodeGroup,
kLabelKeyTpuTopology,
kLabelKeyTpuSliceName,
kLabelKeyTpuWorkerId,
kLabelKeyTpuPodType,
kRayInternalNamespacePrefix,
kRuntimeEnvAgentPortName,
kMetricsAgentPortName,
kMetricsExportPortName,
kDashboardAgentListenPortName,
kGcsServerPortName,
)
from ray.exceptions import (
RayActorError,
ActorAlreadyExistsError,
ActorDiedError,
RayError,
RaySystemError,
AuthenticationError,
RayTaskError,
ObjectStoreFullError,
OutOfDiskError,
GetTimeoutError,
TaskCancelledError,
AsyncioActorExit,
PendingCallsLimitExceeded,
RpcError,
ObjectRefStreamEndOfStreamError,
)
cdef class GcsClientOptions:
"""Cython wrapper class of C++ `ray::gcs::GcsClientOptions`."""
cdef:
unique_ptr[CGcsClientOptions] inner
@classmethod
def create(
cls, gcs_address, cluster_id_hex, allow_cluster_id_nil, fetch_cluster_id_if_nil
):
"""
Creates a GcsClientOption with a maybe-Nil cluster_id, and may fetch from GCS.
"""
cdef CClusterID c_cluster_id = CClusterID.Nil()
if cluster_id_hex:
c_cluster_id = CClusterID.FromHex(cluster_id_hex)
self = GcsClientOptions()
try:
ip, port_str = parse_address(gcs_address)
port = int(port_str)
self.inner.reset(
new CGcsClientOptions(
ip, port, c_cluster_id, allow_cluster_id_nil, allow_cluster_id_nil))
except Exception:
raise ValueError(f"Invalid gcs_address: {gcs_address}")
return self
cdef CGcsClientOptions* native(self):
return <CGcsClientOptions*>(self.inner.get())
cdef int check_status(const CRayStatus& status) except -1 nogil:
if status.ok():
return 0
with gil:
message = status.message().decode()
if status.IsObjectStoreFull():
raise ObjectStoreFullError(message)
elif status.IsInvalidArgument():
raise ValueError(message)
elif status.IsAlreadyExists():
raise ActorAlreadyExistsError(message)
elif status.IsOutOfDisk():
raise OutOfDiskError(message)
elif status.IsObjectRefEndOfStream():
raise ObjectRefStreamEndOfStreamError(message)
elif status.IsInterrupted():
raise KeyboardInterrupt()
elif status.IsTimedOut():
raise GetTimeoutError(message)
elif status.IsNotFound():
raise ValueError(message)
elif status.IsObjectNotFound():
raise ValueError(message)
elif status.IsObjectUnknownOwner():
raise ValueError(message)
elif status.IsIOError():
raise IOError(message)
elif status.IsUnauthenticated():
raise AuthenticationError(message)
elif status.IsRpcError():
raise RpcError(message, rpc_code=status.rpc_code())
elif status.IsIntentionalSystemExit():
with gil:
raise_sys_exit_with_custom_error_message(message)
elif status.IsUnexpectedSystemExit():
with gil:
raise_sys_exit_with_custom_error_message(
message, exit_code=1)
elif status.IsChannelError():
raise RayChannelError(message)
elif status.IsChannelTimeoutError():
raise RayChannelTimeoutError(message)
else:
raise RaySystemError(message)
cdef int check_status_timeout_as_rpc_error(const CRayStatus& status) except -1 nogil:
"""
Same as check_status, except that it raises RpcError for timeout. This is for
backward compatibility: on timeout, `ray.get` raises GetTimeoutError, while
GcsClient methods raise RpcError. So in the binding, `get_objects` use check_status
and GcsClient methods use check_status_timeout_as_rpc_error.
"""
if status.IsTimedOut():
raise RpcError(status.message().decode(),
rpc_code=CGrpcStatusCode.DEADLINE_EXCEEDED)
return check_status(status)
WORKER_PROCESS_SETUP_HOOK_KEY_NAME_GCS = str(kWorkerSetupHookKeyName)
RESOURCE_UNIT_SCALING = kResourceUnitScaling
IMPLICIT_RESOURCE_PREFIX = kImplicitResourcePrefix.decode()
STREAMING_GENERATOR_RETURN = kStreamingGeneratorReturn
GCS_AUTOSCALER_STATE_NAMESPACE = kGcsAutoscalerStateNamespace.decode()
GCS_AUTOSCALER_V2_ENABLED_KEY = kGcsAutoscalerV2EnabledKey.decode()
GCS_AUTOSCALER_CLUSTER_CONFIG_KEY = kGcsAutoscalerClusterConfigKey.decode()
GCS_PID_KEY = kGcsPidKey.decode()
# Ray node label related constants from src/ray/common/constants.h
NODE_TYPE_NAME_ENV = kNodeTypeNameEnv.decode()
NODE_MARKET_TYPE_ENV = kNodeMarketTypeEnv.decode()
NODE_REGION_ENV = kNodeRegionEnv.decode()
NODE_ZONE_ENV = kNodeZoneEnv.decode()
RAY_NODE_ACCELERATOR_TYPE_KEY = kLabelKeyNodeAcceleratorType.decode()
RAY_NODE_MARKET_TYPE_KEY = kLabelKeyNodeMarketType.decode()
RAY_NODE_REGION_KEY = kLabelKeyNodeRegion.decode()
RAY_NODE_ZONE_KEY = kLabelKeyNodeZone.decode()
# Keep this in sync with NODE_ID_LABEL_KEY in ray.util.placement_group. That
# module cannot import this exported value because it forms a circular dependency
RAY_NODE_ID_KEY = kLabelKeyNodeID.decode()
RAY_NODE_GROUP_KEY = kLabelKeyNodeGroup.decode()
# TPU specifc Ray node label related constants
RAY_NODE_TPU_TOPOLOGY_KEY = kLabelKeyTpuTopology.decode()
RAY_NODE_TPU_SLICE_NAME_KEY = kLabelKeyTpuSliceName.decode()
RAY_NODE_TPU_WORKER_ID_KEY = kLabelKeyTpuWorkerId.decode()
RAY_NODE_TPU_POD_TYPE_KEY = kLabelKeyTpuPodType.decode()
RAY_INTERNAL_NAMESPACE_PREFIX = kRayInternalNamespacePrefix.decode()
# Prefix for namespaces which are used internally by ray.
# Jobs within these namespaces should be hidden from users
# Port names for local port discovery
RUNTIME_ENV_AGENT_PORT_NAME = kRuntimeEnvAgentPortName.decode()
METRICS_AGENT_PORT_NAME = kMetricsAgentPortName.decode()
METRICS_EXPORT_PORT_NAME = kMetricsExportPortName.decode()
DASHBOARD_AGENT_LISTEN_PORT_NAME = kDashboardAgentListenPortName.decode()
GCS_SERVER_PORT_NAME = kGcsServerPortName.decode()
# and should not be considered user activity.
RAY_INTERNAL_DASHBOARD_NAMESPACE = f"{RAY_INTERNAL_NAMESPACE_PREFIX}dashboard"
# Util functions for async handling
cdef incremented_fut():
fut = concurrent.futures.Future()
cpython.Py_INCREF(fut)
return fut
cdef void assign_and_decrement_fut(result, fut) noexcept with gil:
assert isinstance(fut, concurrent.futures.Future)
assert not fut.done()
try:
ret, exc = result
if exc:
fut.set_exception(exc)
else:
fut.set_result(ret)
finally:
# We INCREFed it in `incremented_fut` to keep it alive during the async wait,
# and we DECREF it here to balance it.
cpython.Py_DECREF(fut)
cdef raise_or_return(tup):
ret, exc = tup
if exc:
raise exc
return ret
+30
View File
@@ -0,0 +1,30 @@
from libc.stdint cimport int64_t
from libcpp.string cimport string as c_string
from libcpp.memory cimport unique_ptr
from libcpp.vector cimport vector as c_vector
cdef extern from "ray/observability/ray_event_interface.h" namespace "ray::observability" nogil:
cdef cppclass CRayEventInterface "ray::observability::RayEventInterface":
pass
cdef extern from "ray/observability/python_event_interface.h" namespace "ray::observability" nogil:
unique_ptr[CRayEventInterface] CreatePythonRayEvent(
int source_type,
int event_type,
int severity,
const c_string &entity_id,
const c_string &message,
const c_string &session_name,
const c_string &serialized_event_data,
int nested_event_field_number,
const c_string &event_id,
int64_t timestamp_ns)
cdef cppclass CPythonEventRecorder "ray::observability::PythonEventRecorder":
CPythonEventRecorder(int aggregator_port,
const c_string &node_ip,
const c_string &node_id_hex,
size_t max_buffer_size,
const c_string &metric_source)
void AddEvents(c_vector[unique_ptr[CRayEventInterface]] &&data_list)
void Shutdown()
+239
View File
@@ -0,0 +1,239 @@
"""Cython bindings for RayEventRecorder.
This module provides Python access to the C++ RayEventRecorder for emitting
internal Ray events from Python code (e.g., submission job events).
"""
from ray.includes.event_recorder cimport (
CRayEventInterface,
CPythonEventRecorder,
CreatePythonRayEvent,
)
from ray.includes.common cimport move
from libc.stdint cimport int64_t
from libcpp.memory cimport unique_ptr
from libcpp.vector cimport vector as c_vector
from libcpp.string cimport string as c_string
import logging
import threading
logger = logging.getLogger(__name__)
cdef class RayEvent:
"""Python wrapper holding event data for transfer to C++.
This class stores event metadata and serialized protobuf data. When added
to the EventRecorder, it creates the underlying C++ RayEvent object.
Args:
source_type: Integer value of RayEvent.SourceType enum.
event_type: Integer value of RayEvent.EventType enum.
severity: Integer value of RayEvent.Severity enum.
entity_id: Unique identifier for the event entity (e.g., submission_id).
message: Optional message associated with the event.
session_name: The Ray session name.
serialized_data: Serialized protobuf bytes of the nested event message.
nested_event_field_number: The field number in RayEvent proto for the
nested event message. Use RayEventProto.<FIELD>_FIELD_NUMBER constants.
event_id: Optional explicit event id bytes. Pass empty bytes to let the
C++ layer generate a random id (matching the convention used by the
other RayEventInterface subclasses). A non-empty value is preserved
as-is — use this to reuse an id from an upstream source
(e.g., a Kubernetes event uid).
timestamp_ns: Optional explicit event timestamp in nanoseconds since the
unix epoch. Pass 0 to let the C++ layer capture the time at
construction via absl::Now().
"""
cdef:
int _source_type
int _event_type
int _severity
str _entity_id
str _message
str _session_name
bytes _serialized_data
int _nested_event_field_number
bytes _event_id
int64_t _timestamp_ns
def __init__(
self,
int source_type,
int event_type,
int severity,
str entity_id,
str message,
str session_name,
bytes serialized_data,
int nested_event_field_number,
bytes event_id = b"",
int64_t timestamp_ns = 0,
):
self._source_type = source_type
self._event_type = event_type
self._severity = severity
self._entity_id = entity_id
self._message = message
self._session_name = session_name
self._serialized_data = serialized_data
self._nested_event_field_number = nested_event_field_number
self._event_id = event_id
self._timestamp_ns = timestamp_ns
cdef unique_ptr[CRayEventInterface] to_cpp_event(self):
"""Create the underlying C++ event. Ownership is transferred to caller."""
return CreatePythonRayEvent(
self._source_type,
self._event_type,
self._severity,
self._entity_id.encode("utf-8"),
self._message.encode("utf-8"),
self._session_name.encode("utf-8"),
self._serialized_data,
self._nested_event_field_number,
self._event_id,
self._timestamp_ns,
)
@property
def entity_id(self):
return self._entity_id
@property
def event_type(self):
return self._event_type
# module-level Singleton instance, lazily created by EventRecorder.initialize().
_event_recorder_instance = None
# Guards singleton lifecycle and emission against concurrent shutdown/initialize.
_event_recorder_lock = threading.RLock()
cdef class EventRecorder:
"""Per-process singleton for recording Ray events."""
cdef unique_ptr[CPythonEventRecorder] _recorder
def __dealloc__(self):
"""Safety-net cleanup. C++ destructor also calls Shutdown()."""
if self._recorder.get() != NULL:
self._recorder.get().Shutdown()
self._recorder.reset()
@staticmethod
def initialize(
int aggregator_port,
str node_ip,
str node_id_hex,
size_t max_buffer_size,
str metric_source = "python",
):
"""Initialize the per-process event recorder.
Creates the underlying C++ PythonEventRecorder with a background I/O
thread and gRPC client. No-op if already initialized.
Args:
aggregator_port: Port of the event aggregator server (bound on 127.0.0.1).
node_ip: IP address of the current node.
node_id_hex: Hex-encoded node ID.
max_buffer_size: Maximum number of events to buffer.
metric_source: Label for the "Source" tag on dropped-events metrics
(default "python").
"""
global _event_recorder_instance
cdef EventRecorder rec
with _event_recorder_lock:
if _event_recorder_instance is not None:
return
rec = EventRecorder()
rec._recorder.reset(
new CPythonEventRecorder(
aggregator_port,
node_ip.encode("utf-8"),
node_id_hex.encode("utf-8"),
max_buffer_size,
metric_source.encode("utf-8"),
)
)
_event_recorder_instance = rec
@staticmethod
def instance():
"""Get the per-process EventRecorder singleton.
Returns:
The EventRecorder instance if initialized, None otherwise.
"""
with _event_recorder_lock:
return _event_recorder_instance
@staticmethod
def shutdown():
"""Shutdown the event recorder.
Stops exporting events, performs a final flush, and releases all
C++ resources. After this call, emit() and emit_batch() will be
no-ops until initialize() is called again.
"""
global _event_recorder_instance
cdef EventRecorder rec
with _event_recorder_lock:
if _event_recorder_instance is None:
return
rec = <EventRecorder>_event_recorder_instance
if rec._recorder.get() != NULL:
with nogil:
rec._recorder.get().Shutdown()
rec._recorder.reset()
_event_recorder_instance = None
@staticmethod
def emit(RayEvent event):
"""Emit a single event. No-op if not initialized.
Args:
event: A RayEvent object (created via InternalEventBuilder.build()).
Returns:
True if the event was successfully queued, False otherwise.
"""
return EventRecorder.emit_batch([event])
@staticmethod
def emit_batch(list events):
"""Emit multiple events. No-op if not initialized.
Args:
events: List of RayEvent objects to emit.
Returns:
True if events were successfully queued, False otherwise.
"""
cdef EventRecorder rec
cdef c_vector[unique_ptr[CRayEventInterface]] cpp_events
cdef RayEvent ev
if not events:
return True
for ev in events:
cpp_events.push_back(move(ev.to_cpp_event()))
with _event_recorder_lock:
if _event_recorder_instance is None:
logger.debug(
"Event recorder not initialized, dropping %d events",
len(events),
)
return False
rec = <EventRecorder>_event_recorder_instance
with nogil:
rec._recorder.get().AddEvents(move(cpp_events))
return True
@@ -0,0 +1,80 @@
from libc.stdint cimport uint8_t, uint64_t
from libcpp cimport bool as c_bool
from libcpp.memory cimport unique_ptr, shared_ptr
from libcpp.string cimport string as c_string
from libcpp.unordered_map cimport unordered_map
from libcpp.vector cimport vector as c_vector
from ray.includes.common cimport (
CLanguage,
)
from ray.includes.unique_ids cimport (
CActorID,
CJobID,
CObjectID,
CTaskID,
)
cdef extern from "src/ray/protobuf/common.pb.h" nogil:
cdef cppclass CFunctionDescriptorType \
"ray::FunctionDescriptorType":
pass
cdef CFunctionDescriptorType EmptyFunctionDescriptorType \
"ray::FunctionDescriptorType::FUNCTION_DESCRIPTOR_NOT_SET"
cdef CFunctionDescriptorType JavaFunctionDescriptorType \
"ray::FunctionDescriptorType::kJavaFunctionDescriptor"
cdef CFunctionDescriptorType PythonFunctionDescriptorType \
"ray::FunctionDescriptorType::kPythonFunctionDescriptor"
cdef CFunctionDescriptorType CppFunctionDescriptorType \
"ray::FunctionDescriptorType::kCppFunctionDescriptor"
cdef extern from "ray/common/function_descriptor.h" nogil:
cdef cppclass CFunctionDescriptorInterface \
"ray::FunctionDescriptorInterface":
CFunctionDescriptorType Type()
c_string ToString()
c_string Serialize()
ctypedef shared_ptr[CFunctionDescriptorInterface] CFunctionDescriptor \
"ray::FunctionDescriptor"
cdef cppclass CFunctionDescriptorBuilder "ray::FunctionDescriptorBuilder":
@staticmethod
CFunctionDescriptor Empty()
@staticmethod
CFunctionDescriptor BuildJava(const c_string &class_name,
const c_string &function_name,
const c_string &signature)
@staticmethod
CFunctionDescriptor BuildPython(const c_string &module_name,
const c_string &class_name,
const c_string &function_name,
const c_string &function_source_hash)
@staticmethod
CFunctionDescriptor BuildCpp(const c_string &function_name,
const c_string &caller,
const c_string &class_name)
@staticmethod
CFunctionDescriptor Deserialize(const c_string &serialized_binary)
cdef cppclass CJavaFunctionDescriptor "ray::JavaFunctionDescriptor":
c_string ClassName()
c_string FunctionName()
c_string Signature()
cdef cppclass CPythonFunctionDescriptor "ray::PythonFunctionDescriptor":
c_string ModuleName()
c_string ClassName()
c_string FunctionName()
c_string FunctionHash()
cdef cppclass CCppFunctionDescriptor "ray::CppFunctionDescriptor":
c_string FunctionName()
c_string Caller()
c_string ClassName()
+396
View File
@@ -0,0 +1,396 @@
from ray.includes.function_descriptor cimport (
CFunctionDescriptor,
CFunctionDescriptorBuilder,
CPythonFunctionDescriptor,
CJavaFunctionDescriptor,
CCppFunctionDescriptor,
EmptyFunctionDescriptorType,
JavaFunctionDescriptorType,
PythonFunctionDescriptorType,
CppFunctionDescriptorType,
)
import hashlib
import cython
import inspect
import uuid
import ray._private.ray_constants as ray_constants
ctypedef object (*FunctionDescriptor_from_cpp)(const CFunctionDescriptor &)
cdef unordered_map[int, FunctionDescriptor_from_cpp] \
FunctionDescriptor_constructor_map
cdef CFunctionDescriptorToPython(CFunctionDescriptor function_descriptor):
cdef int function_descriptor_type = <int>function_descriptor.get().Type()
it = FunctionDescriptor_constructor_map.find(function_descriptor_type)
if it == FunctionDescriptor_constructor_map.end():
raise Exception("Can't construct FunctionDescriptor from type {}"
.format(function_descriptor_type))
else:
constructor = dereference(it).second
return constructor(function_descriptor)
@cython.auto_pickle(False)
cdef class FunctionDescriptor:
def __cinit__(self, *args, **kwargs):
if type(self) == FunctionDescriptor:
raise Exception("type {} is abstract".format(type(self).__name__))
def __hash__(self):
return hash(self.descriptor.get().ToString())
def __eq__(self, other):
return (type(self) == type(other) and
self.descriptor.get().ToString() ==
(<FunctionDescriptor>other).descriptor.get().ToString())
def __repr__(self):
return <str>self.descriptor.get().ToString()
def to_dict(self):
d = {"type": type(self).__name__}
for k, v in vars(type(self)).items():
if inspect.isgetsetdescriptor(v):
d[k] = v.__get__(self)
return d
@property
def repr(self):
return self.__repr__()
FunctionDescriptor_constructor_map[<int>EmptyFunctionDescriptorType] = \
EmptyFunctionDescriptor.from_cpp
@cython.auto_pickle(False)
cdef class EmptyFunctionDescriptor(FunctionDescriptor):
def __cinit__(self):
self.descriptor = CFunctionDescriptorBuilder.Empty()
def __reduce__(self):
return EmptyFunctionDescriptor, ()
@staticmethod
cdef from_cpp(const CFunctionDescriptor &c_function_descriptor):
return EmptyFunctionDescriptor()
@property
def repr(self):
return "FunctionDescriptor(empty)"
FunctionDescriptor_constructor_map[<int>JavaFunctionDescriptorType] = \
JavaFunctionDescriptor.from_cpp
@cython.auto_pickle(False)
cdef class JavaFunctionDescriptor(FunctionDescriptor):
cdef:
CJavaFunctionDescriptor *typed_descriptor
def __cinit__(self,
class_name,
function_name,
signature):
self.descriptor = CFunctionDescriptorBuilder.BuildJava(
class_name, function_name, signature)
self.typed_descriptor = <CJavaFunctionDescriptor*>(
self.descriptor.get())
def __reduce__(self):
return JavaFunctionDescriptor, (self.typed_descriptor.ClassName(),
self.typed_descriptor.FunctionName(),
self.typed_descriptor.Signature())
@staticmethod
cdef from_cpp(const CFunctionDescriptor &c_function_descriptor):
cdef CJavaFunctionDescriptor *typed_descriptor = \
<CJavaFunctionDescriptor*>(c_function_descriptor.get())
return JavaFunctionDescriptor(typed_descriptor.ClassName(),
typed_descriptor.FunctionName(),
typed_descriptor.Signature())
@property
def class_name(self):
"""Get the class name of current function descriptor.
Returns:
The class name of the function descriptor. It could be
empty if the function is not a class method.
"""
return <str>self.typed_descriptor.ClassName()
@property
def function_name(self):
"""Get the function name of current function descriptor.
Returns:
The function name of the function descriptor.
"""
return <str>self.typed_descriptor.FunctionName()
@property
def signature(self):
"""Get the signature of current function descriptor.
Returns:
The signature of the function descriptor.
"""
return <str>self.typed_descriptor.Signature()
@property
def repr(self):
return f"{self.class_name}.{self.function_name}"
FunctionDescriptor_constructor_map[<int>PythonFunctionDescriptorType] = \
PythonFunctionDescriptor.from_cpp
@cython.auto_pickle(False)
cdef class PythonFunctionDescriptor(FunctionDescriptor):
cdef:
CPythonFunctionDescriptor *typed_descriptor
object _function_id
def __cinit__(self,
module_name,
function_name,
class_name="",
function_source_hash=""):
self.descriptor = CFunctionDescriptorBuilder.BuildPython(
module_name, class_name, function_name, function_source_hash)
self.typed_descriptor = <CPythonFunctionDescriptor*>(
self.descriptor.get())
def __reduce__(self):
return PythonFunctionDescriptor, (self.typed_descriptor.ModuleName(),
self.typed_descriptor.FunctionName(),
self.typed_descriptor.ClassName(),
self.typed_descriptor.FunctionHash())
@staticmethod
cdef from_cpp(const CFunctionDescriptor &c_function_descriptor):
cdef CPythonFunctionDescriptor *typed_descriptor = \
<CPythonFunctionDescriptor*>(c_function_descriptor.get())
return PythonFunctionDescriptor(typed_descriptor.ModuleName(),
typed_descriptor.FunctionName(),
typed_descriptor.ClassName(),
typed_descriptor.FunctionHash())
@classmethod
def from_function(cls, function, function_uuid):
"""Create a FunctionDescriptor from a function instance.
This function is used to create the function descriptor from
a python function. If a function is a class function, it should
not be used by this function.
Args:
cls: Current class which is required argument for classmethod.
function: the python function used to create the function
descriptor.
function_uuid: Used to uniquely identify a function.
Ideally we can use the pickled function bytes
but cloudpickle isn't stable in some cases
for the same function.
Returns:
The FunctionDescriptor instance created according to the function.
"""
module_name = cls._get_module_name(function)
function_name = function.__qualname__
class_name = ""
return cls(module_name, function_name, class_name, function_uuid.hex)
@classmethod
def from_class(cls, target_class):
"""Create a FunctionDescriptor from a class.
Args:
cls: Current class which is required argument for classmethod.
target_class: the python class used to create the function
descriptor.
Returns:
The FunctionDescriptor instance created according to the class.
"""
module_name = cls._get_module_name(target_class)
class_name = target_class.__qualname__
# Use a random uuid as function hash to solve actor name conflict.
return cls(module_name, "__init__", class_name, uuid.uuid4().hex)
@property
def module_name(self):
"""Get the module name of current function descriptor.
Returns:
The module name of the function descriptor.
"""
return <str>self.typed_descriptor.ModuleName()
@property
def class_name(self):
"""Get the class name of current function descriptor.
Returns:
The class name of the function descriptor. It could be
empty if the function is not a class method.
"""
return <str>self.typed_descriptor.ClassName()
@property
def function_name(self):
"""Get the function name of current function descriptor.
Returns:
The function name of the function descriptor.
"""
return <str>self.typed_descriptor.FunctionName()
@property
def function_hash(self):
"""Get the hash string of the function source code.
Returns:
The hex of function hash if the source code is available.
Otherwise, it will be an empty string.
"""
return <str>self.typed_descriptor.FunctionHash()
@property
def function_id(self):
"""Get the function id calculated from this descriptor.
Returns:
The value of ray.ObjectRef that represents the function id.
"""
if not self._function_id:
self._function_id = self._get_function_id()
return self._function_id
@property
def repr(self):
"""Get the module_name.Optional[class_name].function_name
of the descriptor.
Returns:
The value of module_name.Optional[class_name].function_name
"""
if self.is_actor_method():
return ".".join(
[self.module_name, self.class_name, self.function_name])
else:
return ".".join(
[self.module_name, self.function_name])
def _get_function_id(self):
"""Calculate the function id of current function descriptor.
This function id is calculated from all the fields of function
descriptor.
Returns:
ray.ObjectRef to represent the function descriptor.
"""
function_id_hash = hashlib.shake_128()
# Include the function module and name in the hash.
function_id_hash.update(self.typed_descriptor.ModuleName())
function_id_hash.update(self.typed_descriptor.FunctionName())
function_id_hash.update(self.typed_descriptor.ClassName())
function_id_hash.update(self.typed_descriptor.FunctionHash())
# Compute the function ID.
function_id = function_id_hash.digest(ray_constants.ID_SIZE)
return ray.FunctionID(function_id)
@staticmethod
def _get_module_name(object):
"""Get the module name from object. If the module is __main__,
get the module name from file.
Returns:
Module name of object.
"""
module_name = object.__module__
if module_name == "__main__":
try:
file_path = inspect.getfile(object)
n = inspect.getmodulename(file_path)
if n:
module_name = n
except (TypeError, OSError):
pass
return module_name
def is_actor_method(self):
"""Whether this function descriptor is an actor method.
Returns:
True if it's an actor method, False if it's a normal function.
"""
return not self.typed_descriptor.ClassName().empty()
FunctionDescriptor_constructor_map[<int>CppFunctionDescriptorType] = \
CppFunctionDescriptor.from_cpp
@cython.auto_pickle(False)
cdef class CppFunctionDescriptor(FunctionDescriptor):
cdef:
CCppFunctionDescriptor *typed_descriptor
def __cinit__(self,
function_name, caller, class_name=""):
self.descriptor = CFunctionDescriptorBuilder.BuildCpp(
function_name, caller, class_name)
self.typed_descriptor = <CCppFunctionDescriptor*>(
self.descriptor.get())
def __reduce__(self):
return CppFunctionDescriptor, (self.typed_descriptor.FunctionName(),
self.typed_descriptor.Caller(),
self.typed_descriptor.ClassName())
@staticmethod
cdef from_cpp(const CFunctionDescriptor &c_function_descriptor):
cdef CCppFunctionDescriptor *typed_descriptor = \
<CCppFunctionDescriptor*>(c_function_descriptor.get())
return CppFunctionDescriptor(typed_descriptor.FunctionName(),
typed_descriptor.Caller(),
typed_descriptor.ClassName())
@property
def function_name(self):
"""Get the function name of current function descriptor.
Returns:
The function name of the function descriptor.
"""
return <str>self.typed_descriptor.FunctionName()
@property
def caller(self):
"""Get the caller of current function descriptor.
Returns:
The caller of the function descriptor.
"""
return <str>self.typed_descriptor.Caller()
@property
def class_name(self):
"""Get the class name of current function descriptor,
when it is empty, it is a non-member function.
Returns:
The class name of the function descriptor.
"""
return <str>self.typed_descriptor.ClassName()
@property
def repr(self):
return f"{self.class_name}::{self.function_name}"
File diff suppressed because it is too large Load Diff
+150
View File
@@ -0,0 +1,150 @@
import random
from libcpp.memory cimport shared_ptr
from libcpp.string cimport string as c_string
from libcpp.vector cimport vector as c_vector
from libcpp.utility cimport move
from ray.includes.common cimport(
CPythonGcsSubscriber,
CErrorTableData,
CLogBatch,
PythonGetLogBatchLines,
RAY_ERROR_INFO_CHANNEL,
RAY_LOG_CHANNEL,
)
cdef class _GcsSubscriber:
"""Cython wrapper class of C++ `ray::pubsub::PythonGcsSubscriber`."""
cdef:
shared_ptr[CPythonGcsSubscriber] inner
def _construct(self, address, channel, worker_id):
cdef:
c_worker_id = worker_id or b""
# subscriber_id needs to match the binary format of a random
# SubscriberID / UniqueID, which is 28 (kUniqueIDSize) random bytes.
subscriber_id = bytes(bytearray(random.getrandbits(8) for _ in range(28)))
gcs_address, gcs_port = parse_address(address)
self.inner.reset(new CPythonGcsSubscriber(
gcs_address, int(gcs_port), channel, subscriber_id, c_worker_id))
def subscribe(self):
"""Registers a subscription for the subscriber's channel type.
Before the registration, published messages in the channel will not be
saved for the subscriber.
"""
with nogil:
check_status(self.inner.get().Subscribe())
@property
def last_batch_size(self):
"""Batch size of the result from last poll.
Used to indicate whether the subscriber can keep up.
"""
return self.inner.get().last_batch_size()
def close(self):
"""Closes the subscriber and its active subscription."""
with nogil:
check_status(self.inner.get().Close())
cdef class GcsErrorSubscriber(_GcsSubscriber):
"""Subscriber to error info. Thread safe.
Usage example:
subscriber = GcsErrorSubscriber()
# Subscribe to the error channel.
subscriber.subscribe()
...
while running:
error_id, error_data = subscriber.poll()
......
# Unsubscribe from the error channels.
subscriber.close()
"""
def __init__(self, address, worker_id=None):
self._construct(address, RAY_ERROR_INFO_CHANNEL, worker_id)
def poll(self, timeout=None):
"""Polls for new error messages.
Returns:
A tuple of error message ID and dict describing the error,
or None, None if polling times out or subscriber closed.
"""
cdef:
CErrorTableData error_data
c_string key_id
int64_t timeout_ms = round(1000 * timeout) if timeout else -1
with nogil:
check_status(self.inner.get().PollError(&key_id, timeout_ms, &error_data))
if key_id == b"":
return None, None
return (bytes(key_id), {
"job_id": error_data.job_id(),
"type": error_data.type().decode(),
"error_message": error_data.error_message().decode(),
"timestamp": error_data.timestamp(),
})
cdef class GcsLogSubscriber(_GcsSubscriber):
"""Subscriber to logs. Thread safe.
Usage example:
subscriber = GcsLogSubscriber()
# Subscribe to the log channel.
subscriber.subscribe()
...
while running:
log = subscriber.poll()
......
# Unsubscribe from the log channel.
subscriber.close()
"""
def __init__(self, address, worker_id=None):
self._construct(address, RAY_LOG_CHANNEL, worker_id)
def poll(self, timeout=None):
"""Polls for new log messages.
Returns:
A dict containing a batch of log lines and their metadata.
"""
cdef:
CLogBatch log_batch
c_string key_id
int64_t timeout_ms = round(1000 * timeout) if timeout else -1
c_vector[c_string] c_log_lines
c_string c_log_line
with nogil:
check_status(self.inner.get().PollLogs(&key_id, timeout_ms, &log_batch))
result = {
"ip": log_batch.ip().decode(),
"pid": log_batch.pid().decode(),
"job": log_batch.job_id().decode(),
"is_err": log_batch.is_error(),
"actor_name": log_batch.actor_name().decode(),
"task_name": log_batch.task_name().decode(),
}
with nogil:
c_log_lines = PythonGetLogBatchLines(move(log_batch))
log_lines = []
for c_log_line in c_log_lines:
log_lines.append(c_log_line.decode())
result["lines"] = log_lines
return result
@@ -0,0 +1,140 @@
from libcpp.string cimport string as c_string
from libcpp cimport bool as c_bool
from libcpp.vector cimport vector as c_vector
from libcpp.unordered_map cimport unordered_map
from libcpp.memory cimport unique_ptr
from libc.stdint cimport (
int32_t as c_int32_t,
uint32_t as c_uint32_t,
int64_t as c_int64_t,
)
from ray.includes.unique_ids cimport (
CActorID,
CJobID,
CNodeID,
CObjectID,
CWorkerID,
CPlacementGroupID,
)
from ray.includes.common cimport (
CRayStatus,
CGcsClientOptions,
)
from ray.includes.optional cimport (
optional
)
cdef extern from "ray/gcs_rpc_client/global_state_accessor.h" nogil:
cdef cppclass CGlobalStateAccessor "ray::gcs::GlobalStateAccessor":
CGlobalStateAccessor(const CGcsClientOptions&)
c_bool Connect()
void Disconnect()
c_vector[c_string] GetAllJobInfo(
c_bool skip_submission_job_info_field, c_bool skip_is_running_tasks_field)
CJobID GetNextJobID()
c_vector[c_string] GetAllNodeInfo()
c_vector[c_string] GetAllAvailableResources()
c_vector[c_string] GetAllTotalResources()
unordered_map[CNodeID, c_int64_t] GetDrainingNodes()
unique_ptr[c_string] GetInternalKV(
const c_string &namespace, const c_string &key)
c_vector[c_string] GetAllTaskEvents()
unique_ptr[c_string] GetObjectInfo(const CObjectID &object_id)
unique_ptr[c_string] GetAllResourceUsage()
c_vector[c_string] GetAllActorInfo(
optional[CActorID], optional[CJobID], optional[c_string])
unique_ptr[c_string] GetActorInfo(const CActorID &actor_id)
unique_ptr[c_string] GetWorkerInfo(const CWorkerID &worker_id)
c_vector[c_string] GetAllWorkerInfo()
c_bool AddWorkerInfo(const c_string &serialized_string)
c_bool UpdateWorkerDebuggerPort(const CWorkerID &worker_id,
const c_uint32_t debuger_port)
c_bool UpdateWorkerNumPausedThreads(const CWorkerID &worker_id,
const c_int32_t num_paused_threads_delta)
c_uint32_t GetWorkerDebuggerPort(const CWorkerID &worker_id)
unique_ptr[c_string] GetPlacementGroupInfo(
const CPlacementGroupID &placement_group_id)
unique_ptr[c_string] GetPlacementGroupByName(
const c_string &placement_group_name,
const c_string &ray_namespace,
)
c_vector[c_string] GetAllPlacementGroupInfo()
c_string GetSystemConfig()
CRayStatus GetNode(
const c_string &node_id_hex_str,
c_string *node_info)
cdef extern from * namespace "ray::gcs" nogil:
"""
#include <thread>
#include "ray/gcs/store_client_kv.h"
#include "ray/gcs/store_client/redis_store_client.h"
#include "ray/util/clock.h"
#include "ray/util/raii.h"
namespace ray {
namespace gcs {
bool RedisGetKeySync(const std::string& host,
int32_t port,
const std::string& username,
const std::string& password,
bool use_ssl,
const std::string& config,
const std::string& key,
std::string* data) {
// Logging default value see class `RayLog`.
InitShutdownRAII ray_log_shutdown_raii(ray::RayLog::StartRayLog,
ray::RayLog::ShutDownRayLog,
"ray_init",
ray::RayLogLevel::WARNING,
/*log_filepath=*/"",
/*err_log_filepath=*/"",
/*log_rotation_max_size=*/1ULL << 29,
/*log_rotation_file_num=*/10);
std::string config_list;
RAY_CHECK(absl::Base64Unescape(config, &config_list));
RayConfig::instance().initialize(config_list);
instrumented_io_context io_service{/*enable_lag_probe=*/false, /*running_on_single_thread=*/true};
RedisClientOptions options{host, port, username, password, use_ssl};
Clock clock;
auto client = std::make_unique<StoreClientInternalKV>(
std::make_unique<RedisStoreClient>(io_service, options, clock));
bool ret_val = false;
client->Get("session", key, {[&](std::optional<std::string> result) {
if (result.has_value()) {
*data = result.value();
ret_val = true;
} else {
RAY_LOG(INFO) << "Failed to retrieve the key " << key
<< " from persistent storage.";
ret_val = false;
}
}, io_service});
io_service.run_for(std::chrono::milliseconds(1000));
return ret_val;
}
}
}
"""
c_bool RedisGetKeySync(const c_string& host,
c_int32_t port,
const c_string& username,
const c_string& password,
c_bool use_ssl,
const c_string& config,
const c_string& key,
c_string* data)
cdef extern from "ray/gcs/store_client/redis_store_client.h" namespace "ray::gcs" nogil:
c_bool RedisDelKeyPrefixSync(const c_string& host,
c_int32_t port,
const c_string& username,
const c_string& password,
c_bool use_ssl,
const c_string& key_prefix)
@@ -0,0 +1,289 @@
from ray.includes.common cimport (
CGcsClientOptions,
CGcsNodeState,
PythonGetResourcesTotal,
PythonGetNodeLabels
)
from ray.includes.unique_ids cimport (
CActorID,
CNodeID,
CObjectID,
CWorkerID,
CPlacementGroupID
)
from ray.includes.global_state_accessor cimport (
CGlobalStateAccessor,
RedisDelKeyPrefixSync,
)
from ray.includes.optional cimport (
optional,
nullopt,
make_optional
)
from libc.stdint cimport uint32_t as c_uint32_t, int32_t as c_int32_t
from libcpp.string cimport string as c_string
from libcpp.memory cimport make_unique as c_make_unique
cdef class GlobalStateAccessor:
"""Cython wrapper class of C++ `ray::gcs::GlobalStateAccessor`."""
cdef:
unique_ptr[CGlobalStateAccessor] inner
def __cinit__(self, GcsClientOptions gcs_options):
cdef CGcsClientOptions *opts
opts = gcs_options.native()
self.inner = c_make_unique[CGlobalStateAccessor](opts[0])
def connect(self):
cdef c_bool result
with nogil:
result = self.inner.get().Connect()
return result
def get_job_table(
self, *, skip_submission_job_info_field=False, skip_is_running_tasks_field=False
):
cdef c_vector[c_string] result
cdef c_bool c_skip_submission_job_info_field = skip_submission_job_info_field
cdef c_bool c_skip_is_running_tasks_field = skip_is_running_tasks_field
with nogil:
result = self.inner.get().GetAllJobInfo(
c_skip_submission_job_info_field, c_skip_is_running_tasks_field)
return result
def get_next_job_id(self):
cdef CJobID cjob_id
with nogil:
cjob_id = self.inner.get().GetNextJobID()
return cjob_id.ToInt()
def get_node_table(self):
cdef:
c_vector[c_string] items
c_string item
CGcsNodeInfo c_node_info
unordered_map[c_string, double] c_resources
with nogil:
items = self.inner.get().GetAllNodeInfo()
results = []
for item in items:
c_node_info.ParseFromString(item)
node_info = {
"NodeID": ray._common.utils.binary_to_hex(c_node_info.node_id()),
"Alive": c_node_info.state() == CGcsNodeState.ALIVE,
"NodeManagerAddress": c_node_info.node_manager_address().decode(),
"NodeManagerHostname": c_node_info.node_manager_hostname().decode(),
"NodeManagerPort": c_node_info.node_manager_port(),
"ObjectManagerPort": c_node_info.object_manager_port(),
"ObjectStoreSocketName":
c_node_info.object_store_socket_name().decode(),
"RayletSocketName": c_node_info.raylet_socket_name().decode(),
"MetricsExportPort": c_node_info.metrics_export_port(),
"MetricsAgentPort": c_node_info.metrics_agent_port(),
"DashboardAgentListenPort": c_node_info.dashboard_agent_listen_port(),
"NodeName": c_node_info.node_name().decode(),
"RuntimeEnvAgentPort": c_node_info.runtime_env_agent_port(),
"DeathReason": c_node_info.death_info().reason(),
"DeathReasonMessage":
c_node_info.death_info().reason_message().decode(),
}
node_info["alive"] = node_info["Alive"]
c_resources = PythonGetResourcesTotal(c_node_info)
node_info["Resources"] = (
{key.decode(): value for key, value in c_resources}
if node_info["Alive"]
else {}
)
c_labels = PythonGetNodeLabels(c_node_info)
node_info["Labels"] = \
{key.decode(): value.decode() for key, value in c_labels}
results.append(node_info)
return results
def get_draining_nodes(self):
cdef:
unordered_map[CNodeID, int64_t] draining_nodes
unordered_map[CNodeID, int64_t].iterator draining_nodes_it
with nogil:
draining_nodes = self.inner.get().GetDrainingNodes()
draining_nodes_it = draining_nodes.begin()
results = {}
while draining_nodes_it != draining_nodes.end():
draining_node_id = dereference(draining_nodes_it).first
results[ray._common.utils.binary_to_hex(
draining_node_id.Binary())] = dereference(draining_nodes_it).second
postincrement(draining_nodes_it)
return results
def get_internal_kv(self, namespace, key):
cdef:
c_string c_namespace = namespace
c_string c_key = key
unique_ptr[c_string] result
with nogil:
result = self.inner.get().GetInternalKV(c_namespace, c_key)
if result:
return c_string(result.get().data(), result.get().size())
return None
def get_all_available_resources(self):
cdef c_vector[c_string] result
with nogil:
result = self.inner.get().GetAllAvailableResources()
return result
def get_all_total_resources(self):
cdef c_vector[c_string] result
with nogil:
result = self.inner.get().GetAllTotalResources()
return result
def get_task_events(self):
cdef c_vector[c_string] result
with nogil:
result = self.inner.get().GetAllTaskEvents()
return result
def get_all_resource_usage(self):
"""Get newest resource usage of all nodes from GCS service."""
cdef unique_ptr[c_string] result
with nogil:
result = self.inner.get().GetAllResourceUsage()
if result:
return c_string(result.get().data(), result.get().size())
return None
def get_actor_table(self, job_id, actor_state_name):
cdef c_vector[c_string] result
cdef optional[CActorID] cactor_id = nullopt
cdef optional[CJobID] cjob_id
cdef optional[c_string] cactor_state_name
cdef c_string c_name
if job_id is not None:
cjob_id = make_optional[CJobID](CJobID.FromBinary(job_id.binary()))
if actor_state_name is not None:
c_name = actor_state_name
cactor_state_name = make_optional[c_string](c_name)
with nogil:
result = self.inner.get().GetAllActorInfo(
cactor_id, cjob_id, cactor_state_name)
return result
def get_actor_info(self, actor_id):
cdef unique_ptr[c_string] actor_info
cdef CActorID cactor_id = CActorID.FromBinary(actor_id.binary())
with nogil:
actor_info = self.inner.get().GetActorInfo(cactor_id)
if actor_info:
return c_string(actor_info.get().data(), actor_info.get().size())
return None
def get_worker_table(self):
cdef c_vector[c_string] result
with nogil:
result = self.inner.get().GetAllWorkerInfo()
return result
def get_worker_info(self, worker_id):
cdef unique_ptr[c_string] worker_info
cdef CWorkerID cworker_id = <CWorkerID>CUniqueID.FromBinary(worker_id.binary())
with nogil:
worker_info = self.inner.get().GetWorkerInfo(cworker_id)
if worker_info:
return c_string(worker_info.get().data(), worker_info.get().size())
return None
def add_worker_info(self, serialized_string):
cdef c_bool result
cdef c_string cserialized_string = serialized_string
with nogil:
result = self.inner.get().AddWorkerInfo(cserialized_string)
return result
def get_worker_debugger_port(self, worker_id):
cdef c_uint32_t result
cdef CWorkerID cworker_id = <CWorkerID>CUniqueID.FromBinary(worker_id.binary())
with nogil:
result = self.inner.get().GetWorkerDebuggerPort(cworker_id)
return result
def update_worker_debugger_port(self, worker_id, debugger_port):
cdef c_bool result
cdef CWorkerID cworker_id = <CWorkerID>CUniqueID.FromBinary(worker_id.binary())
cdef c_uint32_t cdebugger_port = debugger_port
with nogil:
result = self.inner.get().UpdateWorkerDebuggerPort(
cworker_id,
cdebugger_port)
return result
def update_worker_num_paused_threads(self, worker_id, num_paused_threads_delta):
cdef c_bool result
cdef CWorkerID cworker_id = <CWorkerID>CUniqueID.FromBinary(worker_id.binary())
cdef c_int32_t cnum_paused_threads_delta = num_paused_threads_delta
with nogil:
result = self.inner.get().UpdateWorkerNumPausedThreads(
cworker_id, cnum_paused_threads_delta)
return result
def get_placement_group_table(self):
cdef c_vector[c_string] result
with nogil:
result = self.inner.get().GetAllPlacementGroupInfo()
return result
def get_placement_group_info(self, placement_group_id):
cdef unique_ptr[c_string] result
cdef CPlacementGroupID cplacement_group_id = (
CPlacementGroupID.FromBinary(placement_group_id.binary()))
with nogil:
result = self.inner.get().GetPlacementGroupInfo(
cplacement_group_id)
if result:
return c_string(result.get().data(), result.get().size())
return None
def get_placement_group_by_name(self, placement_group_name, ray_namespace):
cdef unique_ptr[c_string] result
cdef c_string cplacement_group_name = placement_group_name
cdef c_string cray_namespace = ray_namespace
with nogil:
result = self.inner.get().GetPlacementGroupByName(
cplacement_group_name, cray_namespace)
if result:
return c_string(result.get().data(), result.get().size())
return None
def get_system_config(self):
return self.inner.get().GetSystemConfig()
def get_node(self, node_id):
cdef CRayStatus status
cdef c_string cnode_id = node_id
cdef c_string cnode_info_str
cdef CGcsNodeInfo c_node_info
with nogil:
status = self.inner.get().GetNode(cnode_id, &cnode_info_str)
if not status.ok():
raise RuntimeError(status.message())
c_node_info.ParseFromString(cnode_info_str)
c_labels = PythonGetNodeLabels(c_node_info)
return {
"object_store_socket_name": c_node_info.object_store_socket_name().decode(),
"raylet_socket_name": c_node_info.raylet_socket_name().decode(),
"node_manager_port": c_node_info.node_manager_port(),
"node_id": c_node_info.node_id().hex(),
"runtime_env_agent_port": c_node_info.runtime_env_agent_port(),
"metrics_agent_port": c_node_info.metrics_agent_port(),
"metrics_export_port": c_node_info.metrics_export_port(),
"dashboard_agent_listen_port": c_node_info.dashboard_agent_listen_port(),
"labels": {key.decode(): value.decode() for key, value in c_labels},
}
+506
View File
@@ -0,0 +1,506 @@
# cython: profile = False
# distutils: language = c++
# cython: embedsignature = True
from libc.stdint cimport int64_t, uint64_t
from libcpp cimport bool as c_bool
from libcpp.functional cimport function
from libcpp.memory cimport shared_ptr, unique_ptr
from libcpp.pair cimport pair as c_pair
from libcpp.string cimport string as c_string
from libcpp.unordered_map cimport unordered_map
from libcpp.utility cimport pair
from libcpp.vector cimport vector as c_vector
from ray.includes.unique_ids cimport (
CActorID,
CClusterID,
CNodeID,
CJobID,
CTaskID,
CObjectID,
CPlacementGroupID,
CWorkerID,
ObjectIDIndexType,
)
from ray.includes.common cimport (
CAddress,
CObjectReference,
CActorCreationOptions,
CBuffer,
CPlacementGroupCreationOptions,
CObjectLocation,
CObjectReference,
CRayFunction,
CRayObject,
CRayStatus,
CTaskArg,
CTaskOptions,
CTaskType,
CWorkerType,
CLanguage,
CGcsClientOptions,
LocalMemoryBuffer,
CJobConfig,
CConcurrencyGroup,
CSchedulingStrategy,
CWorkerExitType,
CLineageReconstructionTask,
)
from ray.includes.function_descriptor cimport (
CFunctionDescriptor,
)
from ray.includes.optional cimport (
optional,
)
ctypedef unordered_map[c_string, c_vector[pair[int64_t, double]]] \
ResourceMappingType
ctypedef void (*ray_callback_function) \
(shared_ptr[CRayObject] result_object,
CObjectID object_id, void* user_data)
ctypedef void (*plasma_callback_function) \
(CObjectID object_id, int64_t data_size, int64_t metadata_size)
# NOTE: This ctypedef is needed, because Cython doesn't compile
# "pair[shared_ptr[const CActorHandle], CRayStatus]".
# This is a bug of cython: https://github.com/cython/cython/issues/3967.
ctypedef shared_ptr[const CActorHandle] ActorHandleSharedPtr
cdef extern from "ray/core_worker/profile_event.h" nogil:
cdef cppclass CProfileEvent "ray::core::worker::ProfileEvent":
void SetExtraData(const c_string &extra_data)
cdef extern from "ray/core_worker/task_execution/fiber.h" nogil:
cdef cppclass CFiberEvent "ray::core::FiberEvent":
CFiberEvent()
void Wait()
void Notify()
cdef extern from "ray/core_worker/experimental_mutable_object_manager.h" nogil:
cdef cppclass CReaderRefInfo "ray::experimental::ReaderRefInfo":
CReaderRefInfo()
CObjectID reader_ref_id
CActorID owner_reader_actor_id
int64_t num_reader_actors
cdef extern from "ray/core_worker/context.h" nogil:
cdef cppclass CWorkerContext "ray::core::WorkerContext":
c_bool CurrentActorIsAsync()
void SetCurrentActorShouldExit()
c_bool GetCurrentActorShouldExit()
const c_string &GetCurrentSerializedRuntimeEnv()
int CurrentActorMaxConcurrency()
CActorID GetRootDetachedActorID()
cdef extern from "ray/core_worker/generator_waiter.h" nogil:
cdef cppclass CTaskGeneratorBackpressureWaiter "ray::core::TaskGeneratorBackpressureWaiter": # noqa
CTaskGeneratorBackpressureWaiter(
int64_t generator_backpressure_num_objects,
(CRayStatus() nogil) check_signals)
CRayStatus WaitAllObjectsReported()
CRayStatus WaitUntilObjectConsumed()
c_bool IsBackpressured()
c_bool NeedsObjectConsumedUpdates()
cdef cppclass CActorWideGeneratorBackpressureWaiter "ray::core::ActorWideGeneratorBackpressureWaiter": # noqa
pass
cdef cppclass CActorTaskBackpressureMetadata "ray::core::ActorTaskBackpressureMetadata": # noqa
CActorTaskBackpressureMetadata(
shared_ptr[CActorWideGeneratorBackpressureWaiter] actor_waiter)
CRayStatus ReserveSlot(int64_t num_objects)
c_bool TryReserveSlot(int64_t num_objects)
void ReleaseSlot(int64_t num_objects)
void Teardown()
cdef extern from "ray/core_worker/core_worker.h" nogil:
cdef cppclass CActorHandle "ray::core::ActorHandle":
CActorID GetActorID() const
CJobID CreationJobID() const
CLanguage ActorLanguage() const
CFunctionDescriptor ActorCreationTaskFunctionDescriptor() const
c_string ExtensionData() const
int MaxPendingCalls() const
int MaxTaskRetries() const
c_bool EnableTaskEvents() const
c_bool AllowOutOfOrderExecution() const
c_bool EnableTensorTransport() const
int64_t ActorGeneratorBackpressureNumObjects() const
cdef cppclass CCoreWorker "ray::core::CoreWorker":
CWorkerType GetWorkerType()
CLanguage GetLanguage()
c_vector[CObjectReference] SubmitTask(
const CRayFunction &function,
const c_vector[unique_ptr[CTaskArg]] &args,
const CTaskOptions &options,
int max_retries,
c_bool retry_exceptions,
const CSchedulingStrategy &scheduling_strategy,
c_string debugger_breakpoint,
c_string serialized_retry_exception_allowlist,
c_string call_site,
const CTaskID current_task_id)
CRayStatus CreateActor(
const CRayFunction &function,
const c_vector[unique_ptr[CTaskArg]] &args,
const CActorCreationOptions &options,
const c_string &extension_data,
c_string call_site,
CActorID *actor_id)
CRayStatus CreatePlacementGroup(
const CPlacementGroupCreationOptions &options,
CPlacementGroupID *placement_group_id)
CRayStatus RemovePlacementGroup(
const CPlacementGroupID &placement_group_id)
CRayStatus WaitPlacementGroupReady(
const CPlacementGroupID &placement_group_id, int64_t timeout_seconds)
CObjectID AsyncWaitPlacementGroupReady(
const CPlacementGroupID &placement_group_id,
const c_string &serialized_object_data,
const c_string &serialized_object_metadata)
CRayStatus SubmitActorTask(
const CActorID &actor_id, const CRayFunction &function,
const c_vector[unique_ptr[CTaskArg]] &args,
const CTaskOptions &options,
int max_retries,
c_bool retry_exceptions,
c_string serialized_retry_exception_allowlist,
c_string call_site,
c_vector[CObjectReference] &task_returns,
const CTaskID current_task_id)
CRayStatus KillActor(
const CActorID &actor_id, c_bool force_kill,
c_bool no_restart)
CRayStatus CancelTask(const CObjectID &object_id, c_bool force_kill,
c_bool recursive)
c_bool IsTaskCanceled(const CTaskID &task_id) const
c_bool ShouldInterruptTaskForCancellation() const
unique_ptr[CProfileEvent] CreateProfileEvent(
const c_string &event_type)
CRayStatus AllocateReturnObject(
const CObjectID &object_id,
const size_t &data_size,
const shared_ptr[CBuffer] &metadata,
const c_vector[CObjectID] &contained_object_id,
const CAddress &caller_address,
int64_t *task_output_inlined_bytes,
shared_ptr[CRayObject] *return_object)
CRayStatus SealReturnObject(
const CObjectID &return_id,
const shared_ptr[CRayObject] &return_object,
const CObjectID &generator_id,
const CAddress &caller_address
)
c_bool PinExistingReturnObject(
const CObjectID &return_id,
shared_ptr[CRayObject] *return_object,
const CObjectID &generator_id,
const CAddress &caller_address)
void AsyncDelObjectRefStream(const CObjectID &generator_id)
CRayStatus TryReadObjectRefStream(
const CObjectID &generator_id,
CObjectReference *object_ref_out)
CRayStatus TryReadObjectRefStreamN(
const CObjectID &generator_id,
int64_t num_items)
c_bool StreamingGeneratorIsFinished(const CObjectID &generator_id) const
pair[CObjectReference, c_bool] PeekObjectRefStream(
const CObjectID &generator_id)
c_vector[pair[CObjectReference, c_bool]] PeekObjectRefStreamN(
const CObjectID &generator_id, int64_t num_items)
CObjectID PeekObjectIdStream(
const CObjectID &generator_id)
CObjectID AllocateDynamicReturnId(
const CAddress &owner_address,
const CTaskID &task_id,
optional[ObjectIDIndexType] put_index)
CJobID GetCurrentJobId()
CTaskID GetCurrentTaskId()
const c_string GetCurrentTaskName()
const c_string GetCurrentTaskFunctionName()
void UpdateTaskIsDebuggerPaused(
const CTaskID &task_id,
const c_bool is_debugger_paused)
int64_t GetCurrentTaskAttemptNumber()
CNodeID GetCurrentNodeId()
int64_t GetTaskDepth()
c_bool GetCurrentTaskRetryExceptions()
CPlacementGroupID GetCurrentPlacementGroupId() const
CWorkerID GetWorkerID()
c_bool ShouldCaptureChildTasksInPlacementGroup()
CActorID GetActorId() const
const c_string GetActorName()
const ResourceMappingType &GetResourceIDs() const
void RemoveActorHandleReference(const CActorID &actor_id)
optional[int] GetLocalActorState(const CActorID &actor_id) const
CActorID DeserializeAndRegisterActorHandle(const c_string &bytes, const
CObjectID &outer_object_id,
c_bool add_local_ref)
CRayStatus SerializeActorHandle(const CActorID &actor_id, c_string
*bytes,
CObjectID *c_actor_handle_id)
ActorHandleSharedPtr GetActorHandle(const CActorID &actor_id) const
pair[ActorHandleSharedPtr, CRayStatus] GetNamedActorHandle(
const c_string &name, const c_string &ray_namespace)
pair[c_vector[c_pair[c_string, c_string]], CRayStatus] ListNamedActors(
c_bool all_namespaces)
void AddLocalReference(const CObjectID &object_id)
void RemoveLocalReference(const CObjectID &object_id)
c_bool AddObjectOutOfScopeOrFreedCallback(
const CObjectID &object_id,
void (*callback)(const CObjectID &, void *) nogil,
void *callback_context)
CRayStatus CheckObjectOwnedByUs(const CObjectID &object_id) const
void PutObjectIntoPlasma(const CRayObject &object,
const CObjectID &object_id)
const CAddress &GetRpcAddress() const
CRayStatus GetOwnerAddress(const CObjectID &object_id,
CAddress *owner_address) const
c_vector[CObjectReference] GetObjectRefs(
const c_vector[CObjectID] &object_ids) const
CRayStatus GetOwnershipInfo(const CObjectID &object_id,
CAddress *owner_address,
c_string *object_status)
void RegisterOwnershipInfoAndResolveFuture(
const CObjectID &object_id,
const CObjectID &outer_object_id,
const CAddress &owner_address,
const c_string &object_status)
CRayStatus Put(const CRayObject &object,
const c_vector[CObjectID] &contained_object_ids,
CObjectID *object_id)
CRayStatus Put(const CRayObject &object,
const c_vector[CObjectID] &contained_object_ids,
const CObjectID &object_id)
CRayStatus CreateOwnedAndIncrementLocalRef(
c_bool is_mutable,
const shared_ptr[CBuffer] &metadata,
const size_t data_size,
const c_vector[CObjectID] &contained_object_ids,
CObjectID *object_id, shared_ptr[CBuffer] *data,
c_bool inline_small_object,
const optional[c_string] &tensor_transport)
CRayStatus CreateExisting(const shared_ptr[CBuffer] &metadata,
const size_t data_size,
const CObjectID &object_id,
const CAddress &owner_address,
shared_ptr[CBuffer] *data,
c_bool created_by_worker)
CRayStatus ExperimentalChannelWriteAcquire(
const CObjectID &object_id,
const shared_ptr[CBuffer] &metadata,
uint64_t data_size,
int64_t num_readers,
int64_t timeout_ms,
shared_ptr[CBuffer] *data)
CRayStatus ExperimentalChannelWriteRelease(
const CObjectID &object_id)
CRayStatus ExperimentalChannelSetError(
const CObjectID &object_id)
void ExperimentalRegisterMutableObjectWriter(
const CObjectID &writer_object_id,
const c_vector[CNodeID] &remote_reader_node_ids)
CRayStatus ExperimentalRegisterMutableObjectReader(const CObjectID &object_id)
CRayStatus ExperimentalRegisterMutableObjectReaderRemote(
const CObjectID &object_id,
const c_vector[CReaderRefInfo] &remote_reader_ref_info)
CRayStatus SealOwned(const CObjectID &object_id, c_bool pin_object)
CRayStatus SealExisting(const CObjectID &object_id, c_bool pin_object,
const CObjectID &generator_id,
const unique_ptr[CAddress] &owner_address)
CRayStatus Get(const c_vector[CObjectID] &ids, int64_t timeout_ms,
c_vector[shared_ptr[CRayObject]] results)
CRayStatus GetIfLocal(
const c_vector[CObjectID] &ids,
c_vector[shared_ptr[CRayObject]] *results)
CRayStatus Contains(const CObjectID &object_id, c_bool *has_object,
c_bool *is_in_plasma)
CRayStatus Wait(const c_vector[CObjectID] &object_ids, int num_objects,
int64_t timeout_ms, c_vector[c_bool] *results,
c_bool fetch_local)
CRayStatus Delete(const c_vector[CObjectID] &object_ids,
c_bool local_only)
CRayStatus GetLocalObjectLocations(
const c_vector[CObjectID] &object_ids,
c_vector[optional[CObjectLocation]] *results)
CRayStatus GetLocationFromOwner(
const c_vector[CObjectID] &object_ids,
int64_t timeout_ms,
c_vector[shared_ptr[CObjectLocation]] *results)
CRayStatus TriggerGlobalGC()
CRayStatus ReportGeneratorItemReturns(
const c_vector[c_pair[CObjectID, shared_ptr[CRayObject]]] &dynamic_return_objects,
const CObjectID &generator_id,
const CAddress &caller_address,
int64_t item_index,
uint64_t attempt_number,
shared_ptr[CTaskGeneratorBackpressureWaiter] waiter,
shared_ptr[CActorTaskBackpressureMetadata] actor_metadata)
void MarkGeneratorBackpressureTaskFinished(
const CObjectID &generator_id)
c_bool TeardownGeneratorBackpressureTask(
const CObjectID &generator_id)
void RegisterGeneratorBackpressureState(
const CObjectID &generator_id,
shared_ptr[CTaskGeneratorBackpressureWaiter] waiter,
shared_ptr[CActorTaskBackpressureMetadata] actor_metadata,
const CAddress &owner_address)
void SetAsyncGeneratorBackpressureUnblockNotify(
const CObjectID &generator_id,
(void(void *) noexcept nogil) fn,
void *ctx)
void ClearAsyncGeneratorBackpressureUnblockNotify(
const CObjectID &generator_id)
void NotifyAsyncGeneratorBackpressureUnblock(
const CObjectID &generator_id,
c_bool notify_all)
shared_ptr[CActorWideGeneratorBackpressureWaiter] GetActorGeneratorWaiter() const
# Param output contains the usage string if successful.
# Returns an error status if unable to communicate with the plasma store.
CRayStatus GetPlasmaUsage(c_string &output)
int GetMemoryStoreSize()
CWorkerContext &GetWorkerContext()
void YieldCurrentFiber(CFiberEvent &coroutine_done)
unordered_map[CObjectID, pair[size_t, size_t]] GetAllReferenceCounts()
c_vector[CTaskID] GetPendingChildrenTasks(const CTaskID &task_id) const
void GetAsync(const CObjectID &object_id,
ray_callback_function success_callback,
void* python_user_callback)
CRayStatus PushError(const CJobID &job_id, const c_string &type,
const c_string &error_message, double timestamp)
CRayStatus SetResource(const c_string &resource_name,
const double capacity,
const CNodeID &client_Id)
CJobConfig GetJobConfig()
int64_t GetLocalMemoryStoreBytesUsed() const
void RecordTaskLogStart(
const CTaskID &task_id,
int attempt_number,
const c_string& stdout_path,
const c_string& stderr_path,
int64_t stdout_start_offset,
int64_t stderr_start_offset) const
void RecordTaskLogEnd(
const CTaskID &task_id,
int attempt_number,
int64_t stdout_end_offset,
int64_t stderr_end_offset) const
void Exit(const CWorkerExitType exit_type,
const c_string &detail,
const shared_ptr[LocalMemoryBuffer] &creation_task_exception_pb_bytes)
unordered_map[CLineageReconstructionTask, uint64_t] \
GetLocalOngoingLineageReconstructionTasks() const
cdef cppclass CCoreWorkerOptions "ray::core::CoreWorkerOptions":
CWorkerType worker_type
CLanguage language
c_string store_socket
c_string raylet_socket
CJobID job_id
CGcsClientOptions gcs_options
c_bool enable_logging
c_string log_dir
c_bool install_failure_signal_handler
c_bool interactive
c_string node_ip_address
int node_manager_port
c_string driver_name
(CRayStatus(
const CAddress &caller_address,
CTaskType task_type,
const c_string name,
const CRayFunction &ray_function,
const unordered_map[c_string, double] &resources,
const c_vector[shared_ptr[CRayObject]] &args,
const c_vector[CObjectReference] &arg_refs,
const c_string debugger_breakpoint,
const c_string serialized_retry_exception_allowlist,
c_vector[c_pair[CObjectID, shared_ptr[CRayObject]]] *returns,
c_vector[c_pair[CObjectID, shared_ptr[CRayObject]]] *dynamic_returns,
c_vector[c_pair[CObjectID, c_bool]] *streaming_generator_returns,
shared_ptr[LocalMemoryBuffer]
&creation_task_exception_pb_bytes,
c_bool *is_retryable_error,
c_string *actor_repr_name,
c_string *application_error,
const c_vector[CConcurrencyGroup] &defined_concurrency_groups,
const c_string name_of_concurrency_group_to_execute,
c_bool is_reattempt,
c_bool is_streaming_generator,
c_bool should_retry_exceptions,
int64_t generator_backpressure_num_objects,
int64_t num_objects_per_yield,
optional[c_string] tensor_transport
) nogil) task_execution_callback
(void(const CObjectID &) nogil) free_actor_object_callback
(void(const CObjectID &, const c_string &) nogil) set_direct_transport_metadata
(function[void()]() nogil) initialize_thread_callback
(CRayStatus() nogil) check_signals
(void() nogil) gc_collect
(c_vector[c_string](
const c_vector[CObjectReference] &) nogil) spill_objects
(int64_t(
const c_vector[CObjectReference] &,
const c_vector[c_string] &) nogil) restore_spilled_objects
(void(
const c_vector[c_string]&,
CWorkerType) nogil) delete_spilled_objects
(void(
const c_string&,
const c_vector[c_string]&) nogil) run_on_util_worker_handler
(void(const CRayObject&) nogil) unhandled_exception_handler
(c_bool(const CTaskID &c_task_id) nogil) cancel_async_actor_task
(void() noexcept nogil) actor_shutdown_callback
(void(c_string *stack_out) nogil) get_lang_stack
int num_workers
(c_bool(const CTaskID &) nogil) kill_main
CCoreWorkerOptions()
c_string serialized_job_config
int metrics_agent_port
int runtime_env_hash
CWorkerID worker_id
CClusterID cluster_id
c_string session_name
c_string entrypoint
int64_t worker_launch_time_ms
int64_t worker_launched_time_ms
c_string debug_source
cdef cppclass CCoreWorkerProcess "ray::core::CoreWorkerProcess":
@staticmethod
void Initialize(const CCoreWorkerOptions &options)
# Only call this in CoreWorker.__cinit__,
# use CoreWorker.core_worker to access C++ CoreWorker.
@staticmethod
CCoreWorker &GetCoreWorker()
@staticmethod
void Shutdown()
@staticmethod
void RunTaskExecutionLoop()
+48
View File
@@ -0,0 +1,48 @@
from libcpp.string cimport string as c_string
from ray.includes.libcoreworker cimport CProfileEvent
import json
import traceback
cdef class ProfileEvent:
"""Cython wrapper class of C++ `ray::core::worker::ProfileEvent`."""
cdef:
unique_ptr[CProfileEvent] inner
object extra_data
@staticmethod
cdef make(unique_ptr[CProfileEvent] event, object extra_data):
cdef ProfileEvent self = ProfileEvent.__new__(ProfileEvent)
self.inner = move(event)
self.extra_data = extra_data
return self
def set_extra_data(self, c_string extra_data):
self.inner.get().SetExtraData(extra_data)
def __enter__(self):
pass
def __exit__(self, type, value, tb):
extra_data = None
if type is not None:
extra_data = {
"type": str(type),
"value": str(value),
"traceback": str(traceback.format_exc()),
}
elif self.extra_data is not None:
extra_data = self.extra_data
if not extra_data:
self.inner.get().SetExtraData(b"{}")
elif isinstance(extra_data, dict):
self.inner.get().SetExtraData(
json.dumps(extra_data).encode("ascii"))
else:
self.inner.get().SetExtraData(extra_data)
# Deleting the CProfileEvent will add it to a queue to be pushed to
# the driver.
self.inner.reset()
+39
View File
@@ -0,0 +1,39 @@
from libcpp.string cimport string as c_string
from libcpp.vector cimport vector as c_vector
from libcpp.pair cimport pair as c_pair
cdef extern from "ray/stats/metric.h" nogil:
cdef cppclass CMetric "ray::stats::Metric":
CMetric(const c_string &name,
const c_string &description,
const c_string &unit,
const c_vector[c_string] &tag_keys)
c_string GetName() const
void Record(double value)
void RecordForCython(double value,
c_vector[c_pair[c_string, c_string]] tags)
cdef cppclass CGauge "ray::stats::Gauge":
CGauge(const c_string &name,
const c_string &description,
const c_string &unit,
const c_vector[c_string] &tag_keys)
cdef cppclass CCount "ray::stats::Count":
CCount(const c_string &name,
const c_string &description,
const c_string &unit,
const c_vector[c_string] &tag_keys)
cdef cppclass CSum "ray::stats::Sum":
CSum(const c_string &name,
const c_string &description,
const c_string &unit,
const c_vector[c_string] &tag_keys)
cdef cppclass CHistogram "ray::stats::Histogram":
CHistogram(const c_string &name,
const c_string &description,
const c_string &unit,
const c_vector[double] &boundaries,
const c_vector[c_string] &tag_keys)
+205
View File
@@ -0,0 +1,205 @@
from ray.includes.metric cimport (
CCount,
CGauge,
CHistogram,
CSum,
CMetric,
)
from libcpp.utility cimport move
from libcpp.memory cimport unique_ptr
from libcpp.string cimport string as c_string
from libcpp.vector cimport vector as c_vector
from libcpp.pair cimport pair as c_pair
cdef class Metric:
"""Cython wrapper class of C++ `ray::stats::Metric`.
It's an abstract class of all metric types.
"""
cdef:
unique_ptr[CMetric] metric
c_vector[c_string] c_tag_keys
def __init__(self, tag_keys):
for tag_key in tag_keys:
self.c_tag_keys.push_back(tag_key.encode("ascii"))
def record(self, value, tags=None):
"""Record a measurement of metric.
Flush a metric raw point to stats module with a key-value dict tags.
Args:
value (double): metric name.
tags (dict): default none.
"""
cdef c_vector[c_pair[c_string, c_string]] c_tags
cdef double c_value
# Default tags will be exported if it's empty map.
if tags:
c_tags.reserve(len(tags))
for tag_k, tag_v in tags.items():
if tag_v is not None:
c_tags.push_back(c_pair[c_string, c_string](
tag_k.encode("ascii"),
tag_v.encode("ascii")))
c_value = value
with nogil:
self.metric.get().RecordForCython(c_value, move(c_tags))
def get_name(self):
return self.metric.get().GetName()
cdef class Gauge(Metric):
"""Cython wrapper class of C++ `ray::stats::Gauge`.
Gauge: Keeps the last recorded value, drops everything before.
Example:
>>> gauge = Gauge(
"ray.worker.metric",
"description",
["tagk1", "tagk2"]).
value = 5
key1= "key1"
key2 = "key2"
gauge.record(value, {"tagk1": key1, "tagk2": key2})
"""
def __init__(self, name, description, tag_keys):
"""Create a gauge metric
Args:
name (string): metric name.
description (string): description of this metric.
tag_keys (list): a list of tay keys in string format.
"""
super().__init__(tag_keys)
self.metric.reset(
new CGauge(
name.encode("ascii"),
description.encode("ascii"),
b"", # Unit, unused.
self.c_tag_keys
)
)
cdef class Count(Metric):
"""Cython wrapper class of C++ `ray::stats::Count`.
Example:
>>> count = Count(
"ray.worker.metric",
"description",
["tagk1", "tagk2"]).
value = 5
key1= "key1"
key2 = "key2"
count.record(value, {"tagk1": key1, "tagk2": key2})
Count: The count of the number of metric points.
"""
def __init__(self, name, description, tag_keys):
"""Create a count metric
Args:
name (string): metric name.
description (string): description of this metric.
tag_keys (list): a list of tay keys in string format.
"""
super().__init__(tag_keys)
self.metric.reset(
new CCount(
name.encode("ascii"),
description.encode("ascii"),
b"", # Unit, unused.
self.c_tag_keys
)
)
cdef class Sum(Metric):
"""Cython wrapper class of C++ `ray::stats::Sum`.
Example:
>>> metric_sum = Sum(
"ray.worker.metric",
"description",
["tagk1", "tagk2"]).
value = 5
key1= "key1"
key2 = "key2"
metric_sum.record(value, {"tagk1": key1, "tagk2": key2})
Sum: A sum up of the metric points.
"""
def __init__(self, name, description, tag_keys):
"""Create a sum metric
Args:
name (string): metric name.
description (string): description of this metric.
tag_keys (list): a list of tay keys in string format.
"""
super().__init__(tag_keys)
self.metric.reset(
new CSum(
name.encode("ascii"),
description.encode("ascii"),
b"", # Unit, unused.
self.c_tag_keys
)
)
cdef class Histogram(Metric):
"""Cython wrapper class of C++ `ray::stats::Histogram`.
Example:
>>> histogram = Histogram(
"ray.worker.histogram1",
"description",
[1.0, 2.0], # boundaries.
["tagk1"])
value = 5
key1= "key1"
histogram.record(value, {"tagk1": key1})
Histogram: Histogram distribution of metric points.
"""
def __init__(self, name, description, boundaries, tag_keys):
"""Create a sum metric
Args:
name (string): metric name.
description (string): description of this metric.
boundaries (list): a double type list boundaries of histogram.
tag_keys (list): a list of tay key in string format.
"""
super().__init__(tag_keys)
cdef c_vector[double] c_boundaries
for value in boundaries:
c_boundaries.push_back(value)
self.metric.reset(
new CHistogram(
name.encode("ascii"),
description.encode("ascii"),
b"", # Unit, unused.
c_boundaries,
self.c_tag_keys
)
)
+15
View File
@@ -0,0 +1,15 @@
from libc.stddef cimport size_t
from libcpp.string cimport string
from libcpp cimport bool
from ray.includes.array cimport array_string_2
from ray.includes.optional cimport optional
cdef extern from "ray/util/network_util.h" namespace "ray":
string BuildAddress(const string &host, int port)
string BuildAddress(const string &host, const string &port)
optional[array_string_2] ParseAddress(const string &address)
string GetNodeIpAddressFromPerspective(const optional[string] &address)
bool IsIPv6(const string &host)
string GetLocalhostIP()
string GetAllInterfacesIP()
bool IsLocalhost(const string &address)
+123
View File
@@ -0,0 +1,123 @@
from ray.includes.network_util cimport (
BuildAddress,
ParseAddress,
GetNodeIpAddressFromPerspective,
IsIPv6,
GetLocalhostIP,
GetAllInterfacesIP,
IsLocalhost,
array_string_2,
optional,
)
from libcpp.string cimport string
from typing import Optional, Tuple, Union
def parse_address(address: str) -> Optional[Tuple[str, str]]:
"""Parse a network address string into host and port.
Args:
address: The address string to parse (e.g., "localhost:8000", "[::1]:8000").
Returns:
Tuple with (host, port) if port found, None if no colon separator.
"""
cdef optional[array_string_2] res = ParseAddress(address.encode('utf-8'))
if not res.has_value():
return None
cdef array_string_2 ip_port = res.value()
return (ip_port[0].decode('utf-8'), ip_port[1].decode('utf-8'))
def build_address(host: str, port: Union[int, str]) -> str:
"""Build a network address string from host and port.
Args:
host: The hostname or IP address.
port: The port number (int or string).
Returns:
Formatted address string (e.g., "localhost:8000" or "[::1]:8000").
"""
cdef string host_c = host.encode('utf-8')
cdef string result
cdef string port_c
if isinstance(port, int):
result = BuildAddress(host_c, <int>port)
else:
port_c = str(port).encode('utf-8')
result = BuildAddress(host_c, port_c)
return result.decode('utf-8')
def node_ip_address_from_perspective(address=None) -> str:
"""IP address by which the local node can be reached from the address.
If no address is given, defaults to public DNS servers for detection.
Args:
address: The IP address and port of any known live service on the
network you care about.
Returns:
The IP address by which the local node can be reached from the address.
"""
cdef string node_ip
cdef optional[string] address_c
cdef string address_str
if address is not None:
address_str = address.encode('utf-8')
address_c = optional[string](address_str)
else:
address_c = optional[string]()
node_ip = GetNodeIpAddressFromPerspective(address_c)
return node_ip.decode('utf-8')
def is_ipv6(host: str) -> bool:
"""Check if a host is resolved to IPv6.
Args:
host: The IP or domain name to check (must be without port).
Returns:
True if the host is resolved to IPv6, False if IPv4.
"""
cdef string host_c = host.encode('utf-8')
return IsIPv6(host_c)
def get_localhost_ip() -> str:
"""Get localhost loopback IP with IPv4/IPv6 support.
Returns:
"127.0.0.1" for IPv4 or "::1" for IPv6.
"""
cdef string result = GetLocalhostIP()
return result.decode('utf-8')
def get_all_interfaces_ip() -> str:
"""Get the IP address to bind to all network interfaces.
Returns "0.0.0.0" for IPv4 or "::" for IPv6, depending on the system's
localhost resolution.
"""
cdef string result = GetAllInterfacesIP()
return result.decode('utf-8')
def is_localhost(host: str) -> bool:
"""Check if the given host string represents a localhost address.
Args:
host: The hostname or IP address to check.
Returns:
True if the host is a localhost address (127.0.0.1, ::1, or "localhost").
"""
cdef string host_c = host.encode('utf-8')
return IsLocalhost(host_c)
+182
View File
@@ -0,0 +1,182 @@
from ray.includes.unique_ids cimport CObjectID
from ray.includes.optional cimport (
optional,
nullopt,
)
import asyncio
import concurrent.futures
import functools
import logging
import threading
from typing import Callable, Any, Union, Optional
from _collections_abc import GenericAlias
from builtins import StopAsyncIteration
import ray
import cython
logger = logging.getLogger(__name__)
def _set_future_helper(
result: Any,
*,
py_future: Union[asyncio.Future, concurrent.futures.Future],
):
# Issue #11030, #8841
# If this future has result set already, we just need to
# skip the set result/exception procedure.
if py_future.done():
return
if isinstance(result, RayTaskError):
exc = result.as_instanceof_cause()
# Convert StopIteration to RuntimeError to prevent segfaults due to
# Cpython's behavior w.r.t. PEP479
if isinstance(exc, StopIteration) or isinstance(exc, StopAsyncIteration):
runtime_error = RuntimeError(f"generator raised {type(exc).__name__}")
runtime_error.__cause__ = exc
py_future.set_exception(runtime_error)
else:
py_future.set_exception(exc)
elif isinstance(result, RayError):
# Directly raise exception for RayActorError
py_future.set_exception(result)
else:
py_future.set_result(result)
cdef class ObjectRef(BaseID):
__class_getitem__ = classmethod(GenericAlias) # should match how typing.Generic works
def __cinit__(self):
self.in_core_worker = False
def __init__(
self, id, owner_addr="", call_site_data="",
skip_adding_local_ref=False, tensor_transport: Optional[str] = None):
self._set_id(id)
self.owner_addr = owner_addr
self.in_core_worker = False
self.call_site_data = call_site_data
self._tensor_transport = tensor_transport
worker = ray._private.worker.global_worker
# TODO(edoakes): We should be able to remove the in_core_worker flag.
# But there are still some dummy object refs being created outside the
# context of a core worker.
if hasattr(worker, "core_worker"):
if not skip_adding_local_ref:
worker.core_worker.add_object_ref_reference(self)
self.in_core_worker = True
def __dealloc__(self):
if self.in_core_worker:
try:
worker = ray._private.worker.global_worker
worker.core_worker.remove_object_ref_reference(self)
except Exception as e:
# There is a strange error in rllib that causes the above to
# fail. Somehow the global 'ray' variable corresponding to the
# imported package is None when this gets called. Unfortunately
# this is hard to debug because __dealloc__ is called during
# garbage collection so we can't get a good stack trace. In any
# case, there's not much we can do besides ignore it
# (re-importing ray won't help).
pass
cdef CObjectID native(self):
return <CObjectID>self.data
def binary(self):
return self.data.Binary()
def hex(self):
return decode(self.data.Hex())
def is_nil(self):
return self.data.IsNil()
cdef size_t hash(self):
return self.data.Hash()
def task_id(self):
return TaskID(self.data.TaskId().Binary())
def job_id(self):
return self.task_id().job_id()
def owner_address(self):
return self.owner_addr
def call_site(self):
return decode(self.call_site_data)
@classmethod
def size(cls):
return CObjectID.Size()
def _set_id(self, id):
check_id(id)
self.data = CObjectID.FromBinary(<c_string>id)
@classmethod
def nil(cls):
return cls(CObjectID.Nil().Binary())
@classmethod
def from_random(cls):
return cls(CObjectID.FromRandom().Binary())
def future(self) -> concurrent.futures.Future:
"""Wrap ObjectRef with a concurrent.futures.Future
Note that the future cancellation will not cancel the correspoding
task when the ObjectRef representing return object of a task.
Additionally, future.running() will always be ``False`` even if the
underlying task is running.
"""
py_future = concurrent.futures.Future()
self._on_completed(
functools.partial(_set_future_helper, py_future=py_future))
# A hack to keep a reference to the object ref for ref counting.
py_future.object_ref = self
return py_future
def __await__(self):
return self.as_future(_internal=True).__await__()
def as_future(self, _internal=False) -> asyncio.Future:
"""Wrap ObjectRef with an asyncio.Future.
Note that the future cancellation will not cancel the correspoding
task when the ObjectRef representing return object of a task.
"""
if not _internal:
logger.warning("ref.as_future() is deprecated in favor of "
"asyncio.wrap_future(ref.future()).")
return asyncio.wrap_future(self.future())
def _on_completed(self, py_callback: Callable[[Any], None]):
"""Register a callback that will be called after Object is ready.
If the ObjectRef is already ready, the callback will be called soon.
The callback should take the result as the only argument. The result
can be an exception object in case of task error.
"""
core_worker = ray._private.worker.global_worker.core_worker
core_worker.set_get_async_callback(self, py_callback)
return self
def tensor_transport(self):
return self._tensor_transport
cdef optional[c_string] c_tensor_transport(self):
cdef:
optional[c_string] c_tensor_transport = nullopt
c_string c_tensor_transport_str
if self._tensor_transport is not None:
c_tensor_transport_str = self._tensor_transport.encode("utf-8")
c_tensor_transport.emplace(move(c_tensor_transport_str))
return c_tensor_transport
+74
View File
@@ -0,0 +1,74 @@
# source: object_ref.pxi
import asyncio
import concurrent.futures
from typing import Any, Awaitable, Callable, Generator, Optional, TypeVar, Union
from ray.includes.unique_ids import BaseID, JobID, TaskID
_T = TypeVar("_T")
def _set_future_helper(
result: _T,
*,
py_future: Union[asyncio.Future[_T], concurrent.futures.Future[_T]],
) -> None: ...
_OR = TypeVar("_OR", bound=ObjectRef)
class ObjectRef(BaseID, Awaitable[_T]):
def __init__(
self, id: bytes, owner_addr: str = "", call_site_data: str = "",
skip_adding_local_ref: bool = False, tensor_transport: Optional[str] = None) -> None: ...
def __dealloc__(self) -> None: ...
def task_id(self) -> TaskID: ...
def job_id(self) -> JobID: ...
def owner_address(self) -> str: ...
def call_site(self) -> str: ...
@classmethod
def size(cls) -> int: ...
def _set_id(self, id: bytes) -> None: ...
@classmethod
def nil(cls: type[_OR]) -> _OR: ...
@classmethod
def from_random(cls: type[_OR]) -> _OR: ...
def future(self) -> concurrent.futures.Future[_T]:
"""Wrap ObjectRef with a concurrent.futures.Future
Note that the future cancellation will not cancel the correspoding
task when the ObjectRef representing return object of a task.
Additionally, future.running() will always be ``False`` even if the
underlying task is running.
"""
...
def __await__(self) -> Generator[Any, None, _T]: ...
def as_future(self, _internal=False) -> asyncio.Future[_T]:
"""Wrap ObjectRef with an asyncio.Future.
Note that the future cancellation will not cancel the correspoding
task when the ObjectRef representing return object of a task.
"""
...
def _on_completed(self, py_callback: Callable[[_T], None]):
"""Register a callback that will be called after Object is ready.
If the ObjectRef is already ready, the callback will be called soon.
The callback should take the result as the only argument. The result
can be an exception object in case of task error.
"""
...
def tensor_transport(self) -> int: ...
+36
View File
@@ -0,0 +1,36 @@
# Currently Cython does not support std::optional.
# See: https://github.com/cython/cython/pull/3294
from libcpp cimport bool
cdef extern from "<optional>" namespace "std" nogil:
cdef cppclass nullopt_t:
nullopt_t()
cdef nullopt_t nullopt
cdef cppclass optional[T]:
ctypedef T value_type
optional()
optional(nullopt_t)
optional(optional&) except +
optional(T&) except +
bool has_value()
T& value()
T& value_or[U](U& default_value)
void swap(optional&)
void reset()
T& emplace(...)
T& operator*()
# T* operator->() # Not Supported
optional& operator=(optional&)
optional& operator=[U](U&)
bool operator bool()
bool operator!()
bool operator==[U](optional&, U&)
bool operator!=[U](optional&, U&)
bool operator<[U](optional&, U&)
bool operator>[U](optional&, U&)
bool operator<=[U](optional&, U&)
bool operator>=[U](optional&, U&)
optional[T] make_optional[T](...) except +
+88
View File
@@ -0,0 +1,88 @@
from libcpp cimport bool as c_bool
from libc.stdint cimport int64_t, uint64_t, uint32_t
from libcpp.string cimport string as c_string
from libcpp.unordered_map cimport unordered_map
cdef extern from "ray/common/ray_config.h" nogil:
cdef cppclass RayConfig "RayConfig":
@staticmethod
RayConfig &instance()
void initialize(const c_string& config_list)
int64_t ray_cookie() const
int64_t handler_warning_timeout_ms() const
int64_t debug_dump_period_milliseconds() const
int64_t object_timeout_milliseconds() const
int64_t raylet_client_num_connect_attempts() const
int64_t raylet_client_connect_timeout_milliseconds() const
int64_t kill_worker_timeout_milliseconds() const
int64_t worker_register_timeout_seconds() const
int64_t redis_db_connect_retries()
int64_t redis_db_connect_wait_milliseconds() const
int object_manager_pull_timeout_ms() const
int object_manager_push_timeout_ms() const
uint32_t maximum_gcs_deletion_batch_size() const
int64_t max_direct_call_object_size() const
int64_t task_rpc_inlined_bytes_limit() const
uint64_t metrics_report_interval_ms() const
c_bool enable_timeline() const
uint32_t max_grpc_message_size() const
c_bool record_ref_creation_sites() const
c_string REDIS_CA_CERT() const
c_string REDIS_CA_PATH() const
c_string REDIS_CLIENT_CERT() const
c_string REDIS_CLIENT_KEY() const
c_string REDIS_SERVER_NAME() const
int64_t health_check_initial_delay_ms() const
int64_t health_check_period_ms() const
int64_t health_check_timeout_ms() const
int64_t health_check_failure_threshold() const
int64_t grpc_client_keepalive_time_ms() const
int64_t grpc_client_keepalive_timeout_ms() const
c_bool enable_autoscaler_v2() const
c_string predefined_unit_instance_resources() const
c_string custom_unit_instance_resources() const
int64_t nums_py_gcs_reconnect_retry() const
int64_t py_gcs_connect_timeout_s() const
int maximum_gcs_destroyed_actor_cached_count() const
c_bool record_task_actor_creation_sites() const
c_bool start_python_gc_manager_thread() const
+141
View File
@@ -0,0 +1,141 @@
from libcpp.string cimport string as c_string
from ray.includes.ray_config cimport RayConfig
cdef class Config:
@staticmethod
def initialize(c_string config_list):
return RayConfig.instance().initialize(config_list)
@staticmethod
def ray_cookie():
return RayConfig.instance().ray_cookie()
@staticmethod
def handler_warning_timeout_ms():
return RayConfig.instance().handler_warning_timeout_ms()
@staticmethod
def debug_dump_period_milliseconds():
return RayConfig.instance().debug_dump_period_milliseconds()
@staticmethod
def object_timeout_milliseconds():
return (RayConfig.instance()
.object_timeout_milliseconds())
@staticmethod
def raylet_client_num_connect_attempts():
return RayConfig.instance().raylet_client_num_connect_attempts()
@staticmethod
def raylet_client_connect_timeout_milliseconds():
return (RayConfig.instance()
.raylet_client_connect_timeout_milliseconds())
@staticmethod
def kill_worker_timeout_milliseconds():
return RayConfig.instance().kill_worker_timeout_milliseconds()
@staticmethod
def worker_register_timeout_seconds():
return RayConfig.instance().worker_register_timeout_seconds()
@staticmethod
def redis_db_connect_retries():
return RayConfig.instance().redis_db_connect_retries()
@staticmethod
def redis_db_connect_wait_milliseconds():
return RayConfig.instance().redis_db_connect_wait_milliseconds()
@staticmethod
def object_manager_pull_timeout_ms():
return RayConfig.instance().object_manager_pull_timeout_ms()
@staticmethod
def object_manager_push_timeout_ms():
return RayConfig.instance().object_manager_push_timeout_ms()
@staticmethod
def maximum_gcs_deletion_batch_size():
return RayConfig.instance().maximum_gcs_deletion_batch_size()
@staticmethod
def metrics_report_interval_ms():
return RayConfig.instance().metrics_report_interval_ms()
@staticmethod
def enable_timeline():
return RayConfig.instance().enable_timeline()
@staticmethod
def max_grpc_message_size():
return RayConfig.instance().max_grpc_message_size()
@staticmethod
def record_ref_creation_sites():
return RayConfig.instance().record_ref_creation_sites()
@staticmethod
def REDIS_CA_CERT():
return RayConfig.instance().REDIS_CA_CERT()
@staticmethod
def REDIS_CA_PATH():
return RayConfig.instance().REDIS_CA_PATH()
@staticmethod
def REDIS_CLIENT_CERT():
return RayConfig.instance().REDIS_CLIENT_CERT()
@staticmethod
def REDIS_CLIENT_KEY():
return RayConfig.instance().REDIS_CLIENT_KEY()
@staticmethod
def REDIS_SERVER_NAME():
return RayConfig.instance().REDIS_SERVER_NAME()
@staticmethod
def health_check_initial_delay_ms():
return RayConfig.instance().health_check_initial_delay_ms()
@staticmethod
def health_check_period_ms():
return RayConfig.instance().health_check_period_ms()
@staticmethod
def health_check_timeout_ms():
return RayConfig.instance().health_check_timeout_ms()
@staticmethod
def health_check_failure_threshold():
return RayConfig.instance().health_check_failure_threshold()
@staticmethod
def grpc_client_keepalive_time_ms():
return RayConfig.instance().grpc_client_keepalive_time_ms()
@staticmethod
def grpc_client_keepalive_timeout_ms():
return RayConfig.instance().grpc_client_keepalive_timeout_ms()
@staticmethod
def enable_autoscaler_v2():
return RayConfig.instance().enable_autoscaler_v2()
@staticmethod
def nums_py_gcs_reconnect_retry():
return RayConfig.instance().nums_py_gcs_reconnect_retry()
@staticmethod
def py_gcs_connect_timeout_s():
return RayConfig.instance().py_gcs_connect_timeout_s()
@staticmethod
def maximum_gcs_destroyed_actor_cached_count():
return RayConfig.instance().maximum_gcs_destroyed_actor_cached_count()
@staticmethod
def start_python_gc_manager_thread():
return RayConfig.instance().start_python_gc_manager_thread()
+67
View File
@@ -0,0 +1,67 @@
from asyncio import Future
import concurrent.futures
from libcpp.vector cimport vector as c_vector
from libcpp.string cimport string as c_string
from libc.stdint cimport int32_t
from libcpp.utility cimport move
from libcpp.memory cimport unique_ptr, make_unique, shared_ptr
from ray.includes.common cimport (
CRayletClientWithIoContext,
CRayStatus,
CAddress,
OptionalItemPyCallback,
)
from ray.includes.optional cimport optional
cdef convert_optional_vector_int32(
CRayStatus status, optional[c_vector[int32_t]] vec) with gil:
try:
check_status_timeout_as_rpc_error(status)
assert vec.has_value()
return move(vec.value()), None
except Exception as e:
return None, e
cdef class RayletClient:
cdef:
unique_ptr[CRayletClientWithIoContext] inner
def __cinit__(self, ip_address: str, port: int):
cdef:
c_string c_ip_address
int32_t c_port
c_ip_address = ip_address.encode('utf-8')
c_port = <int32_t>port
self.inner = make_unique[CRayletClientWithIoContext](c_ip_address, c_port)
def async_get_worker_pids(self, timeout_ms: int = 1000) -> Future[list[int]]:
"""Get the PIDs of all workers registered with the raylet."""
cdef:
fut = incremented_fut()
int32_t timeout = <int32_t>timeout_ms
assert self.inner.get() is not NULL
with nogil:
self.inner.get().GetWorkerPIDs(
OptionalItemPyCallback[c_vector[int32_t]](
&convert_optional_vector_int32,
assign_and_decrement_fut,
fut),
timeout)
return asyncio.wrap_future(fut)
def async_get_agent_pids(self, timeout_ms: int = 1000) -> Future[list[int]]:
"""Get the PIDs of dashboard and runtime_env agent."""
cdef:
fut = incremented_fut()
int32_t timeout = <int32_t>timeout_ms
assert self.inner.get() is not NULL
with nogil:
self.inner.get().GetAgentPIDs(
OptionalItemPyCallback[c_vector[int32_t]](
&convert_optional_vector_int32,
assign_and_decrement_fut,
fut),
timeout)
return asyncio.wrap_future(fut)
@@ -0,0 +1,43 @@
from libcpp cimport bool as c_bool
from libcpp.memory cimport shared_ptr
from libcpp.string cimport string
from ray.includes.optional cimport optional
cdef extern from "ray/rpc/authentication/authentication_mode.h" namespace "ray::rpc" nogil:
cdef enum CAuthenticationMode "ray::rpc::AuthenticationMode":
DISABLED "ray::rpc::AuthenticationMode::DISABLED"
TOKEN "ray::rpc::AuthenticationMode::TOKEN"
CAuthenticationMode GetAuthenticationMode()
c_bool IsK8sTokenAuthEnabled()
cdef extern from "ray/rpc/authentication/authentication_token.h" namespace "ray::rpc" nogil:
cdef cppclass CAuthenticationToken "ray::rpc::AuthenticationToken":
CAuthenticationToken()
CAuthenticationToken(string value)
c_bool empty()
c_bool Equals(const CAuthenticationToken& other)
string ToAuthorizationHeaderValue()
string GetRawValue()
@staticmethod
CAuthenticationToken FromMetadata(string metadata_value)
cdef extern from "ray/rpc/authentication/authentication_token_loader.h" namespace "ray::rpc" nogil:
cdef cppclass CTokenLoadResult "ray::rpc::TokenLoadResult":
optional[CAuthenticationToken] token
string error_message
c_bool hasError()
cdef cppclass CAuthenticationTokenLoader "ray::rpc::AuthenticationTokenLoader":
@staticmethod
CAuthenticationTokenLoader& instance()
void ResetCache()
shared_ptr[const CAuthenticationToken] GetToken(c_bool ignore_auth_mode)
CTokenLoadResult TryLoadToken(c_bool ignore_auth_mode)
cdef extern from "ray/rpc/authentication/authentication_token_validator.h" namespace "ray::rpc" nogil:
cdef cppclass CAuthenticationTokenValidator "ray::rpc::AuthenticationTokenValidator":
@staticmethod
CAuthenticationTokenValidator& instance()
c_bool ValidateToken(const shared_ptr[const CAuthenticationToken]& expected_token, const string& provided_metadata)
@@ -0,0 +1,156 @@
from libcpp cimport bool as c_bool
from libcpp.memory cimport shared_ptr
from ray.includes.rpc_token_authentication cimport (
CAuthenticationMode,
GetAuthenticationMode,
IsK8sTokenAuthEnabled,
CAuthenticationToken,
CAuthenticationTokenLoader,
CAuthenticationTokenValidator,
CTokenLoadResult,
)
from ray._private.authentication.authentication_constants import AUTHORIZATION_HEADER_NAME
import logging
logger = logging.getLogger(__name__)
# Authentication mode enum exposed to Python
class AuthenticationMode:
DISABLED = CAuthenticationMode.DISABLED
TOKEN = CAuthenticationMode.TOKEN
def get_authentication_mode():
"""Get the current authentication mode.
Returns:
AuthenticationMode enum value (DISABLED or TOKEN)
"""
return GetAuthenticationMode()
def is_k8s_token_auth_enabled():
"""Returns whether Kubernetes token authentication is enabled.
Returns:
bool: True if Kubernetes token auth is enabled, false otherwise.
"""
return IsK8sTokenAuthEnabled()
def validate_authentication_token(provided_metadata: str) -> bool:
"""Validate provided authentication token.
For TOKEN mode, compares against the expected token.
If K8S auth is enabled, validates against the Kubernetes API.
Args:
provided_metadata: Full authorization header value (e.g., "Bearer <token>")
Returns:
bool: True if token is valid, False otherwise
"""
cdef shared_ptr[const CAuthenticationToken] expected_ptr
if get_authentication_mode() == CAuthenticationMode.TOKEN and not is_k8s_token_auth_enabled():
expected_ptr = CAuthenticationTokenLoader.instance().GetToken(False)
if not expected_ptr:
return False
return CAuthenticationTokenValidator.instance().ValidateToken(
expected_ptr, provided_metadata.encode())
class AuthenticationTokenLoader:
"""Python wrapper for C++ AuthenticationTokenLoader singleton."""
@staticmethod
def instance():
"""Get the singleton instance (returns a wrapper for convenience)."""
return AuthenticationTokenLoader()
def has_token(self, ignore_auth_mode=False):
"""Check if an authentication token exists without crashing.
Args:
ignore_auth_mode: If True, bypass auth mode check and attempt to load token
regardless of RAY_AUTH_MODE setting.
Returns:
bool: True if a token exists, False otherwise
Raises:
AuthenticationError: If any issues loading the token
"""
cdef CTokenLoadResult result
cdef c_bool c_ignore_auth_mode = ignore_auth_mode
with nogil:
result = CAuthenticationTokenLoader.instance().TryLoadToken(c_ignore_auth_mode)
if result.hasError():
from ray.exceptions import AuthenticationError
raise AuthenticationError(result.error_message.decode('utf-8'))
if not result.token.has_value() or result.token.value().empty():
return False
return True
def reset_cache(self):
"""Reset the C++ authentication token cache.
This forces the token loader to reload the token from environment
variables or files on the next request.
"""
CAuthenticationTokenLoader.instance().ResetCache()
def get_token_for_http_header(self, ignore_auth_mode=False) -> dict:
"""Get authentication token as a dictionary for HTTP headers.
This method loads the token from C++ AuthenticationTokenLoader and returns it
as a dictionary that can be merged with existing headers. It returns an empty
dictionary if:
- A token does not exist
- The token is empty
Args:
ignore_auth_mode: If True, bypass auth mode check and attempt to load token
regardless of RAY_AUTH_MODE setting.
Returns:
dict: Empty dict or {"authorization": "Bearer <token>"}
"""
if not self.has_token(ignore_auth_mode):
return {}
# Get the token from C++ layer (returns shared_ptr)
cdef shared_ptr[const CAuthenticationToken] token_ptr = \
CAuthenticationTokenLoader.instance().GetToken(ignore_auth_mode)
if not token_ptr or token_ptr.get().empty():
return {}
return {AUTHORIZATION_HEADER_NAME: token_ptr.get().ToAuthorizationHeaderValue().decode('utf-8')}
def get_raw_token(self, ignore_auth_mode=False) -> str:
"""Get the raw authentication token value.
Args:
ignore_auth_mode: If True, bypass auth mode check and attempt to load token
regardless of RAY_AUTH_MODE setting.
Returns:
str: The raw token string, or empty string if no token exists
"""
if not self.has_token(ignore_auth_mode):
return ""
# Get the token from C++ layer (returns shared_ptr)
cdef shared_ptr[const CAuthenticationToken] token_ptr = \
CAuthenticationTokenLoader.instance().GetToken(ignore_auth_mode)
if not token_ptr or token_ptr.get().empty():
return ""
return token_ptr.get().GetRawValue().decode('utf-8')
+537
View File
@@ -0,0 +1,537 @@
from libc.string cimport memcpy
from libc.stdint cimport uintptr_t, uint64_t, INT32_MAX
import contextlib
import cython
DEF MEMCOPY_THREADS = 6
# This is the default alignment value for len(buffer) < 2048.
DEF kMinorBufferAlign = 8
# This is the default alignment value for len(buffer) >= 2048.
# Some projects like Arrow use it for possible SIMD acceleration.
DEF kMajorBufferAlign = 64
DEF kMajorBufferSize = 2048
DEF kMemcopyDefaultBlocksize = 64
DEF kMemcopyDefaultThreshold = 1024 * 1024
DEF kLanguageSpecificTypeExtensionId = 101
DEF kMessagePackOffset = 9
cdef extern from "ray/util/memory.h" namespace "ray" nogil:
void parallel_memcopy(uint8_t* dst, const uint8_t* src, int64_t nbytes,
uintptr_t block_size, int num_threads)
cdef extern from "google/protobuf/repeated_field.h" nogil:
cdef cppclass RepeatedField[Element]:
const Element* data() const
cdef extern from "src/ray/protobuf/serialization.pb.h" nogil:
cdef cppclass CPythonBuffer "ray::serialization::PythonBuffer":
void set_address(uint64_t value)
uint64_t address() const
void set_length(int64_t value)
int64_t length() const
void set_itemsize(int64_t value)
int64_t itemsize()
void set_ndim(int32_t value)
int32_t ndim()
void set_readonly(c_bool value)
c_bool readonly()
void set_format(const c_string& value)
const c_string &format()
c_string* release_format()
void add_shape(int64_t value)
int64_t shape(int index)
const RepeatedField[int64_t] &shape() const
int shape_size()
void add_strides(int64_t value)
int64_t strides(int index)
const RepeatedField[int64_t] &strides() const
int strides_size()
cdef cppclass CPythonObject "ray::serialization::PythonObject":
uint64_t inband_data_size() const
void set_inband_data_size(uint64_t value)
uint64_t raw_buffers_size() const
void set_raw_buffers_size(uint64_t value)
CPythonBuffer* add_buffer()
CPythonBuffer& buffer(int index) const
int buffer_size() const
size_t ByteSizeLong() const
int GetCachedSize() const
uint8_t *SerializeWithCachedSizesToArray(uint8_t *target)
c_bool ParseFromArray(void* data, int size)
cdef int64_t padded_length(int64_t offset, int64_t alignment):
return ((offset + alignment - 1) // alignment) * alignment
cdef uint8_t* aligned_address(uint8_t* addr, uint64_t alignment) nogil:
cdef uintptr_t u_addr = <uintptr_t>addr
return <uint8_t*>(((u_addr + alignment - 1) // alignment) * alignment)
cdef class SubBuffer:
cdef:
void *buf
Py_ssize_t len
int readonly
c_string _format
int ndim
c_vector[Py_ssize_t] _shape
c_vector[Py_ssize_t] _strides
Py_ssize_t *suboffsets
Py_ssize_t itemsize
void *internal
object buffer
def __cinit__(self, object buffer):
# Increase ref count.
self.buffer = buffer
self.suboffsets = NULL
self.internal = NULL
def __len__(self):
return self.len // self.itemsize
@property
def nbytes(self):
"""
The buffer size in bytes.
"""
return self.len
@property
def readonly(self):
return self.readonly
def tobytes(self):
"""
Return this buffer as a Python bytes object. Memory is copied.
"""
return PyBytes_FromStringAndSize(
<const char*> self.buf, self.len)
def __getbuffer__(self, Py_buffer* buffer, int flags):
if flags & cpython.PyBUF_WRITABLE:
# Ray ensures all buffers are immutable.
raise BufferError
buffer.readonly = self.readonly
buffer.buf = self.buf
buffer.format = <char *>self._format.c_str()
buffer.internal = self.internal
buffer.itemsize = self.itemsize
buffer.len = self.len
buffer.ndim = self.ndim
buffer.obj = self # This is important for GC.
buffer.shape = self._shape.data()
buffer.strides = self._strides.data()
buffer.suboffsets = self.suboffsets
def __getsegcount__(self, Py_ssize_t *len_out):
if len_out != NULL:
len_out[0] = <Py_ssize_t> self.size
return 1
def __getreadbuffer__(self, Py_ssize_t idx, void ** p):
if idx != 0:
raise SystemError("accessing non-existent buffer segment")
if p != NULL:
p[0] = self.buf
return self.size
def __getwritebuffer__(self, Py_ssize_t idx, void ** p):
if idx != 0:
raise SystemError("accessing non-existent buffer segment")
if p != NULL:
p[0] = self.buf
return self.size
@contextlib.contextmanager
def _temporarily_disable_gc():
gc_enabled = gc.isenabled()
try:
if gc_enabled:
gc.disable()
yield
finally:
if gc_enabled:
gc.enable()
cdef class MessagePackSerializer(object):
@staticmethod
def dumps(o, python_serializer=None):
def _default(obj):
if python_serializer is not None:
return msgpack.ExtType(kLanguageSpecificTypeExtensionId,
msgpack.dumps(python_serializer(obj)))
return obj
try:
# If we let strict_types is False, then whether list or tuple will
# be packed to a message pack array. So, they can't be
# distinguished when unpacking.
return msgpack.dumps(o, default=_default,
use_bin_type=True, strict_types=True)
except ValueError as ex:
# msgpack can't handle recursive objects, so we serialize them by
# python serializer, e.g. pickle.
return msgpack.dumps(_default(o), default=_default,
use_bin_type=True, strict_types=True)
@classmethod
def loads(cls, s, python_deserializer=None):
def _ext_hook(code, data):
if code == kLanguageSpecificTypeExtensionId:
if python_deserializer is not None:
return python_deserializer(msgpack.loads(data))
raise Exception('Unrecognized ext type id: {}'.format(code))
with _temporarily_disable_gc(): # Performance optimization for msgpack
return msgpack.loads(s, ext_hook=_ext_hook, raw=False,
strict_map_key=False)
@cython.boundscheck(False)
@cython.wraparound(False)
def split_buffer(Buffer buf):
cdef:
size_t size = buf.buffer.get().Size()
uint8_t[:] bufferview = buf
int64_t msgpack_bytes_length
assert kMessagePackOffset <= size
header_unpacker = msgpack.Unpacker()
header_unpacker.feed(bufferview[:kMessagePackOffset])
msgpack_bytes_length = header_unpacker.unpack()
assert kMessagePackOffset + msgpack_bytes_length <= <int64_t>size
return (bufferview[kMessagePackOffset:
kMessagePackOffset + msgpack_bytes_length],
bufferview[kMessagePackOffset + msgpack_bytes_length:])
# Note [Pickle5 serialization layout & alignment]
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# To ensure efficient data access, our serialize enforces alignment
# when writing data to a buffer. See 'serialization.proto' for
# the detail memory layout and alignment.
@cython.boundscheck(False)
@cython.wraparound(False)
def unpack_pickle5_buffers(uint8_t[:] bufferview):
cdef:
const uint8_t *data = &bufferview[0]
CPythonObject python_object
CPythonBuffer *buffer_meta
int inband_offset = sizeof(int64_t) * 2
int64_t inband_size
int64_t protobuf_size
int32_t i
const uint8_t *buffers_segment
inband_size = (<int64_t*>data)[0]
if inband_size < 0:
raise ValueError("The inband data size should be positive."
"Got negative instead. "
"Maybe the buffer has been corrupted.")
protobuf_size = (<int64_t*>data)[1]
if protobuf_size > INT32_MAX or protobuf_size < 0:
raise ValueError("Incorrect protobuf size. "
"Maybe the buffer has been corrupted.")
inband_data = bufferview[inband_offset:inband_offset + inband_size]
if not python_object.ParseFromArray(
data + inband_offset + inband_size, <int32_t>protobuf_size):
raise ValueError("Protobuf object is corrupted.")
buffers_segment = aligned_address(
<uint8_t*>data + inband_offset + inband_size + protobuf_size,
kMajorBufferAlign)
pickled_buffers = []
# Now read buffer meta
for i in range(python_object.buffer_size()):
buffer_meta = <CPythonBuffer *>&python_object.buffer(i)
buffer = SubBuffer(bufferview)
buffer.buf = <void*>(buffers_segment + buffer_meta.address())
buffer.len = buffer_meta.length()
buffer.itemsize = buffer_meta.itemsize()
buffer.readonly = buffer_meta.readonly()
buffer.ndim = buffer_meta.ndim()
buffer._format = buffer_meta.format()
buffer._shape.assign(
buffer_meta.shape().data(),
buffer_meta.shape().data() + buffer_meta.ndim())
buffer._strides.assign(
buffer_meta.strides().data(),
buffer_meta.strides().data() + buffer_meta.ndim())
buffer.internal = NULL
buffer.suboffsets = NULL
pickled_buffers.append(buffer)
return inband_data, pickled_buffers
cdef class Pickle5Writer:
cdef:
CPythonObject python_object
c_vector[Py_buffer] buffers
# Address of end of the current buffer, relative to the
# begin offset of our buffers.
uint64_t _curr_buffer_addr
uint64_t _protobuf_offset
int64_t _total_bytes
def __cinit__(self):
self._curr_buffer_addr = 0
self._total_bytes = -1
def __dealloc__(self):
# We must release the buffer, or we could experience memory leaks.
for i in range(self.buffers.size()):
cpython.PyBuffer_Release(&self.buffers[i])
def buffer_callback(self, pickle_buffer):
cdef:
Py_buffer view
int32_t i
CPythonBuffer* buffer = self.python_object.add_buffer()
cpython.PyObject_GetBuffer(pickle_buffer, &view,
cpython.PyBUF_FULL_RO)
buffer.set_length(view.len)
buffer.set_ndim(view.ndim)
# It should be 'view.readonly'. But for the sake of shared memory,
# we have to make it immutable.
buffer.set_readonly(1)
buffer.set_itemsize(view.itemsize)
if view.format:
buffer.set_format(view.format)
if view.shape:
for i in range(view.ndim):
buffer.add_shape(view.shape[i])
if view.strides:
for i in range(view.ndim):
buffer.add_strides(view.strides[i])
# Increase buffer address.
if view.len < kMajorBufferSize:
self._curr_buffer_addr = padded_length(
self._curr_buffer_addr, kMinorBufferAlign)
else:
self._curr_buffer_addr = padded_length(
self._curr_buffer_addr, kMajorBufferAlign)
buffer.set_address(self._curr_buffer_addr)
self._curr_buffer_addr += view.len
self.buffers.push_back(view)
def get_total_bytes(self, const uint8_t[:] inband):
cdef:
size_t protobuf_bytes = 0
uint64_t inband_data_offset = sizeof(int64_t) * 2
self.python_object.set_inband_data_size(len(inband))
self.python_object.set_raw_buffers_size(self._curr_buffer_addr)
# Since calculating the output size is expensive, we will
# reuse the cached size.
# However, protobuf could change the output size according to
# different values, so we MUST NOT change 'python_object' afterwards.
protobuf_bytes = self.python_object.ByteSizeLong()
if protobuf_bytes > INT32_MAX:
raise ValueError("Total buffer metadata size is bigger than %d. "
"Consider reduce the number of buffers "
"(number of numpy arrays, etc)." % INT32_MAX)
self._protobuf_offset = inband_data_offset + len(inband)
self._total_bytes = self._protobuf_offset + protobuf_bytes
if self._curr_buffer_addr > 0:
# reserve 'kMajorBufferAlign' bytes for possible buffer alignment
self._total_bytes += kMajorBufferAlign + self._curr_buffer_addr
return self._total_bytes
@cython.boundscheck(False)
@cython.wraparound(False)
cdef void write_to(self, const uint8_t[:] inband, uint8_t[:] data,
int memcopy_threads) nogil:
cdef:
uint8_t *ptr = &data[0]
uint64_t buffer_addr
uint64_t buffer_len
int i
int64_t protobuf_size = self.python_object.GetCachedSize()
if self._total_bytes < 0:
raise ValueError("Must call 'get_total_bytes()' first "
"to get the actual size")
# Write inband data & protobuf size for deserialization.
(<int64_t*>ptr)[0] = len(inband)
(<int64_t*>ptr)[1] = protobuf_size
# Write inband data.
ptr += sizeof(int64_t) * 2
with nogil:
memcpy(ptr, &inband[0], len(inband))
# Write protobuf data.
ptr += len(inband)
self.python_object.SerializeWithCachedSizesToArray(ptr)
ptr += protobuf_size
if self._curr_buffer_addr <= 0:
# End of serialization. Writing more stuff will corrupt the memory.
return
# aligned to 64 bytes
ptr = aligned_address(ptr, kMajorBufferAlign)
for i in range(self.python_object.buffer_size()):
buffer_addr = self.python_object.buffer(i).address()
buffer_len = self.python_object.buffer(i).length()
with nogil:
if (memcopy_threads > 1 and
buffer_len > kMemcopyDefaultThreshold):
parallel_memcopy(ptr + buffer_addr,
<const uint8_t*> self.buffers[i].buf,
buffer_len,
kMemcopyDefaultBlocksize, memcopy_threads)
else:
memcpy(ptr + buffer_addr, self.buffers[i].buf, buffer_len)
cdef class SerializedObject(object):
cdef:
object _metadata
object _contained_object_refs
def __init__(self, metadata, contained_object_refs=None):
self._metadata = metadata
self._contained_object_refs = contained_object_refs or []
@property
def total_bytes(self):
raise NotImplementedError("{}.total_bytes not implemented.".format(
type(self).__name__))
@property
def metadata(self):
return self._metadata
@property
def contained_object_refs(self):
return self._contained_object_refs
@cython.boundscheck(False)
@cython.wraparound(False)
cdef void write_to(self, uint8_t[:] buffer) nogil:
raise NotImplementedError("{}.write_to not implemented.".format(
type(self).__name__))
cdef class Pickle5SerializedObject(SerializedObject):
cdef:
const uint8_t[:] inband
Pickle5Writer writer
object _total_bytes
def __init__(self, metadata, inband, Pickle5Writer writer,
contained_object_refs):
super(Pickle5SerializedObject, self).__init__(metadata,
contained_object_refs)
self.inband = inband
self.writer = writer
# cached total bytes
self._total_bytes = None
@property
def total_bytes(self):
if self._total_bytes is None:
self._total_bytes = self.writer.get_total_bytes(self.inband)
return self._total_bytes
@cython.boundscheck(False)
@cython.wraparound(False)
cdef void write_to(self, uint8_t[:] buffer) nogil:
self.writer.write_to(self.inband, buffer, MEMCOPY_THREADS)
cdef class MessagePackSerializedObject(SerializedObject):
cdef:
SerializedObject nest_serialized_object
object msgpack_header
object msgpack_data
int64_t _msgpack_header_bytes
int64_t _msgpack_data_bytes
int64_t _total_bytes
const uint8_t *msgpack_header_ptr
const uint8_t *msgpack_data_ptr
def __init__(self, metadata, msgpack_data, contained_object_refs,
SerializedObject nest_serialized_object=None):
if nest_serialized_object:
contained_object_refs.extend(
nest_serialized_object.contained_object_refs
)
total_bytes = nest_serialized_object.total_bytes
else:
total_bytes = 0
super(MessagePackSerializedObject, self).__init__(
metadata,
contained_object_refs,
)
self.nest_serialized_object = nest_serialized_object
self.msgpack_header = msgpack_header = msgpack.dumps(len(msgpack_data))
self.msgpack_data = msgpack_data
self._msgpack_header_bytes = len(msgpack_header)
self._msgpack_data_bytes = len(msgpack_data)
self._total_bytes = (kMessagePackOffset +
self._msgpack_data_bytes +
total_bytes)
self.msgpack_header_ptr = <const uint8_t*>msgpack_header
self.msgpack_data_ptr = <const uint8_t*>msgpack_data
assert self._msgpack_header_bytes <= kMessagePackOffset
@property
def total_bytes(self):
return self._total_bytes
def to_bytes(self) -> bytes:
cdef shared_ptr[CBuffer] data = \
dynamic_pointer_cast[CBuffer, LocalMemoryBuffer](
make_shared[LocalMemoryBuffer](self._total_bytes))
buffer = Buffer.make(data)
self.write_to(buffer)
return buffer.to_pybytes()
@cython.boundscheck(False)
@cython.wraparound(False)
cdef void write_to(self, uint8_t[:] buffer) nogil:
cdef uint8_t *ptr = &buffer[0]
# Write msgpack data first.
with nogil:
memcpy(ptr, self.msgpack_header_ptr, self._msgpack_header_bytes)
memcpy(ptr + kMessagePackOffset,
self.msgpack_data_ptr, self._msgpack_data_bytes)
if self.nest_serialized_object is not None:
self.nest_serialized_object.write_to(
buffer[kMessagePackOffset + self._msgpack_data_bytes:])
cdef class RawSerializedObject(SerializedObject):
cdef:
object value
const uint8_t *value_ptr
int64_t _total_bytes
def __init__(self, value):
super(RawSerializedObject,
self).__init__(ray_constants.OBJECT_METADATA_TYPE_RAW)
self.value = value
self.value_ptr = <const uint8_t*> value
self._total_bytes = len(value)
@property
def total_bytes(self):
return self._total_bytes
@cython.boundscheck(False)
@cython.wraparound(False)
cdef void write_to(self, uint8_t[:] buffer) nogil:
with nogil:
if (MEMCOPY_THREADS > 1 and
self._total_bytes > kMemcopyDefaultThreshold):
parallel_memcopy(&buffer[0],
self.value_ptr,
self._total_bytes, kMemcopyDefaultBlocksize,
MEMCOPY_THREADS)
else:
memcpy(&buffer[0], self.value_ptr, self._total_bytes)
+17
View File
@@ -0,0 +1,17 @@
from libcpp.string cimport string as c_string
cdef extern from *:
"""
extern "C" {
#include "ray/thirdparty/setproctitle/spt_setup.h"
}
"""
int spt_setup()
cdef extern from *:
"""
extern "C" {
#include "ray/thirdparty/setproctitle/spt_status.h"
}
"""
void set_ps_display(const char *activity, bint force)
+33
View File
@@ -0,0 +1,33 @@
import sys
import psutil
import subprocess
import threading
from libcpp.string cimport string as c_string
from ray.includes.setproctitle cimport (
spt_setup,
set_ps_display
)
_current_proctitle = None
_current_proctitle_lock = threading.Lock()
def setproctitle(title: str):
global _current_proctitle
cdef c_string c_title = title.encode("utf-8")
with _current_proctitle_lock:
spt_setup()
set_ps_display(c_title.c_str(), True)
_current_proctitle = title
def getproctitle() -> str:
global _current_proctitle
with _current_proctitle_lock:
if _current_proctitle is None:
# The process title is not change so getting the process cmdline as the
# initial title.
_current_proctitle = subprocess.list2cmdline(psutil.Process().cmdline())
return _current_proctitle
@@ -0,0 +1,16 @@
from libcpp.string cimport string as c_string
from libc.stdint cimport uint64_t
from libcpp cimport bool as c_bool
cdef extern from "ray/util/stream_redirection_options.h" nogil:
cdef cppclass CStreamRedirectionOptions "ray::StreamRedirectionOption":
CStreamRedirectionOptions()
c_string file_path
uint64_t rotation_max_size
uint64_t rotation_max_file_count
c_bool tee_to_stdout
c_bool tee_to_stderr
cdef extern from "ray/util/stream_redirection.h" namespace "ray" nogil:
void RedirectStdoutOncePerProcess(const CStreamRedirectionOptions& opt)
void RedirectStderrOncePerProcess(const CStreamRedirectionOptions& opt)
+168
View File
@@ -0,0 +1,168 @@
from typing import Dict
from libcpp.string cimport string as c_string
from libcpp.unordered_map cimport unordered_map
from libcpp.utility cimport move
from libcpp.vector cimport vector as c_vector
from ray.includes.common cimport (
CConcurrencyGroup,
CFallbackOption,
CLabelSelector,
CNodeResources,
SetNodeResourcesLabels,
)
from ray.includes.function_descriptor cimport (
CFunctionDescriptor,
CFunctionDescriptorBuilder,
)
from ray.includes.ray_config cimport RayConfig
cdef int prepare_labels(
dict label_dict,
unordered_map[c_string, c_string] *label_map) except -1:
if label_dict is None:
return 0
label_map[0].reserve(len(label_dict))
for key, value in label_dict.items():
if not isinstance(key, str):
raise ValueError(f"Label key must be string, but got {type(key)}")
if not isinstance(value, str):
raise ValueError(f"Label value must be string, but got {type(value)}")
label_map[0][key.encode("utf-8")] = value.encode("utf-8")
return 0
cdef int prepare_label_selector(
dict label_selector_dict,
CLabelSelector *c_label_selector) except -1:
c_label_selector[0] = CLabelSelector()
if label_selector_dict is None:
return 0
for key, value in label_selector_dict.items():
if not isinstance(key, str):
raise ValueError(f"Label selector key type must be string, but got {type(key)}")
if not isinstance(value, str):
raise ValueError(f"Label selector value must be string, but got {type(value)}")
if key == "":
raise ValueError("Label selector key must be a non-empty string.")
if (value.startswith("in(") and value.endswith(")")) or \
(value.startswith("!in(") and value.endswith(")")):
inner = value[value.index("(")+1:-1].strip()
if not inner:
raise ValueError(f"No values provided for Label Selector '{value[:value.index('(')]}' operator on key '{key}'.")
# Add key-value constraint to the LabelSelector object.
c_label_selector[0].AddConstraint(key.encode("utf-8"), value.encode("utf-8"))
return 0
def node_labels_match_selector(node_labels: Dict[str, str], selector: Dict[str, str]) -> bool:
"""
Checks if the given node labels satisfy the label selector. This helper function exposes
the C++ logic for determining if a node satisfies a label selector to the Python layer.
"""
cdef:
CNodeResources c_node_resources
CLabelSelector c_label_selector
unordered_map[c_string, c_string] c_labels_map
prepare_labels(node_labels, &c_labels_map)
SetNodeResourcesLabels(c_node_resources, c_labels_map)
prepare_label_selector(selector, &c_label_selector)
# Return whether the node resources satisfy the label constraint.
return c_node_resources.HasRequiredLabels(c_label_selector)
cdef int prepare_fallback_strategy(
list fallback_strategy,
c_vector[CFallbackOption] *fallback_strategy_vector) except -1:
cdef dict label_selector_dict
cdef CLabelSelector c_label_selector
if fallback_strategy is None:
return 0
for strategy_dict in fallback_strategy:
if not isinstance(strategy_dict, dict):
raise ValueError(
"Fallback strategy must be a list of dicts, "
f"but got list containing {type(strategy_dict)}")
label_selector_dict = strategy_dict.get("label_selector")
if label_selector_dict is not None and not isinstance(label_selector_dict, dict):
raise ValueError("Invalid fallback strategy element: invalid 'label_selector'.")
prepare_label_selector(label_selector_dict, &c_label_selector)
fallback_strategy_vector.push_back(
CFallbackOption(c_label_selector)
)
return 0
cdef int prepare_resources(
dict resource_dict,
unordered_map[c_string, double] *resource_map) except -1:
cdef:
list unit_resources
if resource_dict is None:
raise ValueError("Must provide resource map.")
resource_map[0].reserve(len(resource_dict))
for key, value in resource_dict.items():
if not (isinstance(value, int) or isinstance(value, float)):
raise ValueError("Resource quantities may only be ints or floats.")
if value < 0:
raise ValueError("Resource quantities may not be negative.")
if value > 0:
unit_resources = (
f"{RayConfig.instance().predefined_unit_instance_resources()\
.decode('utf-8')},"
f"{RayConfig.instance().custom_unit_instance_resources()\
.decode('utf-8')}"
).split(",")
if (value >= 1 and isinstance(value, float)
and not value.is_integer() and str(key) in unit_resources):
raise ValueError(
f"{key} resource quantities >1 must",
f" be whole numbers. The specified quantity {value} is invalid.")
resource_map[0][key.encode("ascii")] = float(value)
return 0
cdef c_vector[CFunctionDescriptor] prepare_function_descriptors(pyfd_list):
cdef:
c_vector[CFunctionDescriptor] fd_list
fd_list.reserve(len(pyfd_list))
for pyfd in pyfd_list:
fd_list.push_back(CFunctionDescriptorBuilder.BuildPython(
pyfd.module_name, pyfd.class_name, pyfd.function_name, b""))
return fd_list
cdef int prepare_actor_concurrency_groups(
dict concurrency_groups_dict,
c_vector[CConcurrencyGroup] *concurrency_groups):
cdef:
c_vector[CFunctionDescriptor] c_fd_list
if concurrency_groups_dict is None:
raise ValueError("Must provide it...")
concurrency_groups.reserve(len(concurrency_groups_dict))
for key, value in concurrency_groups_dict.items():
c_fd_list = prepare_function_descriptors(value["function_descriptors"])
concurrency_groups.push_back(CConcurrencyGroup(
key.encode("ascii"), value["max_concurrency"], move(c_fd_list)))
return 1
+462
View File
@@ -0,0 +1,462 @@
# cython: profile=False
# cython: boundscheck=False
# cython: wraparound=False
# cython: cdivision=True
# cython: initializedcheck=False
# =============================================================================
# WARNING: This file is used EXCLUSIVELY by Ray Serve.
# =============================================================================
#
# These Cython-optimized timeseries utilities exist solely to support the
# Ray Serve controller's autoscaling metrics pipeline (specifically the
# per-deployment timeseries aggregation in the Serve replica scheduler).
#
# This code lives in `ray/includes/` and is compiled into `_raylet.so` because
# Rather than introducing a new top-level `.pyx` / `.so` for a single
# Serve-internal optimization, we include it here alongside the other
# `.pxi` helpers that ship in `_raylet`.
#
# If you are not working on Ray Serve autoscaling, you almost certainly do not
# need to modify this file.
# =============================================================================
# C library imports
from libc.stdlib cimport malloc, free
from libc.math cimport round as c_round, isnan, nan, isinf
# Heap node for k-way merge
cdef struct _TsHeapNode:
double timestamp
int series_idx
double value
int position_in_series # Current position within the series
cdef inline void _ts_heap_sift_down(_TsHeapNode* heap, int size, int pos) noexcept nogil:
"""Sift down operation for min-heap (inline for performance)."""
cdef int smallest, left, right
cdef _TsHeapNode temp
while True:
smallest = pos
left = 2 * pos + 1
right = 2 * pos + 2
if left < size and heap[left].timestamp < heap[smallest].timestamp:
smallest = left
if right < size and heap[right].timestamp < heap[smallest].timestamp:
smallest = right
if smallest == pos:
break
# Swap
temp = heap[pos]
heap[pos] = heap[smallest]
heap[smallest] = temp
pos = smallest
cdef inline void _ts_heap_sift_up(_TsHeapNode* heap, int pos) noexcept nogil:
"""Sift up operation for min-heap (inline for performance)."""
cdef int parent
cdef _TsHeapNode temp
while pos > 0:
parent = (pos - 1) // 2
if heap[parent].timestamp <= heap[pos].timestamp:
break
# Swap
temp = heap[pos]
heap[pos] = heap[parent]
heap[parent] = temp
pos = parent
cdef inline void _ts_heap_pop(_TsHeapNode* heap, int* size) noexcept nogil:
"""Remove minimum element from heap."""
if size[0] <= 0:
return
heap[0] = heap[size[0] - 1]
size[0] -= 1
if size[0] > 0:
_ts_heap_sift_down(heap, size[0], 0)
cdef inline void _ts_heap_push(_TsHeapNode* heap, int* size, _TsHeapNode node) noexcept nogil:
"""Add element to heap."""
heap[size[0]] = node
_ts_heap_sift_up(heap, size[0])
size[0] += 1
cdef int _kway_merge_timeseries_nogil(double** timestamps_arrays, double** values_arrays,
int* series_lengths, int num_series,
int result_capacity,
double** out_timestamps, double** out_values) noexcept nogil:
"""
Fully nogil k-way merge operating on C arrays.
Assumptions:
- Each input series is sorted by timestamp in ascending order
- Values represent instantaneous gauge measurements (non-negative)
Args:
timestamps_arrays: Array of pointers to timestamp arrays for each series
values_arrays: Array of pointers to value arrays for each series
series_lengths: Array of lengths for each series
num_series: Number of series to merge
result_capacity: Pre-allocated capacity (should be >= sum of all series lengths)
out_timestamps: Output pointer for result timestamps
out_values: Output pointer for result values
Returns: Number of points in merged result, or -1 on error
"""
cdef:
int i, pos_in_series, series_idx
int heap_size = 0
double timestamp, value, old_value
double running_total = 0.0
double rounded_timestamp, last_rounded_timestamp = -1.0
_TsHeapNode new_node
int result_count = 0
# C arrays for performance
double* current_values = <double*>malloc(num_series * sizeof(double))
int* series_positions = <int*>malloc(num_series * sizeof(int))
_TsHeapNode* merge_heap = <_TsHeapNode*>malloc(num_series * sizeof(_TsHeapNode))
double* result_timestamps = <double*>malloc(result_capacity * sizeof(double))
double* result_values = <double*>malloc(result_capacity * sizeof(double))
if not current_values or not series_positions or not merge_heap or not result_timestamps or not result_values:
# Memory allocation failed
if current_values:
free(current_values)
if series_positions:
free(series_positions)
if merge_heap:
free(merge_heap)
if result_timestamps:
free(result_timestamps)
if result_values:
free(result_values)
return -1
# Initialize arrays
for i in range(num_series):
current_values[i] = 0.0
series_positions[i] = 0
# Push first element from each series to heap
if series_lengths[i] > 0:
merge_heap[heap_size].timestamp = timestamps_arrays[i][0]
merge_heap[heap_size].series_idx = i
merge_heap[heap_size].value = values_arrays[i][0]
merge_heap[heap_size].position_in_series = 0
heap_size += 1
# Build initial heap
for i in range(heap_size // 2 - 1, -1, -1):
_ts_heap_sift_down(merge_heap, heap_size, i)
# K-way merge
while heap_size > 0:
# Get minimum element
timestamp = merge_heap[0].timestamp
series_idx = merge_heap[0].series_idx
value = merge_heap[0].value
pos_in_series = merge_heap[0].position_in_series
# Update running total
old_value = current_values[series_idx]
current_values[series_idx] = value
running_total += value - old_value
# Remove from heap
_ts_heap_pop(merge_heap, &heap_size)
# Push next element from same series if available
series_positions[series_idx] = pos_in_series + 1
if series_positions[series_idx] < series_lengths[series_idx]:
new_node.timestamp = timestamps_arrays[series_idx][series_positions[series_idx]]
new_node.series_idx = series_idx
new_node.value = values_arrays[series_idx][series_positions[series_idx]]
new_node.position_in_series = series_positions[series_idx]
_ts_heap_push(merge_heap, &heap_size, new_node)
# Only add point if value changed
if value != old_value:
# Round to 10ms precision
rounded_timestamp = c_round(timestamp * 100.0) / 100.0
# Check if we can merge with last point
if result_count > 0 and last_rounded_timestamp == rounded_timestamp:
# Update last point's value
result_values[result_count - 1] = running_total
else:
# Add new point (capacity is pre-allocated to be large enough)
result_timestamps[result_count] = rounded_timestamp
result_values[result_count] = running_total
result_count += 1
last_rounded_timestamp = rounded_timestamp
# Clean up
free(current_values)
free(series_positions)
free(merge_heap)
# Return results
out_timestamps[0] = result_timestamps
out_values[0] = result_values
return result_count
def merge_instantaneous_total_cython(list replicas_timeseries):
"""
Cython-optimized k-way merge for timeseries.
This is a drop-in replacement for the Python version with 5-10x speedup.
Assumptions:
- Each input timeseries is sorted by timestamp in ascending order
- Values represent instantaneous gauge measurements
Args:
replicas_timeseries: List of timeseries. Each timeseries is a list of
objects with .timestamp and .value attributes.
Returns:
List of (timestamp, value) tuples representing the merged timeseries.
"""
# Filter empty series
cdef list active_series = [series for series in replicas_timeseries if series]
if not active_series:
return []
if len(active_series) == 1:
# Convert to tuples for consistent return type
return [(point.timestamp, point.value) for point in active_series[0]]
cdef:
int num_series = len(active_series)
int i, j
int total_points = 0
object point, series
bint alloc_failed = False
# C arrays for all timestamps and values
double** timestamps_arrays = <double**>malloc(num_series * sizeof(double*))
double** values_arrays = <double**>malloc(num_series * sizeof(double*))
int* series_lengths = <int*>malloc(num_series * sizeof(int))
double* result_timestamps = NULL
double* result_values = NULL
int result_count
if not timestamps_arrays or not values_arrays or not series_lengths:
# Memory allocation failed
if timestamps_arrays:
free(timestamps_arrays)
if values_arrays:
free(values_arrays)
if series_lengths:
free(series_lengths)
raise MemoryError("Failed to allocate memory for merge operation")
# Initialize pointers to NULL for safe cleanup
for i in range(num_series):
timestamps_arrays[i] = NULL
values_arrays[i] = NULL
try:
# Extract all data from Python objects into C arrays
for i in range(num_series):
series = active_series[i]
series_lengths[i] = len(series)
total_points += series_lengths[i]
timestamps_arrays[i] = <double*>malloc(series_lengths[i] * sizeof(double))
values_arrays[i] = <double*>malloc(series_lengths[i] * sizeof(double))
if not timestamps_arrays[i] or not values_arrays[i]:
alloc_failed = True
break
# Copy data from Python objects to C arrays
for j in range(series_lengths[i]):
point = series[j]
timestamps_arrays[i][j] = point.timestamp
values_arrays[i][j] = point.value
if alloc_failed:
raise MemoryError("Failed to allocate memory for series data")
# Perform merge with full nogil
# Pass total_points as capacity (worst case: all points output)
with nogil:
result_count = _kway_merge_timeseries_nogil(timestamps_arrays, values_arrays,
series_lengths, num_series,
total_points,
&result_timestamps, &result_values)
if result_count < 0:
# Note: _kway_merge_timeseries_nogil frees all memory on error
raise MemoryError("Failed during merge operation")
# Convert C arrays back to Python tuples
merged = [None] * result_count
for i in range(result_count):
merged[i] = (result_timestamps[i], result_values[i])
return merged
finally:
# Centralized cleanup: safe even if some allocations failed
# Free result arrays (allocated by _kway_merge_timeseries_nogil)
if result_timestamps:
free(result_timestamps)
if result_values:
free(result_values)
if timestamps_arrays:
for i in range(num_series):
if timestamps_arrays[i]:
free(timestamps_arrays[i])
free(timestamps_arrays)
if values_arrays:
for i in range(num_series):
if values_arrays[i]:
free(values_arrays[i])
free(values_arrays)
if series_lengths:
free(series_lengths)
cdef double _compute_time_weighted_average_nogil(double* timestamps, double* values, int n,
double window_start, double window_end) noexcept nogil:
"""
Fully nogil time-weighted average computation on C arrays.
Returns: Time-weighted average or NaN to indicate None (invalid result)
"""
cdef:
int i
double total_weighted_value = 0.0
double total_duration = 0.0
double current_value = 0.0
double current_time
double timestamp, value, duration
if window_end <= window_start:
return nan("")
current_time = window_start
# Find value at window_start (LOCF)
for i in range(n):
timestamp = timestamps[i]
if timestamp <= window_start:
current_value = values[i]
else:
break
# Process segments
for i in range(n):
timestamp = timestamps[i]
value = values[i]
if timestamp <= window_start:
continue
if timestamp >= window_end:
break
# Add contribution of current segment
# Note: timestamp < window_end is guaranteed here due to the break above
duration = timestamp - current_time
if duration > 0:
total_weighted_value += current_value * duration
total_duration += duration
current_value = value
current_time = timestamp
# Add final segment
if current_time < window_end:
duration = window_end - current_time
total_weighted_value += current_value * duration
total_duration += duration
if total_duration > 0:
return total_weighted_value / total_duration
return nan("")
def time_weighted_average_cython(list timeseries, double window_start,
double window_end, double last_window_s=1.0):
"""
Cython-optimized time-weighted average calculation.
Assumptions:
- Input timeseries is sorted by timestamp in ascending order
- Values are treated as a step function (LOCF - Last Observation Carried Forward)
Args:
timeseries: List of objects with .timestamp and .value attributes
window_start: Start of window (negative infinity means use first timestamp)
window_end: End of window (negative infinity means use last timestamp + last_window_s)
last_window_s: Window size for last segment
Returns:
Time-weighted average or None (returned as None when result would be invalid)
"""
if not timeseries:
return None
cdef:
int n = len(timeseries)
int i
double result
object point
double* timestamps = <double*>malloc(n * sizeof(double))
double* values = <double*>malloc(n * sizeof(double))
if not timestamps or not values:
if timestamps:
free(timestamps)
if values:
free(values)
raise MemoryError("Failed to allocate memory for time weighted average")
try:
# Extract data from Python objects into C arrays
for i in range(n):
point = timeseries[i]
timestamps[i] = point.timestamp
values[i] = point.value
# Handle window boundaries
# Use negative infinity as sentinel for None (any valid float including -1.0 works)
if isinf(window_start) and window_start < 0:
window_start = timestamps[0]
if isinf(window_end) and window_end < 0:
window_end = timestamps[n - 1] + last_window_s
# Compute with full nogil
with nogil:
result = _compute_time_weighted_average_nogil(timestamps, values, n,
window_start, window_end)
return None if isnan(result) else result
finally:
free(timestamps)
free(values)
+191
View File
@@ -0,0 +1,191 @@
from libcpp cimport bool as c_bool
from libcpp.string cimport string as c_string
from libc.stdint cimport uint8_t, uint32_t, int64_t
# Note: we removed the staticmethod declarations in
# https://github.com/ray-project/ray/pull/47984 due
# to a compiler bug in Cython 3.0.x -- we should see
# if we can bring them back in Cython 3.1.x if the
# bug is fixed.
cdef extern from "ray/common/id.h" namespace "ray" nogil:
cdef cppclass CBaseID[T]:
size_t Hash() const
c_bool IsNil() const
c_bool operator==(const CBaseID &rhs) const
c_bool operator!=(const CBaseID &rhs) const
const uint8_t *data() const
c_string Binary() const
c_string Hex() const
cdef cppclass CUniqueID "ray::UniqueID"(CBaseID[CUniqueID]):
CUniqueID()
@staticmethod
size_t Size()
@staticmethod
CUniqueID FromRandom()
@staticmethod
CUniqueID FromBinary(const c_string &binary)
@staticmethod
const CUniqueID Nil()
cdef cppclass CActorClassID "ray::ActorClassID"(CBaseID[CActorClassID]):
@staticmethod
CActorClassID FromHex(const c_string &hex_str)
cdef cppclass CActorID "ray::ActorID"(CBaseID[CActorID]):
@staticmethod
CActorID FromBinary(const c_string &binary)
@staticmethod
CActorID FromHex(const c_string &hex_str)
@staticmethod
const CActorID Nil()
@staticmethod
size_t Size()
@staticmethod
CActorID Of(CJobID job_id, CTaskID parent_task_id,
int64_t parent_task_counter)
CJobID JobId()
cdef cppclass CNodeID "ray::NodeID"(CBaseID[CNodeID]):
@staticmethod
CNodeID FromHex(const c_string &hex_str)
@staticmethod
const CNodeID Nil()
cdef cppclass CConfigID "ray::ConfigID"(CBaseID[CConfigID]):
pass
cdef cppclass CFunctionID "ray::FunctionID"(CBaseID[CFunctionID]):
@staticmethod
CFunctionID FromHex(const c_string &hex_str)
cdef cppclass CJobID "ray::JobID"(CBaseID[CJobID]):
@staticmethod
CJobID FromBinary(const c_string &binary)
@staticmethod
CJobID FromHex(const c_string &hex_str)
@staticmethod
const CJobID Nil()
@staticmethod
size_t Size()
@staticmethod
CJobID FromInt(uint32_t value)
uint32_t ToInt()
cdef cppclass CTaskID "ray::TaskID"(CBaseID[CTaskID]):
@staticmethod
CTaskID FromBinary(const c_string &binary)
@staticmethod
CTaskID FromHex(const c_string &hex_str)
@staticmethod
const CTaskID Nil()
@staticmethod
size_t Size()
@staticmethod
CTaskID ForDriverTask(const CJobID &job_id)
@staticmethod
CTaskID FromRandom(const CJobID &job_id)
@staticmethod
CTaskID ForActorCreationTask(CActorID actor_id)
@staticmethod
CTaskID ForActorTask(CJobID job_id, CTaskID parent_task_id,
int64_t parent_task_counter, CActorID actor_id)
@staticmethod
CTaskID ForNormalTask(CJobID job_id, CTaskID parent_task_id,
int64_t parent_task_counter)
CActorID ActorId() const
CJobID JobId() const
cdef cppclass CObjectID" ray::ObjectID"(CBaseID[CObjectID]):
@staticmethod
int64_t MaxObjectIndex()
@staticmethod
CObjectID FromBinary(const c_string &binary)
@staticmethod
CObjectID FromRandom()
@staticmethod
const CObjectID Nil()
@staticmethod
CObjectID FromIndex(const CTaskID &task_id, int64_t index)
@staticmethod
size_t Size()
c_bool is_put()
int64_t ObjectIndex() const
CTaskID TaskId() const
cdef cppclass CClusterID "ray::ClusterID"(CBaseID[CClusterID]):
@staticmethod
CClusterID FromHex(const c_string &hex_str)
@staticmethod
CClusterID FromRandom()
@staticmethod
const CClusterID Nil()
cdef cppclass CWorkerID "ray::WorkerID"(CBaseID[CWorkerID]):
@staticmethod
CWorkerID FromHex(const c_string &hex_str)
cdef cppclass CPlacementGroupID "ray::PlacementGroupID" \
(CBaseID[CPlacementGroupID]):
@staticmethod
CPlacementGroupID FromBinary(const c_string &binary)
@staticmethod
CPlacementGroupID FromHex(const c_string &hex_str)
@staticmethod
const CPlacementGroupID Nil()
@staticmethod
size_t Size()
@staticmethod
CPlacementGroupID Of(CJobID job_id)
ctypedef uint32_t ObjectIDIndexType
+436
View File
@@ -0,0 +1,436 @@
"""This is a module for unique IDs in Ray.
We define different types for different IDs for type safety.
See https://github.com/ray-project/ray/issues/3721.
"""
import logging
import os
from ray.includes.unique_ids cimport (
CActorClassID,
CActorID,
CNodeID,
CConfigID,
CJobID,
CFunctionID,
CObjectID,
CTaskID,
CUniqueID,
CWorkerID,
CPlacementGroupID,
CClusterID,
)
import ray
from ray._common.utils import decode
logger = logging.getLogger(__name__)
def check_id(b, size=kUniqueIDSize):
if not isinstance(b, bytes):
raise TypeError(f"Unsupported type: {type(b)}, expected bytes")
if len(b) != size:
raise ValueError("ID string needs to have length " +
str(size) + ", got " + str(len(b)))
def check_nil(id_obj):
if id_obj.is_nil():
raise ValueError(f"{id_obj.__class__.__name__} is nil")
cdef extern from "ray/common/constants.h" nogil:
cdef int64_t kUniqueIDSize
cdef class BaseID:
@classmethod
def from_binary(cls, id_bytes):
return cls(id_bytes)
@classmethod
def from_hex(cls, hex_id):
raise NotImplementedError
cdef size_t hash(self):
raise NotImplementedError
def binary(self):
raise NotImplementedError
@classmethod
def size(cls):
raise NotImplementedError
def hex(self):
raise NotImplementedError
def is_nil(self):
raise NotImplementedError
def __hash__(self):
return self.hash()
def __eq__(self, other):
return type(self) == type(other) and self.binary() == other.binary()
def __ne__(self, other):
return type(self) != type(other) or self.binary() != other.binary()
def __bytes__(self):
return self.binary()
def __hex__(self):
return self.hex()
def __repr__(self):
return self.__class__.__name__ + "(" + self.hex() + ")"
def __str__(self):
return self.__repr__()
def __reduce__(self):
return type(self), (self.binary(),)
def redis_shard_hash(self):
# NOTE: The hash function used here must match the one in
# GetRedisContext in src/ray/gcs/tables.h. Changes to the
# hash function should only be made through std::hash in
# src/common/common.h.
# Do not use __hash__ that returns signed uint64_t, which
# is different from std::hash in c++ code.
return self.hash()
cdef class UniqueID(BaseID):
cdef CUniqueID data
def __init__(self, id):
check_id(id)
self.data = CUniqueID.FromBinary(id)
@classmethod
def nil(cls):
return cls(CUniqueID.Nil().Binary())
@classmethod
def from_random(cls):
return cls(CUniqueID.FromRandom().Binary())
@classmethod
def size(cls):
return CUniqueID.Size()
def binary(self):
return self.data.Binary()
def hex(self):
return decode(self.data.Hex())
def is_nil(self):
return self.data.IsNil()
cdef size_t hash(self):
return self.data.Hash()
cdef class TaskID(BaseID):
cdef CTaskID data
def __init__(self, id):
check_id(id, CTaskID.Size())
self.data = CTaskID.FromBinary(<c_string>id)
@classmethod
def from_hex(cls, hex_id):
binary_id = CTaskID.FromHex(<c_string>hex_id).Binary()
return cls(binary_id)
cdef CTaskID native(self):
return <CTaskID>self.data
@classmethod
def size(cls):
return CTaskID.Size()
def binary(self):
return self.data.Binary()
def hex(self):
return decode(self.data.Hex())
def is_nil(self):
return self.data.IsNil()
def actor_id(self):
return ActorID(self.data.ActorId().Binary())
def job_id(self):
check_nil(self)
return JobID(self.data.JobId().Binary())
cdef size_t hash(self):
return self.data.Hash()
@classmethod
def nil(cls):
return cls(CTaskID.Nil().Binary())
@classmethod
def for_fake_task(cls, job_id):
return cls(CTaskID.FromRandom(
CJobID.FromBinary(job_id.binary())).Binary())
@classmethod
def for_driver_task(cls, job_id):
return cls(CTaskID.ForDriverTask(
CJobID.FromBinary(job_id.binary())).Binary())
@classmethod
def for_actor_creation_task(cls, actor_id):
assert isinstance(actor_id, ActorID)
return cls(CTaskID.ForActorCreationTask(
CActorID.FromBinary(actor_id.binary())).Binary())
@classmethod
def for_actor_task(cls, job_id, parent_task_id,
parent_task_counter, actor_id):
assert isinstance(job_id, JobID)
assert isinstance(parent_task_id, TaskID)
assert isinstance(actor_id, ActorID)
return cls(CTaskID.ForActorTask(
CJobID.FromBinary(job_id.binary()),
CTaskID.FromBinary(parent_task_id.binary()),
parent_task_counter,
CActorID.FromBinary(actor_id.binary())).Binary())
@classmethod
def for_normal_task(cls, job_id, parent_task_id, parent_task_counter):
assert isinstance(job_id, JobID)
assert isinstance(parent_task_id, TaskID)
return cls(CTaskID.ForNormalTask(
CJobID.FromBinary(job_id.binary()),
CTaskID.FromBinary(parent_task_id.binary()),
parent_task_counter).Binary())
cdef class NodeID(UniqueID):
def __init__(self, id):
check_id(id)
self.data = CUniqueID.FromBinary(<c_string>id)
@classmethod
def from_hex(cls, hex_id):
binary_id = CNodeID.FromHex(<c_string>hex_id).Binary()
return cls(binary_id)
cdef CNodeID native(self):
return <CNodeID>self.data
cdef class JobID(BaseID):
cdef CJobID data
def __init__(self, id):
check_id(id, CJobID.Size())
self.data = CJobID.FromBinary(<c_string>id)
@classmethod
def from_hex(cls, hex_id):
binary_id = CJobID.FromHex(<c_string>hex_id).Binary()
return cls(binary_id)
cdef CJobID native(self):
return <CJobID>self.data
@classmethod
def from_int(cls, value):
assert value < 2**32, "Maximum JobID integer is 2**32 - 1."
return cls(CJobID.FromInt(value).Binary())
@classmethod
def nil(cls):
return cls(CJobID.Nil().Binary())
@classmethod
def size(cls):
return CJobID.Size()
def int(self):
return self.data.ToInt()
def binary(self):
return self.data.Binary()
def hex(self):
return decode(self.data.Hex())
def is_nil(self):
return self.data.IsNil()
cdef size_t hash(self):
return self.data.Hash()
cdef class WorkerID(UniqueID):
def __init__(self, id):
check_id(id)
self.data = CUniqueID.FromBinary(<c_string>id)
@classmethod
def from_hex(cls, hex_id):
binary_id = CWorkerID.FromHex(<c_string>hex_id).Binary()
return cls(binary_id)
cdef CWorkerID native(self):
return <CWorkerID>self.data
cdef class ActorID(BaseID):
def __init__(self, id):
self._set_id(id)
@classmethod
def of(cls, job_id, parent_task_id, parent_task_counter):
assert isinstance(job_id, JobID)
assert isinstance(parent_task_id, TaskID)
return cls(CActorID.Of(CJobID.FromBinary(job_id.binary()),
CTaskID.FromBinary(parent_task_id.binary()),
parent_task_counter).Binary())
@classmethod
def from_hex(cls, hex_id):
binary_id = CActorID.FromHex(<c_string>hex_id).Binary()
return cls(binary_id)
@classmethod
def nil(cls):
return cls(CActorID.Nil().Binary())
@classmethod
def from_random(cls):
return cls(os.urandom(CActorID.Size()))
@classmethod
def size(cls):
return CActorID.Size()
def _set_id(self, id):
check_id(id, CActorID.Size())
self.data = CActorID.FromBinary(<c_string>id)
@property
def job_id(self):
check_nil(self)
return JobID(self.data.JobId().Binary())
def binary(self):
return self.data.Binary()
def hex(self):
return decode(self.data.Hex())
def is_nil(self):
return self.data.IsNil()
cdef size_t hash(self):
return self.data.Hash()
cdef CActorID native(self):
return <CActorID>self.data
cdef class FunctionID(UniqueID):
def __init__(self, id):
check_id(id)
self.data = CUniqueID.FromBinary(<c_string>id)
@classmethod
def from_hex(cls, hex_id):
binary_id = CFunctionID.FromHex(<c_string>hex_id).Binary()
return cls(binary_id)
cdef CFunctionID native(self):
return <CFunctionID>self.data
cdef class ActorClassID(UniqueID):
def __init__(self, id):
check_id(id)
self.data = CUniqueID.FromBinary(<c_string>id)
@classmethod
def from_hex(cls, hex_id):
binary_id = CActorClassID.FromHex(<c_string>hex_id).Binary()
return cls(binary_id)
cdef CActorClassID native(self):
return <CActorClassID>self.data
cdef class ClusterID(UniqueID):
def __init__(self, id):
check_id(id)
self.data = CUniqueID.FromBinary(<c_string>id)
@classmethod
def from_hex(cls, hex_id):
binary_id = CClusterID.FromHex(<c_string>hex_id).Binary()
return cls(binary_id)
cdef CClusterID native(self):
return <CClusterID>self.data
# This type alias is for backward compatibility.
ObjectID = ObjectRef
cdef class PlacementGroupID(BaseID):
cdef CPlacementGroupID data
def __init__(self, id):
check_id(id, CPlacementGroupID.Size())
self.data = CPlacementGroupID.FromBinary(<c_string>id)
cdef CPlacementGroupID native(self):
return <CPlacementGroupID>self.data
@classmethod
def from_hex(cls, hex_id):
binary_id = CPlacementGroupID.FromHex(<c_string>hex_id).Binary()
return cls(binary_id)
@classmethod
def from_random(cls):
return cls(os.urandom(CPlacementGroupID.Size()))
@classmethod
def of(cls, job_id):
assert isinstance(job_id, JobID)
return cls(CPlacementGroupID.Of(CJobID.FromBinary(job_id.binary())).Binary())
@classmethod
def nil(cls):
return cls(CPlacementGroupID.Nil().Binary())
@classmethod
def size(cls):
return CPlacementGroupID.Size()
def binary(self):
return self.data.Binary()
def hex(self):
return decode(self.data.Hex())
def is_nil(self):
return self.data.IsNil()
cdef size_t hash(self):
return self.data.Hash()
+146
View File
@@ -0,0 +1,146 @@
from __future__ import annotations
from typing import Tuple, TypeVar
# backwards compatibility. Luckily circular references are fine in type stubs
from ray._raylet import ObjectRef
ObjectID = ObjectRef
# implementations are in unique_ids.pxi
def check_id(b: bytes, size: int = ...) -> None: ...
_BID = TypeVar("_BID", bound=BaseID)
class BaseID:
@classmethod
def from_binary(cls: type[_BID], id_bytes: bytes) -> _BID: ...
@classmethod
def from_hex(cls: type[_BID], hex_id: str | bytes) -> _BID: ...
def binary(self) -> bytes: ...
@classmethod
def size(cls) -> int: ...
def hex(self) -> str: ...
def is_nil(self) -> bool: ...
def __hash__(self) -> int: ...
def __eq__(self, other: object) -> bool: ...
def __ne__(self, other: object) -> bool: ...
def __bytes__(self) -> bytes: ...
def __hex__(self) -> str: ...
def __repr__(self) -> str: ...
def __str__(self) -> str: ...
def __reduce__(self: _BID) -> Tuple[type[_BID], Tuple[bytes]]: ...
def redis_shard_hash(self) -> int: ...
_UID = TypeVar("_UID", bound=UniqueID)
class UniqueID(BaseID):
def __init__(self, id: bytes) -> None: ...
@classmethod
def nil(cls: type[_UID]) -> _UID: ...
@classmethod
def from_random(cls: type[_UID]) -> _UID: ...
_TID = TypeVar("_TID", bound=TaskID)
class TaskID(BaseID):
def __init__(self, id: bytes) -> None: ...
def actor_id(self) -> ActorID: ...
def job_id(self) -> JobID: ...
@classmethod
def nil(cls: type[_TID]) -> _TID: ...
@classmethod
def for_fake_task(cls: type[_TID], job_id: JobID) -> _TID: ...
@classmethod
def for_driver_task(cls: type[_TID], job_id: JobID) -> _TID: ...
@classmethod
def for_actor_creation_task(cls: type[_TID], actor_id: ActorID) -> _TID: ...
@classmethod
def for_actor_task(cls: type[_TID], job_id: JobID, parent_task_id: TaskID,
parent_task_counter: int, actor_id: ActorID) -> _TID: ...
@classmethod
def for_normal_task(cls: type[_TID], job_id: JobID, parent_task_id: TaskID, parent_task_counter: int) -> _TID: ...
class NodeID(UniqueID): ...
_JID = TypeVar("_JID", bound=JobID)
class JobID(BaseID):
def __init__(self, id: bytes) -> None: ...
@classmethod
def from_int(cls: type[_JID], value: int) -> _JID: ...
@classmethod
def nil(cls: type[_JID]) -> _JID: ...
def int(self) -> int: ...
class WorkerID(UniqueID): ...
_AID = TypeVar("_AID", bound=ActorID)
class ActorID(BaseID):
def __init__(self, id: bytes) -> None: ...
@classmethod
def of(cls: type[_AID], job_id: JobID, parent_task_id: TaskID, parent_task_counter: int) -> _AID: ...
@classmethod
def nil(cls: type[_AID]) -> _AID: ...
@classmethod
def from_random(cls: type[_AID]) -> _AID: ...
def _set_id(self, id: bytes) -> None: ...
@property
def job_id(self) -> JobID: ...
class FunctionID(UniqueID): ...
class ActorClassID(UniqueID): ...
class ClusterID(UniqueID): ...
_PGID = TypeVar("_PGID", bound=PlacementGroupID)
class PlacementGroupID(BaseID):
def __init__(self, id: bytes) -> None: ...
@classmethod
def from_random(cls: type[_PGID]) -> _PGID: ...
@classmethod
def of(cls: type[_PGID], job_id: JobID) -> _PGID: ...
@classmethod
def nil(cls: type[_PGID]) -> _PGID: ...