-
Notifications
You must be signed in to change notification settings - Fork 406
Start work on a minimal admin client. #93
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
Conversation
bigtable/admin/admin_client.cc
Outdated
mutable std::mutex mu_; | ||
std::shared_ptr<grpc::Channel> channel_; | ||
std::unique_ptr< | ||
google::bigtable::admin::v2::BigtableTableAdmin::StubInterface> |
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.
Be consistent whether this is google::[...]
or ::google::[...]
— it's different even in this one file.
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.
Fixed.
|
||
std::string const& project() const override { return project_; } | ||
void on_completion(grpc::Status const& status) override; | ||
::google::bigtable::admin::v2::BigtableTableAdmin::StubInterface& |
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.
Be consistent whether this is google::[...]
or ::google::[...]
— it's different even in this one file.
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.
Or the next line! Thanks, fixed.
bigtable/admin/admin_client.cc
Outdated
} | ||
|
||
void SimpleAdminClient::refresh_credentials_and_channel() { | ||
std::unique_lock<std::mutex> lk(mu_); |
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.
Are we going to use std::mutex
or absl::Mutex
in this library?
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.
std::mutex
, it is good enough for everybody.
bigtable/admin/admin_client.cc
Outdated
private: | ||
std::string project_; | ||
bigtable::ClientOptions options_; | ||
mutable std::mutex mu_; |
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.
Please add GUARDED_BY
annotations (via Abseil's thread_annotations.h
) to ensure that:
- it's clear what member vars are guarded by
mu_
and - the locks are enforced at compile time
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. But there are bigger problems here.
bigtable/admin/admin_client.cc
Outdated
if (table_admin_stub_) { | ||
return; | ||
} | ||
// Release the lock before executing potentially slow operations. |
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.
Rather than doing that, would it make sense to create separate blocks, and use std::unique_lock
and let its destructor automatically unlock, so you don't also have to relock? E.g., something like:
{
std::unique_lock lk(mu_);
// some code under mutex here
}
// some slow code here
{
std::unique_lock lk(mu_);
// some more code under mutex here
}
It might read better (potentially), you skip the manual unlock/lock steps. Is constructing a new std::unique_lock
expensive?
Thoughts?
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.
bigtable/admin/admin_client_test.cc
Outdated
@@ -0,0 +1,30 @@ | |||
// Copyright 2017 Google Inc. |
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.
Too many spaces after the //
comment in this file for some reason.
bigtable/admin/table_admin_test.cc
Outdated
@@ -0,0 +1,137 @@ | |||
// Copyright 2017 Google Inc. |
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.
Too many spaces after //
in this file as well.
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.
Fixed. How did that happen?
EXPECT_EQ(typeid(grpc::GoogleDefaultCredentials()), | ||
typeid(client_options_object.credentials())); | ||
} | ||
|
||
TEST(ClientOptionsTest, ClientOptionsCustomEndpoint) { | ||
// TODO(#23) - setenv() is a Unix specific call ... | ||
setenv("BIGTABLE_EMULATOR_HOST", "testendpoint.googleapis.com", 1); |
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.
Have to be really careful here: if this test ends too early (due to ASSERT_*
being used instead of EXPECT_*
at some point in the future, for example, this env var will stick around, affecting other tests, and making it quite hard to debug.
A way to guarantee that this happens is to create a custom test fixture class, where the SetUp()
method does the setenv()
call and TearDown()
does the unsetenv()
call. Definitely comes with some overhead, but will ensure that each test is isolated from other tests. It might be possible to write a single test fixture class, and then reuse it several times (via inheritance or template instantiation) so that you don't have to write this many times, but it's quite straight-forward and will save headaches in the future, I think.
This doesn't need to be done in this PR (since it's slightly a side comment, and the setenv()
is from an earlier PR; please consider adding a TODO and/or filing an issue for this.
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.
EXPECT_EQ(typeid(grpc::GoogleDefaultCredentials()), | ||
typeid(client_options_object.credentials())); | ||
} | ||
|
||
TEST(ClientOptionsTest, ClientOptionsCustomEndpoint) { | ||
// TODO(#23) - setenv() is a Unix specific call ... |
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.
#ifdef WIN32
SetEnvironmentVariable(...);
#else
setenv(...);
#endif
Maybe it's worthwhile to start building a "common" library within google-cloud-cpp
that will contain utility functions across products, such as a platform abstraction library and include such methods?
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.
Created #100 to track this.
67da59d
to
036f7d7
Compare
PTAL. Rebased, addressed comments, and opened bug to do the thread safety right the next time. |
Also add a whopper of a TODO() item.
Add TODO with link to a proper bug.
036f7d7
to
2b58421
Compare
auto actual = tested.ListTables(btproto::Table::FULL); | ||
std::string instance_name = static_cast<std::string>(tested.instance_name()); | ||
ASSERT_EQ(2UL, actual.size()); | ||
EXPECT_EQ(instance_name + "/tables/t0", actual[0].name()); |
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.
Using std::string::operator+()
in a few tests is probably OK, but for non-test code, we should probably be using abseil::StrCat()
, I hear it's faster. :-)
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.
Ack.
bigtable/admin/admin_client.cc
Outdated
#include <absl/memory/memory.h> | ||
|
||
namespace { | ||
//github.com/ An implementation of the bigtable::AdminClient interface. |
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 is basically an English version of the next line; maybe rewrite it in terms of what it can vs. cannot do, instead of the mechanics of how it did this?
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.
bigtable/admin/table_admin.cc
Outdated
namespace bigtable { | ||
inline namespace BIGTABLE_CLIENT_NS { | ||
std::string TableAdmin::CreateInstanceName() const { | ||
std::string result("projects/"); |
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.
Please use abseil::StrCat
here instead, without a temporary var.
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.
That requires compiling abseil
. Let me take a look, but it is not trivial.
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. That function better be amazing fast, because it drags an interesting set of dependencies.
bigtable/client/client_options.h
Outdated
|
||
//github.com/ Return the current endpoint for data RPCs. | ||
const std::string& data_endpoint() const { return data_endpoint_; } | ||
ClientOptions& SetDataEndpoint(std::string endpoint) { |
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 not be set_data_endpoint()
since it's a trivial setter method? Same for set_admin_endpoint
below.
CamelCase implies a non-trivial / expensive implementation.
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.
And that is one of the wrong things with the Google Style Guide naming conventions, they leak details about the implementation in the name of the functions.
class ClientOptionsEmulatorTest : public ::testing::Test { | ||
protected: | ||
void SetUp() override { | ||
// TODO(#100) - setenv() is a Unix specific call ... |
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.
s/ ..././
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.
void SetUp() override { | ||
// TODO(#100) - setenv() is a Unix specific call ... | ||
setenv("BIGTABLE_EMULATOR_HOST", "testendpoint.googleapis.com", 1); | ||
previous_ = std::getenv("BIGTABLE_EMULATOR_HOST"); |
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.
Don't you want to call getenv()
before you call setenv()
?
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.
Oops.
setenv("BIGTABLE_EMULATOR_HOST", "testendpoint.googleapis.com", 1); | ||
previous_ = std::getenv("BIGTABLE_EMULATOR_HOST"); | ||
} | ||
void TearDown() override { unsetenv("BIGTABLE_EMULATOR_HOST"); } |
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.
If previous_
is non-null, did you want to restore it here?
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.
Double oops.
PTAL. |
The last commit did not disable it, and my local testing failed because of cached results.
Caching hid this problem from me (again!).
I was not thinking when I removed it. Left a comment for future unthinking me.
Now for realsies. The builds passed. Note to self: do not add dependencies in the middle of a PR. |
CMakeLists.txt
Outdated
@@ -104,7 +104,7 @@ elseif ("${GOOGLE_CLOUD_CPP_GRPC_PROVIDER}" STREQUAL "package") | |||
add_subdirectory(googletest EXCLUDE_FROM_ALL) | |||
endif () | |||
|
|||
# Enable unit tests. | |||
# We need to enable testing in this directory so we can do a top-level `make test`. |
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.
Drop the "We need to" prefix, it's cleaner.
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.
bigtable/admin/admin_client.cc
Outdated
* | ||
* This class should not be used by multiple threads, it makes no attempt to | ||
* protect its critical sections. While it is rare that the admin interface | ||
* will be used by multiple threads we should use the same approach here and in |
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.
s/threads we/threads, we/
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.
bigtable/admin/admin_client.cc
Outdated
table_admin() override; | ||
|
||
private: | ||
void refresh_credentials_and_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.
This seems like it's not a simple getter or setter (and it makes network requests!), so presumably, its name should be RefreshCredentialsAndChannel()
, no?
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.
bigtable/admin/table_admin_test.cc
Outdated
return [expected_token, returned_token]( | ||
grpc::ClientContext* ctx, btproto::ListTablesRequest const& request, | ||
btproto::ListTablesResponse* response) { | ||
EXPECT_EQ("projects/the-project/instances/the-instance", request.parent()); |
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 line and a line below have an implicit dependency on a constant "the-project" defined in the test fixture, and the constant "the-instance" as used in every test.
At a minimum, there should be a comment that these dependencies exist.
In addition, maybe the project and instance should be constants in this file outside of the test fixture class, so that they can be referenced from the test fixture, this lambda, and test cases symbolically to show the relationship? Thoughts?
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.
Let me know if I missed something.
bigtable/admin/table_admin_test.cc
Outdated
|
||
EXPECT_NE(nullptr, response); | ||
auto& t0 = *response->add_tables(); | ||
t0.set_name("projects/the-project/instances/the-instance/tables/t0"); |
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.
Ditto here and for t1
below.
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.
Fixed.
auto actual = tested.ListTables(btproto::Table::FULL); | ||
std::string instance_name = static_cast<std::string>(tested.instance_name()); | ||
ASSERT_EQ(4UL, actual.size()); | ||
EXPECT_EQ(instance_name + "/tables/t0", actual[0].name()); |
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.
It's not quite clear, without reading create_list_tables_lambda()
, why this is the case that there are 4 returned values, of which 2 are repeated, and may confuse future readers, because this might be technically invalid output to have repeated tables.
Also, at first, I expected "but-wait-theres-more" to be a table and appear in the output, since test inputs should be related to expected test outputs somehow, but that's not the case here (since those are just continuation tokens, not expected outputs).
Would it make sense to modify create_list_tables_lambda()
to accept a list of tables to be returned as a parameter, so that there's something meaningful in the test body that appears in the output? This will also allow the tables to be unique.
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.
Fixed.
class ClientOptionsEmulatorTest : public ::testing::Test { | ||
protected: | ||
void SetUp() override { | ||
// TODO(#100) - setenv() is a Unix specific call. |
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.
Same comment in TearDown()
?
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.
EXPECT_EQ(typeid(grpc::InsecureChannelCredentials()), | ||
typeid(client_options_object.credentials())); | ||
unsetenv("BIGTABLE_EMULATOR_HOST"); | ||
} | ||
|
||
TEST(ClientOptionsTest, EditEndpoint) { |
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.
s/EditEndpoint/EditDataEndpoint/ for clarity.
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.
@@ -119,7 +119,7 @@ WARN_LOGFILE = | |||
# Configuration options related to the input files | |||
#--------------------------------------------------------------------------- | |||
|
|||
INPUT = README.md doc client | |||
INPUT = README.md doc client admin |
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.
Technically, both Data API and Admin API interfaces are "clients". While earlier, having just one client (for data) meant that client
= "data client" and that was OK, now that you're adding an admin client, having client
= "data client" while admin
= "admin client" is a bit confusing.
Can we make this explicit and have a data_client
and admin_client
?
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.
Created #103 to track this. I would prefer not to make another big change in this PR.
cmake/IncludeGMock.cmake
Outdated
PUBLIC ${PROJECT_THIRD_PARTY_DIR}/googletest/googletest | ||
PUBLIC ${PROJECT_THIRD_PARTY_DIR}/googletest/googlemock/include | ||
PUBLIC ${PROJECT_THIRD_PARTY_DIR}/googletest/googlemock) | ||
|
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.
Extra blank line?
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.
Fixed.
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.
LGTM, one minor comment below. Let me know if you want me to re-review after you address it.
bigtable/admin/table_admin_test.cc
Outdated
}; | ||
EXPECT_CALL(*table_admin_stub_, ListTables(_, _, _)) | ||
.WillOnce(Invoke(mock_unrecoverable_failure)); | ||
// We expect the TableAdmin to make 5 calls and to let the client know about |
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 "5" in this line is in stark contrast to the "1" on the next line — should this be updated?
This is part of the fixes for #79. While this is not needed for the MVP milestone it might help us with some of our integration tests. In this PR we just introduce the
AdminClient
andTableAdmin
classes, andTableAdmin
has a singleListTables
member function.Future PRs will introduce additional member functions to implement the full API for
TableAdmin
.