-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Rust API for Storage Channel (#77) #137
Add Rust API for Storage Channel (#77) #137
Conversation
oak/server/oak_node.cc
Outdated
@@ -318,6 +320,13 @@ grpc::Status OakNode::ProcessModuleInvocation(grpc::GenericServerContext* contex | |||
channels_[LOGGING_CHANNEL_HANDLE] = std::move(logging_channel); | |||
LOG(INFO) << "Created logging channel"; | |||
|
|||
// TODO: Move to initialization method. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I expect this to end up being a new type of (pseudo-)node:
Lines 38 to 42 in 4523eed
oneof node_type { | |
LogNode log_node = 2; | |
GrpcServerNode grpc_server_node = 3; | |
WebAssemblyNode web_assembly_node = 4; | |
} |
oak/proto/storage.proto
Outdated
} | ||
|
||
message RollbackResponse { | ||
} | ||
|
||
// Wrapper message used to send requests via the StorageChannel interface. | ||
message StorageOperationRequest { | ||
// The name of the storage on which to perform the operation. | ||
// The StorageChannel generates a name-based (version 5) UUID from this value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I follow this, is the UUID deterministically generated from the name? What purpose does it serve?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated comment.
rust/oak/src/lib.rs
Outdated
@@ -132,3 +137,65 @@ pub extern "C" fn oak_handle_grpc_call() { | |||
} | |||
}); | |||
} | |||
|
|||
pub fn execute_storage_operation( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest moving the storage-related stuff in its own module to start with (so it can be invoked as oak::storage::execute_storage_operation
or similar, unless previously imported)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
a8f0a8d
to
c713556
Compare
All (the pull request submitter and all commit authors) CLAs are signed, but one or more commits were authored or co-authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that by leaving a comment that contains only Note to project maintainer: There may be cases where the author cannot leave a comment, or the comment is not properly detected as consent. In those cases, you can manually confirm consent of the commit author(s), and set the ℹ️ Googlers: Go here for more info. |
2b96408
to
c1689a0
Compare
CLAs look good, thanks! ℹ️ Googlers: Go here for more info. |
6ffd8b8
to
06226e7
Compare
acc5323
to
5ec2cdc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, adding @daviddrysdale to look at the channel stuff more closely
oak/proto/oak_api.proto
Outdated
STORAGE_READ = 4; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think STORAGE_IN
and STORAGE_OUT
make more sense here, both for consistency, and also so they don't suggest that one is for reading and the other for writing data to storage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
oak/server/oak_node.cc
Outdated
@@ -183,7 +186,15 @@ static void RunModule(wabt::interp::Environment* env, wabt::interp::DefinedModul | |||
} | |||
} | |||
|
|||
OakNode::OakNode() : Service() {} | |||
OakNode::OakNode() : Service() { | |||
std::unique_ptr<ChannelHalf> storage_read_channel = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be around
Lines 188 to 263 in 7d75624
std::unique_ptr<OakNode> OakNode::Create(const std::string& module) { | |
LOG(INFO) << "Creating Oak Node"; | |
std::unique_ptr<OakNode> node = absl::WrapUnique(new OakNode()); | |
node->InitEnvironment(&node->env_); | |
LOG(INFO) << "Host func count: " << node->env_.GetFuncCount(); | |
wabt::Errors errors; | |
LOG(INFO) << "Reading module"; | |
wabt::Result result = ReadModule(module, &node->env_, &errors, &node->module_); | |
if (!wabt::Succeeded(result)) { | |
LOG(WARNING) << "Could not read module: " << result; | |
LOG(WARNING) << "Errors: " << wabt::FormatErrorsToString(errors, wabt::Location::Type::Binary); | |
return nullptr; | |
} | |
LOG(INFO) << "Reading module done"; | |
if (!CheckModuleExports(&node->env_, node->module_)) { | |
LOG(WARNING) << "Failed to validate module"; | |
return nullptr; | |
} | |
// Create a logging channel for the module. | |
{ | |
std::shared_ptr<MessageChannel> channel = std::make_shared<MessageChannel>(); | |
node->channel_halves_[ChannelHandle::LOGGING] = | |
absl::make_unique<MessageChannelWriteHalf>(channel); | |
LOG(INFO) << "Created logging channel " << ChannelHandle::LOGGING; | |
// Spawn a thread that reads and logs messages on this channel forever. | |
std::thread t([channel] { | |
std::unique_ptr<MessageChannelReadHalf> read_chan = | |
absl::make_unique<MessageChannelReadHalf>(channel); | |
while (true) { | |
ReadResult result = read_chan->BlockingRead(INT_MAX); | |
if (result.required_size > 0) { | |
LOG(ERROR) << "Message size too large: " << result.required_size; | |
return; | |
} | |
LOG(INFO) << "LOG: " << std::string(result.data->data(), result.data->size()); | |
} | |
}); | |
// TODO: join() instead when we have node termination | |
t.detach(); | |
} | |
// Create the channels needed for gRPC interactions. | |
{ | |
// Incoming request channel: keep the write half in C++, but map the read | |
// half to a well-known channel handle. | |
std::shared_ptr<MessageChannel> channel = std::make_shared<MessageChannel>(); | |
node->channel_halves_[ChannelHandle::GRPC_IN] = | |
absl::make_unique<MessageChannelReadHalf>(channel); | |
node->req_half_ = absl::make_unique<MessageChannelWriteHalf>(channel); | |
LOG(INFO) << "Created gRPC input channel: " << ChannelHandle::GRPC_IN; | |
} | |
{ | |
// Outgoing response channel: keep the read half in C++, but map the write | |
// half to a well-known channel handle. | |
std::shared_ptr<MessageChannel> channel = std::make_shared<MessageChannel>(); | |
node->channel_halves_[ChannelHandle::GRPC_OUT] = | |
absl::make_unique<MessageChannelWriteHalf>(channel); | |
node->rsp_half_ = absl::make_unique<MessageChannelReadHalf>(channel); | |
LOG(INFO) << "Created gRPC output channel: " << ChannelHandle::GRPC_IN; | |
} | |
// Spin up a per-node Wasm thread to run forever; the Node object must | |
// outlast this thread. | |
LOG(INFO) << "Executing module oak_main"; | |
std::thread t([&node] { RunModule(&node->env_, node->module_); }); | |
// TODO: join() instead when we have node termination | |
t.detach(); | |
LOG(INFO) << "Started module execution thread"; | |
return node; | |
} |
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, keeping all of the well-known channel creation stuff together makes sense, either here or in CreateNode
(and I'd probably go for the latter because we don't want to start a logging thread until we're sure the Node instance is good to go).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
namespace oak { | ||
|
||
// A channel implementation that reads from a StorageService. | ||
class StorageReadChannel final : public ChannelHalf { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this extend
Line 84 in 7d75624
class MessageChannel : public ChannelHalf { |
BTW, @daviddrysdale maybe channel_halves_
should keep track of MessageChannel
instance rather than ChannelHalf
? I guess we expect all channels to provide async methods for reading / writing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the moment we've got a single (per-Node) numbering space for channel handles, regardless of direction, so channel_halves_
maps to a channel half rather than a channel. (To put it another way: when we get as far as creating channels at runtime, the creator will get two numbers to refer to the new channel, one for read and one for write.)
More generally, I'm idly wondering if the whole storage channel thing could be replaced with a couple of MessageChannel
instances that connect to some C++ code (i.e. a pseudo-node, to use the terminology from manager.proto
).
But I'm not yet ramped up enough on the storage design to know if that's a sensible or a silly idea…
oak/server/oak_node.cc
Outdated
@@ -183,7 +186,15 @@ static void RunModule(wabt::interp::Environment* env, wabt::interp::DefinedModul | |||
} | |||
} | |||
|
|||
OakNode::OakNode() : Service() {} | |||
OakNode::OakNode() : Service() { | |||
std::unique_ptr<ChannelHalf> storage_read_channel = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, keeping all of the well-known channel creation stuff together makes sense, either here or in CreateNode
(and I'd probably go for the latter because we don't want to start a logging thread until we're sure the Node instance is good to go).
|
||
private: | ||
// The serialized StorageOperationResponse message set by WriteResponseData. | ||
std::string operation_response_data_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this class be replaced by an instance of MessageChannel
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can probably follow up with a refactoring... will discuss offline.
StorageManager() {} | ||
|
||
std::unique_ptr<Message> ReadResponseData(uint32_t size) { | ||
absl::Span<const char> response_data = operation_response_view_.subspan(0, size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it's aligned more with the previous stream-based channels, rather than the current message-based approach?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, @michael-kernel-sanders do you plan to fix this in this PR or to follow up later? If the latter, perhaps add a TODO somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added TODO.
oak/proto/oak_api.proto
Outdated
STORAGE_READ = 4; | ||
STORAGE_WRITE = 5; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update README.md to describe the new well-known channel handles?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
oak/proto/storage.proto
Outdated
} | ||
|
||
message RollbackResponse { | ||
} | ||
|
||
// Wrapper message used to send requests via the StorageChannel interface. | ||
message StorageOperationRequest { | ||
// The name of the storage on which to perform the operation. | ||
// The StorageChannel generates a deterministic name-based (version 5) UUID | ||
// from this value and the Oak Module to use as the storage identifier. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I didn't really follow this, but that might be lack of background)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also still not entirely sure what this is used for or whether it's necessary / nice to have (and why).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In what sense does the StorageChannel generate something from this value and the Oak Module? Do you mean the name of the module? or the hash of the code? or something else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarified.
namespace oak { | ||
|
||
// A channel implementation that reads from a StorageService. | ||
class StorageReadChannel final : public ChannelHalf { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the moment we've got a single (per-Node) numbering space for channel handles, regardless of direction, so channel_halves_
maps to a channel half rather than a channel. (To put it another way: when we get as far as creating channels at runtime, the creator will get two numbers to refer to the new channel, one for read and one for write.)
More generally, I'm idly wondering if the whole storage channel thing could be replaced with a couple of MessageChannel
instances that connect to some C++ code (i.e. a pseudo-node, to use the terminology from manager.proto
).
But I'm not yet ramped up enough on the storage design to know if that's a sensible or a silly idea…
STORAGE_IN = 4; | ||
STORAGE_OUT = 5; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update README.md to describe the new well-known channel handles?
oak/proto/storage.proto
Outdated
@@ -77,7 +77,9 @@ message RollbackResponse { | |||
message StorageOperationRequest { | |||
// The name of the storage on which to perform the operation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think this is quite vague, what is a "storage" and what defines its name? Maybe this should point to some high level comment? Or this proto message should contain more information on how the storage works? e.g. "a storage backend is subdivided into different namespaces, whose values are UUIDs derived from a user-provided string... if multiple mutually distrusting clients end up choosing the same name then $X happens" etc. . Maybe this is already present in the code, so you can just add a link to it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Attempted to clarify.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separated the storage channel and service messages.
411a11d
to
43d3731
Compare
2e976f0
to
aac3301
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Michael and sorry for the delay!
This change is