[Streaming] Streaming data transfer and python integration (#6185)

This commit is contained in:
Chaokun Yang
2019-12-10 20:33:24 +08:00
committed by Hao Chen
parent c1d4ab8bb4
commit 6272907a57
93 changed files with 8434 additions and 1480 deletions
+240
View File
@@ -0,0 +1,240 @@
#include "message.h"
namespace ray {
namespace streaming {
const uint32_t Message::MagicNum = 0xBABA0510;
std::unique_ptr<LocalMemoryBuffer> Message::ToBytes() {
uint8_t *bytes = nullptr;
std::string pboutput;
ToProtobuf(&pboutput);
int64_t fbs_length = pboutput.length();
queue::protobuf::StreamingQueueMessageType type = Type();
size_t total_len =
sizeof(Message::MagicNum) + sizeof(type) + sizeof(fbs_length) + fbs_length;
if (buffer_ != nullptr) {
total_len += buffer_->Size();
}
bytes = new uint8_t[total_len];
STREAMING_CHECK(bytes != nullptr) << "allocate bytes fail.";
uint8_t *p_cur = bytes;
memcpy(p_cur, &Message::MagicNum, sizeof(Message::MagicNum));
p_cur += sizeof(Message::MagicNum);
memcpy(p_cur, &type, sizeof(type));
p_cur += sizeof(type);
memcpy(p_cur, &fbs_length, sizeof(fbs_length));
p_cur += sizeof(fbs_length);
uint8_t *fbs_bytes = (uint8_t *)pboutput.data();
memcpy(p_cur, fbs_bytes, fbs_length);
p_cur += fbs_length;
if (buffer_ != nullptr) {
memcpy(p_cur, buffer_->Data(), buffer_->Size());
}
// COPY
std::unique_ptr<LocalMemoryBuffer> buffer =
std::unique_ptr<LocalMemoryBuffer>(new LocalMemoryBuffer(bytes, total_len, true));
delete bytes;
return buffer;
}
void DataMessage::ToProtobuf(std::string *output) {
queue::protobuf::StreamingQueueDataMsg msg;
msg.set_src_actor_id(actor_id_.Binary());
msg.set_dst_actor_id(peer_actor_id_.Binary());
msg.set_queue_id(queue_id_.Binary());
msg.set_seq_id(seq_id_);
msg.set_length(buffer_->Size());
msg.set_raw(raw_);
msg.SerializeToString(output);
}
std::shared_ptr<DataMessage> DataMessage::FromBytes(uint8_t *bytes) {
bytes += sizeof(uint32_t) + sizeof(queue::protobuf::StreamingQueueMessageType);
uint64_t *fbs_length = (uint64_t *)bytes;
bytes += sizeof(uint64_t);
std::string inputpb(reinterpret_cast<char const *>(bytes), *fbs_length);
queue::protobuf::StreamingQueueDataMsg message;
message.ParseFromString(inputpb);
ActorID src_actor_id = ActorID::FromBinary(message.src_actor_id());
ActorID dst_actor_id = ActorID::FromBinary(message.dst_actor_id());
ObjectID queue_id = ObjectID::FromBinary(message.queue_id());
uint64_t seq_id = message.seq_id();
uint64_t length = message.length();
bool raw = message.raw();
bytes += *fbs_length;
/// Copy data and create a new buffer for streaming queue.
std::shared_ptr<LocalMemoryBuffer> buffer =
std::make_shared<LocalMemoryBuffer>(bytes, (size_t)length, true);
std::shared_ptr<DataMessage> data_msg = std::make_shared<DataMessage>(
src_actor_id, dst_actor_id, queue_id, seq_id, buffer, raw);
return data_msg;
}
void NotificationMessage::ToProtobuf(std::string *output) {
queue::protobuf::StreamingQueueNotificationMsg msg;
msg.set_src_actor_id(actor_id_.Binary());
msg.set_dst_actor_id(peer_actor_id_.Binary());
msg.set_queue_id(queue_id_.Binary());
msg.set_seq_id(seq_id_);
msg.SerializeToString(output);
}
std::shared_ptr<NotificationMessage> NotificationMessage::FromBytes(uint8_t *bytes) {
bytes += sizeof(uint32_t) + sizeof(queue::protobuf::StreamingQueueMessageType);
uint64_t *length = (uint64_t *)bytes;
bytes += sizeof(uint64_t);
std::string inputpb(reinterpret_cast<char const *>(bytes), *length);
queue::protobuf::StreamingQueueNotificationMsg message;
message.ParseFromString(inputpb);
STREAMING_LOG(INFO) << "message.src_actor_id: " << message.src_actor_id();
ActorID src_actor_id = ActorID::FromBinary(message.src_actor_id());
ActorID dst_actor_id = ActorID::FromBinary(message.dst_actor_id());
ObjectID queue_id = ObjectID::FromBinary(message.queue_id());
uint64_t seq_id = message.seq_id();
std::shared_ptr<NotificationMessage> notify_msg =
std::make_shared<NotificationMessage>(src_actor_id, dst_actor_id, queue_id, seq_id);
return notify_msg;
}
void CheckMessage::ToProtobuf(std::string *output) {
queue::protobuf::StreamingQueueCheckMsg msg;
msg.set_src_actor_id(actor_id_.Binary());
msg.set_dst_actor_id(peer_actor_id_.Binary());
msg.set_queue_id(queue_id_.Binary());
msg.SerializeToString(output);
}
std::shared_ptr<CheckMessage> CheckMessage::FromBytes(uint8_t *bytes) {
bytes += sizeof(uint32_t) + sizeof(queue::protobuf::StreamingQueueMessageType);
uint64_t *length = (uint64_t *)bytes;
bytes += sizeof(uint64_t);
std::string inputpb(reinterpret_cast<char const *>(bytes), *length);
queue::protobuf::StreamingQueueCheckMsg message;
message.ParseFromString(inputpb);
ActorID src_actor_id = ActorID::FromBinary(message.src_actor_id());
ActorID dst_actor_id = ActorID::FromBinary(message.dst_actor_id());
ObjectID queue_id = ObjectID::FromBinary(message.queue_id());
std::shared_ptr<CheckMessage> check_msg =
std::make_shared<CheckMessage>(src_actor_id, dst_actor_id, queue_id);
return check_msg;
}
void CheckRspMessage::ToProtobuf(std::string *output) {
queue::protobuf::StreamingQueueCheckRspMsg msg;
msg.set_src_actor_id(actor_id_.Binary());
msg.set_dst_actor_id(peer_actor_id_.Binary());
msg.set_queue_id(queue_id_.Binary());
msg.set_err_code(err_code_);
msg.SerializeToString(output);
}
std::shared_ptr<CheckRspMessage> CheckRspMessage::FromBytes(uint8_t *bytes) {
bytes += sizeof(uint32_t) + sizeof(queue::protobuf::StreamingQueueMessageType);
uint64_t *length = (uint64_t *)bytes;
bytes += sizeof(uint64_t);
std::string inputpb(reinterpret_cast<char const *>(bytes), *length);
queue::protobuf::StreamingQueueCheckRspMsg message;
message.ParseFromString(inputpb);
ActorID src_actor_id = ActorID::FromBinary(message.src_actor_id());
ActorID dst_actor_id = ActorID::FromBinary(message.dst_actor_id());
ObjectID queue_id = ObjectID::FromBinary(message.queue_id());
queue::protobuf::StreamingQueueError err_code = message.err_code();
std::shared_ptr<CheckRspMessage> check_rsp_msg =
std::make_shared<CheckRspMessage>(src_actor_id, dst_actor_id, queue_id, err_code);
return check_rsp_msg;
}
void TestInitMessage::ToProtobuf(std::string *output) {
queue::protobuf::StreamingQueueTestInitMsg msg;
msg.set_role(role_);
msg.set_src_actor_id(actor_id_.Binary());
msg.set_dst_actor_id(peer_actor_id_.Binary());
msg.set_actor_handle(actor_handle_serialized_);
for (auto &queue_id : queue_ids_) {
msg.add_queue_ids(queue_id.Binary());
}
for (auto &queue_id : rescale_queue_ids_) {
msg.add_rescale_queue_ids(queue_id.Binary());
}
msg.set_test_suite_name(test_suite_name_);
msg.set_test_name(test_name_);
msg.set_param(param_);
msg.SerializeToString(output);
}
std::shared_ptr<TestInitMessage> TestInitMessage::FromBytes(uint8_t *bytes) {
bytes += sizeof(uint32_t) + sizeof(queue::protobuf::StreamingQueueMessageType);
uint64_t *length = (uint64_t *)bytes;
bytes += sizeof(uint64_t);
std::string inputpb(reinterpret_cast<char const *>(bytes), *length);
queue::protobuf::StreamingQueueTestInitMsg message;
message.ParseFromString(inputpb);
queue::protobuf::StreamingQueueTestRole role = message.role();
ActorID src_actor_id = ActorID::FromBinary(message.src_actor_id());
ActorID dst_actor_id = ActorID::FromBinary(message.dst_actor_id());
std::string actor_handle_serialized = message.actor_handle();
std::vector<ObjectID> queue_ids;
for (int i = 0; i < message.queue_ids_size(); i++) {
queue_ids.push_back(ObjectID::FromBinary(message.queue_ids(i)));
}
std::vector<ObjectID> rescale_queue_ids;
for (int i = 0; i < message.rescale_queue_ids_size(); i++) {
rescale_queue_ids.push_back(ObjectID::FromBinary(message.rescale_queue_ids(i)));
}
std::string test_suite_name = message.test_suite_name();
std::string test_name = message.test_name();
uint64_t param = message.param();
std::shared_ptr<TestInitMessage> test_init_msg = std::make_shared<TestInitMessage>(
role, src_actor_id, dst_actor_id, actor_handle_serialized, queue_ids,
rescale_queue_ids, test_suite_name, test_name, param);
return test_init_msg;
}
void TestCheckStatusRspMsg::ToProtobuf(std::string *output) {
queue::protobuf::StreamingQueueTestCheckStatusRspMsg msg;
msg.set_test_name(test_name_);
msg.set_status(status_);
msg.SerializeToString(output);
}
std::shared_ptr<TestCheckStatusRspMsg> TestCheckStatusRspMsg::FromBytes(uint8_t *bytes) {
bytes += sizeof(uint32_t) + sizeof(queue::protobuf::StreamingQueueMessageType);
uint64_t *length = (uint64_t *)bytes;
bytes += sizeof(uint64_t);
std::string inputpb(reinterpret_cast<char const *>(bytes), *length);
queue::protobuf::StreamingQueueTestCheckStatusRspMsg message;
message.ParseFromString(inputpb);
std::string test_name = message.test_name();
bool status = message.status();
std::shared_ptr<TestCheckStatusRspMsg> test_check_msg =
std::make_shared<TestCheckStatusRspMsg>(test_name, status);
return test_check_msg;
}
} // namespace streaming
} // namespace ray
+235
View File
@@ -0,0 +1,235 @@
#ifndef _STREAMING_QUEUE_MESSAGE_H_
#define _STREAMING_QUEUE_MESSAGE_H_
#include "protobuf/streaming_queue.pb.h"
#include "ray/common/buffer.h"
#include "ray/common/id.h"
#include "util/streaming_logging.h"
namespace ray {
namespace streaming {
/// Base class of all message classes.
/// All payloads transferred through direct actor call are packed into a unified package,
/// consisting of protobuf-formatted metadata and data, including data and control
/// messages. These message classes wrap the package defined in
/// protobuf/streaming_queue.proto respectively.
class Message {
public:
/// Construct a Message instance.
/// \param[in] actor_id ActorID of message sender.
/// \param[in] peer_actor_id ActorID of message receiver.
/// \param[in] queue_id queue id to identify which queue the message is sent to.
/// \param[in] buffer an optional param, a chunk of data to send.
Message(const ActorID &actor_id, const ActorID &peer_actor_id, const ObjectID &queue_id,
std::shared_ptr<LocalMemoryBuffer> buffer = nullptr)
: actor_id_(actor_id),
peer_actor_id_(peer_actor_id),
queue_id_(queue_id),
buffer_(buffer) {}
Message() {}
virtual ~Message() {}
ActorID ActorId() { return actor_id_; }
ActorID PeerActorId() { return peer_actor_id_; }
ObjectID QueueId() { return queue_id_; }
std::shared_ptr<LocalMemoryBuffer> Buffer() { return buffer_; }
/// Serialize all meta data and data to a LocalMemoryBuffer, which can be sent through
/// direct actor call. \return serialized buffer .
std::unique_ptr<LocalMemoryBuffer> ToBytes();
/// Get message type.
/// \return message type.
virtual queue::protobuf::StreamingQueueMessageType Type() = 0;
/// All subclasses should implement `ToProtobuf` to serialize its own protobuf data.
virtual void ToProtobuf(std::string *output) = 0;
protected:
ActorID actor_id_;
ActorID peer_actor_id_;
ObjectID queue_id_;
std::shared_ptr<LocalMemoryBuffer> buffer_;
public:
/// A magic number to identify a valid message.
static const uint32_t MagicNum;
};
/// Wrap StreamingQueueDataMsg in streaming_queue.proto.
/// DataMessage encapsulates the memory buffer of QueueItem, a one-to-one relationship
/// exists between DataMessage and QueueItem.
class DataMessage : public Message {
public:
DataMessage(const ActorID &actor_id, const ActorID &peer_actor_id, ObjectID queue_id,
uint64_t seq_id, std::shared_ptr<LocalMemoryBuffer> buffer, bool raw)
: Message(actor_id, peer_actor_id, queue_id, buffer), seq_id_(seq_id), raw_(raw) {}
virtual ~DataMessage() {}
static std::shared_ptr<DataMessage> FromBytes(uint8_t *bytes);
virtual void ToProtobuf(std::string *output);
uint64_t SeqId() { return seq_id_; }
bool IsRaw() { return raw_; }
queue::protobuf::StreamingQueueMessageType Type() { return type_; }
private:
uint64_t seq_id_;
bool raw_;
const queue::protobuf::StreamingQueueMessageType type_ =
queue::protobuf::StreamingQueueMessageType::StreamingQueueDataMsgType;
};
/// Wrap StreamingQueueNotificationMsg in streaming_queue.proto.
/// NotificationMessage, downstream queues sends to upstream queues, for the data reader
/// to inform the data writer of the consumed offset.
class NotificationMessage : public Message {
public:
NotificationMessage(const ActorID &actor_id, const ActorID &peer_actor_id,
const ObjectID &queue_id, uint64_t seq_id)
: Message(actor_id, peer_actor_id, queue_id), seq_id_(seq_id) {}
virtual ~NotificationMessage() {}
static std::shared_ptr<NotificationMessage> FromBytes(uint8_t *bytes);
virtual void ToProtobuf(std::string *output);
uint64_t SeqId() { return seq_id_; }
queue::protobuf::StreamingQueueMessageType Type() { return type_; }
private:
uint64_t seq_id_;
const queue::protobuf::StreamingQueueMessageType type_ =
queue::protobuf::StreamingQueueMessageType::StreamingQueueNotificationMsgType;
};
/// Wrap StreamingQueueCheckMsg in streaming_queue.proto.
/// CheckMessage, upstream queues sends to downstream queues, fot the data writer to check
/// whether the corresponded downstream queue is read or not.
class CheckMessage : public Message {
public:
CheckMessage(const ActorID &actor_id, const ActorID &peer_actor_id,
const ObjectID &queue_id)
: Message(actor_id, peer_actor_id, queue_id) {}
virtual ~CheckMessage() {}
static std::shared_ptr<CheckMessage> FromBytes(uint8_t *bytes);
virtual void ToProtobuf(std::string *output);
queue::protobuf::StreamingQueueMessageType Type() { return type_; }
private:
const queue::protobuf::StreamingQueueMessageType type_ =
queue::protobuf::StreamingQueueMessageType::StreamingQueueCheckMsgType;
};
/// Wrap StreamingQueueCheckRspMsg in streaming_queue.proto.
/// CheckRspMessage, downstream queues sends to upstream queues, the response message to
/// CheckMessage to indicate whether downstream queue is ready or not.
class CheckRspMessage : public Message {
public:
CheckRspMessage(const ActorID &actor_id, const ActorID &peer_actor_id,
const ObjectID &queue_id, queue::protobuf::StreamingQueueError err_code)
: Message(actor_id, peer_actor_id, queue_id), err_code_(err_code) {}
virtual ~CheckRspMessage() {}
static std::shared_ptr<CheckRspMessage> FromBytes(uint8_t *bytes);
virtual void ToProtobuf(std::string *output);
queue::protobuf::StreamingQueueMessageType Type() { return type_; }
queue::protobuf::StreamingQueueError Error() { return err_code_; }
private:
queue::protobuf::StreamingQueueError err_code_;
const queue::protobuf::StreamingQueueMessageType type_ =
queue::protobuf::StreamingQueueMessageType::StreamingQueueCheckRspMsgType;
};
/// Wrap StreamingQueueTestInitMsg in streaming_queue.proto.
/// TestInitMessage, used for test, driver sends to test workers to init test suite.
class TestInitMessage : public Message {
public:
TestInitMessage(const queue::protobuf::StreamingQueueTestRole role,
const ActorID &actor_id, const ActorID &peer_actor_id,
const std::string actor_handle_serialized,
const std::vector<ObjectID> &queue_ids,
const std::vector<ObjectID> &rescale_queue_ids,
std::string test_suite_name, std::string test_name, uint64_t param)
: Message(actor_id, peer_actor_id, queue_ids[0]),
actor_handle_serialized_(actor_handle_serialized),
queue_ids_(queue_ids),
rescale_queue_ids_(rescale_queue_ids),
role_(role),
test_suite_name_(test_suite_name),
test_name_(test_name),
param_(param) {}
virtual ~TestInitMessage() {}
static std::shared_ptr<TestInitMessage> FromBytes(uint8_t *bytes);
virtual void ToProtobuf(std::string *output);
queue::protobuf::StreamingQueueMessageType Type() { return type_; }
std::string ActorHandleSerialized() { return actor_handle_serialized_; }
queue::protobuf::StreamingQueueTestRole Role() { return role_; }
std::vector<ObjectID> QueueIds() { return queue_ids_; }
std::vector<ObjectID> RescaleQueueIds() { return rescale_queue_ids_; }
std::string TestSuiteName() { return test_suite_name_; }
std::string TestName() { return test_name_; }
uint64_t Param() { return param_; }
std::string ToString() {
std::ostringstream os;
os << "actor_handle_serialized: " << actor_handle_serialized_;
os << " actor_id: " << ActorId();
os << " peer_actor_id: " << PeerActorId();
os << " queue_ids:[";
for (auto &qid : queue_ids_) {
os << qid << ",";
}
os << "], rescale_queue_ids:[";
for (auto &qid : rescale_queue_ids_) {
os << qid << ",";
}
os << "],";
os << " role:" << queue::protobuf::StreamingQueueTestRole_Name(role_);
os << " suite_name: " << test_suite_name_;
os << " test_name: " << test_name_;
os << " param: " << param_;
return os.str();
}
private:
const queue::protobuf::StreamingQueueMessageType type_ =
queue::protobuf::StreamingQueueMessageType::StreamingQueueTestInitMsgType;
std::string actor_handle_serialized_;
std::vector<ObjectID> queue_ids_;
std::vector<ObjectID> rescale_queue_ids_;
queue::protobuf::StreamingQueueTestRole role_;
std::string test_suite_name_;
std::string test_name_;
uint64_t param_;
};
/// Wrap StreamingQueueTestCheckStatusRspMsg in streaming_queue.proto.
/// TestCheckStatusRspMsg, used for test, driver sends to test workers to check
/// whether test has completed or failed.
class TestCheckStatusRspMsg : public Message {
public:
TestCheckStatusRspMsg(const std::string test_name, bool status)
: test_name_(test_name), status_(status) {}
virtual ~TestCheckStatusRspMsg() {}
static std::shared_ptr<TestCheckStatusRspMsg> FromBytes(uint8_t *bytes);
virtual void ToProtobuf(std::string *output);
queue::protobuf::StreamingQueueMessageType Type() { return type_; }
std::string TestName() { return test_name_; }
bool Status() { return status_; }
private:
const queue::protobuf::StreamingQueueMessageType type_ =
queue::protobuf::StreamingQueueMessageType::StreamingQueueTestCheckStatusRspMsgType;
std::string test_name_;
bool status_;
};
} // namespace streaming
} // namespace ray
#endif
+211
View File
@@ -0,0 +1,211 @@
#include "queue.h"
#include <chrono>
#include <thread>
#include "queue_handler.h"
#include "util/streaming_util.h"
namespace ray {
namespace streaming {
bool Queue::Push(QueueItem item) {
std::unique_lock<std::mutex> lock(mutex_);
if (max_data_size_ < item.DataSize() + data_size_) return false;
buffer_queue_.push_back(item);
data_size_ += item.DataSize();
readable_cv_.notify_one();
return true;
}
QueueItem Queue::FrontProcessed() {
std::unique_lock<std::mutex> lock(mutex_);
STREAMING_CHECK(buffer_queue_.size() != 0) << "WriterQueue Pop fail";
if (watershed_iter_ == buffer_queue_.begin()) {
return InvalidQueueItem();
}
QueueItem item = buffer_queue_.front();
return item;
}
QueueItem Queue::PopProcessed() {
std::unique_lock<std::mutex> lock(mutex_);
STREAMING_CHECK(buffer_queue_.size() != 0) << "WriterQueue Pop fail";
if (watershed_iter_ == buffer_queue_.begin()) {
return InvalidQueueItem();
}
QueueItem item = buffer_queue_.front();
buffer_queue_.pop_front();
data_size_ -= item.DataSize();
data_size_sent_ -= item.DataSize();
return item;
}
QueueItem Queue::PopPending() {
std::unique_lock<std::mutex> lock(mutex_);
auto it = std::next(watershed_iter_);
QueueItem item = *it;
data_size_sent_ += it->DataSize();
buffer_queue_.splice(watershed_iter_, buffer_queue_, it, std::next(it));
return item;
}
QueueItem Queue::PopPendingBlockTimeout(uint64_t timeout_us) {
std::unique_lock<std::mutex> lock(mutex_);
std::chrono::system_clock::time_point point =
std::chrono::system_clock::now() + std::chrono::microseconds(timeout_us);
if (readable_cv_.wait_until(lock, point, [this] {
return std::next(watershed_iter_) != buffer_queue_.end();
})) {
auto it = std::next(watershed_iter_);
QueueItem item = *it;
data_size_sent_ += it->DataSize();
buffer_queue_.splice(watershed_iter_, buffer_queue_, it, std::next(it));
return item;
} else {
uint8_t data[1];
return QueueItem(QUEUE_INVALID_SEQ_ID, data, 1, 0, true);
}
}
QueueItem Queue::BackPending() {
std::unique_lock<std::mutex> lock(mutex_);
if (std::next(watershed_iter_) == buffer_queue_.end()) {
uint8_t data[1];
return QueueItem(QUEUE_INVALID_SEQ_ID, data, 1, 0, true);
}
return buffer_queue_.back();
}
bool Queue::IsPendingEmpty() {
std::unique_lock<std::mutex> lock(mutex_);
return std::next(watershed_iter_) == buffer_queue_.end();
}
bool Queue::IsPendingFull(uint64_t data_size) {
std::unique_lock<std::mutex> lock(mutex_);
return max_data_size_ < data_size + data_size_;
}
size_t Queue::ProcessedCount() {
std::unique_lock<std::mutex> lock(mutex_);
if (watershed_iter_ == buffer_queue_.begin()) return 0;
auto begin = buffer_queue_.begin();
auto end = std::prev(watershed_iter_);
return end->SeqId() + 1 - begin->SeqId();
}
size_t Queue::PendingCount() {
std::unique_lock<std::mutex> lock(mutex_);
if (std::next(watershed_iter_) == buffer_queue_.end()) return 0;
auto begin = std::next(watershed_iter_);
auto end = std::prev(buffer_queue_.end());
return begin->SeqId() - end->SeqId() + 1;
}
Status WriterQueue::Push(uint64_t seq_id, uint8_t *data, uint32_t data_size,
uint64_t timestamp, bool raw) {
if (IsPendingFull(data_size)) {
return Status::OutOfMemory("Queue Push OutOfMemory");
}
while (is_pulling_) {
STREAMING_LOG(INFO) << "This queue is sending pull data, wait.";
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
QueueItem item(seq_id, data, data_size, timestamp, raw);
Queue::Push(item);
return Status::OK();
}
void WriterQueue::Send() {
while (!IsPendingEmpty()) {
// FIXME: front -> send -> pop
QueueItem item = PopPending();
DataMessage msg(actor_id_, peer_actor_id_, queue_id_, item.SeqId(), item.Buffer(),
item.IsRaw());
std::unique_ptr<LocalMemoryBuffer> buffer = msg.ToBytes();
STREAMING_CHECK(transport_ != nullptr);
transport_->Send(std::move(buffer),
DownstreamQueueMessageHandler::peer_async_function_);
}
}
Status WriterQueue::TryEvictItems() {
STREAMING_LOG(INFO) << "TryEvictItems";
QueueItem item = FrontProcessed();
uint64_t first_seq_id = item.SeqId();
STREAMING_LOG(INFO) << "TryEvictItems first_seq_id: " << first_seq_id
<< " min_consumed_id_: " << min_consumed_id_
<< " eviction_limit_: " << eviction_limit_;
if (min_consumed_id_ == QUEUE_INVALID_SEQ_ID || first_seq_id > min_consumed_id_) {
return Status::OutOfMemory("The queue is full and some reader doesn't consume");
}
if (eviction_limit_ == QUEUE_INVALID_SEQ_ID || first_seq_id > eviction_limit_) {
return Status::OutOfMemory("The queue is full and eviction limit block evict");
}
uint64_t evict_target_seq_id = std::min(min_consumed_id_, eviction_limit_);
while (item.SeqId() <= evict_target_seq_id) {
PopProcessed();
STREAMING_LOG(INFO) << "TryEvictItems directly " << item.SeqId();
item = FrontProcessed();
}
return Status::OK();
}
void WriterQueue::OnNotify(std::shared_ptr<NotificationMessage> notify_msg) {
STREAMING_LOG(INFO) << "OnNotify target seq_id: " << notify_msg->SeqId();
min_consumed_id_ = notify_msg->SeqId();
}
void ReaderQueue::OnConsumed(uint64_t seq_id) {
STREAMING_LOG(INFO) << "OnConsumed: " << seq_id;
QueueItem item = FrontProcessed();
while (item.SeqId() <= seq_id) {
PopProcessed();
item = FrontProcessed();
}
Notify(seq_id);
}
void ReaderQueue::Notify(uint64_t seq_id) {
std::vector<TaskArg> task_args;
CreateNotifyTask(seq_id, task_args);
// SubmitActorTask
NotificationMessage msg(actor_id_, peer_actor_id_, queue_id_, seq_id);
std::unique_ptr<LocalMemoryBuffer> buffer = msg.ToBytes();
transport_->Send(std::move(buffer), UpstreamQueueMessageHandler::peer_async_function_);
}
void ReaderQueue::CreateNotifyTask(uint64_t seq_id, std::vector<TaskArg> &task_args) {}
void ReaderQueue::OnData(QueueItem &item) {
if (item.SeqId() != expect_seq_id_) {
STREAMING_LOG(WARNING) << "OnData ignore seq_id: " << item.SeqId()
<< " expect_seq_id_: " << expect_seq_id_;
return;
}
last_recv_seq_id_ = item.SeqId();
STREAMING_LOG(DEBUG) << "ReaderQueue::OnData seq_id: " << last_recv_seq_id_;
Push(item);
expect_seq_id_++;
}
} // namespace streaming
} // namespace ray
+213
View File
@@ -0,0 +1,213 @@
#ifndef _STREAMING_QUEUE_H_
#define _STREAMING_QUEUE_H_
#include <iterator>
#include <list>
#include <vector>
#include "ray/common/id.h"
#include "ray/util/util.h"
#include "queue_item.h"
#include "transport.h"
#include "util/streaming_logging.h"
#include "utils.h"
namespace ray {
namespace streaming {
using ray::ObjectID;
enum QueueType { UPSTREAM = 0, DOWNSTREAM };
/// A queue-like data structure, which does not delete its items after poped.
/// The lifecycle of each item is:
/// - Pending, an item is pushed into a queue, but has not been processed (sent out or
/// consumed),
/// - Processed, has been handled by the user, but should not be deleted.
/// - Evicted, useless to the user, should be poped and destroyed.
/// At present, this data structure is implemented with one std::list,
/// using a watershed iterator to divided.
class Queue {
public:
/// \param[in] queue_id the unique identification of a pair of queues (upstream and
/// downstream). \param[in] size max size of the queue in bytes. \param[in] transport
/// transport to send items to peer.
Queue(ObjectID queue_id, uint64_t size, std::shared_ptr<Transport> transport)
: queue_id_(queue_id), max_data_size_(size), data_size_(0), data_size_sent_(0) {
buffer_queue_.push_back(InvalidQueueItem());
watershed_iter_ = buffer_queue_.begin();
}
virtual ~Queue() {}
/// Push an item into the queue.
/// \param[in] item the QueueItem object to be send to peer.
/// \return false if the queue is full.
bool Push(QueueItem item);
/// Get the front of item which in processed state.
QueueItem FrontProcessed();
/// Pop the front of item which in processed state.
QueueItem PopProcessed();
/// Pop the front of item which in pending state, the item
/// will not be evicted at this moment, its state turn to
/// processed.
QueueItem PopPending();
/// PopPending with timeout in microseconds.
QueueItem PopPendingBlockTimeout(uint64_t timeout_us);
/// Return the last item in pending state.
QueueItem BackPending();
bool IsPendingEmpty();
bool IsPendingFull(uint64_t data_size = 0);
/// Return the size in bytes of all items in queue.
uint64_t QueueSize() { return data_size_; }
/// Return the size in bytes of all items in pending state.
uint64_t PendingDataSize() { return data_size_ - data_size_sent_; }
/// Return the size in bytes of all items in processed state.
uint64_t ProcessedDataSize() { return data_size_sent_; }
/// Return item count of the queue.
size_t Count() { return buffer_queue_.size(); }
/// Return item count in pending state.
size_t PendingCount();
/// Return item count in processed state.
size_t ProcessedCount();
protected:
ObjectID queue_id_;
std::list<QueueItem> buffer_queue_;
std::list<QueueItem>::iterator watershed_iter_;
/// max data size in bytes
uint64_t max_data_size_;
uint64_t data_size_;
uint64_t data_size_sent_;
std::mutex mutex_;
std::condition_variable readable_cv_;
};
/// Queue in upstream.
class WriterQueue : public Queue {
public:
/// \param queue_id, the unique ObjectID to identify a queue
/// \param actor_id, the actor id of upstream worker
/// \param peer_actor_id, the actor id of downstream worker
/// \param size, max data size in bytes
/// \param transport, transport
WriterQueue(const ObjectID &queue_id, const ActorID &actor_id,
const ActorID &peer_actor_id, uint64_t size,
std::shared_ptr<Transport> transport)
: Queue(queue_id, size, transport),
actor_id_(actor_id),
peer_actor_id_(peer_actor_id),
eviction_limit_(QUEUE_INVALID_SEQ_ID),
min_consumed_id_(QUEUE_INVALID_SEQ_ID),
peer_last_msg_id_(0),
peer_last_seq_id_(QUEUE_INVALID_SEQ_ID),
transport_(transport),
is_pulling_(false) {}
/// Push a continuous buffer into queue.
/// NOTE: the buffer should be copied.
Status Push(uint64_t seq_id, uint8_t *data, uint32_t data_size, uint64_t timestamp,
bool raw = false);
/// Callback function, will be called when downstream queue notifies
/// it has consumed some items.
/// NOTE: this callback function is called in queue thread.
void OnNotify(std::shared_ptr<NotificationMessage> notify_msg);
/// Send items through direct call.
void Send();
/// Called when user pushs item into queue. The count of items
/// can be evicted, determined by eviction_limit_ and min_consumed_id_.
Status TryEvictItems();
void SetQueueEvictionLimit(uint64_t eviction_limit) {
eviction_limit_ = eviction_limit;
}
uint64_t EvictionLimit() { return eviction_limit_; }
uint64_t GetMinConsumedSeqID() { return min_consumed_id_; }
void SetPeerLastIds(uint64_t msg_id, uint64_t seq_id) {
peer_last_msg_id_ = msg_id;
peer_last_seq_id_ = seq_id;
}
uint64_t GetPeerLastMsgId() { return peer_last_msg_id_; }
uint64_t GetPeerLastSeqId() { return peer_last_seq_id_; }
private:
ActorID actor_id_;
ActorID peer_actor_id_;
uint64_t eviction_limit_;
uint64_t min_consumed_id_;
uint64_t peer_last_msg_id_;
uint64_t peer_last_seq_id_;
std::shared_ptr<Transport> transport_;
std::atomic<bool> is_pulling_;
};
/// Queue in downstream.
class ReaderQueue : public Queue {
public:
/// \param queue_id, the unique ObjectID to identify a queue
/// \param actor_id, the actor id of upstream worker
/// \param peer_actor_id, the actor id of downstream worker
/// \param transport, transport
/// NOTE: we do not restrict queue size of ReaderQueue
ReaderQueue(const ObjectID &queue_id, const ActorID &actor_id,
const ActorID &peer_actor_id, std::shared_ptr<Transport> transport)
: Queue(queue_id, std::numeric_limits<uint64_t>::max(), transport),
actor_id_(actor_id),
peer_actor_id_(peer_actor_id),
min_consumed_id_(QUEUE_INVALID_SEQ_ID),
last_recv_seq_id_(QUEUE_INVALID_SEQ_ID),
expect_seq_id_(1),
transport_(transport) {}
/// Delete processed items whose seq id <= seq_id,
/// then notify upstream queue.
void OnConsumed(uint64_t seq_id);
void OnData(QueueItem &item);
uint64_t GetMinConsumedSeqID() { return min_consumed_id_; }
uint64_t GetLastRecvSeqId() { return last_recv_seq_id_; }
void SetExpectSeqId(uint64_t expect) { expect_seq_id_ = expect; }
private:
void Notify(uint64_t seq_id);
void CreateNotifyTask(uint64_t seq_id, std::vector<TaskArg> &task_args);
private:
ActorID actor_id_;
ActorID peer_actor_id_;
uint64_t min_consumed_id_;
uint64_t last_recv_seq_id_;
uint64_t expect_seq_id_;
std::shared_ptr<PromiseWrapper> promise_for_pull_;
std::shared_ptr<Transport> transport_;
};
} // namespace streaming
} // namespace ray
#endif
+25
View File
@@ -0,0 +1,25 @@
#include "queue_client.h"
namespace ray {
namespace streaming {
void WriterClient::OnWriterMessage(std::shared_ptr<LocalMemoryBuffer> buffer) {
upstream_handler_->DispatchMessageAsync(buffer);
}
std::shared_ptr<LocalMemoryBuffer> WriterClient::OnWriterMessageSync(
std::shared_ptr<LocalMemoryBuffer> buffer) {
return upstream_handler_->DispatchMessageSync(buffer);
}
void ReaderClient::OnReaderMessage(std::shared_ptr<LocalMemoryBuffer> buffer) {
downstream_handler_->DispatchMessageAsync(buffer);
}
std::shared_ptr<LocalMemoryBuffer> ReaderClient::OnReaderMessageSync(
std::shared_ptr<LocalMemoryBuffer> buffer) {
return downstream_handler_->DispatchMessageSync(buffer);
}
} // namespace streaming
} // namespace ray
+62
View File
@@ -0,0 +1,62 @@
#ifndef _STREAMING_QUEUE_CLIENT_H_
#define _STREAMING_QUEUE_CLIENT_H_
#include "queue_handler.h"
#include "transport.h"
namespace ray {
namespace streaming {
/// The interface of the streaming queue for DataReader.
/// A ReaderClient should be created before DataReader created in Cython/Jni, and hold by
/// Jobworker. When DataReader receive a buffer from upstream DataWriter (DataReader's
/// raycall function is called), it calls `OnReaderMessage` to pass the buffer to its own
/// downstream queue, or `OnReaderMessageSync` to wait for handle result.
class ReaderClient {
public:
/// Construct a ReaderClient object.
/// \param[in] core_worker CoreWorker C++ pointer of current actor
/// \param[in] async_func DataReader's raycall function descriptor to be called by
/// DataWriter, asynchronous semantics \param[in] sync_func DataReader's raycall
/// function descriptor to be called by DataWriter, synchronous semantics
ReaderClient(CoreWorker *core_worker, RayFunction &async_func, RayFunction &sync_func)
: core_worker_(core_worker) {
DownstreamQueueMessageHandler::peer_async_function_ = async_func;
DownstreamQueueMessageHandler::peer_sync_function_ = sync_func;
downstream_handler_ = ray::streaming::DownstreamQueueMessageHandler::CreateService(
core_worker_, core_worker_->GetWorkerContext().GetCurrentActorID());
}
/// Post buffer to downstream queue service, asynchronously.
void OnReaderMessage(std::shared_ptr<LocalMemoryBuffer> buffer);
/// Post buffer to downstream queue service, synchronously.
/// \return handle result.
std::shared_ptr<LocalMemoryBuffer> OnReaderMessageSync(
std::shared_ptr<LocalMemoryBuffer> buffer);
private:
CoreWorker *core_worker_;
std::shared_ptr<DownstreamQueueMessageHandler> downstream_handler_;
};
/// Interface of streaming queue for DataWriter. Similar to ReaderClient.
class WriterClient {
public:
WriterClient(CoreWorker *core_worker, RayFunction &async_func, RayFunction &sync_func)
: core_worker_(core_worker) {
UpstreamQueueMessageHandler::peer_async_function_ = async_func;
UpstreamQueueMessageHandler::peer_sync_function_ = sync_func;
upstream_handler_ = ray::streaming::UpstreamQueueMessageHandler::CreateService(
core_worker, core_worker_->GetWorkerContext().GetCurrentActorID());
}
void OnWriterMessage(std::shared_ptr<LocalMemoryBuffer> buffer);
std::shared_ptr<LocalMemoryBuffer> OnWriterMessageSync(
std::shared_ptr<LocalMemoryBuffer> buffer);
private:
CoreWorker *core_worker_;
std::shared_ptr<UpstreamQueueMessageHandler> upstream_handler_;
};
} // namespace streaming
} // namespace ray
#endif
+358
View File
@@ -0,0 +1,358 @@
#include "queue_handler.h"
#include "util/streaming_util.h"
#include "utils.h"
namespace ray {
namespace streaming {
constexpr uint64_t COMMON_SYNC_CALL_TIMEOUTT_MS = 5 * 1000;
std::shared_ptr<UpstreamQueueMessageHandler>
UpstreamQueueMessageHandler::upstream_handler_ = nullptr;
std::shared_ptr<DownstreamQueueMessageHandler>
DownstreamQueueMessageHandler::downstream_handler_ = nullptr;
RayFunction UpstreamQueueMessageHandler::peer_sync_function_;
RayFunction UpstreamQueueMessageHandler::peer_async_function_;
RayFunction DownstreamQueueMessageHandler::peer_sync_function_;
RayFunction DownstreamQueueMessageHandler::peer_async_function_;
std::shared_ptr<Message> QueueMessageHandler::ParseMessage(
std::shared_ptr<LocalMemoryBuffer> buffer) {
uint8_t *bytes = buffer->Data();
uint8_t *p_cur = bytes;
uint32_t *magic_num = (uint32_t *)p_cur;
STREAMING_CHECK(*magic_num == Message::MagicNum)
<< *magic_num << " " << Message::MagicNum;
p_cur += sizeof(Message::MagicNum);
queue::protobuf::StreamingQueueMessageType *type =
(queue::protobuf::StreamingQueueMessageType *)p_cur;
std::shared_ptr<Message> message = nullptr;
switch (*type) {
case queue::protobuf::StreamingQueueMessageType::StreamingQueueNotificationMsgType:
message = NotificationMessage::FromBytes(bytes);
break;
case queue::protobuf::StreamingQueueMessageType::StreamingQueueDataMsgType:
message = DataMessage::FromBytes(bytes);
break;
case queue::protobuf::StreamingQueueMessageType::StreamingQueueCheckMsgType:
message = CheckMessage::FromBytes(bytes);
break;
case queue::protobuf::StreamingQueueMessageType::StreamingQueueCheckRspMsgType:
message = CheckRspMessage::FromBytes(bytes);
break;
default:
STREAMING_CHECK(false) << "nonsupport message type: "
<< queue::protobuf::StreamingQueueMessageType_Name(*type);
break;
}
return message;
}
void QueueMessageHandler::DispatchMessageAsync(
std::shared_ptr<LocalMemoryBuffer> buffer) {
queue_service_.post(
boost::bind(&QueueMessageHandler::DispatchMessageInternal, this, buffer, nullptr));
}
std::shared_ptr<LocalMemoryBuffer> QueueMessageHandler::DispatchMessageSync(
std::shared_ptr<LocalMemoryBuffer> buffer) {
std::shared_ptr<LocalMemoryBuffer> result = nullptr;
std::shared_ptr<PromiseWrapper> promise = std::make_shared<PromiseWrapper>();
queue_service_.post(
boost::bind(&QueueMessageHandler::DispatchMessageInternal, this, buffer,
[&promise, &result](std::shared_ptr<LocalMemoryBuffer> rst) {
result = rst;
promise->Notify(ray::Status::OK());
}));
Status st = promise->Wait();
STREAMING_CHECK(st.ok());
return result;
}
std::shared_ptr<Transport> QueueMessageHandler::GetOutTransport(
const ObjectID &queue_id) {
auto it = out_transports_.find(queue_id);
if (it == out_transports_.end()) return nullptr;
return it->second;
}
void QueueMessageHandler::SetPeerActorID(const ObjectID &queue_id,
const ActorID &actor_id) {
actors_.emplace(queue_id, actor_id);
out_transports_.emplace(
queue_id, std::make_shared<ray::streaming::Transport>(core_worker_, actor_id));
}
ActorID QueueMessageHandler::GetPeerActorID(const ObjectID &queue_id) {
auto it = actors_.find(queue_id);
STREAMING_CHECK(it != actors_.end());
return it->second;
}
void QueueMessageHandler::Release() {
actors_.clear();
out_transports_.clear();
}
void QueueMessageHandler::Start() {
queue_thread_ = std::thread(&QueueMessageHandler::QueueThreadCallback, this);
}
void QueueMessageHandler::Stop() {
STREAMING_LOG(INFO) << "QueueMessageHandler Stop.";
queue_service_.stop();
if (queue_thread_.joinable()) {
queue_thread_.join();
}
}
std::shared_ptr<UpstreamQueueMessageHandler> UpstreamQueueMessageHandler::CreateService(
CoreWorker *core_worker, const ActorID &actor_id) {
if (nullptr == upstream_handler_) {
upstream_handler_ =
std::make_shared<UpstreamQueueMessageHandler>(core_worker, actor_id);
}
return upstream_handler_;
}
std::shared_ptr<UpstreamQueueMessageHandler> UpstreamQueueMessageHandler::GetService() {
return upstream_handler_;
}
std::shared_ptr<WriterQueue> UpstreamQueueMessageHandler::CreateUpstreamQueue(
const ObjectID &queue_id, const ActorID &peer_actor_id, uint64_t size) {
STREAMING_LOG(INFO) << "CreateUpstreamQueue: " << queue_id << " " << actor_id_ << "->"
<< peer_actor_id;
std::shared_ptr<WriterQueue> queue = GetUpQueue(queue_id);
if (queue != nullptr) {
STREAMING_LOG(WARNING) << "Duplicate to create up queue." << queue_id;
return queue;
}
queue = std::unique_ptr<streaming::WriterQueue>(new streaming::WriterQueue(
queue_id, actor_id_, peer_actor_id, size, GetOutTransport(queue_id)));
upstream_queues_[queue_id] = queue;
return queue;
}
bool UpstreamQueueMessageHandler::UpstreamQueueExists(const ObjectID &queue_id) {
return nullptr != GetUpQueue(queue_id);
}
std::shared_ptr<streaming::WriterQueue> UpstreamQueueMessageHandler::GetUpQueue(
const ObjectID &queue_id) {
auto it = upstream_queues_.find(queue_id);
if (it == upstream_queues_.end()) return nullptr;
return it->second;
}
bool UpstreamQueueMessageHandler::CheckQueueSync(const ObjectID &queue_id) {
ActorID peer_actor_id = GetPeerActorID(queue_id);
STREAMING_LOG(INFO) << "CheckQueueSync queue_id: " << queue_id
<< " peer_actor_id: " << peer_actor_id;
CheckMessage msg(actor_id_, peer_actor_id, queue_id);
std::unique_ptr<LocalMemoryBuffer> buffer = msg.ToBytes();
auto transport_it = GetOutTransport(queue_id);
STREAMING_CHECK(transport_it != nullptr);
std::shared_ptr<LocalMemoryBuffer> result_buffer = transport_it->SendForResultWithRetry(
std::move(buffer), DownstreamQueueMessageHandler::peer_sync_function_, 10,
COMMON_SYNC_CALL_TIMEOUTT_MS);
if (result_buffer == nullptr) {
return false;
}
std::shared_ptr<Message> result_msg = ParseMessage(result_buffer);
STREAMING_CHECK(
result_msg->Type() ==
queue::protobuf::StreamingQueueMessageType::StreamingQueueCheckRspMsgType);
std::shared_ptr<CheckRspMessage> check_rsp_msg =
std::dynamic_pointer_cast<CheckRspMessage>(result_msg);
STREAMING_LOG(INFO) << "CheckQueueSync return queue_id: " << check_rsp_msg->QueueId();
STREAMING_CHECK(check_rsp_msg->PeerActorId() == actor_id_);
return queue::protobuf::StreamingQueueError::OK == check_rsp_msg->Error();
}
void UpstreamQueueMessageHandler::WaitQueues(const std::vector<ObjectID> &queue_ids,
int64_t timeout_ms,
std::vector<ObjectID> &failed_queues) {
failed_queues.insert(failed_queues.begin(), queue_ids.begin(), queue_ids.end());
uint64_t start_time_us = current_time_ms();
uint64_t current_time_us = start_time_us;
while (!failed_queues.empty() && current_time_us < start_time_us + timeout_ms * 1000) {
for (auto it = failed_queues.begin(); it != failed_queues.end();) {
if (CheckQueueSync(*it)) {
STREAMING_LOG(INFO) << "Check queue: " << *it << " return, ready.";
it = failed_queues.erase(it);
} else {
STREAMING_LOG(INFO) << "Check queue: " << *it << " return, not ready.";
std::this_thread::sleep_for(std::chrono::milliseconds(50));
it++;
}
}
current_time_us = current_time_ms();
}
}
void UpstreamQueueMessageHandler::DispatchMessageInternal(
std::shared_ptr<LocalMemoryBuffer> buffer,
std::function<void(std::shared_ptr<LocalMemoryBuffer>)> callback) {
std::shared_ptr<Message> msg = ParseMessage(buffer);
STREAMING_LOG(DEBUG) << "QueueMessageHandler::DispatchMessageInternal: "
<< " qid: " << msg->QueueId() << " actorid " << msg->ActorId()
<< " peer actorid: " << msg->PeerActorId() << " type: "
<< queue::protobuf::StreamingQueueMessageType_Name(msg->Type());
if (msg->Type() ==
queue::protobuf::StreamingQueueMessageType::StreamingQueueNotificationMsgType) {
OnNotify(std::dynamic_pointer_cast<NotificationMessage>(msg));
} else if (msg->Type() ==
queue::protobuf::StreamingQueueMessageType::StreamingQueueCheckRspMsgType) {
STREAMING_CHECK(false) << "Should not receive StreamingQueueCheckRspMsg";
} else {
STREAMING_CHECK(false) << "message type should be added: "
<< queue::protobuf::StreamingQueueMessageType_Name(
msg->Type());
}
}
void UpstreamQueueMessageHandler::OnNotify(
std::shared_ptr<NotificationMessage> notify_msg) {
auto queue = GetUpQueue(notify_msg->QueueId());
if (queue == nullptr) {
STREAMING_LOG(WARNING) << "Can not find queue for "
<< queue::protobuf::StreamingQueueMessageType_Name(
notify_msg->Type())
<< ", maybe queue has been destroyed, ignore it."
<< " seq id: " << notify_msg->SeqId();
return;
}
queue->OnNotify(notify_msg);
}
void UpstreamQueueMessageHandler::ReleaseAllUpQueues() {
STREAMING_LOG(INFO) << "ReleaseAllUpQueues";
upstream_queues_.clear();
Release();
}
std::shared_ptr<DownstreamQueueMessageHandler>
DownstreamQueueMessageHandler::CreateService(CoreWorker *core_worker,
const ActorID &actor_id) {
if (nullptr == downstream_handler_) {
downstream_handler_ =
std::make_shared<DownstreamQueueMessageHandler>(core_worker, actor_id);
}
return downstream_handler_;
}
std::shared_ptr<DownstreamQueueMessageHandler>
DownstreamQueueMessageHandler::GetService() {
return downstream_handler_;
}
bool DownstreamQueueMessageHandler::DownstreamQueueExists(const ObjectID &queue_id) {
return nullptr != GetDownQueue(queue_id);
}
std::shared_ptr<ReaderQueue> DownstreamQueueMessageHandler::CreateDownstreamQueue(
const ObjectID &queue_id, const ActorID &peer_actor_id) {
STREAMING_LOG(INFO) << "CreateDownstreamQueue: " << queue_id << " " << peer_actor_id
<< "->" << actor_id_;
auto it = downstream_queues_.find(queue_id);
if (it != downstream_queues_.end()) {
STREAMING_LOG(WARNING) << "Duplicate to create down queue!!!! " << queue_id;
return it->second;
}
std::shared_ptr<streaming::ReaderQueue> queue =
std::unique_ptr<streaming::ReaderQueue>(new streaming::ReaderQueue(
queue_id, actor_id_, peer_actor_id, GetOutTransport(queue_id)));
downstream_queues_[queue_id] = queue;
return queue;
}
std::shared_ptr<streaming::ReaderQueue> DownstreamQueueMessageHandler::GetDownQueue(
const ObjectID &queue_id) {
auto it = downstream_queues_.find(queue_id);
if (it == downstream_queues_.end()) return nullptr;
return it->second;
}
std::shared_ptr<LocalMemoryBuffer> DownstreamQueueMessageHandler::OnCheckQueue(
std::shared_ptr<CheckMessage> check_msg) {
queue::protobuf::StreamingQueueError err_code =
queue::protobuf::StreamingQueueError::OK;
auto down_queue = downstream_queues_.find(check_msg->QueueId());
if (down_queue == downstream_queues_.end()) {
STREAMING_LOG(WARNING) << "OnCheckQueue " << check_msg->QueueId() << " not found.";
err_code = queue::protobuf::StreamingQueueError::QUEUE_NOT_EXIST;
}
CheckRspMessage msg(check_msg->PeerActorId(), check_msg->ActorId(),
check_msg->QueueId(), err_code);
std::shared_ptr<LocalMemoryBuffer> buffer = msg.ToBytes();
return buffer;
}
void DownstreamQueueMessageHandler::ReleaseAllDownQueues() {
STREAMING_LOG(INFO) << "ReleaseAllDownQueues size: " << downstream_queues_.size();
downstream_queues_.clear();
Release();
}
void DownstreamQueueMessageHandler::DispatchMessageInternal(
std::shared_ptr<LocalMemoryBuffer> buffer,
std::function<void(std::shared_ptr<LocalMemoryBuffer>)> callback) {
std::shared_ptr<Message> msg = ParseMessage(buffer);
STREAMING_LOG(DEBUG) << "QueueMessageHandler::DispatchMessageInternal: "
<< " qid: " << msg->QueueId() << " actorid " << msg->ActorId()
<< " peer actorid: " << msg->PeerActorId() << " type: "
<< queue::protobuf::StreamingQueueMessageType_Name(msg->Type());
if (msg->Type() ==
queue::protobuf::StreamingQueueMessageType::StreamingQueueDataMsgType) {
OnData(std::dynamic_pointer_cast<DataMessage>(msg));
} else if (msg->Type() ==
queue::protobuf::StreamingQueueMessageType::StreamingQueueCheckMsgType) {
std::shared_ptr<LocalMemoryBuffer> check_result =
this->OnCheckQueue(std::dynamic_pointer_cast<CheckMessage>(msg));
if (callback != nullptr) {
callback(check_result);
}
} else {
STREAMING_CHECK(false) << "message type should be added: "
<< queue::protobuf::StreamingQueueMessageType_Name(
msg->Type());
}
}
void DownstreamQueueMessageHandler::OnData(std::shared_ptr<DataMessage> msg) {
auto queue = GetDownQueue(msg->QueueId());
if (queue == nullptr) {
STREAMING_LOG(WARNING) << "Can not find queue for "
<< queue::protobuf::StreamingQueueMessageType_Name(msg->Type())
<< ", maybe queue has been destroyed, ignore it."
<< " seq id: " << msg->SeqId();
return;
}
QueueItem item(msg);
queue->OnData(item);
}
} // namespace streaming
} // namespace ray
+194
View File
@@ -0,0 +1,194 @@
#ifndef _QUEUE_SERVICE_H_
#define _QUEUE_SERVICE_H_
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <thread>
#include "queue.h"
#include "util/streaming_logging.h"
namespace ray {
namespace streaming {
/// Base class of UpstreamQueueMessageHandler and DownstreamQueueMessageHandler.
/// A queue service manages a group of queues, upstream queues or downstream queues of
/// the current actor. Each queue service holds a boost.asio io_service, to handle
/// messages asynchronously. When a message received by Writer/Reader in ray call thread,
/// the message was delivered to
/// UpstreamQueueMessageHandler/DownstreamQueueMessageHandler, then the ray call thread
/// returns immediately. The queue service parses meta infomation from the message,
/// including queue_id actor_id, etc, and dispatchs message to queue according to
/// queue_id.
class QueueMessageHandler {
public:
/// Construct a QueueMessageHandler instance.
/// \param[in] core_worker CoreWorker C++ pointer of current actor, used to call Core
/// Worker's api.
/// For Python worker, the pointer can be obtained from
/// ray.worker.global_worker.core_worker; For Java worker, obtained from
/// RayNativeRuntime object through java reflection.
/// \param[in] actor_id actor id of current actor.
QueueMessageHandler(CoreWorker *core_worker, const ActorID &actor_id)
: core_worker_(core_worker),
actor_id_(actor_id),
queue_dummy_work_(queue_service_) {
Start();
}
virtual ~QueueMessageHandler() { Stop(); }
/// Dispatch message buffer to asio service.
/// \param[in] buffer serialized message received from peer actor.
void DispatchMessageAsync(std::shared_ptr<LocalMemoryBuffer> buffer);
/// Dispatch message buffer to asio service synchronously, and wait for handle result.
/// \param[in] buffer serialized message received from peer actor.
/// \return handle result.
std::shared_ptr<LocalMemoryBuffer> DispatchMessageSync(
std::shared_ptr<LocalMemoryBuffer> buffer);
/// Get transport to a peer actor specified by actor_id.
/// \param[in] actor_id actor id of peer actor
/// \return transport
std::shared_ptr<Transport> GetOutTransport(const ObjectID &actor_id);
/// The actual function where message being dispatched, called by DispatchMessageAsync
/// and DispatchMessageSync.
/// \param[in] buffer serialized message received from peer actor.
/// \param[in] callback the callback function used by DispatchMessageSync, called
/// after message processed complete. The std::shared_ptr<LocalMemoryBuffer>
/// parameter is the return value.
virtual void DispatchMessageInternal(
std::shared_ptr<LocalMemoryBuffer> buffer,
std::function<void(std::shared_ptr<LocalMemoryBuffer>)> callback) = 0;
/// Save actor_id of the peer actor specified by queue_id. For a upstream queue, the
/// peer actor refer specifically to the actor in current ray cluster who has a
/// downstream queue with same queue_id, and vice versa.
/// \param[in] queue_id queue id of current queue.
/// \param[in] actor_id actor_id actor id of corresponded peer actor.
void SetPeerActorID(const ObjectID &queue_id, const ActorID &actor_id);
/// Obtain the actor id of the peer actor specified by queue_id.
/// \return actor id
ActorID GetPeerActorID(const ObjectID &queue_id);
/// Release all queues in current queue service.
void Release();
private:
/// Start asio service
void Start();
/// Stop asio service
void Stop();
/// The callback function of internal thread.
void QueueThreadCallback() { queue_service_.run(); }
protected:
/// CoreWorker C++ pointer of current actor
CoreWorker *core_worker_;
/// actor_id actor id of current actor
ActorID actor_id_;
/// Helper function, parse message buffer to Message object.
std::shared_ptr<Message> ParseMessage(std::shared_ptr<LocalMemoryBuffer> buffer);
private:
/// Map from queue id to a actor id of the queue's peer actor.
std::unordered_map<ObjectID, ActorID> actors_;
/// Map from queue id to a transport of the queue's peer actor.
std::unordered_map<ObjectID, std::shared_ptr<Transport>> out_transports_;
/// The internal thread which asio service run with.
std::thread queue_thread_;
/// The internal asio service.
boost::asio::io_service queue_service_;
/// The asio work which keeps queue_service_ alive.
boost::asio::io_service::work queue_dummy_work_;
};
/// UpstreamQueueMessageHandler holds and manages all upstream queues of current actor.
class UpstreamQueueMessageHandler : public QueueMessageHandler {
public:
/// Construct a UpstreamQueueMessageHandler instance.
UpstreamQueueMessageHandler(CoreWorker *core_worker, const ActorID &actor_id)
: QueueMessageHandler(core_worker, actor_id) {}
/// Create a upstream queue.
/// \param[in] queue_id queue id of the queue to be created.
/// \param[in] peer_actor_id actor id of peer actor.
/// \param[in] size the max memory size of the queue.
std::shared_ptr<WriterQueue> CreateUpstreamQueue(const ObjectID &queue_id,
const ActorID &peer_actor_id,
uint64_t size);
/// Check whether the upstream queue specified by queue_id exists or not.
bool UpstreamQueueExists(const ObjectID &queue_id);
/// Wait all queues in queue_ids vector ready, until timeout.
/// \param[in] queue_ids a group of queues.
/// \param[in] timeout_ms max timeout time interval for wait all queues.
/// \param[out] failed_queues a group of queues which are not ready when timeout.
void WaitQueues(const std::vector<ObjectID> &queue_ids, int64_t timeout_ms,
std::vector<ObjectID> &failed_queues);
/// Handle notify message from corresponded downstream queue.
void OnNotify(std::shared_ptr<NotificationMessage> notify_msg);
/// Obtain upstream queue specified by queue_id.
std::shared_ptr<streaming::WriterQueue> GetUpQueue(const ObjectID &queue_id);
/// Release all upstream queues
void ReleaseAllUpQueues();
virtual void DispatchMessageInternal(
std::shared_ptr<LocalMemoryBuffer> buffer,
std::function<void(std::shared_ptr<LocalMemoryBuffer>)> callback) override;
static std::shared_ptr<UpstreamQueueMessageHandler> CreateService(
CoreWorker *core_worker, const ActorID &actor_id);
static std::shared_ptr<UpstreamQueueMessageHandler> GetService();
static RayFunction peer_sync_function_;
static RayFunction peer_async_function_;
private:
bool CheckQueueSync(const ObjectID &queue_ids);
private:
std::unordered_map<ObjectID, std::shared_ptr<streaming::WriterQueue>> upstream_queues_;
static std::shared_ptr<UpstreamQueueMessageHandler> upstream_handler_;
};
/// UpstreamQueueMessageHandler holds and manages all downstream queues of current actor.
class DownstreamQueueMessageHandler : public QueueMessageHandler {
public:
DownstreamQueueMessageHandler(CoreWorker *core_worker, const ActorID &actor_id)
: QueueMessageHandler(core_worker, actor_id) {}
std::shared_ptr<ReaderQueue> CreateDownstreamQueue(const ObjectID &queue_id,
const ActorID &peer_actor_id);
bool DownstreamQueueExists(const ObjectID &queue_id);
void UpdateDownActor(const ObjectID &queue_id, const ActorID &actor_id);
std::shared_ptr<LocalMemoryBuffer> OnCheckQueue(
std::shared_ptr<CheckMessage> check_msg);
std::shared_ptr<streaming::ReaderQueue> GetDownQueue(const ObjectID &queue_id);
void ReleaseAllDownQueues();
void OnData(std::shared_ptr<DataMessage> msg);
virtual void DispatchMessageInternal(
std::shared_ptr<LocalMemoryBuffer> buffer,
std::function<void(std::shared_ptr<LocalMemoryBuffer>)> callback);
static std::shared_ptr<DownstreamQueueMessageHandler> CreateService(
CoreWorker *core_worker, const ActorID &actor_id);
static std::shared_ptr<DownstreamQueueMessageHandler> GetService();
static RayFunction peer_sync_function_;
static RayFunction peer_async_function_;
private:
std::unordered_map<ObjectID, std::shared_ptr<streaming::ReaderQueue>>
downstream_queues_;
static std::shared_ptr<DownstreamQueueMessageHandler> downstream_handler_;
};
} // namespace streaming
} // namespace ray
#endif
+109
View File
@@ -0,0 +1,109 @@
#ifndef _STREAMING_QUEUE_ITEM_H_
#define _STREAMING_QUEUE_ITEM_H_
#include <iterator>
#include <list>
#include <thread>
#include <vector>
#include "ray/common/id.h"
#include "message.h"
#include "message/message_bundle.h"
#include "util/streaming_logging.h"
namespace ray {
namespace streaming {
using ray::ObjectID;
const uint64_t QUEUE_INVALID_SEQ_ID = std::numeric_limits<uint64_t>::max();
/// QueueItem is the element stored in `Queue`. Actually, when DataWriter pushes a message
/// bundle into a queue, the bundle is packed into one QueueItem, so a one-to-one
/// relationship exists between message bundle and QueueItem. Meanwhile, the QueueItem is
/// also the minimum unit to send through direct actor call. Each QueueItem holds a
/// LocalMemoryBuffer shared_ptr, which will be sent out by Transport.
class QueueItem {
public:
/// Construct a QueueItem object.
/// \param[in] seq_id the sequential id assigned by DataWriter for a message bundle and
/// QueueItem.
/// \param[in] data the data buffer to be stored in this QueueItem.
/// \param[in] data_size the data size in bytes.
/// \param[in] timestamp the time when this QueueItem created.
/// \param[in] raw whether the data content is raw bytes, only used in some tests.
QueueItem(uint64_t seq_id, uint8_t *data, uint32_t data_size, uint64_t timestamp,
bool raw = false)
: seq_id_(seq_id),
timestamp_(timestamp),
raw_(raw),
/*COPY*/ buffer_(std::make_shared<LocalMemoryBuffer>(data, data_size, true)) {}
QueueItem(uint64_t seq_id, std::shared_ptr<LocalMemoryBuffer> buffer,
uint64_t timestamp, bool raw = false)
: seq_id_(seq_id), timestamp_(timestamp), raw_(raw), buffer_(buffer) {}
QueueItem(std::shared_ptr<DataMessage> data_msg)
: seq_id_(data_msg->SeqId()),
raw_(data_msg->IsRaw()),
buffer_(data_msg->Buffer()) {}
QueueItem(const QueueItem &&item) {
buffer_ = item.buffer_;
seq_id_ = item.seq_id_;
timestamp_ = item.timestamp_;
raw_ = item.raw_;
}
QueueItem(const QueueItem &item) {
buffer_ = item.buffer_;
seq_id_ = item.seq_id_;
timestamp_ = item.timestamp_;
raw_ = item.raw_;
}
QueueItem &operator=(const QueueItem &item) {
buffer_ = item.buffer_;
seq_id_ = item.seq_id_;
timestamp_ = item.timestamp_;
raw_ = item.raw_;
return *this;
}
virtual ~QueueItem() = default;
uint64_t SeqId() { return seq_id_; }
bool IsRaw() { return raw_; }
uint64_t TimeStamp() { return timestamp_; }
size_t DataSize() { return buffer_->Size(); }
std::shared_ptr<LocalMemoryBuffer> Buffer() { return buffer_; }
/// Get max message id in this item.
/// \return max message id.
uint64_t MaxMsgId() {
if (raw_) {
return 0;
}
auto message_bundle = StreamingMessageBundleMeta::FromBytes(buffer_->Data());
return message_bundle->GetLastMessageId();
}
protected:
uint64_t seq_id_;
uint64_t timestamp_;
bool raw_;
std::shared_ptr<LocalMemoryBuffer> buffer_;
};
class InvalidQueueItem : public QueueItem {
public:
InvalidQueueItem() : QueueItem(QUEUE_INVALID_SEQ_ID, data_, 1, 0) {}
private:
uint8_t data_[1];
};
typedef std::shared_ptr<QueueItem> QueueItemPtr;
} // namespace streaming
} // namespace ray
#endif
+94
View File
@@ -0,0 +1,94 @@
#include "transport.h"
#include "utils.h"
namespace ray {
namespace streaming {
static constexpr int TASK_OPTION_RETURN_NUM_0 = 0;
static constexpr int TASK_OPTION_RETURN_NUM_1 = 1;
void Transport::SendInternal(std::shared_ptr<LocalMemoryBuffer> buffer,
RayFunction &function, int return_num,
std::vector<ObjectID> &return_ids) {
std::unordered_map<std::string, double> resources;
TaskOptions options{return_num, true, resources};
char meta_data[3] = {'R', 'A', 'W'};
std::shared_ptr<LocalMemoryBuffer> meta =
std::make_shared<LocalMemoryBuffer>((uint8_t *)meta_data, 3, true);
std::vector<TaskArg> args;
if (function.GetLanguage() == Language::PYTHON) {
auto dummy = "__RAY_DUMMY__";
std::shared_ptr<LocalMemoryBuffer> dummyBuffer =
std::make_shared<LocalMemoryBuffer>((uint8_t *)dummy, 13, true);
args.emplace_back(TaskArg::PassByValue(
std::make_shared<RayObject>(std::move(dummyBuffer), meta, true)));
}
args.emplace_back(
TaskArg::PassByValue(std::make_shared<RayObject>(std::move(buffer), meta, true)));
STREAMING_CHECK(core_worker_ != nullptr);
std::vector<std::shared_ptr<RayObject>> results;
ray::Status st =
core_worker_->SubmitActorTask(peer_actor_id_, function, args, options, &return_ids);
if (!st.ok()) {
STREAMING_LOG(ERROR) << "SubmitActorTask failed. " << st;
}
}
void Transport::Send(std::shared_ptr<LocalMemoryBuffer> buffer, RayFunction &function) {
STREAMING_LOG(INFO) << "Transport::Send buffer size: " << buffer->Size();
std::vector<ObjectID> return_ids;
SendInternal(std::move(buffer), function, TASK_OPTION_RETURN_NUM_0, return_ids);
}
std::shared_ptr<LocalMemoryBuffer> Transport::SendForResult(
std::shared_ptr<LocalMemoryBuffer> buffer, RayFunction &function,
int64_t timeout_ms) {
std::vector<ObjectID> return_ids;
SendInternal(buffer, function, TASK_OPTION_RETURN_NUM_1, return_ids);
std::vector<std::shared_ptr<RayObject>> results;
Status get_st = core_worker_->Get(return_ids, timeout_ms, &results);
if (!get_st.ok()) {
STREAMING_LOG(ERROR) << "Get fail.";
return nullptr;
}
STREAMING_CHECK(results.size() >= 1);
if (results[0]->IsException()) {
STREAMING_LOG(ERROR) << "peer actor may has exceptions, should retry.";
return nullptr;
}
STREAMING_CHECK(results[0]->HasData());
if (results[0]->GetData()->Size() == 4) {
STREAMING_LOG(WARNING) << "peer actor may not ready yet, should retry.";
return nullptr;
}
std::shared_ptr<Buffer> result_buffer = results[0]->GetData();
std::shared_ptr<LocalMemoryBuffer> return_buffer = std::make_shared<LocalMemoryBuffer>(
result_buffer->Data(), result_buffer->Size(), true);
return return_buffer;
}
std::shared_ptr<LocalMemoryBuffer> Transport::SendForResultWithRetry(
std::shared_ptr<LocalMemoryBuffer> buffer, RayFunction &function, int retry_cnt,
int64_t timeout_ms) {
STREAMING_LOG(INFO) << "SendForResultWithRetry retry_cnt: " << retry_cnt
<< " timeout_ms: " << timeout_ms
<< " function: " << function.GetFunctionDescriptor()[0];
std::shared_ptr<LocalMemoryBuffer> buffer_shared = std::move(buffer);
for (int cnt = 0; cnt < retry_cnt; cnt++) {
auto result = SendForResult(buffer_shared, function, timeout_ms);
if (result != nullptr) {
return result;
}
}
STREAMING_LOG(WARNING) << "SendForResultWithRetry fail after retry.";
return nullptr;
}
} // namespace streaming
} // namespace ray
+63
View File
@@ -0,0 +1,63 @@
#ifndef _STREAMING_QUEUE_TRANSPORT_H_
#define _STREAMING_QUEUE_TRANSPORT_H_
#include "ray/common/id.h"
#include "ray/core_worker/core_worker.h"
#include "util/streaming_logging.h"
namespace ray {
namespace streaming {
/// Transport is the transfer endpoint to a specific actor, buffers can be sent to peer
/// through direct actor call.
class Transport {
public:
/// Construct a Transport object.
/// \param[in] core_worker CoreWorker C++ pointer of current actor, which we call direct
/// actor call interface with.
/// \param[in] peer_actor_id actor id of peer actor.
Transport(CoreWorker *core_worker, const ActorID &peer_actor_id)
: core_worker_(core_worker), peer_actor_id_(peer_actor_id) {}
virtual ~Transport() = default;
/// Send buffer asynchronously, peer's `function` will be called.
/// \param[in] buffer buffer to be sent.
/// \param[in] function the function descriptor of peer's function.
virtual void Send(std::shared_ptr<LocalMemoryBuffer> buffer, RayFunction &function);
/// Send buffer synchronously, peer's `function` will be called, and return the peer
/// function's return value.
/// \param[in] buffer buffer to be sent.
/// \param[in] function the function descriptor of peer's function.
/// \param[in] timeout_ms max time to wait for result.
/// \return peer function's result.
virtual std::shared_ptr<LocalMemoryBuffer> SendForResult(
std::shared_ptr<LocalMemoryBuffer> buffer, RayFunction &function,
int64_t timeout_ms);
/// Send buffer and get result with retry.
/// return value.
/// \param[in] buffer buffer to be sent.
/// \param[in] function the function descriptor of peer's function.
/// \param[in] max retry count
/// \param[in] timeout_ms max time to wait for result.
/// \return peer function's result.
std::shared_ptr<LocalMemoryBuffer> SendForResultWithRetry(
std::shared_ptr<LocalMemoryBuffer> buffer, RayFunction &function, int retry_cnt,
int64_t timeout_ms);
private:
/// Send buffer internal
/// \param[in] buffer buffer to be sent.
/// \param[in] function the function descriptor of peer's function.
/// \param[in] return_num return value number of the call.
/// \param[out] return_ids return ids from SubmitActorTask.
virtual void SendInternal(std::shared_ptr<LocalMemoryBuffer> buffer,
RayFunction &function, int return_num,
std::vector<ObjectID> &return_ids);
private:
CoreWorker *core_worker_;
ActorID peer_actor_id_;
};
} // namespace streaming
} // namespace ray
#endif
+50
View File
@@ -0,0 +1,50 @@
#ifndef _STREAMING_QUEUE_UTILS_H_
#define _STREAMING_QUEUE_UTILS_H_
#include <chrono>
#include <future>
#include <thread>
#include "ray/util/util.h"
namespace ray {
namespace streaming {
/// Helper class encapulate std::future to help multithread async wait.
class PromiseWrapper {
public:
Status Wait() {
std::future<bool> fut = promise_.get_future();
fut.get();
return status_;
}
Status WaitFor(uint64_t timeout_ms) {
std::future<bool> fut = promise_.get_future();
std::future_status status;
do {
status = fut.wait_for(std::chrono::milliseconds(timeout_ms));
if (status == std::future_status::deferred) {
} else if (status == std::future_status::timeout) {
return Status::Invalid("timeout");
} else if (status == std::future_status::ready) {
return status_;
}
} while (status == std::future_status::deferred);
return status_;
}
void Notify(Status status) {
status_ = status;
promise_.set_value(true);
}
Status GetResultStatus() { return status_; }
private:
std::promise<bool> promise_;
Status status_;
};
} // namespace streaming
} // namespace ray
#endif