From d4866c00404b8385693101c2a0aa57d4d3b925ca Mon Sep 17 00:00:00 2001 From: Carlos O'Ryan Date: Sun, 24 Nov 2019 10:54:58 -0500 Subject: [PATCH 1/2] feat: allow users to configure retry policies Add an overload for `spanner::MakeConnection()` which allows users to override the retry and backoff policies. Implement an example showing how to use this overload. --- google/cloud/spanner/client.cc | 11 ++++++- google/cloud/spanner/client.h | 14 ++++++++ google/cloud/spanner/doc/spanner-main.dox | 2 ++ google/cloud/spanner/samples/samples.cc | 39 +++++++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/google/cloud/spanner/client.cc b/google/cloud/spanner/client.cc index c90138bb..2ec2103d 100644 --- a/google/cloud/spanner/client.cc +++ b/google/cloud/spanner/client.cc @@ -221,13 +221,22 @@ StatusOr Client::ExecutePartitionedDml( std::shared_ptr MakeConnection(Database const& db, ConnectionOptions const& options) { + return MakeConnection(db, options, internal::DefaultConnectionRetryPolicy(), + internal::DefaultConnectionBackoffPolicy()); +} + +std::shared_ptr MakeConnection( + Database const& db, ConnectionOptions const& options, + std::unique_ptr retry_policy, + std::unique_ptr backoff_policy) { std::vector> stubs; int num_channels = std::min(options.num_channels(), 1); stubs.reserve(num_channels); for (int channel_id = 0; channel_id < num_channels; ++channel_id) { stubs.push_back(internal::CreateDefaultSpannerStub(options, channel_id)); } - return internal::MakeConnection(db, std::move(stubs)); + return internal::MakeConnection(db, std::move(stubs), std::move(retry_policy), + std::move(backoff_policy)); } } // namespace SPANNER_CLIENT_NS diff --git a/google/cloud/spanner/client.h b/google/cloud/spanner/client.h index 217aa5b9..acd85a2b 100644 --- a/google/cloud/spanner/client.h +++ b/google/cloud/spanner/client.h @@ -540,6 +540,20 @@ class Client { std::shared_ptr MakeConnection( Database const& db, ConnectionOptions const& options = ConnectionOptions()); +/** + * @copydoc MakeConnection(Database const&, ConnectionOptions const&) + * + * @param retry_policy override the default `RetryPolicy`, controls for how long + * does the returned `Connection` object retry requests on transient + * failures. + * @param backoff_policy override the default `BackoffPolicy`, controls for how + * long does the `Connection` object waits before retrying a failed request. + */ +std::shared_ptr MakeConnection( + Database const& db, ConnectionOptions const& options, + std::unique_ptr retry_policy, + std::unique_ptr backoff_policy); + } // namespace SPANNER_CLIENT_NS } // namespace spanner } // namespace cloud diff --git a/google/cloud/spanner/doc/spanner-main.dox b/google/cloud/spanner/doc/spanner-main.dox index 43d98bdd..f42c2b22 100644 --- a/google/cloud/spanner/doc/spanner-main.dox +++ b/google/cloud/spanner/doc/spanner-main.dox @@ -223,6 +223,8 @@ The default policies are to continue retrying for up to 15 minutes, and to use truncated (at 5 minutes) exponential backoff, doubling the maximum backoff period between retries. +@snippet samples.cc custom-retry-policy + @see [LimitedTimeRetryPolicy](@ref google::cloud::spanner::v0::LimitedTimeRetryPolicy) and [LimitedErrorCountRetryPolicy](@ref google::cloud::spanner::v0::LimitedErrorCountRetryPolicy) for alternative retry policies. diff --git a/google/cloud/spanner/samples/samples.cc b/google/cloud/spanner/samples/samples.cc index 5436c285..ee93e317 100644 --- a/google/cloud/spanner/samples/samples.cc +++ b/google/cloud/spanner/samples/samples.cc @@ -1605,6 +1605,44 @@ void ExampleStatusOr(google::cloud::spanner::Client client) { (std::move(client)); } +void CustomRetryPolicy(std::vector argv) { + if (argv.size() != 3) { + throw std::runtime_error( + "custom-retry-policy "); + } + //! [custom-retry-policy] + namespace spanner = google::cloud::spanner; + [](std::string const& project_id, std::string const& instance_id, + std::string const& database_id) { + auto client = spanner::Client(spanner::MakeConnection( + spanner::Database(project_id, instance_id, database_id), + spanner::ConnectionOptions{}, + // Retry for at most 25 minutes. + spanner::LimitedTimeRetryPolicy( + /*maximum_duration=*/std::chrono::minutes(25)) + .clone(), + // Use an truncated exponential backoff with jitter to wait between + // retries: + // https://en.wikipedia.org/wiki/Exponential_backoff + // https://cloud.google.com/storage/docs/exponential-backoff + spanner::ExponentialBackoffPolicy( + /*initial_delay=*/std::chrono::seconds(2), + /*maximum_delay=*/std::chrono::minutes(10), + /*scaling=*/1.5) + .clone())); + + auto rows = + client.ExecuteQuery(spanner::SqlStatement("SELECT 'Hello World'")); + + for (auto const& row : spanner::StreamOf>(rows)) { + if (!row) throw std::runtime_error(row.status().message()); + std::cout << std::get<0>(*row) << "\n"; + } + } + //! [custom-retry-policy] + (argv[0], argv[1], argv[2]); +} + class RemoteConnectionFake { public: void SendBinaryStringData(std::string const& serialized_partition) { @@ -1835,6 +1873,7 @@ int RunOneCommand(std::vector argv) { make_command_entry("partition-read", &PartitionRead), make_command_entry("partition-query", &PartitionQuery), make_command_entry("example-status-or", &ExampleStatusOr), + {"custom-retry-policy", &CustomRetryPolicy}, }; static std::string usage_msg = [&argv, &commands] { From 3755477d6ced9388f902fe8812a0c89d4506255d Mon Sep 17 00:00:00 2001 From: Carlos O'Ryan Date: Sun, 24 Nov 2019 11:48:35 -0500 Subject: [PATCH 2/2] I forgot to actually run the example. --- google/cloud/spanner/samples/samples.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/google/cloud/spanner/samples/samples.cc b/google/cloud/spanner/samples/samples.cc index ee93e317..d5456d77 100644 --- a/google/cloud/spanner/samples/samples.cc +++ b/google/cloud/spanner/samples/samples.cc @@ -2125,6 +2125,10 @@ void RunAll() { std::cout << "\nRunning example-status-or sample\n"; ExampleStatusOr(client); + std::cout << "\nRunning custom-retry-policy sample\n"; + RunOneCommand( + {"", "custom-retry-policy", project_id, instance_id, database_id}); + std::cout << "\nRunning spanner_dml_partitioned_update sample\n"; DmlPartitionedUpdate(client);