Skip to content
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

feat(spanner): add the final pieces for the RouteToLeaderOption #11112

Merged
merged 4 commits into from
Mar 28, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
#include "google/cloud/spanner/benchmarks/benchmarks_config.h"
#include "google/cloud/spanner/client.h"
#include "google/cloud/spanner/internal/defaults.h"
#include "google/cloud/spanner/internal/route_to_leader.h"
#include "google/cloud/spanner/internal/session_pool.h"
#include "google/cloud/spanner/internal/spanner_stub.h"
#include "google/cloud/spanner/testing/pick_random_instance.h"
@@ -539,6 +540,7 @@ class ReadExperiment : public BasicExperiment<Traits> {
Status last_status;
for (int i = 0; i != 10; ++i) {
grpc::ClientContext context;
spanner_internal::RouteToLeader(context);
google::spanner::v1::CreateSessionRequest request{};
request.set_database(database.FullName());
auto response = stub->CreateSession(context, request);
@@ -678,6 +680,7 @@ class SelectExperiment : public BasicExperiment<Traits> {
Status last_status;
for (int i = 0; i != ExperimentImpl<Traits>::kColumnCount; ++i) {
grpc::ClientContext context;
spanner_internal::RouteToLeader(context);
google::spanner::v1::CreateSessionRequest request{};
request.set_database(database.FullName());
auto response = stub->CreateSession(context, request);
@@ -837,6 +840,7 @@ class UpdateExperiment : public BasicExperiment<Traits> {
Status last_status;
for (int i = 0; i != 10; ++i) {
grpc::ClientContext context;
spanner_internal::RouteToLeader(context);
google::spanner::v1::CreateSessionRequest request{};
request.set_database(database.FullName());
auto response = stub->CreateSession(context, request);
@@ -1022,6 +1026,7 @@ class MutationExperiment : public BasicExperiment<Traits> {
Status last_status;
for (int i = 0; i != 10; ++i) {
grpc::ClientContext context;
spanner_internal::RouteToLeader(context);
google::spanner::v1::CreateSessionRequest request{};
request.set_database(database.FullName());
auto response = stub->CreateSession(context, request);
31 changes: 23 additions & 8 deletions google/cloud/spanner/internal/connection_impl.cc
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@
#include "google/cloud/spanner/internal/logging_result_set_reader.h"
#include "google/cloud/spanner/internal/partial_result_set_resume.h"
#include "google/cloud/spanner/internal/partial_result_set_source.h"
#include "google/cloud/spanner/internal/route_to_leader.h"
#include "google/cloud/spanner/internal/status_utils.h"
#include "google/cloud/spanner/options.h"
#include "google/cloud/spanner/query_partition.h"
@@ -387,8 +388,10 @@ StatusOr<google::spanner::v1::Transaction> ConnectionImpl::BeginTransaction(
auto response = RetryLoop(
RetryPolicyPrototype()->clone(), BackoffPolicyPrototype()->clone(),
Idempotency::kIdempotent,
[&stub](grpc::ClientContext& context,
google::spanner::v1::BeginTransactionRequest const& request) {
[&stub, route_to_leader = ctx.route_to_leader](
grpc::ClientContext& context,
google::spanner::v1::BeginTransactionRequest const& request) {
if (route_to_leader) RouteToLeader(context);
return stub->BeginTransaction(context, request);
},
begin, func);
@@ -442,11 +445,13 @@ spanner::RowStream ConnectionImpl::ReadImpl(
auto stub = session_pool_->GetStub(*session);
auto const tracing_enabled = RpcStreamTracingEnabled();
auto const& tracing_options = RpcTracingOptions();
auto factory = [stub, request, tracing_enabled,
auto factory = [stub, request, route_to_leader = ctx.route_to_leader,
tracing_enabled,
tracing_options](std::string const& resume_token) mutable {
if (!resume_token.empty()) request->set_resume_token(resume_token);
auto context = std::make_shared<grpc::ClientContext>();
internal::ConfigureContext(*context, internal::CurrentOptions());
if (route_to_leader) RouteToLeader(*context);
auto grpc_reader = stub->StreamingRead(*context, *request);
std::unique_ptr<PartialResultSetReader> reader =
std::make_unique<DefaultPartialResultSetReader>(std::move(context),
@@ -526,6 +531,7 @@ StatusOr<std::vector<spanner::ReadPartition>> ConnectionImpl::PartitionReadImpl(
Idempotency::kIdempotent,
[&stub](grpc::ClientContext& context,
google::spanner::v1::PartitionReadRequest const& request) {
RouteToLeader(context);
return stub->PartitionRead(context, request);
},
request, __func__);
@@ -668,14 +674,16 @@ ResultType ConnectionImpl::CommonQueryImpl(
auto const tracing_enabled = RpcStreamTracingEnabled();
auto const& tracing_options = RpcTracingOptions();
auto retry_resume_fn =
[stub, retry_policy_prototype, backoff_policy_prototype, tracing_enabled,
[stub, retry_policy_prototype, backoff_policy_prototype,
route_to_leader = ctx.route_to_leader, tracing_enabled,
tracing_options](google::spanner::v1::ExecuteSqlRequest& request) mutable
-> StatusOr<std::unique_ptr<ResultSourceInterface>> {
auto factory = [stub, request, tracing_enabled,
auto factory = [stub, request, route_to_leader, tracing_enabled,
tracing_options](std::string const& resume_token) mutable {
if (!resume_token.empty()) request.set_resume_token(resume_token);
auto context = std::make_shared<grpc::ClientContext>();
internal::ConfigureContext(*context, internal::CurrentOptions());
if (route_to_leader) RouteToLeader(*context);
auto grpc_reader = stub->ExecuteStreamingSql(*context, request);
std::unique_ptr<PartialResultSetReader> reader =
std::make_unique<DefaultPartialResultSetReader>(
@@ -745,13 +753,16 @@ StatusOr<ResultType> ConnectionImpl::CommonDmlImpl(

auto retry_resume_fn =
[function_name, stub, retry_policy_prototype, backoff_policy_prototype,
session](google::spanner::v1::ExecuteSqlRequest& request) mutable
session, route_to_leader = ctx.route_to_leader](
google::spanner::v1::ExecuteSqlRequest& request) mutable
-> StatusOr<std::unique_ptr<ResultSourceInterface>> {
StatusOr<google::spanner::v1::ResultSet> response = RetryLoop(
retry_policy_prototype->clone(), backoff_policy_prototype->clone(),
Idempotency::kIdempotent,
[stub](grpc::ClientContext& context,
google::spanner::v1::ExecuteSqlRequest const& request) {
[stub, route_to_leader](
grpc::ClientContext& context,
google::spanner::v1::ExecuteSqlRequest const& request) {
if (route_to_leader) RouteToLeader(context);
return stub->ExecuteSql(context, request);
},
request, function_name);
@@ -830,6 +841,7 @@ ConnectionImpl::PartitionQueryImpl(
Idempotency::kIdempotent,
[&stub](grpc::ClientContext& context,
google::spanner::v1::PartitionQueryRequest const& request) {
RouteToLeader(context);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why no if (route_to_leader) 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.

Because PartitionQuery() calls should be unconditionally routed to the leader.

Copy link
Contributor

Choose a reason for hiding this comment

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

Consider a comment (here and the other places where it is not conditional). You probably won't forget that, I will.

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 stub->PartitionQuery(context, request);
},
request, __func__);
@@ -904,6 +916,7 @@ StatusOr<spanner::BatchDmlResult> ConnectionImpl::ExecuteBatchDmlImpl(
Idempotency::kIdempotent,
[&stub](grpc::ClientContext& context,
google::spanner::v1::ExecuteBatchDmlRequest const& request) {
RouteToLeader(context);
return stub->ExecuteBatchDml(context, request);
},
request, __func__);
@@ -1027,6 +1040,7 @@ StatusOr<spanner::CommitResult> ConnectionImpl::CommitImpl(
Idempotency::kIdempotent,
[&stub](grpc::ClientContext& context,
google::spanner::v1::CommitRequest const& request) {
RouteToLeader(context);
return stub->Commit(context, request);
},
request, __func__);
@@ -1090,6 +1104,7 @@ Status ConnectionImpl::RollbackImpl(
Idempotency::kIdempotent,
[&stub](grpc::ClientContext& context,
google::spanner::v1::RollbackRequest const& request) {
RouteToLeader(context);
return stub->Rollback(context, request);
},
request, __func__);
15 changes: 15 additions & 0 deletions google/cloud/spanner/internal/defaults.cc
Original file line number Diff line number Diff line change
@@ -105,6 +105,20 @@ Options DefaultOptions(Options opts) {
(std::min)(min_sessions, max_sessions_per_channel * num_channels);

if (!opts.has<spanner::RouteToLeaderOption>()) {
#if 1
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want to leave the '#if here or just let version control store the original version?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought this was a little easier/clearer, but I'm happy to go the other way. Done.

// TODO(#11111): Enable on-by-default behavior.
opts.set<spanner::RouteToLeaderOption>(false);
if (auto e = internal::GetEnv("GOOGLE_CLOUD_CPP_SPANNER_ROUTE_TO_LEADER")) {
Copy link
Contributor

Choose a reason for hiding this comment

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

You have previously argued that environment variables should override the values set in the code. This does not seem to do that, 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.

You're correct that this environment variable is different ... it is for overriding the default value rather than a value set in code. Let me think about that and get back to you on whether that difference is a feature or a bug. Thanks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK ... on further thought I concur that this environment variable should follow the "env > user > default" model. It is supposed to be a "Big Red Button" for use when things go wrong (after we restore the on-by-default behavior), so the environment should have highest priority. Thanks. PTAL.

That said, there are environment variables that only supply a default value, which should be overridden by a user setting ... "user > env > default/empty/null. This very file has two: SPANNER_OPTIMIZER_VERSION and SPANNER_OPTIMIZER_STATISTICS_PACKAGE. So, we should be careful to distinguish how the environment is used in each case.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ac. SGTM. We may want to consider some updates to our ADR regarding this, but that can happen elsewhere.

for (auto const* enable : {"Y", "y", "T", "t", "1", "on"}) {
if (*e == enable) {
// Change the default to "for RW/PartitionedDml transactions"
// from "never".
opts.unset<spanner::RouteToLeaderOption>(); // == true
break;
}
}
}
#else
if (auto e = internal::GetEnv("GOOGLE_CLOUD_CPP_SPANNER_ROUTE_TO_LEADER")) {
for (auto const* disable : {"N", "n", "F", "f", "0", "off"}) {
if (*e == disable) {
@@ -115,6 +129,7 @@ Options DefaultOptions(Options opts) {
}
}
}
#endif
}

return opts;
15 changes: 14 additions & 1 deletion google/cloud/spanner/internal/defaults_test.cc
Original file line number Diff line number Diff line change
@@ -85,7 +85,13 @@ TEST(Options, Defaults) {
EXPECT_TRUE(opts.has<SpannerBackoffPolicyOption>());
EXPECT_TRUE(opts.has<spanner_internal::SessionPoolClockOption>());

#if 1
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto regarding #if vs. version control.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ditto response. Done.

// TODO(#11111): Enable on-by-default behavior.
ASSERT_TRUE(opts.has<spanner::RouteToLeaderOption>());
EXPECT_FALSE(opts.get<spanner::RouteToLeaderOption>());
#else
EXPECT_FALSE(opts.has<spanner::RouteToLeaderOption>());
#endif
}

TEST(Options, AdminDefaults) {
@@ -154,14 +160,21 @@ TEST(Options, SpannerEmulatorHost) {
EXPECT_NE(opts.get<GrpcCredentialOption>(), nullptr);
}

TEST(Options, RouteToLeaderFromEnv) {
TEST(Options, RouteToLeaderFromEnvOff) {
testing_util::ScopedEnvironment route_to_leader_env(
"GOOGLE_CLOUD_CPP_SPANNER_ROUTE_TO_LEADER", "off");
auto opts = spanner_internal::DefaultOptions();
EXPECT_TRUE(opts.has<spanner::RouteToLeaderOption>());
EXPECT_FALSE(opts.get<spanner::RouteToLeaderOption>());
}

TEST(Options, RouteToLeaderFromEnvOn) {
testing_util::ScopedEnvironment route_to_leader_env(
"GOOGLE_CLOUD_CPP_SPANNER_ROUTE_TO_LEADER", "on");
auto opts = spanner_internal::DefaultOptions();
EXPECT_FALSE(opts.has<spanner::RouteToLeaderOption>());
}

TEST(Options, TracingComponentsFromEnv) {
testing_util::ScopedEnvironment tracing_components_env(
"GOOGLE_CLOUD_CPP_ENABLE_TRACING", "c1,c2,c3");
4 changes: 4 additions & 0 deletions google/cloud/spanner/internal/session_pool.cc
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
// limitations under the License.

#include "google/cloud/spanner/internal/session_pool.h"
#include "google/cloud/spanner/internal/route_to_leader.h"
#include "google/cloud/spanner/internal/session.h"
#include "google/cloud/spanner/internal/status_utils.h"
#include "google/cloud/spanner/options.h"
@@ -396,6 +397,7 @@ Status SessionPool::CreateSessionsSync(
google::cloud::Idempotency::kIdempotent,
[&stub](grpc::ClientContext& context,
google::spanner::v1::BatchCreateSessionsRequest const& request) {
RouteToLeader(context);
return stub->BatchCreateSessions(context, request);
},
request, __func__);
@@ -455,6 +457,7 @@ SessionPool::AsyncBatchCreateSessions(
Idempotency::kIdempotent, cq,
[stub](CompletionQueue& cq, std::shared_ptr<grpc::ClientContext> context,
google::spanner::v1::BatchCreateSessionsRequest const& request) {
RouteToLeader(*context);
return stub->AsyncBatchCreateSessions(cq, std::move(context), request);
},
std::move(request), __func__);
@@ -491,6 +494,7 @@ SessionPool::AsyncRefreshSession(CompletionQueue& cq,
Idempotency::kIdempotent, cq,
[stub](CompletionQueue& cq, std::shared_ptr<grpc::ClientContext> context,
google::spanner::v1::ExecuteSqlRequest const& request) {
// Read-only transaction, so no route-to-leader.
return stub->AsyncExecuteSql(cq, std::move(context), request);
},
std::move(request), __func__);