Content-Length: 1106216 | pFad | http://github.com/googleapis/google-cloud-cpp/pull/93

2D Start work on a minimal admin client. by coryan · Pull Request #93 · googleapis/google-cloud-cpp · GitHub
Skip to content

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

Merged
merged 28 commits into from
Dec 18, 2017

Conversation

coryan
Copy link
Contributor

@coryan coryan commented Dec 12, 2017

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 and TableAdmin classes, and TableAdmin has a single ListTables member function.

Future PRs will introduce additional member functions to implement the full API for TableAdmin.

@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label Dec 12, 2017
mutable std::mutex mu_;
std::shared_ptr<grpc::Channel> channel_;
std::unique_ptr<
google::bigtable::admin::v2::BigtableTableAdmin::StubInterface>
Copy link
Contributor

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.

Copy link
Contributor Author

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&
Copy link
Contributor

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.

Copy link
Contributor Author

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.

}

void SimpleAdminClient::refresh_credentials_and_channel() {
std::unique_lock<std::mutex> lk(mu_);
Copy link
Contributor

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?

Copy link
Contributor Author

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.

private:
std::string project_;
bigtable::ClientOptions options_;
mutable std::mutex mu_;
Copy link
Contributor

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

Copy link
Contributor Author

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.

if (table_admin_stub_) {
return;
}
// Release the lock before executing potentially slow operations.
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -0,0 +1,30 @@
// Copyright 2017 Google Inc.
Copy link
Contributor

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.

@@ -0,0 +1,137 @@
// Copyright 2017 Google Inc.
Copy link
Contributor

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.

Copy link
Contributor Author

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);
Copy link
Contributor

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.

Copy link
Contributor Author

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 ...
Copy link
Contributor

Choose a reason for hiding this comment

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

Abseil uses:

#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?

Copy link
Contributor Author

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.

@coryan coryan force-pushed the minimal-admin-client branch from 67da59d to 036f7d7 Compare December 14, 2017 12:05
@coryan
Copy link
Contributor Author

coryan commented Dec 14, 2017

PTAL. Rebased, addressed comments, and opened bug to do the thread safety right the next time.

@coryan coryan force-pushed the minimal-admin-client branch from 036f7d7 to 2b58421 Compare December 14, 2017 12:11
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());
Copy link
Contributor

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. :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ack.

#include <absl/memory/memory.h>

namespace {
//github.com/ An implementation of the bigtable::AdminClient interface.
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

namespace bigtable {
inline namespace BIGTABLE_CLIENT_NS {
std::string TableAdmin::CreateInstanceName() const {
std::string result("projects/");
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.


//github.com/ Return the current endpoint for data RPCs.
const std::string& data_endpoint() const { return data_endpoint_; }
ClientOptions& SetDataEndpoint(std::string endpoint) {
Copy link
Contributor

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.

Copy link
Contributor Author

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 ...
Copy link
Contributor

Choose a reason for hiding this comment

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

s/ ..././

Copy link
Contributor Author

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");
Copy link
Contributor

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()?

Copy link
Contributor Author

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"); }
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Double oops.

@coryan
Copy link
Contributor Author

coryan commented Dec 14, 2017

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.
@coryan
Copy link
Contributor Author

coryan commented Dec 14, 2017

Now for realsies. The builds passed. Note to self: do not add dependencies in the middle of a PR.
PTAL.

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`.
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

*
* 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
Copy link
Contributor

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/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

table_admin() override;

private:
void refresh_credentials_and_channel();
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

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());
Copy link
Contributor

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?

Copy link
Contributor Author

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.


EXPECT_NE(nullptr, response);
auto& t0 = *response->add_tables();
t0.set_name("projects/the-project/instances/the-instance/tables/t0");
Copy link
Contributor

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.

Copy link
Contributor Author

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());
Copy link
Contributor

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.

Copy link
Contributor Author

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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment in TearDown()?

Copy link
Contributor Author

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) {
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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?

Copy link
Contributor Author

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.

PUBLIC ${PROJECT_THIRD_PARTY_DIR}/googletest/googletest
PUBLIC ${PROJECT_THIRD_PARTY_DIR}/googletest/googlemock/include
PUBLIC ${PROJECT_THIRD_PARTY_DIR}/googletest/googlemock)

Copy link
Contributor

Choose a reason for hiding this comment

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

Extra blank line?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Contributor

@mbrukman mbrukman left a 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.

};
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
Copy link
Contributor

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?

@coryan coryan merged commit c19f9cb into googleapis:master Dec 18, 2017
@coryan coryan deleted the minimal-admin-client branch December 18, 2017 22:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes This human has signed the Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/googleapis/google-cloud-cpp/pull/93

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy