-
Notifications
You must be signed in to change notification settings - Fork 433
Add C++ builder pattern #1018
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
base: main
Are you sure you want to change the base?
Add C++ builder pattern #1018
Conversation
Summary of ChangesHello @JasonZhang517, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a C++ builder pattern for the Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
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.
Code Review
This pull request introduces a MooncakeStoreBuilder to provide a fluent and readable API for constructing mooncake::Client instances. This is a great improvement over the static Create method with many parameters. The implementation is solid, and it's correctly used to refactor PyClient::setup_internal.
My review includes a few suggestions to further enhance the new builder:
- Making the
Build()methodconstto improve API correctness. - Simplifying the usage of the builder in
pybind_client.cppto a single fluent chain. - A minor refinement in the
Build()method's error message construction for better performance and C++ idiomaticity.
Overall, this is a well-executed enhancement.
mooncake-store/include/client.h
Outdated
| MooncakeStoreBuilder& WithExistingTransferEngine( | ||
| std::shared_ptr<TransferEngine> transfer_engine); | ||
|
|
||
| [[nodiscard]] std::optional<std::shared_ptr<Client>> Build(); |
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.
The Build() method does not modify the state of the MooncakeStoreBuilder. It's good practice to mark such methods as const. This improves API correctness by clearly stating the method's intent and allows it to be called on const builder instances. You'll also need to update the implementation in client.cpp.
| [[nodiscard]] std::optional<std::shared_ptr<Client>> Build(); | |
| [[nodiscard]] std::optional<std::shared_ptr<Client>> Build() const; |
mooncake-store/src/client.cpp
Outdated
| return *this; | ||
| } | ||
|
|
||
| std::optional<std::shared_ptr<Client>> MooncakeStoreBuilder::Build() { |
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.
To match the const qualifier added to the declaration in the header file, the implementation of Build() should also be marked as const.
| std::optional<std::shared_ptr<Client>> MooncakeStoreBuilder::Build() { | |
| std::optional<std::shared_ptr<Client>> MooncakeStoreBuilder::Build() const { |
| for (size_t i = 0; i < missing.size(); ++i) { | ||
| if (i != 0) { | ||
| joined.append(", "); | ||
| } | ||
| joined.append(missing[i]); | ||
| } |
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 loop for joining strings with a separator can be made slightly more efficient and idiomatic by handling the first element outside the loop. This avoids the conditional check if (i != 0) in every iteration.
joined.append(missing[0]);
for (size_t i = 1; i < missing.size(); ++i) {
joined.append(", ");
joined.append(missing[i]);
}
mooncake-store/src/pybind_client.cpp
Outdated
| MooncakeStoreBuilder builder; | ||
| builder.WithLocalHostname(this->local_hostname) | ||
| .WithMetadataConnectionString(metadata_server) | ||
| .UsingProtocol(protocol) | ||
| .WithMasterServerEntry(master_server_addr); | ||
|
|
||
| if (device_name) { | ||
| builder.WithRdmaDeviceNames(*device_name); | ||
| } | ||
| if (transfer_engine) { | ||
| builder.WithExistingTransferEngine(transfer_engine); | ||
| } | ||
|
|
||
| auto client_opt = builder.Build(); |
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.
The builder pattern is most powerful when its methods are chained fluently. The current implementation can be simplified into a single, more readable statement. The if conditions for optional parameters can be avoided by using std::optional::value_or for device_name and by passing the (potentially null) transfer_engine shared_ptr directly, as the builder handles these cases gracefully.
auto client_opt = MooncakeStoreBuilder()
.WithLocalHostname(this->local_hostname)
.WithMetadataConnectionString(metadata_server)
.UsingProtocol(protocol)
.WithMasterServerEntry(master_server_addr)
.WithRdmaDeviceNames(device_name.value_or(""))
.WithExistingTransferEngine(transfer_engine)
.Build();|
Use |
xiaguan
left a comment
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.
Add a simple unit test for the builder.
mooncake-store/src/pybind_client.cpp
Outdated
| auto client_opt = mooncake::Client::Create( | ||
| this->local_hostname, metadata_server, protocol, device_name, | ||
| master_server_addr, transfer_engine); | ||
| MooncakeStoreBuilder builder; |
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.
revert this? just use create is good enough i think
nickyc975
left a comment
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.
Hi, @JasonZhang517. Glad to see the improvement of client initializition! I have several naming suggestions on this PR.
| * specify only the options they need while reusing existing Client::Create | ||
| * logic under the hood. | ||
| */ | ||
| class MooncakeStoreBuilder { |
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 ClientBuilder should be a better name in the context, as we are infact building a Client, not a MooncakeStore.
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.
How about MooncakeStoreClientBuilder? Don't be afraid of a long name.
nickyc975
left a comment
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.
Hi, @JasonZhang517. I have found several problems in the PR. Please take a look.
|
|
||
| MooncakeStoreBuilder& MooncakeStoreBuilder::WithLocalHostname( | ||
| std::string local_hostname) { | ||
| local_hostname_ = std::move(local_hostname); |
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.
Use const std::string& instead of std::string for the parameter, and don't move the parameter. Please also update the other "WithXXX" methods that taking strings as arguments.
| return *this; | ||
| } | ||
|
|
||
| tl::expected<std::shared_ptr<Client>, std::string> MooncakeStoreBuilder::Build() |
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.
Returning string for errors is not a good practice. String is not structured and hard to be programmatically processed. You can see the bad result in the unit tests that you must search the returned string to check whether it is expected.
I suggest to return an ErrorCode for failure. For example, INVALID_PARAMS for missing required field, INTERNAL_ERROR for client creation failure. And provide a GetMissingFields() method to get the missing fields if needed.
|
|
||
| if (!missing.empty()) { | ||
| std::string joined; | ||
| joined.reserve(missing.size() * 16); |
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.
The reserve is not necessary because this is not a performance critical path and 16 is a magic number. It's fine to let the string allocate space as needed.
| for (size_t i = 0; i < missing.size(); ++i) { | ||
| if (i != 0) { | ||
| joined.append(", "); | ||
| } |
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 for loop and the if (i != 0) is redundent and confusing, just remove them.
Corresponding to #978
Based on existing create method:
static std::optional<std::shared_ptr> Create(
const std::string& local_hostname,
const std::string& metadata_connstring, const std::string& protocol,
const std::optionalstd::string& device_names = std::nullopt,
const std::string& master_server_entry = kDefaultMasterAddress,
const std::shared_ptr& transfer_engine = nullptr);
Added builder functions with std::move to reduce copy.