Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1688171
roughly add sync message batching
Oct 6, 2025
65e8bc0
ray syncer ut passed, lint reformated
Oct 7, 2025
506fe11
reformated with clearer encapsulation of create/consume messages
Oct 11, 2025
af43f6f
fix destructor race condition
Oct 11, 2025
3eacfe3
lint update
Oct 11, 2025
3faab25
lint update
Oct 11, 2025
34756c1
remove const
Oct 11, 2025
dfd4af2
fix ut with fixed delay to receive resource view update
Oct 12, 2025
0c30b7f
nit update: change MergedRaySyncMessage to RaySyncMessage
Oct 15, 2025
03e4853
Refactor to use optimistic buffer for batching
Oct 19, 2025
d48b181
nit update
Oct 19, 2025
4f04808
Add configurable buffer for batching
Oct 25, 2025
0b3fd18
nit update and add ut
Oct 26, 2025
11cae15
lint update
Oct 26, 2025
4937c18
Merge branch 'master' into resource_view_sync_message_batching
yancanmao Oct 28, 2025
e244a3f
Merge remote-tracking branch 'upstream/master' into resource_view_syn…
Nov 3, 2025
3c3b14f
Merge branch 'master' into resource_view_sync_message_batching
yancanmao Nov 5, 2025
62bfac5
Update src/ray/protobuf/ray_syncer.proto
yancanmao Nov 10, 2025
b85ea02
Update src/ray/common/ray_config_def.h
yancanmao Nov 10, 2025
0aa3eb1
Update src/ray/common/ray_config_def.h
yancanmao Nov 10, 2025
beae7e5
Update src/ray/protobuf/ray_syncer.proto
yancanmao Nov 10, 2025
82e15f2
Update src/ray/protobuf/ray_syncer.proto
yancanmao Nov 10, 2025
3bdf5d7
lint reformat
Nov 10, 2025
6d0691f
nit update
Nov 10, 2025
e146c09
nit update
Nov 10, 2025
f4256cd
Add complete ut for batching test, nit update
Nov 12, 2025
a29d9ef
nit update
Nov 12, 2025
93fb656
fix according to cursor
Nov 12, 2025
0adf20a
nit update according to the comments
Nov 13, 2025
70e209a
Merge branch 'master' into resource_view_sync_message_batching
edoakes Nov 13, 2025
7045296
nit update according to comments
Nov 14, 2025
a7909cb
super nit update
Nov 14, 2025
853320a
add destructor for timer
Nov 18, 2025
5d52e4a
Merge branch 'master' into resource_view_sync_message_batching
edoakes Nov 18, 2025
c6e5d98
Merge branch 'master' into resource_view_sync_message_batching
jjyao Nov 19, 2025
d42cf04
Update reactor to use shared_ptr. Nit update.
Nov 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ray/common/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ ray_cc_library(
hdrs = ["test_utils.h"],
deps = [
":asio",
":function_descriptor",
":id",
":placement_group",
":ray_object",
Expand Down
12 changes: 12 additions & 0 deletions src/ray/common/ray_config_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -1017,3 +1017,15 @@ RAY_CONFIG(bool, start_python_gc_manager_thread, true)
// Whether to enable the feature of outputting error log if the task is
// still retryable.
RAY_CONFIG(bool, enable_output_error_log_if_still_retry, true)

// The maximum batch size for ray_syncer_bidi_reactor to sync messages to other nodes.
Comment thread
edoakes marked this conversation as resolved.
Resolved
// If `gcs_resource_broadcast_max_batch_size == 1`, the ray_syncer_bidi_reactor will
// disable batching. Otherwise, it will start send with maximum of
// `gcs_resource_broadcast_max_batch_size` messages before timeout.
RAY_CONFIG(size_t, gcs_resource_broadcast_max_batch_size, 1)

// The maximum period that the GCS will wait for resource update messages before
// broadcasting the batch. If `gcs_resource_broadcast_max_batch_size` messages arrive
// before the timeout, the batch will be broadcasted eagerly. This flag only applies if
// `gcs_resource_broadcast_max_batch_size != 1`.
RAY_CONFIG(uint64_t, gcs_resource_broadcast_max_batch_delay_ms, 0)
15 changes: 15 additions & 0 deletions src/ray/gcs/gcs_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -607,9 +607,23 @@ GcsServer::StorageType GcsServer::GetStorageType() const {
}

void GcsServer::InitRaySyncer(const GcsInitData &gcs_init_data) {
if (RayConfig::instance().gcs_resource_broadcast_max_batch_delay_ms() > 0 &&
RayConfig::instance().gcs_resource_broadcast_max_batch_size() == 1) {
RAY_LOG(WARNING) << "Configuration inconsistency detected: "
"gcs_resource_broadcast_max_batch_delay_ms > 0 "
<< "but gcs_resource_broadcast_max_batch_size = 1. The delay "
"setting will have no effect "
<< "since only one message can be batched at a time. Consider "
"increasing batch_size or "
<< "setting delay_ms to 0.";
}

ray_syncer_ = std::make_unique<syncer::RaySyncer>(
io_context_provider_.GetIOContext<syncer::RaySyncer>(),
kGCSNodeID.Binary(),
/* batch_size */ RayConfig::instance().gcs_resource_broadcast_max_batch_size(),
/* batch_delay_ms */
RayConfig::instance().gcs_resource_broadcast_max_batch_delay_ms(),
Comment on lines +624 to +626

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be done separately in a follow up. If the user sets gcs_resource_broadcast_max_batch_delay_ms > 0 and gcs_resource_broadcast_max_batch_size = 1, we should probably log a warning at the very least because they've likely made a mistake.

[this](const NodeID &node_id) {
gcs_healthcheck_manager_->MarkNodeHealthy(node_id);
});
Expand Down Expand Up @@ -976,6 +990,7 @@ void GcsServer::TryGlobalGC() {
std::string serialized_msg;
RAY_CHECK(commands_sync_message.SerializeToString(&serialized_msg));
msg->set_sync_message(std::move(serialized_msg));

ray_syncer_->BroadcastMessage(std::move(msg));
}
}
Expand Down
7 changes: 6 additions & 1 deletion src/ray/protobuf/ray_syncer.proto
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ message RaySyncMessage {
bytes node_id = 4;
}

message RaySyncMessageBatch {
Comment thread
yancanmao marked this conversation as resolved.
Resolved
// Batch of RaySyncMessages.
repeated RaySyncMessage messages = 1;
}

service RaySyncer {
rpc StartSync(stream RaySyncMessage) returns (stream RaySyncMessage);
rpc StartSync(stream RaySyncMessageBatch) returns (stream RaySyncMessageBatch);
}
32 changes: 22 additions & 10 deletions src/ray/ray_syncer/ray_syncer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@ namespace ray::syncer {

RaySyncer::RaySyncer(instrumented_io_context &io_context,
const std::string &local_node_id,
size_t max_batch_size,
uint64_t max_batch_delay_ms,
RpcCompletionCallback on_rpc_completion)
: io_context_(io_context),
local_node_id_(local_node_id),
node_state_(std::make_unique<NodeState>()),
timer_(PeriodicalRunner::Create(io_context)),
max_batch_size_(max_batch_size),
max_batch_delay_ms_(max_batch_delay_ms),
on_rpc_completion_(std::move(on_rpc_completion)) {
stopped_ = std::make_shared<bool>(false);
}
Expand Down Expand Up @@ -79,7 +83,7 @@ void RaySyncer::Connect(const std::string &node_id,
boost::asio::dispatch(
io_context_.get_executor(), std::packaged_task<void()>([=]() {
auto stub = ray::rpc::syncer::RaySyncer::NewStub(channel);
auto *reactor = new RayClientBidiReactor(
auto reactor = std::make_shared<RayClientBidiReactor>(
/* remote_node_id */ node_id,
/* local_node_id */ GetLocalNodeID(),
/* io_context */ io_context_,
Expand All @@ -90,7 +94,7 @@ void RaySyncer::Connect(const std::string &node_id,
const std::string &remote_node_id = bidi_reactor->GetRemoteNodeID();
auto iter = sync_reactors_.find(remote_node_id);
if (iter != sync_reactors_.end()) {
if (iter->second != bidi_reactor) {
if (iter->second.get() != bidi_reactor) {
// The client is already reconnected.
return;
}
Expand All @@ -109,14 +113,17 @@ void RaySyncer::Connect(const std::string &node_id,
node_state_->RemoveNode(remote_node_id);
}
},
/* stub */ std::move(stub));
/* stub */ std::move(stub),
/* max_batch_size */ max_batch_size_,
/* max_batch_delay_ms */ max_batch_delay_ms_);
reactor->SetSelfRef(reactor);
Connect(reactor);
reactor->StartCall();
}))
.get();
}

void RaySyncer::Connect(RaySyncerBidiReactor *reactor) {
void RaySyncer::Connect(std::shared_ptr<RaySyncerBidiReactor> reactor) {
// Bind rpc completion callback.
if (on_rpc_completion_) {
reactor->SetRpcCompletionCallbackForOnce(on_rpc_completion_);
Expand Down Expand Up @@ -217,19 +224,20 @@ void RaySyncer::BroadcastMessage(std::shared_ptr<const RaySyncMessage> message)
}

ServerBidiReactor *RaySyncerService::StartSync(grpc::CallbackServerContext *context) {
auto reactor = new RayServerBidiReactor(
auto reactor = std::make_shared<RayServerBidiReactor>(
context,
syncer_.GetIOContext(),
syncer_.GetLocalNodeID(),
/*message_processor=*/[this](auto msg) mutable { syncer_.BroadcastMessage(msg); },
/*message_processor=*/
[this](auto msg) mutable { syncer_.BroadcastMessage(msg); },
/*cleanup_cb=*/
[this](RaySyncerBidiReactor *bidi_reactor, bool reconnect) mutable {
// No need to reconnect for server side.
RAY_CHECK(!reconnect);
const auto &node_id = bidi_reactor->GetRemoteNodeID();
auto iter = syncer_.sync_reactors_.find(node_id);
if (iter != syncer_.sync_reactors_.end()) {
if (iter->second != bidi_reactor) {
if (iter->second.get() != bidi_reactor) {
// There is a new connection to the node, no need to clean up.
// This can happen when there is transient network error and the client
// reconnects. The sequence of events are:
Expand All @@ -245,22 +253,26 @@ ServerBidiReactor *RaySyncerService::StartSync(grpc::CallbackServerContext *cont
RAY_LOG(INFO).WithField(NodeID::FromBinary(node_id)) << "Connection is broken.";
syncer_.node_state_->RemoveNode(node_id);
},
/*auth_token=*/auth_token_);
/*auth_token=*/auth_token_,
/*max_batch_size=*/syncer_.max_batch_size_,
/*max_batch_delay_ms=*/syncer_.max_batch_delay_ms_);
RAY_LOG(DEBUG).WithField(NodeID::FromBinary(reactor->GetRemoteNodeID()))
<< "Get connection";

reactor->SetSelfRef(reactor);

// If the reactor has already called Finish() (e.g., due to authentication failure),
// skip registration. The reactor will clean itself up via OnDone().
if (reactor->IsFinished()) {
return reactor;
return reactor.get();
}

// Disconnect exiting connection if there is any.
// This can happen when there is transient network error
// and the client reconnects.
syncer_.Disconnect(reactor->GetRemoteNodeID());
syncer_.Connect(reactor);
return reactor;
return reactor.get();
}

} // namespace ray::syncer
18 changes: 15 additions & 3 deletions src/ray/ray_syncer/ray_syncer.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ namespace ray::syncer {
using ray::rpc::syncer::CommandsSyncMessage;
using ray::rpc::syncer::MessageType;
using ray::rpc::syncer::RaySyncMessage;
using ray::rpc::syncer::RaySyncMessageBatch;
using ray::rpc::syncer::ResourceViewSyncMessage;

/// The interface for a reporter. Reporter is defined to be a local module which would
Expand Down Expand Up @@ -93,8 +94,13 @@ class RaySyncer {
/// \param io_context The io context for this component.
/// \param node_id The id of current node.
/// \param on_rpc_completion A callback which invokes after a sync rpc succeeds.
/// \param max_batch_size The max number of messages to be sent in a batch.
/// \param max_batch_delay_ms The max delay in milliseconds to wait before sending a
/// batch.
RaySyncer(instrumented_io_context &io_context,
const std::string &node_id,
size_t max_batch_size,
uint64_t max_batch_delay_ms,
RpcCompletionCallback on_rpc_completion = {});
~RaySyncer();

Expand Down Expand Up @@ -155,7 +161,7 @@ class RaySyncer {
std::vector<std::string> GetAllConnectedNodeIDs() const;

private:
void Connect(RaySyncerBidiReactor *connection);
void Connect(std::shared_ptr<RaySyncerBidiReactor> connection);

std::shared_ptr<bool> stopped_;

Expand All @@ -169,14 +175,20 @@ class RaySyncer {
const std::string local_node_id_;

/// Manage connections. Here the key is the NodeID in binary form.
absl::flat_hash_map<std::string, RaySyncerBidiReactor *> sync_reactors_;
absl::flat_hash_map<std::string, std::shared_ptr<RaySyncerBidiReactor>> sync_reactors_;

/// The local node state
std::unique_ptr<NodeState> node_state_;

/// Timer is used to do broadcasting.
std::shared_ptr<PeriodicalRunner> timer_;

/// The max number of messages to be sent in a batch.
const size_t max_batch_size_;

/// The max delay in milliseconds to wait before sending a batch.
const uint64_t max_batch_delay_ms_;

/// Sync message observer, which is a callback on received message response for
/// [RaySyncerBidiReactor], so should be passed to each of them.
RpcCompletionCallback on_rpc_completion_;
Expand Down Expand Up @@ -204,7 +216,7 @@ class RaySyncerService : public ray::rpc::syncer::RaySyncer::CallbackService {
std::optional<ray::rpc::AuthenticationToken> auth_token = std::nullopt)
: syncer_(syncer), auth_token_(std::move(auth_token)) {}

grpc::ServerBidiReactor<RaySyncMessage, RaySyncMessage> *StartSync(
grpc::ServerBidiReactor<RaySyncMessageBatch, RaySyncMessageBatch> *StartSync(
grpc::CallbackServerContext *context) override;

private:
Expand Down
11 changes: 11 additions & 0 deletions src/ray/ray_syncer/ray_syncer_bidi_reactor.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace ray::syncer {
using ray::rpc::syncer::CommandsSyncMessage;
using ray::rpc::syncer::MessageType;
using ray::rpc::syncer::RaySyncMessage;
using ray::rpc::syncer::RaySyncMessageBatch;
using ray::rpc::syncer::ResourceViewSyncMessage;

/// This is the base class for the bidi-streaming call and defined the method
Expand Down Expand Up @@ -106,13 +107,23 @@ class RaySyncerBidiReactor {
/// Return true if it's disconnected.
std::shared_ptr<bool> IsDisconnected() const { return disconnected_; }

// Set self-owned shared_ptr used to guard lifetime for async callbacks via weak_ptr
// lock.
void SetSelfRef(std::shared_ptr<RaySyncerBidiReactor> self) {
self_ref_ = std::move(self);
}
Comment on lines +110 to +114

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use shared_from_this instead of injecting a self ref here. I will make a small follow-up PR to update this.


// Node id which is communicating with the current reactor.
std::string remote_node_id_;

protected:
/// Sync message observer, which is a callback on received message response.
RpcCompletionCallback on_rpc_completion_;

/// Self-owned shared_ptr for lifetime guarding in async callbacks (timer/dispatch). Set
/// by caller/factory, released in OnDone.
std::shared_ptr<RaySyncerBidiReactor> self_ref_;

private:
virtual void DoDisconnect() = 0;
std::shared_ptr<bool> disconnected_ = std::make_shared<bool>(false);
Expand Down
Loading