diff --git a/ci/abi-dumps/google_cloud_cpp_bigtable.expected.abi.dump.gz b/ci/abi-dumps/google_cloud_cpp_bigtable.expected.abi.dump.gz
index 3c36194ec7649..2a875f740c79a 100644
Binary files a/ci/abi-dumps/google_cloud_cpp_bigtable.expected.abi.dump.gz and b/ci/abi-dumps/google_cloud_cpp_bigtable.expected.abi.dump.gz differ
diff --git a/doc/v3-migration-guide.md b/doc/v3-migration-guide.md
index 9421abcddc5fc..cc8b4135b7d22 100644
--- a/doc/v3-migration-guide.md
+++ b/doc/v3-migration-guide.md
@@ -105,6 +105,7 @@ for (auto& row : table.ReadRows(
+
Removed bigtable::AsyncRowReader<>::NO_ROWS_LIMIT
@@ -121,6 +122,19 @@ auto limit = google::cloud::bigtable::RowReader::NO_ROWS_LIMIT;
+
+Removed bigtable::DataClient and related functions
+
+The `bigtable::DataClient` class and its associated factory functions (e.g.,
+`MakeDataClient`) have been removed. Applications should now use
+`bigtable::DataConnection` and `bigtable::MakeDataConnection()` instead. For
+detailed migration steps and examples, please refer to the official migration
+guide:
+
+[Migrating from DataClient to DataConnection](https://docs.cloud.google.com/cpp/docs/reference/bigtable/latest/migrating-from-dataclient)
+
+
+
### Pubsub
### Spanner
diff --git a/examples/gcs2cbt.cc b/examples/gcs2cbt.cc
index 0b1d5ab889556..4405a2cb8d28b 100644
--- a/examples/gcs2cbt.cc
+++ b/examples/gcs2cbt.cc
@@ -60,8 +60,9 @@ int main(int argc, char* argv[]) try {
// Create a connection to Cloud Bigtable and an object to manipulate the
// specific table used in this demo.
- cbt::Table table(cbt::MakeDataClient(options.project_id, options.instance_id),
- options.table_id);
+ cbt::Table table(cbt::MakeDataConnection(),
+ cbt::TableResource(options.project_id, options.instance_id,
+ options.table_id));
cbt::MutationBatcher batcher(table);
// How often do we print a progress marker ('.') in the reader thread.
diff --git a/google/cloud/bigtable/CMakeLists.txt b/google/cloud/bigtable/CMakeLists.txt
index d294576aed5f9..0086ee24b0e9d 100644
--- a/google/cloud/bigtable/CMakeLists.txt
+++ b/google/cloud/bigtable/CMakeLists.txt
@@ -122,8 +122,6 @@ add_library(
cluster_list_responses.h
column_family.h
completion_queue.h
- data_client.cc
- data_client.h
data_connection.cc
data_connection.h
expr.cc
@@ -194,16 +192,6 @@ add_library(
internal/defaults.h
internal/google_bytes_traits.cc
internal/google_bytes_traits.h
- internal/legacy_async_bulk_apply.cc
- internal/legacy_async_bulk_apply.h
- internal/legacy_async_row_reader.cc
- internal/legacy_async_row_reader.h
- internal/legacy_async_row_sampler.cc
- internal/legacy_async_row_sampler.h
- internal/legacy_row_reader.cc
- internal/legacy_row_reader.h
- internal/logging_data_client.cc
- internal/logging_data_client.h
internal/logging_result_set_reader.cc
internal/logging_result_set_reader.h
internal/metrics.cc
@@ -402,11 +390,8 @@ if (BUILD_TESTING)
testing/cleanup_stale_resources.h
testing/embedded_server_test_fixture.cc
testing/embedded_server_test_fixture.h
- testing/inprocess_data_client.cc
- testing/inprocess_data_client.h
testing/mock_async_failing_rpc_factory.h
testing/mock_bigtable_stub.h
- testing/mock_data_client.h
testing/mock_mutate_rows_limiter.h
testing/mock_mutate_rows_reader.h
testing/mock_partial_result_set_reader.h
@@ -417,9 +402,7 @@ if (BUILD_TESTING)
testing/random_names.cc
testing/random_names.h
testing/table_integration_test.cc
- testing/table_integration_test.h
- testing/table_test_fixture.cc
- testing/table_test_fixture.h)
+ testing/table_integration_test.h)
target_link_libraries(
bigtable_client_testing
PUBLIC google-cloud-cpp::bigtable
@@ -455,7 +438,6 @@ if (BUILD_TESTING)
client_test.cc
cluster_config_test.cc
column_family_test.cc
- data_client_test.cc
data_connection_test.cc
expr_test.cc
filters_test.cc
@@ -484,12 +466,6 @@ if (BUILD_TESTING)
internal/default_row_reader_test.cc
internal/defaults_test.cc
internal/google_bytes_traits_test.cc
- internal/legacy_async_bulk_apply_test.cc
- internal/legacy_async_row_reader_test.cc
- internal/legacy_async_row_sampler_test.cc
- internal/legacy_bulk_mutator_test.cc
- internal/legacy_row_reader_test.cc
- internal/logging_data_client_test.cc
internal/logging_result_set_reader_test.cc
internal/metrics_test.cc
internal/mutate_rows_limiter_test.cc
@@ -503,7 +479,6 @@ if (BUILD_TESTING)
internal/retry_traits_test.cc
internal/traced_row_reader_test.cc
internal/tuple_utils_test.cc
- legacy_table_test.cc
metadata_update_policy_test.cc
mocks/mock_row_reader_test.cc
mutation_batcher_test.cc
@@ -521,15 +496,8 @@ if (BUILD_TESTING)
rpc_retry_policy_test.cc
sql_statement_test.cc
table_admin_test.cc
- table_apply_test.cc
- table_bulk_apply_test.cc
- table_check_and_mutate_row_test.cc
table_config_test.cc
- table_readmodifywriterow_test.cc
- table_readrow_test.cc
- table_readrows_test.cc
table_resource_test.cc
- table_sample_row_keys_test.cc
table_test.cc
testing/cleanup_stale_resources_test.cc
testing/random_names_test.cc
diff --git a/google/cloud/bigtable/benchmarks/benchmark.cc b/google/cloud/bigtable/benchmarks/benchmark.cc
index 87e9baac9b657..d758bb6b09714 100644
--- a/google/cloud/bigtable/benchmarks/benchmark.cc
+++ b/google/cloud/bigtable/benchmarks/benchmark.cc
@@ -16,6 +16,7 @@
#include "google/cloud/bigtable/admin/bigtable_table_admin_client.h"
#include "google/cloud/bigtable/benchmarks/random_mutation.h"
#include "google/cloud/bigtable/resource_names.h"
+#include "google/cloud/grpc_options.h"
#include "google/cloud/internal/background_threads_impl.h"
#include "google/cloud/internal/getenv.h"
#include "google/cloud/internal/make_status.h"
diff --git a/google/cloud/bigtable/benchmarks/embedded_server_test.cc b/google/cloud/bigtable/benchmarks/embedded_server_test.cc
index 9f11bacf0f3a7..79c956d7f5a7f 100644
--- a/google/cloud/bigtable/benchmarks/embedded_server_test.cc
+++ b/google/cloud/bigtable/benchmarks/embedded_server_test.cc
@@ -16,6 +16,7 @@
#include "google/cloud/bigtable/admin/bigtable_table_admin_client.h"
#include "google/cloud/bigtable/resource_names.h"
#include "google/cloud/bigtable/table.h"
+#include "google/cloud/grpc_options.h"
#include "google/cloud/testing_util/status_matchers.h"
#include
#include
diff --git a/google/cloud/bigtable/bigtable_client_testing.bzl b/google/cloud/bigtable/bigtable_client_testing.bzl
index 865460e3aead3..2146dbf028f1a 100644
--- a/google/cloud/bigtable/bigtable_client_testing.bzl
+++ b/google/cloud/bigtable/bigtable_client_testing.bzl
@@ -19,10 +19,8 @@
bigtable_client_testing_hdrs = [
"testing/cleanup_stale_resources.h",
"testing/embedded_server_test_fixture.h",
- "testing/inprocess_data_client.h",
"testing/mock_async_failing_rpc_factory.h",
"testing/mock_bigtable_stub.h",
- "testing/mock_data_client.h",
"testing/mock_mutate_rows_limiter.h",
"testing/mock_mutate_rows_reader.h",
"testing/mock_partial_result_set_reader.h",
@@ -32,14 +30,11 @@ bigtable_client_testing_hdrs = [
"testing/mock_sample_row_keys_reader.h",
"testing/random_names.h",
"testing/table_integration_test.h",
- "testing/table_test_fixture.h",
]
bigtable_client_testing_srcs = [
"testing/cleanup_stale_resources.cc",
"testing/embedded_server_test_fixture.cc",
- "testing/inprocess_data_client.cc",
"testing/random_names.cc",
"testing/table_integration_test.cc",
- "testing/table_test_fixture.cc",
]
diff --git a/google/cloud/bigtable/bigtable_client_unit_tests.bzl b/google/cloud/bigtable/bigtable_client_unit_tests.bzl
index c5dda043da0a4..10d64955cc798 100644
--- a/google/cloud/bigtable/bigtable_client_unit_tests.bzl
+++ b/google/cloud/bigtable/bigtable_client_unit_tests.bzl
@@ -28,7 +28,6 @@ bigtable_client_unit_tests = [
"client_test.cc",
"cluster_config_test.cc",
"column_family_test.cc",
- "data_client_test.cc",
"data_connection_test.cc",
"expr_test.cc",
"filters_test.cc",
@@ -57,12 +56,6 @@ bigtable_client_unit_tests = [
"internal/default_row_reader_test.cc",
"internal/defaults_test.cc",
"internal/google_bytes_traits_test.cc",
- "internal/legacy_async_bulk_apply_test.cc",
- "internal/legacy_async_row_reader_test.cc",
- "internal/legacy_async_row_sampler_test.cc",
- "internal/legacy_bulk_mutator_test.cc",
- "internal/legacy_row_reader_test.cc",
- "internal/logging_data_client_test.cc",
"internal/logging_result_set_reader_test.cc",
"internal/metrics_test.cc",
"internal/mutate_rows_limiter_test.cc",
@@ -76,7 +69,6 @@ bigtable_client_unit_tests = [
"internal/retry_traits_test.cc",
"internal/traced_row_reader_test.cc",
"internal/tuple_utils_test.cc",
- "legacy_table_test.cc",
"metadata_update_policy_test.cc",
"mocks/mock_row_reader_test.cc",
"mutation_batcher_test.cc",
@@ -94,15 +86,8 @@ bigtable_client_unit_tests = [
"rpc_retry_policy_test.cc",
"sql_statement_test.cc",
"table_admin_test.cc",
- "table_apply_test.cc",
- "table_bulk_apply_test.cc",
- "table_check_and_mutate_row_test.cc",
"table_config_test.cc",
- "table_readmodifywriterow_test.cc",
- "table_readrow_test.cc",
- "table_readrows_test.cc",
"table_resource_test.cc",
- "table_sample_row_keys_test.cc",
"table_test.cc",
"testing/cleanup_stale_resources_test.cc",
"testing/random_names_test.cc",
diff --git a/google/cloud/bigtable/data_client.cc b/google/cloud/bigtable/data_client.cc
deleted file mode 100644
index 9932d24c541e7..0000000000000
--- a/google/cloud/bigtable/data_client.cc
+++ /dev/null
@@ -1,242 +0,0 @@
-// Copyright 2017 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "google/cloud/bigtable/data_client.h"
-#include "google/cloud/bigtable/internal/common_client.h"
-#include "google/cloud/bigtable/internal/logging_data_client.h"
-#include "google/cloud/internal/log_wrapper.h"
-#include "google/cloud/log.h"
-
-namespace google {
-namespace cloud {
-namespace bigtable {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-
-namespace btproto = ::google::bigtable::v2;
-
-std::unique_ptr<
- ::grpc::ClientAsyncReaderInterface>
-// NOLINTNEXTLINE(performance-unnecessary-value-param)
-DataClient::PrepareAsyncSampleRowKeys(grpc::ClientContext*,
- btproto::SampleRowKeysRequest const&,
- grpc::CompletionQueue*) {
- return nullptr;
-}
-
-namespace {
-
-// TODO(#8800) - remove after `DataClient` deprecation is complete
-#include "google/cloud/internal/disable_deprecation_warnings.inc"
-
-/**
- * Implement a simple DataClient.
- *
- * This implementation does not support multiple threads, or refresh
- * authorization tokens. In other words, it is extremely bare bones.
- */
-class DefaultDataClient : public DataClient {
- public:
- DefaultDataClient(std::string project, std::string instance,
- Options options = {})
- : project_(std::move(project)),
- instance_(std::move(instance)),
- authority_(options.get()),
- user_project_(
- options.has()
- ? absl::make_optional(options.get())
- : absl::nullopt),
- impl_(std::move(options)) {}
-
- std::string const& project_id() const override { return project_; };
- std::string const& instance_id() const override { return instance_; };
-
- std::shared_ptr Channel() override { return impl_.Channel(); }
- void reset() override { impl_.reset(); }
-
- grpc::Status MutateRow(grpc::ClientContext* context,
- btproto::MutateRowRequest const& request,
- btproto::MutateRowResponse* response) override {
- ApplyOptions(context);
- return impl_.Stub()->MutateRow(context, request, response);
- }
-
- std::unique_ptr<
- grpc::ClientAsyncResponseReaderInterface>
- AsyncMutateRow(grpc::ClientContext* context,
- btproto::MutateRowRequest const& request,
- grpc::CompletionQueue* cq) override {
- ApplyOptions(context);
- return impl_.Stub()->AsyncMutateRow(context, request, cq);
- }
-
- grpc::Status CheckAndMutateRow(
- grpc::ClientContext* context,
- btproto::CheckAndMutateRowRequest const& request,
- btproto::CheckAndMutateRowResponse* response) override {
- ApplyOptions(context);
- return impl_.Stub()->CheckAndMutateRow(context, request, response);
- }
-
- std::unique_ptr>
- AsyncCheckAndMutateRow(grpc::ClientContext* context,
- btproto::CheckAndMutateRowRequest const& request,
- grpc::CompletionQueue* cq) override {
- ApplyOptions(context);
- return impl_.Stub()->AsyncCheckAndMutateRow(context, request, cq);
- }
-
- grpc::Status ReadModifyWriteRow(
- grpc::ClientContext* context,
- btproto::ReadModifyWriteRowRequest const& request,
- btproto::ReadModifyWriteRowResponse* response) override {
- ApplyOptions(context);
- return impl_.Stub()->ReadModifyWriteRow(context, request, response);
- }
-
- std::unique_ptr>
- AsyncReadModifyWriteRow(grpc::ClientContext* context,
- btproto::ReadModifyWriteRowRequest const& request,
- grpc::CompletionQueue* cq) override {
- ApplyOptions(context);
- return impl_.Stub()->AsyncReadModifyWriteRow(context, request, cq);
- }
-
- std::unique_ptr>
- ReadRows(grpc::ClientContext* context,
- btproto::ReadRowsRequest const& request) override {
- ApplyOptions(context);
- return impl_.Stub()->ReadRows(context, request);
- }
-
- std::unique_ptr>
- AsyncReadRows(grpc::ClientContext* context,
- btproto::ReadRowsRequest const& request,
- grpc::CompletionQueue* cq, void* tag) override {
- ApplyOptions(context);
- return impl_.Stub()->AsyncReadRows(context, request, cq, tag);
- }
-
- std::unique_ptr<::grpc::ClientAsyncReaderInterface>
- PrepareAsyncReadRows(grpc::ClientContext* context,
- btproto::ReadRowsRequest const& request,
- grpc::CompletionQueue* cq) override {
- ApplyOptions(context);
- return impl_.Stub()->PrepareAsyncReadRows(context, request, cq);
- }
-
- std::unique_ptr>
- SampleRowKeys(grpc::ClientContext* context,
- btproto::SampleRowKeysRequest const& request) override {
- ApplyOptions(context);
- return impl_.Stub()->SampleRowKeys(context, request);
- }
-
- std::unique_ptr<
- ::grpc::ClientAsyncReaderInterface>
- AsyncSampleRowKeys(grpc::ClientContext* context,
- btproto::SampleRowKeysRequest const& request,
- grpc::CompletionQueue* cq, void* tag) override {
- ApplyOptions(context);
- return impl_.Stub()->AsyncSampleRowKeys(context, request, cq, tag);
- }
-
- std::unique_ptr<
- ::grpc::ClientAsyncReaderInterface>
- PrepareAsyncSampleRowKeys(grpc::ClientContext* context,
- btproto::SampleRowKeysRequest const& request,
- grpc::CompletionQueue* cq) override {
- ApplyOptions(context);
- return impl_.Stub()->PrepareAsyncSampleRowKeys(context, request, cq);
- }
-
- std::unique_ptr>
- MutateRows(grpc::ClientContext* context,
- btproto::MutateRowsRequest const& request) override {
- ApplyOptions(context);
- return impl_.Stub()->MutateRows(context, request);
- }
-
- std::unique_ptr<
- ::grpc::ClientAsyncReaderInterface>
- AsyncMutateRows(grpc::ClientContext* context,
- btproto::MutateRowsRequest const& request,
- grpc::CompletionQueue* cq, void* tag) override {
- ApplyOptions(context);
- return impl_.Stub()->AsyncMutateRows(context, request, cq, tag);
- }
-
- std::unique_ptr<
- ::grpc::ClientAsyncReaderInterface>
- PrepareAsyncMutateRows(grpc::ClientContext* context,
- btproto::MutateRowsRequest const& request,
- grpc::CompletionQueue* cq) override {
- ApplyOptions(context);
- return impl_.Stub()->PrepareAsyncMutateRows(context, request, cq);
- }
-
- private:
- google::cloud::BackgroundThreadsFactory BackgroundThreadsFactory() override {
- return impl_.BackgroundThreadsFactory();
- }
-
- void ApplyOptions(grpc::ClientContext* context) {
- if (!authority_.empty()) context->set_authority(authority_);
- if (user_project_) {
- context->AddMetadata("x-goog-user-project", *user_project_);
- }
- }
-
- std::string project_;
- std::string instance_;
- std::string authority_;
- absl::optional user_project_;
- internal::CommonClient impl_;
-};
-
-// TODO(#8800) - remove after `DataClient` deprecation is complete
-#include "google/cloud/internal/diagnostics_pop.inc"
-
-} // namespace
-
-std::shared_ptr MakeDataClient(std::string project_id,
- std::string instance_id,
- Options options) {
- options = internal::DefaultDataOptions(std::move(options));
- bool tracing_enabled = google::cloud::internal::Contains(
- options.get(), "rpc");
- auto tracing_options = options.get();
-
- std::shared_ptr client = std::make_shared(
- std::move(project_id), std::move(instance_id), std::move(options));
- if (tracing_enabled) {
- GCP_LOG(INFO) << "Enabled logging for gRPC calls";
- client = std::make_shared(
- std::move(client), std::move(tracing_options));
- }
- return client;
-}
-
-std::shared_ptr CreateDefaultDataClient(std::string project_id,
- std::string instance_id,
- ClientOptions options) {
- return MakeDataClient(std::move(project_id), std::move(instance_id),
- internal::MakeOptions(std::move(options)));
-}
-
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
-} // namespace bigtable
-} // namespace cloud
-} // namespace google
diff --git a/google/cloud/bigtable/data_client.h b/google/cloud/bigtable/data_client.h
deleted file mode 100644
index eb7f376cc4483..0000000000000
--- a/google/cloud/bigtable/data_client.h
+++ /dev/null
@@ -1,235 +0,0 @@
-// Copyright 2017 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_DATA_CLIENT_H
-#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_DATA_CLIENT_H
-
-#include "google/cloud/bigtable/client_options.h"
-#include "google/cloud/bigtable/completion_queue.h"
-#include "google/cloud/bigtable/row.h"
-#include "google/cloud/bigtable/version.h"
-#include "google/bigtable/v2/bigtable.grpc.pb.h"
-#include
-
-namespace google {
-namespace cloud {
-// Forward declare some classes so we can be friends.
-namespace bigtable_internal {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-class BulkMutator;
-class LegacyAsyncRowReader;
-class LegacyRowReader;
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
-} // namespace bigtable_internal
-namespace bigtable {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-class Table;
-namespace internal {
-class AsyncRetryBulkApply;
-class LegacyAsyncRowSampler;
-class LoggingDataClient;
-} // namespace internal
-
-/**
- * Connects to Cloud Bigtable's data manipulation APIs.
- *
- * This class is used by the Cloud Bigtable wrappers to access Cloud Bigtable.
- * Multiple `bigtable::Table` objects may share a connection via a single
- * `DataClient` object. The `DataClient` object is configured at construction
- * time, this configuration includes the credentials, access endpoints, default
- * timeouts, and other gRPC configuration options. This is an interface class
- * because it is also used as a dependency injection point in some of the tests.
- *
- * @par Cost
- * Applications should avoid unnecessarily creating new objects of type
- * `DataClient`. Creating a new object of this type typically requires
- * connecting to the Cloud Bigtable servers, and performing the authentication
- * workflows with Google Cloud Platform. These operations can take many
- * milliseconds, therefore applications should try to reuse the same
- * `DataClient` instances when possible.
- *
- * @deprecated #google::cloud::bigtable::DataConnection is the preferred way to
- * communicate with the Bigtable Data API. To migrate existing code, see
- * @ref migrating-from-dataclient "Migrating from DataClient".
- */
-class DataClient {
- public:
- virtual ~DataClient() = default;
-
- virtual std::string const& project_id() const = 0;
- virtual std::string const& instance_id() const = 0;
-
- /**
- * Return a new channel to handle admin operations.
- *
- * Intended to access rarely used services in the same endpoints as the
- * Bigtable admin interfaces, for example, the google.longrunning.Operations.
- *
- * @deprecated This member function is scheduled for deletion and `DataClient`
- * will be marked as `final`. Do not extend this class. Application
- * developers who need to configure the gRPC Channel can pass any of the
- * following options into `MakeDataClient(...)`:
- * * `google::cloud::GrpcChannelArgumentsOption`
- * * `google::cloud::GrpcChannelArgumentsNativeOption`
- */
- GOOGLE_CLOUD_CPP_BIGTABLE_DATA_CLIENT_DEPRECATED("Channel()")
- virtual std::shared_ptr Channel() = 0;
-
- /**
- * Reset and create new Channels.
- *
- * @deprecated This member function is scheduled for deletion and `DataClient`
- * will be marked as `final`. Do not extend this class. The client library
- * will handle all interactions with the gRPC channels.
- */
- GOOGLE_CLOUD_CPP_BIGTABLE_DATA_CLIENT_DEPRECATED("reset()")
- virtual void reset() = 0;
-
- /**
- * The thread factory this client was created with.
- *
- * @deprecated This member function is scheduled for deletion and `DataClient`
- * will be marked as `final`. Do not extend this class. Application
- * developers who need to configure the background threads can pass any
- * of the following options into `MakeDataClient(...)`:
- * * `google::cloud::GrpcBackgroundThreadPoolSizeOption`
- * * `google::cloud::GrpcCompletionQueueOption`
- * * `google::cloud::GrpcBackgroundThreadFactoryOption`
- */
- virtual google::cloud::BackgroundThreadsFactory
- BackgroundThreadsFactory() = 0;
-
- // The member functions of this class are not intended for general use by
- // application developers (they are simply a dependency injection point). Make
- // them protected, so the mock classes can override them, and then make the
- // classes that do use them friends.
- protected:
- friend class Table;
- friend class internal::AsyncRetryBulkApply;
- friend class internal::LegacyAsyncRowSampler;
- friend class bigtable_internal::BulkMutator;
- friend class bigtable_internal::LegacyRowReader;
- friend class bigtable_internal::LegacyAsyncRowReader;
- friend class internal::LoggingDataClient;
-
- ///@{
- /// @name the `google.bigtable.v2.Bigtable` wrappers.
- virtual grpc::Status MutateRow(
- grpc::ClientContext* context,
- google::bigtable::v2::MutateRowRequest const& request,
- google::bigtable::v2::MutateRowResponse* response) = 0;
- virtual std::unique_ptr>
- AsyncMutateRow(grpc::ClientContext* context,
- google::bigtable::v2::MutateRowRequest const& request,
- grpc::CompletionQueue* cq) = 0;
- virtual grpc::Status CheckAndMutateRow(
- grpc::ClientContext* context,
- google::bigtable::v2::CheckAndMutateRowRequest const& request,
- google::bigtable::v2::CheckAndMutateRowResponse* response) = 0;
- virtual std::unique_ptr>
- AsyncCheckAndMutateRow(
- grpc::ClientContext* context,
- google::bigtable::v2::CheckAndMutateRowRequest const& request,
- grpc::CompletionQueue* cq) = 0;
- virtual grpc::Status ReadModifyWriteRow(
- grpc::ClientContext* context,
- google::bigtable::v2::ReadModifyWriteRowRequest const& request,
- google::bigtable::v2::ReadModifyWriteRowResponse* response) = 0;
- virtual std::unique_ptr>
- AsyncReadModifyWriteRow(
- grpc::ClientContext* context,
- google::bigtable::v2::ReadModifyWriteRowRequest const& request,
- grpc::CompletionQueue* cq) = 0;
- virtual std::unique_ptr<
- grpc::ClientReaderInterface>
- ReadRows(grpc::ClientContext* context,
- google::bigtable::v2::ReadRowsRequest const& request) = 0;
- virtual std::unique_ptr<
- grpc::ClientAsyncReaderInterface>
- AsyncReadRows(grpc::ClientContext* context,
- google::bigtable::v2::ReadRowsRequest const& request,
- grpc::CompletionQueue* cq, void* tag) = 0;
- virtual std::unique_ptr<::grpc::ClientAsyncReaderInterface<
- google::bigtable::v2::ReadRowsResponse>>
- PrepareAsyncReadRows(::grpc::ClientContext* context,
- google::bigtable::v2::ReadRowsRequest const& request,
- grpc::CompletionQueue* cq) = 0;
- virtual std::unique_ptr<
- grpc::ClientReaderInterface>
- SampleRowKeys(grpc::ClientContext* context,
- google::bigtable::v2::SampleRowKeysRequest const& request) = 0;
- virtual std::unique_ptr<::grpc::ClientAsyncReaderInterface<
- google::bigtable::v2::SampleRowKeysResponse>>
- AsyncSampleRowKeys(grpc::ClientContext* context,
- google::bigtable::v2::SampleRowKeysRequest const& request,
- grpc::CompletionQueue* cq, void* tag) = 0;
- virtual std::unique_ptr<::grpc::ClientAsyncReaderInterface<
- google::bigtable::v2::SampleRowKeysResponse>>
- PrepareAsyncSampleRowKeys(
- grpc::ClientContext* context,
- google::bigtable::v2::SampleRowKeysRequest const& request,
- grpc::CompletionQueue* cq);
- virtual std::unique_ptr<
- grpc::ClientReaderInterface>
- MutateRows(grpc::ClientContext* context,
- google::bigtable::v2::MutateRowsRequest const& request) = 0;
- virtual std::unique_ptr<::grpc::ClientAsyncReaderInterface<
- google::bigtable::v2::MutateRowsResponse>>
- AsyncMutateRows(::grpc::ClientContext* context,
- google::bigtable::v2::MutateRowsRequest const& request,
- grpc::CompletionQueue* cq, void* tag) = 0;
- virtual std::unique_ptr<::grpc::ClientAsyncReaderInterface<
- google::bigtable::v2::MutateRowsResponse>>
- PrepareAsyncMutateRows(grpc::ClientContext* context,
- google::bigtable::v2::MutateRowsRequest const& request,
- grpc::CompletionQueue* cq) = 0;
- ///@}
-};
-
-/// Create a new data client configured via @p options.
-std::shared_ptr MakeDataClient(std::string project_id,
- std::string instance_id,
- Options options = {});
-
-/**
- * Create a new data client configured via @p options.
- *
- * @deprecated use the `MakeDataClient` method which accepts
- * `google::cloud::Options` instead.
- */
-GOOGLE_CLOUD_CPP_DEPRECATED("use `MakeDataClient` instead")
-std::shared_ptr CreateDefaultDataClient(std::string project_id,
- std::string instance_id,
- ClientOptions options);
-
-/**
- * Return the fully qualified instance name for the @p client.
- *
- * Compute the full path of the instance associated with the client, i.e.,
- * `projects/instances/project_id()>/instances/instance_id()>`
- */
-inline std::string InstanceName(std::shared_ptr const& client) {
- return "projects/" + client->project_id() + "/instances/" +
- client->instance_id();
-}
-
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
-} // namespace bigtable
-} // namespace cloud
-} // namespace google
-
-#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_DATA_CLIENT_H
diff --git a/google/cloud/bigtable/data_client_test.cc b/google/cloud/bigtable/data_client_test.cc
deleted file mode 100644
index 2fcc28caad549..0000000000000
--- a/google/cloud/bigtable/data_client_test.cc
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2017 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "google/cloud/bigtable/data_client.h"
-#include "google/cloud/bigtable/internal/logging_data_client.h"
-#include "google/cloud/testing_util/scoped_environment.h"
-#include
-
-namespace google {
-namespace cloud {
-namespace bigtable {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-namespace {
-
-// TODO(#8800) - remove after `DataClient` deprecation is complete
-#include "google/cloud/internal/disable_deprecation_warnings.inc"
-
-TEST(DataClientTest, Default) {
- auto data_client =
- CreateDefaultDataClient("test-project", "test-instance",
- ClientOptions().set_connection_pool_size(1));
- ASSERT_TRUE(data_client);
- EXPECT_EQ("test-project", data_client->project_id());
- EXPECT_EQ("test-instance", data_client->instance_id());
-
- auto channel0 = data_client->Channel();
- EXPECT_TRUE(channel0);
-
- auto channel1 = data_client->Channel();
- EXPECT_EQ(channel0.get(), channel1.get());
-
- data_client->reset();
- channel1 = data_client->Channel();
- EXPECT_TRUE(channel1);
- EXPECT_NE(channel0.get(), channel1.get());
-}
-
-TEST(DataClientTest, MakeClient) {
- auto data_client = MakeDataClient("test-project", "test-instance",
- Options{}.set(1));
- ASSERT_TRUE(data_client);
- EXPECT_EQ("test-project", data_client->project_id());
- EXPECT_EQ("test-instance", data_client->instance_id());
-
- auto channel0 = data_client->Channel();
- EXPECT_TRUE(channel0);
-
- auto channel1 = data_client->Channel();
- EXPECT_EQ(channel0.get(), channel1.get());
-
- data_client->reset();
- channel1 = data_client->Channel();
- EXPECT_TRUE(channel1);
- EXPECT_NE(channel0.get(), channel1.get());
-}
-
-// TODO(#8800) - remove after `DataClient` deprecation is complete
-#include "google/cloud/internal/diagnostics_pop.inc"
-
-TEST(DataClientTest, Logging) {
- testing_util::ScopedEnvironment env("GOOGLE_CLOUD_CPP_ENABLE_TRACING", "rpc");
-
- auto data_client = MakeDataClient("test-project", "test-instance");
- ASSERT_TRUE(data_client);
- ASSERT_TRUE(
- dynamic_cast(data_client.get()))
- << "Should create LoggingDataClient";
-}
-
-} // namespace
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
-} // namespace bigtable
-} // namespace cloud
-} // namespace google
diff --git a/google/cloud/bigtable/examples/bigtable_instance_admin_snippets.cc b/google/cloud/bigtable/examples/bigtable_instance_admin_snippets.cc
index 0f7984a08ad8b..3ae4f9e1c1c2c 100644
--- a/google/cloud/bigtable/examples/bigtable_instance_admin_snippets.cc
+++ b/google/cloud/bigtable/examples/bigtable_instance_admin_snippets.cc
@@ -23,6 +23,7 @@
#include "google/cloud/location.h"
#include "google/cloud/log.h"
#include "google/cloud/project.h"
+#include "absl/strings/match.h"
#include
namespace {
diff --git a/google/cloud/bigtable/examples/data_snippets.cc b/google/cloud/bigtable/examples/data_snippets.cc
index 1b849dbd47a87..f261a2acdc796 100644
--- a/google/cloud/bigtable/examples/data_snippets.cc
+++ b/google/cloud/bigtable/examples/data_snippets.cc
@@ -17,6 +17,7 @@
#include "google/cloud/bigtable/resource_names.h"
#include "google/cloud/bigtable/testing/cleanup_stale_resources.h"
#include "google/cloud/bigtable/testing/random_names.h"
+#include "google/cloud/grpc_options.h"
//! [bigtable includes]
#include "google/cloud/bigtable/client.h"
#include "google/cloud/bigtable/table.h"
diff --git a/google/cloud/bigtable/google_cloud_cpp_bigtable.bzl b/google/cloud/bigtable/google_cloud_cpp_bigtable.bzl
index 59b2c36e1e004..2f1aa364db1cb 100644
--- a/google/cloud/bigtable/google_cloud_cpp_bigtable.bzl
+++ b/google/cloud/bigtable/google_cloud_cpp_bigtable.bzl
@@ -56,7 +56,6 @@ google_cloud_cpp_bigtable_hdrs = [
"cluster_list_responses.h",
"column_family.h",
"completion_queue.h",
- "data_client.h",
"data_connection.h",
"expr.h",
"filters.h",
@@ -95,11 +94,6 @@ google_cloud_cpp_bigtable_hdrs = [
"internal/default_row_reader.h",
"internal/defaults.h",
"internal/google_bytes_traits.h",
- "internal/legacy_async_bulk_apply.h",
- "internal/legacy_async_row_reader.h",
- "internal/legacy_async_row_sampler.h",
- "internal/legacy_row_reader.h",
- "internal/logging_data_client.h",
"internal/logging_result_set_reader.h",
"internal/metrics.h",
"internal/mutate_rows_limiter.h",
@@ -184,7 +178,6 @@ google_cloud_cpp_bigtable_srcs = [
"client.cc",
"client_options.cc",
"cluster_config.cc",
- "data_client.cc",
"data_connection.cc",
"expr.cc",
"iam_binding.cc",
@@ -217,11 +210,6 @@ google_cloud_cpp_bigtable_srcs = [
"internal/default_row_reader.cc",
"internal/defaults.cc",
"internal/google_bytes_traits.cc",
- "internal/legacy_async_bulk_apply.cc",
- "internal/legacy_async_row_reader.cc",
- "internal/legacy_async_row_sampler.cc",
- "internal/legacy_row_reader.cc",
- "internal/logging_data_client.cc",
"internal/logging_result_set_reader.cc",
"internal/metrics.cc",
"internal/mutate_rows_limiter.cc",
diff --git a/google/cloud/bigtable/internal/async_bulk_apply.cc b/google/cloud/bigtable/internal/async_bulk_apply.cc
index e19c3a0ffddfb..1039aa862b30b 100644
--- a/google/cloud/bigtable/internal/async_bulk_apply.cc
+++ b/google/cloud/bigtable/internal/async_bulk_apply.cc
@@ -14,6 +14,7 @@
#include "google/cloud/bigtable/internal/async_bulk_apply.h"
#include "google/cloud/bigtable/internal/async_streaming_read.h"
+#include "google/cloud/grpc_options.h"
#include "google/cloud/internal/grpc_opentelemetry.h"
#include "google/cloud/internal/retry_loop_helpers.h"
diff --git a/google/cloud/bigtable/internal/async_bulk_apply_test.cc b/google/cloud/bigtable/internal/async_bulk_apply_test.cc
index 930eed5281915..f860d202df592 100644
--- a/google/cloud/bigtable/internal/async_bulk_apply_test.cc
+++ b/google/cloud/bigtable/internal/async_bulk_apply_test.cc
@@ -16,6 +16,7 @@
#include "google/cloud/bigtable/internal/operation_context.h"
#include "google/cloud/bigtable/testing/mock_bigtable_stub.h"
#include "google/cloud/bigtable/testing/mock_mutate_rows_limiter.h"
+#include "google/cloud/grpc_options.h"
#include "google/cloud/internal/async_streaming_read_rpc_impl.h"
#include "google/cloud/internal/background_threads_impl.h"
#ifdef GOOGLE_CLOUD_CPP_BIGTABLE_WITH_OTEL_METRICS
diff --git a/google/cloud/bigtable/internal/bulk_mutator.cc b/google/cloud/bigtable/internal/bulk_mutator.cc
index 7203d23fbd14d..fad9d4342563a 100644
--- a/google/cloud/bigtable/internal/bulk_mutator.cc
+++ b/google/cloud/bigtable/internal/bulk_mutator.cc
@@ -15,6 +15,7 @@
#include "google/cloud/bigtable/internal/bulk_mutator.h"
#include "google/cloud/bigtable/rpc_retry_policy.h"
#include "google/cloud/bigtable/table.h"
+#include "google/cloud/grpc_options.h"
#include "google/cloud/internal/make_status.h"
#include "google/cloud/log.h"
#include
@@ -195,22 +196,6 @@ BulkMutator::BulkMutator(std::string const& app_profile_id,
: state_(app_profile_id, table_name, idempotent_policy, std::move(mut)),
operation_context_(std::move(operation_context)) {}
-grpc::Status BulkMutator::MakeOneRequest(bigtable::DataClient& client,
- grpc::ClientContext& client_context) {
- // Send the request to the server.
- auto const& mutations = state_.BeforeStart();
- auto stream = client.MutateRows(&client_context, mutations);
- // Read the stream of responses.
- btproto::MutateRowsResponse response;
- while (stream->Read(&response)) {
- state_.OnRead(std::move(response));
- }
- // Handle any errors in the stream.
- auto grpc_status = stream->Finish();
- state_.OnFinish(MakeStatusFromRpcError(grpc_status));
- return grpc_status;
-}
-
Status BulkMutator::MakeOneRequest(BigtableStub& stub,
MutateRowsLimiter& limiter,
Options const& options) {
diff --git a/google/cloud/bigtable/internal/bulk_mutator.h b/google/cloud/bigtable/internal/bulk_mutator.h
index bd543dc6f0ccc..922c9caeab426 100644
--- a/google/cloud/bigtable/internal/bulk_mutator.h
+++ b/google/cloud/bigtable/internal/bulk_mutator.h
@@ -16,12 +16,12 @@
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_INTERNAL_BULK_MUTATOR_H
#include "google/cloud/bigtable/completion_queue.h"
-#include "google/cloud/bigtable/data_client.h"
#include "google/cloud/bigtable/idempotent_mutation_policy.h"
#include "google/cloud/bigtable/internal/bigtable_stub.h"
#include "google/cloud/bigtable/internal/mutate_rows_limiter.h"
#include "google/cloud/bigtable/internal/operation_context.h"
#include "google/cloud/bigtable/version.h"
+#include "google/cloud/idempotency.h"
#include "google/cloud/internal/invoke_result.h"
#include "google/cloud/status.h"
#include
@@ -89,7 +89,7 @@ class BulkMutatorState {
* request provided by the application.
*/
int original_index;
- Idempotency idempotency;
+ google::cloud::Idempotency idempotency;
/// Set to `false` if the result is unknown.
bool has_mutation_result;
/**
@@ -123,10 +123,6 @@ class BulkMutator {
/// Return true if there are pending mutations in the mutator
bool HasPendingMutations() const { return state_.HasPendingMutations(); }
- /// Synchronously send one batch request to the given stub.
- grpc::Status MakeOneRequest(bigtable::DataClient& client,
- grpc::ClientContext& client_context);
-
/// Synchronously send one batch request to the given stub.
Status MakeOneRequest(BigtableStub& stub, MutateRowsLimiter& limiter,
Options const& options);
diff --git a/google/cloud/bigtable/internal/bulk_mutator_test.cc b/google/cloud/bigtable/internal/bulk_mutator_test.cc
index 7724da5f21501..e7bf9f7433e9d 100644
--- a/google/cloud/bigtable/internal/bulk_mutator_test.cc
+++ b/google/cloud/bigtable/internal/bulk_mutator_test.cc
@@ -14,6 +14,7 @@
#include "google/cloud/bigtable/internal/bulk_mutator.h"
#include "google/cloud/bigtable/internal/operation_context.h"
+#include "google/cloud/grpc_options.h"
#ifdef GOOGLE_CLOUD_CPP_BIGTABLE_WITH_OTEL_METRICS
#include "google/cloud/bigtable/internal/metrics.h"
#include "google/cloud/testing_util/fake_clock.h"
diff --git a/google/cloud/bigtable/internal/default_row_reader.cc b/google/cloud/bigtable/internal/default_row_reader.cc
index 844bdace38dd2..7cb45dad72e67 100644
--- a/google/cloud/bigtable/internal/default_row_reader.cc
+++ b/google/cloud/bigtable/internal/default_row_reader.cc
@@ -15,6 +15,7 @@
#include "google/cloud/bigtable/internal/default_row_reader.h"
#include "google/cloud/bigtable/table.h"
#include "google/cloud/grpc_error_delegate.h"
+#include "google/cloud/grpc_options.h"
#include "google/cloud/internal/make_status.h"
#include "google/cloud/internal/retry_loop_helpers.h"
diff --git a/google/cloud/bigtable/internal/default_row_reader_test.cc b/google/cloud/bigtable/internal/default_row_reader_test.cc
index bcbd97dfc077e..9c282ad0660cf 100644
--- a/google/cloud/bigtable/internal/default_row_reader_test.cc
+++ b/google/cloud/bigtable/internal/default_row_reader_test.cc
@@ -16,6 +16,7 @@
#include "google/cloud/bigtable/row_reader.h"
#include "google/cloud/bigtable/testing/mock_bigtable_stub.h"
#include "google/cloud/bigtable/testing/mock_policies.h"
+#include "google/cloud/grpc_options.h"
#include "google/cloud/internal/make_status.h"
#ifdef GOOGLE_CLOUD_CPP_BIGTABLE_WITH_OTEL_METRICS
#include "google/cloud/bigtable/internal/metrics.h"
diff --git a/google/cloud/bigtable/internal/legacy_async_bulk_apply.cc b/google/cloud/bigtable/internal/legacy_async_bulk_apply.cc
deleted file mode 100644
index b4d2efa5da9a9..0000000000000
--- a/google/cloud/bigtable/internal/legacy_async_bulk_apply.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "google/cloud/bigtable/internal/legacy_async_bulk_apply.h"
-
-namespace google {
-namespace cloud {
-namespace bigtable {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-namespace internal {
-
-future> AsyncRetryBulkApply::Create(
- CompletionQueue cq, std::unique_ptr rpc_retry_policy,
- std::unique_ptr rpc_backoff_policy,
- IdempotentMutationPolicy& idempotent_policy,
- MetadataUpdatePolicy metadata_update_policy,
- std::shared_ptr client,
- std::string const& app_profile_id, std::string const& table_name,
- BulkMutation mut) {
- if (mut.empty()) return make_ready_future(std::vector{});
-
- std::shared_ptr bulk_apply(new AsyncRetryBulkApply(
- std::move(rpc_retry_policy), std::move(rpc_backoff_policy),
- idempotent_policy, std::move(metadata_update_policy), std::move(client),
- app_profile_id, table_name, std::move(mut)));
- bulk_apply->StartIteration(std::move(cq));
- return bulk_apply->promise_.get_future();
-}
-
-AsyncRetryBulkApply::AsyncRetryBulkApply(
- std::unique_ptr rpc_retry_policy,
- std::unique_ptr rpc_backoff_policy,
- IdempotentMutationPolicy& idempotent_policy,
- MetadataUpdatePolicy metadata_update_policy,
- std::shared_ptr client,
- std::string const& app_profile_id, std::string const& table_name,
- BulkMutation mut)
- : rpc_retry_policy_(std::move(rpc_retry_policy)),
- rpc_backoff_policy_(std::move(rpc_backoff_policy)),
- metadata_update_policy_(std::move(metadata_update_policy)),
- client_(std::move(client)),
- state_(app_profile_id, table_name, idempotent_policy, std::move(mut)) {}
-
-void AsyncRetryBulkApply::StartIteration(CompletionQueue cq) {
- auto context = std::make_unique();
- rpc_retry_policy_->Setup(*context);
- rpc_backoff_policy_->Setup(*context);
- metadata_update_policy_.Setup(*context);
- auto& client = client_;
- auto self = shared_from_this();
- cq.MakeStreamingReadRpc(
- [client](grpc::ClientContext* context,
- google::bigtable::v2::MutateRowsRequest const& request,
- grpc::CompletionQueue* cq) {
- return client->PrepareAsyncMutateRows(context, request, cq);
- },
- state_.BeforeStart(), std::move(context),
- [self, cq](google::bigtable::v2::MutateRowsResponse r) {
- self->OnRead(std::move(r));
- return make_ready_future(true);
- },
- [self, cq](Status const& s) { self->OnFinish(cq, s); });
-}
-
-void AsyncRetryBulkApply::OnRead(
- google::bigtable::v2::MutateRowsResponse response) {
- state_.OnRead(std::move(response));
-}
-
-void AsyncRetryBulkApply::OnFinish(CompletionQueue cq, Status const& status) {
- state_.OnFinish(status);
- if (!state_.HasPendingMutations() || !rpc_retry_policy_->OnFailure(status)) {
- SetPromise();
- return;
- }
-
- auto self = this->shared_from_this();
- cq.MakeRelativeTimer(rpc_backoff_policy_->OnCompletion(status))
- .then([self, cq](auto result) {
- if (result.get()) {
- self->StartIteration(std::move(cq));
- } else {
- self->SetPromise();
- }
- });
-}
-
-void AsyncRetryBulkApply::SetPromise() {
- promise_.set_value(std::move(state_).OnRetryDone());
-}
-
-} // namespace internal
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
-} // namespace bigtable
-} // namespace cloud
-} // namespace google
diff --git a/google/cloud/bigtable/internal/legacy_async_bulk_apply.h b/google/cloud/bigtable/internal/legacy_async_bulk_apply.h
deleted file mode 100644
index f53802f5c50f1..0000000000000
--- a/google/cloud/bigtable/internal/legacy_async_bulk_apply.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_INTERNAL_LEGACY_ASYNC_BULK_APPLY_H
-#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_INTERNAL_LEGACY_ASYNC_BULK_APPLY_H
-
-#include "google/cloud/bigtable/completion_queue.h"
-#include "google/cloud/bigtable/data_client.h"
-#include "google/cloud/bigtable/idempotent_mutation_policy.h"
-#include "google/cloud/bigtable/internal/async_retry_op.h"
-#include "google/cloud/bigtable/internal/bulk_mutator.h"
-#include "google/cloud/bigtable/version.h"
-#include "google/cloud/internal/invoke_result.h"
-#include
-#include
-
-namespace google {
-namespace cloud {
-namespace bigtable {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-namespace internal {
-/**
- * Implement the retry loop for AsyncBulkApply.
- *
- * The retry loop for AsyncBulkApply() is fairly different from all the other
- * retry loops: only those mutations that are idempotent and had a transient
- * failure can be retried, and the result for each mutation arrives in a stream.
- * This class implements that retry loop.
- */
-class AsyncRetryBulkApply
- : public std::enable_shared_from_this {
- public:
- static future> Create(
- CompletionQueue cq, std::unique_ptr rpc_retry_policy,
- std::unique_ptr rpc_backoff_policy,
- IdempotentMutationPolicy& idempotent_policy,
- MetadataUpdatePolicy metadata_update_policy,
- std::shared_ptr client,
- std::string const& app_profile_id, std::string const& table_name,
- BulkMutation mut);
-
- private:
- AsyncRetryBulkApply(std::unique_ptr rpc_retry_policy,
- std::unique_ptr rpc_backoff_policy,
- IdempotentMutationPolicy& idempotent_policy,
- MetadataUpdatePolicy metadata_update_policy,
- std::shared_ptr client,
- std::string const& app_profile_id,
- std::string const& table_name, BulkMutation mut);
-
- void StartIteration(CompletionQueue cq);
- void OnRead(google::bigtable::v2::MutateRowsResponse response);
- void OnFinish(CompletionQueue cq, google::cloud::Status const& status);
- void SetPromise();
-
- std::unique_ptr rpc_retry_policy_;
- std::unique_ptr rpc_backoff_policy_;
- MetadataUpdatePolicy metadata_update_policy_;
- std::shared_ptr client_;
- bigtable_internal::BulkMutatorState state_;
- promise> promise_;
-};
-
-} // namespace internal
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
-} // namespace bigtable
-} // namespace cloud
-} // namespace google
-
-#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_INTERNAL_LEGACY_ASYNC_BULK_APPLY_H
diff --git a/google/cloud/bigtable/internal/legacy_async_bulk_apply_test.cc b/google/cloud/bigtable/internal/legacy_async_bulk_apply_test.cc
deleted file mode 100644
index 9c2e21f730758..0000000000000
--- a/google/cloud/bigtable/internal/legacy_async_bulk_apply_test.cc
+++ /dev/null
@@ -1,522 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "google/cloud/bigtable/internal/legacy_async_bulk_apply.h"
-#include "google/cloud/bigtable/testing/mock_mutate_rows_reader.h"
-#include "google/cloud/bigtable/testing/mock_policies.h"
-#include "google/cloud/bigtable/testing/table_test_fixture.h"
-#include "google/cloud/future.h"
-#include "google/cloud/internal/api_client_header.h"
-#include "google/cloud/testing_util/chrono_literals.h"
-#include "google/cloud/testing_util/fake_completion_queue_impl.h"
-#include "google/cloud/testing_util/status_matchers.h"
-#include "google/cloud/testing_util/validate_metadata.h"
-#include
-#include
-#include
-
-namespace google {
-namespace cloud {
-namespace bigtable {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-namespace {
-
-namespace btproto = ::google::bigtable::v2;
-
-using ::google::cloud::bigtable::testing::MockBackoffPolicy;
-using ::google::cloud::bigtable::testing::MockClientAsyncReaderInterface;
-using ::google::cloud::testing_util::chrono_literals::operator""_ms;
-using ::google::cloud::testing_util::FakeCompletionQueueImpl;
-using ::google::cloud::testing_util::StatusIs;
-
-class AsyncBulkApplyTest : public bigtable::testing::TableTestFixture {
- protected:
- AsyncBulkApplyTest()
- : TableTestFixture(
- CompletionQueue(std::make_shared())),
- rpc_retry_policy_(
- bigtable::DefaultRPCRetryPolicy(internal::kBigtableLimits)),
- rpc_backoff_policy_(bigtable::DefaultRPCBackoffPolicy(
- internal::kBigtableTableAdminLimits)),
- idempotent_mutation_policy_(
- bigtable::DefaultIdempotentMutationPolicy()),
- metadata_update_policy_("my_table", MetadataParamTypes::NAME) {}
-
- void SimulateIteration() {
- cq_impl_->SimulateCompletion(true);
- // state == PROCESSING
- cq_impl_->SimulateCompletion(true);
- // state == PROCESSING, 1 read
- cq_impl_->SimulateCompletion(false);
- // state == FINISHING
- cq_impl_->SimulateCompletion(true);
- }
-
- std::shared_ptr rpc_retry_policy_;
- std::shared_ptr rpc_backoff_policy_;
- std::shared_ptr idempotent_mutation_policy_;
- MetadataUpdatePolicy metadata_update_policy_;
-};
-
-std::vector StatusOnly(std::vector const& failures) {
- std::vector v;
- std::transform(failures.begin(), failures.end(), std::back_inserter(v),
- [](FailedMutation const& f) { return f.status(); });
- return v;
-}
-
-TEST_F(AsyncBulkApplyTest, NoMutations) {
- bigtable::BulkMutation mut;
-
- auto bulk_apply_future = internal::AsyncRetryBulkApply::Create(
- cq_, rpc_retry_policy_->clone(), rpc_backoff_policy_->clone(),
- *idempotent_mutation_policy_, metadata_update_policy_, client_,
- "my-app-profile", "my-table", std::move(mut));
-
- ASSERT_EQ(0U, bulk_apply_future.get().size());
-}
-
-TEST_F(AsyncBulkApplyTest, Success) {
- bigtable::BulkMutation mut{
- bigtable::SingleRowMutation("foo2",
- {bigtable::SetCell("f", "c", 0_ms, "v2")}),
- bigtable::SingleRowMutation("foo3",
- {bigtable::SetCell("f", "c", 0_ms, "v3")}),
- };
-
- auto* reader =
- new MockClientAsyncReaderInterface;
- EXPECT_CALL(*reader, Read)
- .WillOnce([](btproto::MutateRowsResponse* r, void*) {
- auto& r1 = *r->add_entries();
- r1.set_index(0);
- r1.mutable_status()->set_code(grpc::StatusCode::OK);
-
- auto& r2 = *r->add_entries();
- r2.set_index(1);
- r2.mutable_status()->set_code(grpc::StatusCode::OK);
- })
- .WillOnce([](btproto::MutateRowsResponse*, void*) {});
-
- EXPECT_CALL(*reader, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- EXPECT_CALL(*reader, StartCall).Times(1);
-
- EXPECT_CALL(*client_, PrepareAsyncMutateRows)
- .WillOnce([reader](grpc::ClientContext*,
- btproto::MutateRowsRequest const&,
- grpc::CompletionQueue*) {
- return std::unique_ptr<
- MockClientAsyncReaderInterface>(
- reader);
- })
- .RetiresOnSaturation();
-
- auto bulk_apply_future = internal::AsyncRetryBulkApply::Create(
- cq_, rpc_retry_policy_->clone(), rpc_backoff_policy_->clone(),
- *idempotent_mutation_policy_, metadata_update_policy_, client_,
- "my-app-profile", "my-table", std::move(mut));
-
- bulk_apply_future.then(
- [](future> f) { f.get(); });
-
- ASSERT_EQ(1U, cq_impl_->size());
-
- SimulateIteration();
-
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-TEST_F(AsyncBulkApplyTest, PartialSuccessRetry) {
- bigtable::BulkMutation mut{
- bigtable::SingleRowMutation("foo2",
- {bigtable::SetCell("f", "c", 0_ms, "v2")}),
- bigtable::SingleRowMutation("foo3",
- {bigtable::SetCell("f", "c", 0_ms, "v3")}),
- };
-
- auto* reader0 =
- new MockClientAsyncReaderInterface;
- auto* reader1 =
- new MockClientAsyncReaderInterface;
-
- EXPECT_CALL(*reader0, Read)
- .WillOnce([](btproto::MutateRowsResponse* r, void*) {
- auto& r1 = *r->add_entries();
- r1.set_index(0);
- r1.mutable_status()->set_code(grpc::StatusCode::OK);
- })
- .WillOnce([](btproto::MutateRowsResponse*, void*) {});
-
- EXPECT_CALL(*reader1, Read)
- .WillOnce([](btproto::MutateRowsResponse* r, void*) {
- auto& r1 = *r->add_entries();
- r1.set_index(0);
- r1.mutable_status()->set_code(grpc::StatusCode::OK);
- })
- .WillOnce([](btproto::MutateRowsResponse*, void*) {});
-
- EXPECT_CALL(*reader0, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- EXPECT_CALL(*reader1, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- EXPECT_CALL(*reader0, StartCall).Times(1);
- EXPECT_CALL(*reader1, StartCall).Times(1);
-
- EXPECT_CALL(*client_, PrepareAsyncMutateRows)
- .WillOnce([reader0](grpc::ClientContext*,
- btproto::MutateRowsRequest const&,
- grpc::CompletionQueue*) {
- return std::unique_ptr<
- MockClientAsyncReaderInterface>(
- reader0);
- })
- .WillOnce([reader1](grpc::ClientContext*,
- btproto::MutateRowsRequest const&,
- grpc::CompletionQueue*) {
- return std::unique_ptr<
- MockClientAsyncReaderInterface>(
- reader1);
- });
-
- auto bulk_apply_future = internal::AsyncRetryBulkApply::Create(
- cq_, rpc_retry_policy_->clone(), rpc_backoff_policy_->clone(),
- *idempotent_mutation_policy_, metadata_update_policy_, client_,
- "my-app-profile", "my-table", std::move(mut));
-
- SimulateIteration();
- // simulate the backoff timer
- cq_impl_->SimulateCompletion(true);
-
- ASSERT_EQ(1U, cq_impl_->size());
-
- SimulateIteration();
-
- bulk_apply_future.get();
-
- ASSERT_EQ(0U, cq_impl_->size());
- EXPECT_TRUE(cq_impl_->empty());
-}
-
-TEST_F(AsyncBulkApplyTest, DefaultFailureRetry) {
- bigtable::BulkMutation mut{
- bigtable::SingleRowMutation("foo2",
- {bigtable::SetCell("f", "c", 0_ms, "v2")}),
- bigtable::SingleRowMutation("foo3",
- {bigtable::SetCell("f", "c", 0_ms, "v3")}),
- };
-
- auto* reader0 =
- new MockClientAsyncReaderInterface;
- auto* reader1 =
- new MockClientAsyncReaderInterface;
-
- EXPECT_CALL(*reader0, Read)
- .WillOnce([](btproto::MutateRowsResponse* r, void*) {
- auto& r1 = *r->add_entries();
- r1.set_index(0);
- r1.mutable_status()->set_code(grpc::StatusCode::OK);
- })
- .WillOnce([](btproto::MutateRowsResponse*, void*) {});
-
- EXPECT_CALL(*reader1, Read)
- .WillOnce([](btproto::MutateRowsResponse* r, void*) {
- auto& r1 = *r->add_entries();
- r1.set_index(0);
- r1.mutable_status()->set_code(grpc::StatusCode::OK);
-
- auto& r2 = *r->add_entries();
- r2.set_index(1);
- r2.mutable_status()->set_code(grpc::StatusCode::OK);
- })
- .WillOnce([](btproto::MutateRowsResponse*, void*) {});
-
- EXPECT_CALL(*reader0, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status(grpc::StatusCode::UNAVAILABLE, "");
- });
-
- EXPECT_CALL(*reader1, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- EXPECT_CALL(*reader0, StartCall).Times(1);
- EXPECT_CALL(*reader1, StartCall).Times(1);
-
- EXPECT_CALL(*client_, PrepareAsyncMutateRows)
- .WillOnce([reader0](grpc::ClientContext*,
- btproto::MutateRowsRequest const&,
- grpc::CompletionQueue*) {
- return std::unique_ptr<
- MockClientAsyncReaderInterface>(
- reader0);
- })
- .WillOnce([reader1](grpc::ClientContext*,
- btproto::MutateRowsRequest const&,
- grpc::CompletionQueue*) {
- return std::unique_ptr<
- MockClientAsyncReaderInterface>(
- reader1);
- });
-
- auto bulk_apply_future = internal::AsyncRetryBulkApply::Create(
- cq_, rpc_retry_policy_->clone(), rpc_backoff_policy_->clone(),
- *idempotent_mutation_policy_, metadata_update_policy_, client_,
- "my-app-profile", "my-table", std::move(mut));
-
- SimulateIteration();
- // simulate the backoff timer
- cq_impl_->SimulateCompletion(true);
-
- ASSERT_EQ(1U, cq_impl_->size());
-
- SimulateIteration();
-
- bulk_apply_future.get();
-
- ASSERT_EQ(0U, cq_impl_->size());
-
- EXPECT_TRUE(cq_impl_->empty());
-}
-
-TEST_F(AsyncBulkApplyTest, TooManyFailures) {
- bigtable::BulkMutation mut{
- bigtable::SingleRowMutation("foo2",
- {bigtable::SetCell("f", "c", 0_ms, "v2")}),
- bigtable::SingleRowMutation("foo3",
- {bigtable::SetCell("f", "c", 0_ms, "v3")}),
- };
-
- // We give up on the 3rd error.
- auto constexpr kErrorCount = 2;
-
- EXPECT_CALL(*client_, PrepareAsyncMutateRows)
- .Times(kErrorCount + 1)
- .WillRepeatedly([](grpc::ClientContext*,
- btproto::MutateRowsRequest const&,
- grpc::CompletionQueue*) {
- auto reader = std::make_unique<
- MockClientAsyncReaderInterface>();
- EXPECT_CALL(*reader, Read).Times(2);
- EXPECT_CALL(*reader, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status(grpc::StatusCode::UNAVAILABLE, "try again");
- });
- EXPECT_CALL(*reader, StartCall);
- return reader;
- });
-
- auto limited_retry_policy = LimitedErrorCountRetryPolicy(kErrorCount);
- auto bulk_apply_future = internal::AsyncRetryBulkApply::Create(
- cq_, limited_retry_policy.clone(), rpc_backoff_policy_->clone(),
- *idempotent_mutation_policy_, metadata_update_policy_, client_,
- "my-app-profile", "my-table", std::move(mut));
-
- for (int retry = 0; retry < kErrorCount; ++retry) {
- SimulateIteration();
- // simulate the backoff timer
- cq_impl_->SimulateCompletion(true);
- ASSERT_EQ(1U, cq_impl_->size());
- }
-
- SimulateIteration();
-
- auto failures = StatusOnly(bulk_apply_future.get());
- EXPECT_THAT(failures, ElementsAre(StatusIs(StatusCode::kUnavailable),
- StatusIs(StatusCode::kUnavailable)));
-
- ASSERT_EQ(0U, cq_impl_->size());
- EXPECT_TRUE(cq_impl_->empty());
-}
-
-TEST_F(AsyncBulkApplyTest, RetryPolicyUsedForOkStreamsWithFailedMutations) {
- bigtable::BulkMutation mut{bigtable::SingleRowMutation(
- "row", {bigtable::SetCell("f", "c", 0_ms, "v2")})};
-
- // We give up on the 3rd error.
- auto constexpr kErrorCount = 2;
-
- EXPECT_CALL(*client_, PrepareAsyncMutateRows)
- .Times(kErrorCount + 1)
- .WillRepeatedly([](grpc::ClientContext*,
- btproto::MutateRowsRequest const&,
- grpc::CompletionQueue*) {
- auto reader = std::make_unique<
- MockClientAsyncReaderInterface>();
- EXPECT_CALL(*reader, Read)
- .WillOnce([](btproto::MutateRowsResponse* r, void*) {
- auto& r1 = *r->add_entries();
- r1.set_index(0);
- r1.mutable_status()->set_code(grpc::StatusCode::UNAVAILABLE);
- })
- .WillOnce([](btproto::MutateRowsResponse*, void*) {});
- EXPECT_CALL(*reader, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
- EXPECT_CALL(*reader, StartCall);
- return reader;
- });
-
- auto limited_retry_policy = LimitedErrorCountRetryPolicy(kErrorCount);
- auto bulk_apply_future = internal::AsyncRetryBulkApply::Create(
- cq_, limited_retry_policy.clone(), rpc_backoff_policy_->clone(),
- *idempotent_mutation_policy_, metadata_update_policy_, client_,
- "my-app-profile", "my-table", std::move(mut));
-
- for (int retry = 0; retry != kErrorCount; ++retry) {
- SimulateIteration();
- // simulate the backoff timer
- cq_impl_->SimulateCompletion(true);
- ASSERT_EQ(1U, cq_impl_->size());
- }
-
- SimulateIteration();
-
- auto failures = StatusOnly(bulk_apply_future.get());
- EXPECT_THAT(failures, ElementsAre(StatusIs(StatusCode::kUnavailable)));
-
- ASSERT_EQ(0U, cq_impl_->size());
- EXPECT_TRUE(cq_impl_->empty());
-}
-
-TEST_F(AsyncBulkApplyTest, UsesBackoffPolicy) {
- bigtable::BulkMutation mut{
- bigtable::SingleRowMutation("foo2",
- {bigtable::SetCell("f", "c", 0_ms, "v2")}),
- bigtable::SingleRowMutation("foo3",
- {bigtable::SetCell("f", "c", 0_ms, "v3")}),
- };
-
- auto grpc_error = grpc::Status(grpc::StatusCode::UNAVAILABLE, "try again");
- auto error = MakeStatusFromRpcError(grpc_error);
-
- std::unique_ptr mock(new MockBackoffPolicy);
- EXPECT_CALL(*mock, Setup).Times(2);
- EXPECT_CALL(*mock, OnCompletion(error)).WillOnce([](Status const&) {
- return 10_ms;
- });
-
- EXPECT_CALL(*client_, PrepareAsyncMutateRows)
- .WillOnce([grpc_error](grpc::ClientContext*,
- btproto::MutateRowsRequest const&,
- grpc::CompletionQueue*) {
- auto reader = std::make_unique<
- MockClientAsyncReaderInterface>();
- EXPECT_CALL(*reader, Read).Times(2);
- EXPECT_CALL(*reader, Finish)
- .WillOnce([grpc_error](grpc::Status* status, void*) {
- *status = grpc_error;
- });
- EXPECT_CALL(*reader, StartCall);
- return reader;
- })
- .WillOnce([](grpc::ClientContext*, btproto::MutateRowsRequest const&,
- grpc::CompletionQueue*) {
- auto reader = std::make_unique<
- MockClientAsyncReaderInterface>();
- EXPECT_CALL(*reader, Read)
- .WillOnce([](btproto::MutateRowsResponse* r, void*) {
- auto& r1 = *r->add_entries();
- r1.set_index(0);
- r1.mutable_status()->set_code(grpc::StatusCode::OK);
-
- auto& r2 = *r->add_entries();
- r2.set_index(1);
- r2.mutable_status()->set_code(grpc::StatusCode::OK);
- })
- .WillOnce([](btproto::MutateRowsResponse*, void*) {});
- EXPECT_CALL(*reader, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
- EXPECT_CALL(*reader, StartCall);
- return reader;
- });
-
- auto bulk_apply_future = internal::AsyncRetryBulkApply::Create(
- cq_, rpc_retry_policy_->clone(), std::move(mock),
- *idempotent_mutation_policy_, metadata_update_policy_, client_,
- "my-app-profile", "my-table", std::move(mut));
-
- SimulateIteration();
- // simulate the backoff timer
- cq_impl_->SimulateCompletion(true);
-
- ASSERT_EQ(1U, cq_impl_->size());
-
- SimulateIteration();
-
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-TEST_F(AsyncBulkApplyTest, CancelDuringBackoff) {
- bigtable::BulkMutation mut{
- bigtable::SingleRowMutation("foo2",
- {bigtable::SetCell("f", "c", 0_ms, "v2")}),
- bigtable::SingleRowMutation("foo3",
- {bigtable::SetCell("f", "c", 0_ms, "v3")}),
- };
-
- auto grpc_error = grpc::Status(grpc::StatusCode::UNAVAILABLE, "try again");
- auto error = MakeStatusFromRpcError(grpc_error);
-
- std::unique_ptr mock(new MockBackoffPolicy);
- EXPECT_CALL(*mock, Setup);
- EXPECT_CALL(*mock, OnCompletion(error)).WillOnce([](Status const&) {
- return 10_ms;
- });
-
- EXPECT_CALL(*client_, PrepareAsyncMutateRows)
- .WillOnce([grpc_error](grpc::ClientContext*,
- btproto::MutateRowsRequest const&,
- grpc::CompletionQueue*) {
- auto reader = std::make_unique<
- MockClientAsyncReaderInterface>();
- EXPECT_CALL(*reader, Read).Times(2);
- EXPECT_CALL(*reader, Finish)
- .WillOnce([grpc_error](grpc::Status* status, void*) {
- *status = grpc_error;
- });
- EXPECT_CALL(*reader, StartCall);
- return reader;
- });
-
- auto bulk_apply_future = internal::AsyncRetryBulkApply::Create(
- cq_, rpc_retry_policy_->clone(), std::move(mock),
- *idempotent_mutation_policy_, metadata_update_policy_, client_,
- "my-app-profile", "my-table", std::move(mut));
-
- SimulateIteration();
- ASSERT_EQ(1U, cq_impl_->size());
-
- // cancel the pending operation.
- bulk_apply_future.cancel();
- // simulate the backoff timer expiring.
- cq_impl_->SimulateCompletion(false);
-
- ASSERT_EQ(0U, cq_impl_->size());
-
- auto failures = StatusOnly(bulk_apply_future.get());
- EXPECT_THAT(failures, ElementsAre(StatusIs(StatusCode::kUnavailable),
- StatusIs(StatusCode::kUnavailable)));
-}
-
-} // namespace
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
-} // namespace bigtable
-} // namespace cloud
-} // namespace google
diff --git a/google/cloud/bigtable/internal/legacy_async_row_reader.cc b/google/cloud/bigtable/internal/legacy_async_row_reader.cc
deleted file mode 100644
index 80ce9973850d0..0000000000000
--- a/google/cloud/bigtable/internal/legacy_async_row_reader.cc
+++ /dev/null
@@ -1,281 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "google/cloud/bigtable/internal/legacy_async_row_reader.h"
-#include "google/cloud/bigtable/version.h"
-#include "google/cloud/log.h"
-
-namespace google {
-namespace cloud {
-namespace bigtable_internal {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-
-void LegacyAsyncRowReader::MakeRequest() {
- status_ = Status();
- google::bigtable::v2::ReadRowsRequest request;
-
- request.set_app_profile_id(app_profile_id_);
- request.set_table_name(table_name_);
- auto row_set_proto = row_set_.as_proto();
- request.mutable_rows()->Swap(&row_set_proto);
-
- auto filter_proto = filter_.as_proto();
- request.mutable_filter()->Swap(&filter_proto);
-
- if (rows_limit_ != NO_ROWS_LIMIT) {
- request.set_rows_limit(rows_limit_ - rows_count_);
- }
- parser_ = parser_factory_->Create(false);
-
- auto context = std::make_unique();
- rpc_retry_policy_->Setup(*context);
- rpc_backoff_policy_->Setup(*context);
- metadata_update_policy_.Setup(*context);
-
- auto& client = client_;
- auto self = this->shared_from_this();
- cq_.MakeStreamingReadRpc(
- [client](grpc::ClientContext* context,
- google::bigtable::v2::ReadRowsRequest const& request,
- grpc::CompletionQueue* cq) {
- return client->PrepareAsyncReadRows(context, request, cq);
- },
- request, std::move(context),
- [self](google::bigtable::v2::ReadRowsResponse r) {
- return self->OnDataReceived(std::move(r));
- },
- [self](Status s) { self->OnStreamFinished(std::move(s)); });
-}
-
-void LegacyAsyncRowReader::TryGiveRowToUser() {
- // The user is likely to ask for more rows immediately after receiving a
- // row, which means that this function will be called recursively. The depth
- // of the recursion can be as deep as the size of ready_rows_, which might
- // be significant and potentially lead to stack overflow. The way to
- // overcome this is to always switch thread to a CompletionQueue thread.
- // Switching thread for every row has a non-trivial cost, though. To find a
- // good balance, we allow for recursion no deeper than 100 and achieve it by
- // tracking the level in `recursion_level_`.
- //
- // The magic value 100 is arbitrary, but back-of-the-envelope calculation
- // indicates it should cap this stack usage to below 100K. Default stack
- // size is usually 1MB.
- struct CountFrames {
- explicit CountFrames(int& cntr) : cntr(++cntr) {}
- ~CountFrames() { --cntr; }
- int& cntr;
- } counter(recursion_level_);
-
- if (ready_rows_.empty()) {
- if (whole_op_finished_) {
- // The scan is finished for good, there will be no more rows.
- on_finish_(status_);
- return;
- }
- if (!continue_reading_) {
- GCP_LOG(FATAL)
- << "No rows are ready and we can't continue reading. This is a bug, "
- "please report it at "
- "https://github.com/googleapis/google-cloud-cpp/issues/new";
- }
- // No rows, but we can fetch some.
- auto continue_reading = std::move(continue_reading_);
- continue_reading_.reset();
- continue_reading->set_value(true);
- return;
- }
-
- // Yay! We have something to give to the user and they want it.
- auto row = std::move(ready_rows_.front());
- ready_rows_.pop();
-
- auto self = this->shared_from_this();
- bool const break_recursion = recursion_level_ >= 100;
- on_row_(std::move(row)).then([self, break_recursion](future fut) {
- bool should_cancel;
-#if GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
- try {
- should_cancel = !fut.get();
- } catch (std::exception& ex) {
- self->Cancel(
- std::string("future<> returned from the user callback threw an "
- "exception: ") +
- ex.what());
- return;
- } catch (...) {
- self->Cancel(
- "future<> returned from the user callback threw an unknown "
- "exception");
- return;
- }
-#else // GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
- should_cancel = !fut.get();
-#endif // GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
- if (should_cancel) {
- self->Cancel("User cancelled");
- return;
- }
- if (break_recursion) {
- self->cq_.RunAsync([self] { self->UserWantsRows(); });
- return;
- }
- self->UserWantsRows();
- });
-}
-
-future LegacyAsyncRowReader::OnDataReceived(
- google::bigtable::v2::ReadRowsResponse response) {
- // assert(!whole_op_finished_);
- // assert(!continue_reading_);
- // assert(status_.ok());
- status_ = ConsumeResponse(std::move(response));
- // We've processed the response.
- //
- // If there were errors (e.g. malformed response from the server), we should
- // interrupt this stream. Interrupting it will yield lower layers calling
- // `OnStreamFinished` with a status unrelated to the real reason, so we
- // store the actual reason in status_ and proceed exactly the
- // same way as if the stream was broken for other reasons.
- //
- // Even if status_ is not OK, we might have consumed some rows,
- // but, don't give them to the user yet. We want to keep the invariant that
- // either the user doesn't hold a `future<>` when we're fetching more rows.
- // Retries (successful or not) will do it. Improving this behavior makes
- // little sense because parser errors are very unexpected and probably not
- // retryable anyway.
-
- if (status_.ok()) {
- continue_reading_.emplace(promise());
- auto res = continue_reading_->get_future();
- TryGiveRowToUser();
- return res;
- }
- return make_ready_future(false);
-}
-
-void LegacyAsyncRowReader::OnStreamFinished(Status status) {
- // assert(!continue_reading_);
- if (status_.ok()) {
- status_ = std::move(status);
- }
- grpc::Status parser_status;
- parser_->HandleEndOfStream(parser_status);
- if (!parser_status.ok() && status_.ok()) {
- // If there stream finished with an error ignore what the parser says.
- status_ = MakeStatusFromRpcError(parser_status);
- }
-
- // In the unlikely case when we have already reached the requested
- // number of rows and still receive an error (the parser can throw
- // an error at end of stream for example), there is no need to
- // retry and we have no good value for rows_limit anyway.
- if (rows_limit_ != NO_ROWS_LIMIT && rows_limit_ <= rows_count_) {
- status_ = Status();
- }
-
- if (!last_read_row_key_.empty()) {
- // We've returned some rows and need to make sure we don't
- // request them again.
- row_set_ =
- row_set_.Intersect(bigtable::RowRange::Open(last_read_row_key_, ""));
- }
-
- // If we receive an error, but the retryable set is empty, consider it a
- // success.
- if (row_set_.IsEmpty()) {
- status_ = Status();
- }
-
- if (status_.ok()) {
- // We've successfully finished the scan.
- whole_op_finished_ = true;
- TryGiveRowToUser();
- return;
- }
-
- if (!rpc_retry_policy_->OnFailure(status_)) {
- // Can't retry.
- whole_op_finished_ = true;
- TryGiveRowToUser();
- return;
- }
- auto self = this->shared_from_this();
- cq_.MakeRelativeTimer(rpc_backoff_policy_->OnCompletion(status_))
- .then(
- [self](
- future> result) {
- if (auto tp = result.get()) {
- self->MakeRequest();
- } else {
- self->whole_op_finished_ = true;
- self->TryGiveRowToUser();
- }
- });
-}
-
-void LegacyAsyncRowReader::Cancel(std::string const& reason) {
- ready_rows_ = std::queue();
- auto continue_reading = std::move(continue_reading_);
- continue_reading_.reset();
- Status status(StatusCode::kCancelled, reason);
- if (!continue_reading) {
- // If we're not in the middle of the stream fire some user callbacks, but
- // also override the overall status.
- // assert(whole_op_finished_);
- status_ = std::move(status);
- TryGiveRowToUser();
- return;
- }
- // If we are in the middle of the stream, cancel the stream.
- status_ = std::move(status);
- continue_reading->set_value(false);
-}
-
-Status LegacyAsyncRowReader::DrainParser() {
- grpc::Status status;
- while (parser_->HasNext()) {
- bigtable::Row parsed_row = parser_->Next(status);
- if (!status.ok()) {
- return MakeStatusFromRpcError(status);
- }
- ++rows_count_;
- last_read_row_key_ = parsed_row.row_key();
- ready_rows_.emplace(std::move(parsed_row));
- }
- return Status();
-}
-
-Status LegacyAsyncRowReader::ConsumeResponse(
- google::bigtable::v2::ReadRowsResponse response) {
- for (auto& chunk : *response.mutable_chunks()) {
- grpc::Status status;
- parser_->HandleChunk(std::move(chunk), status);
- if (!status.ok()) {
- return MakeStatusFromRpcError(status);
- }
- Status parser_status = DrainParser();
- if (!parser_status.ok()) {
- return parser_status;
- }
- }
- if (!response.last_scanned_row_key().empty()) {
- last_read_row_key_ = std::move(*response.mutable_last_scanned_row_key());
- }
- return Status();
-}
-
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
-} // namespace bigtable_internal
-} // namespace cloud
-} // namespace google
diff --git a/google/cloud/bigtable/internal/legacy_async_row_reader.h b/google/cloud/bigtable/internal/legacy_async_row_reader.h
deleted file mode 100644
index 0202f2ea1348d..0000000000000
--- a/google/cloud/bigtable/internal/legacy_async_row_reader.h
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_INTERNAL_LEGACY_ASYNC_ROW_READER_H
-#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_INTERNAL_LEGACY_ASYNC_ROW_READER_H
-
-#include "google/cloud/bigtable/completion_queue.h"
-#include "google/cloud/bigtable/data_client.h"
-#include "google/cloud/bigtable/filters.h"
-#include "google/cloud/bigtable/internal/readrowsparser.h"
-#include "google/cloud/bigtable/metadata_update_policy.h"
-#include "google/cloud/bigtable/row.h"
-#include "google/cloud/bigtable/row_set.h"
-#include "google/cloud/bigtable/rpc_backoff_policy.h"
-#include "google/cloud/bigtable/rpc_retry_policy.h"
-#include "google/cloud/bigtable/version.h"
-#include "google/cloud/future.h"
-#include "google/cloud/grpc_error_delegate.h"
-#include "google/cloud/optional.h"
-#include "google/cloud/status_or.h"
-#include "absl/types/optional.h"
-#include
-#include
-#include
-
-namespace google {
-namespace cloud {
-namespace bigtable_internal {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-
-/**
- * Objects of this class represent the state of reading rows via AsyncReadRows.
- */
-class LegacyAsyncRowReader
- : public std::enable_shared_from_this {
- using RowFunctor = std::function(bigtable::Row)>;
- using FinishFunctor = std::function;
-
- public:
- /// Special value to be used as rows_limit indicating no limit.
- // NOLINTNEXTLINE(readability-identifier-naming)
- static std::int64_t constexpr NO_ROWS_LIMIT = 0;
- // Callbacks keep pointers to these objects.
- LegacyAsyncRowReader(LegacyAsyncRowReader&&) = delete;
- LegacyAsyncRowReader(LegacyAsyncRowReader const&) = delete;
-
- static std::shared_ptr Create(
- CompletionQueue cq, std::shared_ptr client,
- std::string app_profile_id, std::string table_name, RowFunctor on_row,
- FinishFunctor on_finish, bigtable::RowSet row_set,
- std::int64_t rows_limit, bigtable::Filter filter,
- std::unique_ptr rpc_retry_policy,
- std::unique_ptr rpc_backoff_policy,
- bigtable::MetadataUpdatePolicy metadata_update_policy,
- std::unique_ptr
- parser_factory) {
- std::shared_ptr res(new LegacyAsyncRowReader(
- std::move(cq), std::move(client), std::move(app_profile_id),
- std::move(table_name), std::move(on_row), std::move(on_finish),
- std::move(row_set), rows_limit, std::move(filter),
- std::move(rpc_retry_policy), std::move(rpc_backoff_policy),
- std::move(metadata_update_policy), std::move(parser_factory)));
- res->MakeRequest();
- return res;
- }
-
- private:
- LegacyAsyncRowReader(
- CompletionQueue cq, std::shared_ptr client,
- std::string app_profile_id, std::string table_name, RowFunctor on_row,
- FinishFunctor on_finish, bigtable::RowSet row_set,
- std::int64_t rows_limit, bigtable::Filter filter,
- std::unique_ptr rpc_retry_policy,
- std::unique_ptr rpc_backoff_policy,
- bigtable::MetadataUpdatePolicy metadata_update_policy,
- std::unique_ptr parser_factory)
- : cq_(std::move(cq)),
- client_(std::move(client)),
- app_profile_id_(std::move(app_profile_id)),
- table_name_(std::move(table_name)),
- on_row_(std::move(on_row)),
- on_finish_(std::move(on_finish)),
- row_set_(std::move(row_set)),
- rows_limit_(rows_limit),
- filter_(std::move(filter)),
- rpc_retry_policy_(std::move(rpc_retry_policy)),
- rpc_backoff_policy_(std::move(rpc_backoff_policy)),
- metadata_update_policy_(std::move(metadata_update_policy)),
- parser_factory_(std::move(parser_factory)) {}
-
- void MakeRequest();
-
- /**
- * Called when the user asks for more rows via satisfying the future returned
- * from the row callback.
- */
- void UserWantsRows() { TryGiveRowToUser(); }
-
- /**
- * Attempt to call a user callback.
- *
- * If no rows are ready, this will not call the callback immediately and
- * instead ask lower layers for more data.
- */
- void TryGiveRowToUser();
-
- /// Called when lower layers provide us with a response chunk.
- future OnDataReceived(google::bigtable::v2::ReadRowsResponse response);
-
- /// Called when the whole stream finishes.
- void OnStreamFinished(Status status);
-
- /// User satisfied the future returned from the row callback with false.
- void Cancel(std::string const& reason);
-
- /// Process everything that is accumulated in the parser.
- Status DrainParser();
-
- /// Parse the data from the response.
- Status ConsumeResponse(google::bigtable::v2::ReadRowsResponse response);
-
- std::mutex mu_;
- CompletionQueue cq_;
- std::shared_ptr client_;
- std::string app_profile_id_;
- std::string table_name_;
- RowFunctor on_row_;
- FinishFunctor on_finish_;
- bigtable::RowSet row_set_;
- std::int64_t rows_limit_;
- bigtable::Filter filter_;
- std::unique_ptr rpc_retry_policy_;
- std::unique_ptr rpc_backoff_policy_;
- bigtable::MetadataUpdatePolicy metadata_update_policy_;
- std::unique_ptr parser_factory_;
- std::unique_ptr parser_;
- /// Number of rows read so far, used to set row_limit in retries.
- std::int64_t rows_count_ = 0;
- /// Holds the last read row key, for retries.
- bigtable::RowKeyType last_read_row_key_;
- /// The queue of rows which we already received but no one has asked for them.
- std::queue ready_rows_;
- /**
- * The promise to the underlying stream to either continue reading or cancel.
- *
- * If the `absl::optional` is empty, it means that either the whole scan is
- * finished or the underlying layers are already trying to fetch more data.
- *
- * If the `absl::optional` is not empty, the lower layers are waiting for this
- * to be satisfied before they start fetching more data.
- */
- absl::optional> continue_reading_;
- /// The final status of the operation.
- bool whole_op_finished_ = false;
- /**
- * The status of the last retry attempt_.
- *
- * It is reset to OK at the beginning of every retry. If an error is
- * encountered (be it while parsing the response or on stream finish), it is
- * stored here (unless a different error had already been stored).
- */
- Status status_;
- /// Tracks the level of recursion of TryGiveRowToUser
- int recursion_level_ = 0;
-};
-
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
-} // namespace bigtable_internal
-} // namespace cloud
-} // namespace google
-
-#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_INTERNAL_LEGACY_ASYNC_ROW_READER_H
diff --git a/google/cloud/bigtable/internal/legacy_async_row_reader_test.cc b/google/cloud/bigtable/internal/legacy_async_row_reader_test.cc
deleted file mode 100644
index 83eef314300b0..0000000000000
--- a/google/cloud/bigtable/internal/legacy_async_row_reader_test.cc
+++ /dev/null
@@ -1,1159 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "google/cloud/bigtable/table.h"
-#include "google/cloud/bigtable/testing/mock_data_client.h"
-#include "google/cloud/bigtable/testing/mock_read_rows_reader.h"
-#include "google/cloud/bigtable/testing/mock_response_reader.h"
-#include "google/cloud/bigtable/testing/table_test_fixture.h"
-#include "google/cloud/internal/api_client_header.h"
-#include "google/cloud/testing_util/chrono_literals.h"
-#include "google/cloud/testing_util/fake_completion_queue_impl.h"
-#include "google/cloud/testing_util/status_matchers.h"
-#include "google/cloud/testing_util/validate_metadata.h"
-#include
-#include
-#include
-#include
-
-namespace google {
-namespace cloud {
-namespace bigtable {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-namespace {
-
-namespace btproto = ::google::bigtable::v2;
-
-using ::google::cloud::bigtable::testing::MockClientAsyncReaderInterface;
-using ::google::cloud::testing_util::ValidateMetadataFixture;
-using ::google::cloud::testing_util::chrono_literals::operator""_ms;
-using ::google::cloud::testing_util::FakeCompletionQueueImpl;
-using ::testing::HasSubstr;
-using ::testing::Values;
-using ::testing::WithParamInterface;
-
-template
-bool Unsatisfied(future const& fut) {
- return std::future_status::timeout == fut.wait_for(1_ms);
-}
-
-class TableAsyncReadRowsTest : public bigtable::testing::TableTestFixture {
- protected:
- TableAsyncReadRowsTest()
- : TableTestFixture(
- CompletionQueue(std::make_shared())),
- stream_status_future_(stream_status_promise_.get_future()) {}
-
- MockClientAsyncReaderInterface& AddReader(
- std::function request_expectations,
- bool expect_a_read = true) {
- readers_.emplace_back(
- new MockClientAsyncReaderInterface);
- reader_started_.push_back(false);
- size_t idx = reader_started_.size() - 1;
- auto& reader = LastReader();
- // We can't move request_expectations into the lambda because of lack of
- // generalized lambda capture in C++11, so let's pass it by pointer.
- auto request_expectations_ptr =
- std::make_shared(
- std::move(request_expectations));
-
- EXPECT_CALL(*client_, PrepareAsyncReadRows)
- .WillOnce([this, &reader, request_expectations_ptr](
- grpc::ClientContext* context,
- btproto::ReadRowsRequest const& r,
- grpc::CompletionQueue*) {
- validate_metadata_fixture_.IsContextMDValid(
- *context, "google.bigtable.v2.Bigtable.ReadRows", r,
- google::cloud::internal::HandCraftedLibClientHeader());
- (*request_expectations_ptr)(r);
- return std::unique_ptr<
- MockClientAsyncReaderInterface>(
- &reader);
- })
- .RetiresOnSaturation();
-
- EXPECT_CALL(reader, StartCall).WillOnce([idx, this](void*) {
- reader_started_[idx] = true;
- });
- if (expect_a_read) {
- // The last call, to which we'll return ok==false.
- EXPECT_CALL(reader, Read).WillOnce([](btproto::ReadRowsResponse*, void*) {
- });
- }
- return reader;
- }
-
- MockClientAsyncReaderInterface& LastReader() {
- return *readers_.back();
- }
-
- // Start Table::AsyncReadRows.
- void ReadRows(int row_limit = RowReader::NO_ROWS_LIMIT) {
- table_.AsyncReadRows(
- [this](Row const& row) {
- EXPECT_EQ(expected_rows_.front(), row.row_key());
- expected_rows_.pop();
- row_promises_.front().set_value(row.row_key());
- row_promises_.pop();
- auto ret = std::move(futures_from_user_cb_.front());
- futures_from_user_cb_.pop();
- return ret;
- },
- [this](Status const& stream_status) {
- stream_status_promise_.set_value(stream_status);
- },
- RowSet(), row_limit, Filter::PassAllFilter());
- }
-
- /// Expect a row whose row key is equal to this function's argument.
- template
- void ExpectRow(T const& row) {
- row_promises_.emplace();
- row_futures_.emplace_back(row_promises_.back().get_future());
- promises_from_user_cb_.emplace_back();
- futures_from_user_cb_.emplace(promises_from_user_cb_.back().get_future());
- expected_rows_.push(RowKeyType(row));
- }
-
- /// A wrapper around ExpectRow to expect many rows.
- template
- void ExpectRows(std::initializer_list const& rows) {
- for (auto const& row : rows) {
- ExpectRow(row);
- }
- }
-
- std::vector*>
- readers_;
- // Whether `Start()` was called on i-th retry attempt.
- std::vector reader_started_;
- std::queue> row_promises_;
- /**
- * Future at idx i corresponse to i-th expected row. It will be satisfied
- * when the relevant `on_row` callback of AsyncReadRows is called.
- */
- std::vector> row_futures_;
- std::queue expected_rows_;
- promise stream_status_promise_;
- /// Future which will be satisfied with the status passed in on_finished.
- future stream_status_future_;
- /// I-th promise corresponds to the future returned from the ith on_row cb.
- std::vector> promises_from_user_cb_;
- std::queue> futures_from_user_cb_;
- ValidateMetadataFixture validate_metadata_fixture_;
-};
-
-/// @test Verify that successfully reading a single row works.
-TEST_F(TableAsyncReadRowsTest, SingleRow) {
- auto& stream = AddReader([](btproto::ReadRowsRequest const&) {});
-
- EXPECT_CALL(stream, Read)
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- R"(
- chunks {
- row_key: "r1"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- })");
- })
- .RetiresOnSaturation();
- EXPECT_CALL(stream, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- ExpectRow("r1");
- ReadRows();
-
- EXPECT_TRUE(reader_started_[0]);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
-
- EXPECT_TRUE(Unsatisfied(row_futures_[0]));
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Return data
- auto row = row_futures_[0].get();
-
- // Check that we're not asking for data unless someone is waiting for it.
- ASSERT_EQ(0U, cq_impl_->size());
- promises_from_user_cb_[0].set_value(true);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- EXPECT_TRUE(Unsatisfied(stream_status_future_));
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- auto stream_status = stream_status_future_.get();
- ASSERT_STATUS_OK(stream_status);
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-/// @test Like SingleRow, but the future returned from the cb is satisfied.
-TEST_F(TableAsyncReadRowsTest, SingleRowInstantFinish) {
- auto& stream = AddReader([](btproto::ReadRowsRequest const&) {});
-
- EXPECT_CALL(stream, Read)
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- R"(
- chunks {
- row_key: "r1"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- })");
- })
- .RetiresOnSaturation();
- EXPECT_CALL(stream, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- ExpectRow("r1");
- promises_from_user_cb_[0].set_value(true);
- ReadRows();
-
- EXPECT_TRUE(reader_started_[0]);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
-
- EXPECT_TRUE(Unsatisfied(row_futures_[0]));
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Return data
- auto row = row_futures_[0].get();
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- EXPECT_TRUE(Unsatisfied(stream_status_future_));
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- auto stream_status = stream_status_future_.get();
- ASSERT_STATUS_OK(stream_status);
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-/// @test Verify that reading 2 rows delivered in 2 responses works.
-TEST_F(TableAsyncReadRowsTest, MultipleChunks) {
- auto& stream = AddReader([](btproto::ReadRowsRequest const&) {});
-
- EXPECT_CALL(stream, Read)
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- R"(
- chunks {
- row_key: "r1"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- })");
- })
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- R"(
- chunks {
- row_key: "r2"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- })");
- })
- .RetiresOnSaturation();
- EXPECT_CALL(stream, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- ExpectRow("r1");
- ExpectRow("r2");
- promises_from_user_cb_[1].set_value(true);
- ReadRows();
-
- EXPECT_TRUE(reader_started_[0]);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
-
- EXPECT_TRUE(Unsatisfied(row_futures_[0]));
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Return data
- row_futures_[0].get();
-
- // Check that we're not asking for data unless someone is waiting for it.
- ASSERT_EQ(0U, cq_impl_->size());
- promises_from_user_cb_[0].set_value(true);
-
- EXPECT_TRUE(Unsatisfied(row_futures_[1]));
- cq_impl_->SimulateCompletion(true); // Return data
- row_futures_[1].get();
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- EXPECT_TRUE(Unsatisfied(stream_status_future_));
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- auto stream_status = stream_status_future_.get();
- ASSERT_STATUS_OK(stream_status);
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-/// @test Like MultipleChunks but the future returned from on_row is satisfied.
-TEST_F(TableAsyncReadRowsTest, MultipleChunksImmediatelySatisfied) {
- auto& stream = AddReader([](btproto::ReadRowsRequest const&) {});
-
- EXPECT_CALL(stream, Read)
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- R"(
- chunks {
- row_key: "r1"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- })");
- })
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- R"(
- chunks {
- row_key: "r2"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- })");
- })
- .RetiresOnSaturation();
- EXPECT_CALL(stream, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- ExpectRow("r1");
- ExpectRow("r2");
- promises_from_user_cb_[0].set_value(true);
- promises_from_user_cb_[1].set_value(true);
- ReadRows();
-
- EXPECT_TRUE(reader_started_[0]);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
-
- EXPECT_TRUE(Unsatisfied(row_futures_[0]));
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Return data
- row_futures_[0].get();
-
- EXPECT_TRUE(Unsatisfied(row_futures_[1]));
- cq_impl_->SimulateCompletion(true); // Return data
- row_futures_[1].get();
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- EXPECT_TRUE(Unsatisfied(stream_status_future_));
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- auto stream_status = stream_status_future_.get();
- ASSERT_STATUS_OK(stream_status);
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-/// @test Verify that a single row can span multiple responses.
-TEST_F(TableAsyncReadRowsTest, ResponseInMultipleChunks) {
- auto& stream = AddReader([](btproto::ReadRowsRequest const&) {});
-
- EXPECT_CALL(stream, Read)
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- R"(
- chunks {
- row_key: "r1"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: false
- })");
- })
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- R"(
- chunks {
- row_key: "r1"
- family_name { value: "fam" }
- qualifier { value: "col2" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- })");
- })
- .RetiresOnSaturation();
- EXPECT_CALL(stream, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- ExpectRow("r1");
- promises_from_user_cb_[0].set_value(true);
- ReadRows();
- EXPECT_TRUE(reader_started_[0]);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Return data
- EXPECT_TRUE(Unsatisfied(row_futures_[0]));
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Return data
-
- row_futures_[0].get();
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- auto stream_status = stream_status_future_.get();
- ASSERT_STATUS_OK(stream_status);
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-/// @test Verify that parser fails if the stream finishes prematurely.
-TEST_F(TableAsyncReadRowsTest, ParserEofFailsOnUnfinishedRow) {
- auto& stream = AddReader([](btproto::ReadRowsRequest const&) {});
-
- EXPECT_CALL(stream, Read)
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- // missing final commit
- R"(
- chunks {
- row_key: "r1"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: false
- })");
- })
- .RetiresOnSaturation();
- EXPECT_CALL(stream, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- ReadRows();
-
- EXPECT_TRUE(reader_started_[0]);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Return data
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- EXPECT_TRUE(Unsatisfied(stream_status_future_));
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- ASSERT_FALSE(stream_status_future_.get().ok());
-}
-
-/// @test Check that we ignore HandleEndOfStream errors if enough rows were read
-TEST_F(TableAsyncReadRowsTest, ParserEofDoesntFailsOnUnfinishedRowIfRowLimit) {
- auto& stream = AddReader([](btproto::ReadRowsRequest const&) {});
-
- EXPECT_CALL(stream, Read)
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- // missing final commit
- R"(
- chunks {
- row_key: "r1"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- }
- chunks {
- row_key: "r2"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: false
- })");
- })
- .RetiresOnSaturation();
- EXPECT_CALL(stream, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- ExpectRow("r1");
- promises_from_user_cb_[0].set_value(true);
- ReadRows(1);
-
- EXPECT_TRUE(reader_started_[0]);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
- ASSERT_EQ(1U, cq_impl_->size());
- EXPECT_TRUE(Unsatisfied(row_futures_[0]));
- cq_impl_->SimulateCompletion(true); // Return data
-
- row_futures_[0].get();
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- auto stream_status = stream_status_future_.get();
- ASSERT_STATUS_OK(stream_status);
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-/// @test Verify that permanent errors are not retried and properly passed.
-TEST_F(TableAsyncReadRowsTest, PermanentFailure) {
- auto& stream = AddReader([](btproto::ReadRowsRequest const&) {});
-
- EXPECT_CALL(stream, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status(grpc::StatusCode::PERMISSION_DENIED, "noooo");
- });
-
- ReadRows();
- EXPECT_TRUE(reader_started_[0]);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- EXPECT_TRUE(Unsatisfied(stream_status_future_));
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- auto stream_status = stream_status_future_.get();
- ASSERT_EQ(StatusCode::kPermissionDenied, stream_status.code());
-}
-
-/// @test Verify that transient errors are retried.
-TEST_F(TableAsyncReadRowsTest, TransientErrorIsRetried) {
- auto& stream2 = AddReader([](btproto::ReadRowsRequest const& req) {
- // Verify that we're not asking for the same rows again.
- EXPECT_TRUE(req.has_rows());
- auto const& rows = req.rows();
- EXPECT_EQ(1, rows.row_ranges_size());
- auto const& range = rows.row_ranges(0);
- EXPECT_EQ("r1", range.start_key_open());
- });
- auto& stream1 = AddReader([](btproto::ReadRowsRequest const&) {});
-
- // Make it a bit trickier by delivering the error while parsing second row.
- EXPECT_CALL(stream1, Read)
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- R"(
- chunks {
- row_key: "r1"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- }
- chunks {
- row_key: "r2"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: false
- })");
- })
- .RetiresOnSaturation();
- EXPECT_CALL(stream1, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status(grpc::StatusCode::UNAVAILABLE, "oh no");
- });
-
- EXPECT_CALL(stream2, Read)
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- R"(
- chunks {
- row_key: "r2"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- })");
- })
- .RetiresOnSaturation();
- EXPECT_CALL(stream2, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- ExpectRows({"r1", "r2"});
- promises_from_user_cb_[0].set_value(true);
- promises_from_user_cb_[1].set_value(true);
- ReadRows();
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Return data
-
- row_futures_[0].get();
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream with failure
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish timer
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
- ASSERT_EQ(1U, cq_impl_->size());
- EXPECT_TRUE(Unsatisfied(row_futures_[1]));
- cq_impl_->SimulateCompletion(true); // Return data
-
- row_futures_[1].get();
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- EXPECT_TRUE(Unsatisfied(stream_status_future_));
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- auto stream_status = stream_status_future_.get();
- ASSERT_STATUS_OK(stream_status);
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-/// @test Verify that the last scanned row is respected.
-TEST_F(TableAsyncReadRowsTest, LastScannedRowKeyIsRespected) {
- auto& stream2 = AddReader([](btproto::ReadRowsRequest const& req) {
- // The server has told that "r2" has been scanned. Our second request
- // should use the range ("r2", ""]. This is what is under test.
- EXPECT_TRUE(req.has_rows());
- auto const& rows = req.rows();
- EXPECT_EQ(1, rows.row_ranges_size());
- auto const& range = rows.row_ranges(0);
- EXPECT_EQ("r2", range.start_key_open());
- });
- auto& stream1 = AddReader([](btproto::ReadRowsRequest const&) {});
-
- EXPECT_CALL(stream1, Read)
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- R"(
- chunks {
- row_key: "r1"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- })");
- })
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- std::cout << "Second Read for stream1 " << std::endl;
- btproto::ReadRowsResponse resp;
- resp.set_last_scanned_row_key("r2");
- *r = resp;
- })
- .RetiresOnSaturation();
- EXPECT_CALL(stream1, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status(grpc::StatusCode::UNAVAILABLE, "retry");
- });
-
- EXPECT_CALL(stream2, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- ExpectRows({"r1", "r2", "r3"});
- promises_from_user_cb_[0].set_value(true);
- ReadRows();
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Return "r1"
-
- row_futures_[0].get();
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Return last_scanned_row_key = "r2"
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream1 with failure
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish timer
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- EXPECT_TRUE(Unsatisfied(stream_status_future_));
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- auto stream_status = stream_status_future_.get();
- ASSERT_STATUS_OK(stream_status);
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-/// @test Verify proper handling of bogus responses from the service.
-TEST_F(TableAsyncReadRowsTest, ParserFailure) {
- auto& stream = AddReader([](btproto::ReadRowsRequest const&) {});
-
- EXPECT_CALL(stream, Read)
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- // Row not in increasing order.
- R"(
- chunks {
- row_key: "r2"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- }
- chunks {
- row_key: "r1"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- })");
- })
- .RetiresOnSaturation();
- EXPECT_CALL(stream, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- ExpectRow("r2");
- promises_from_user_cb_[0].set_value(true);
- ReadRows();
-
- EXPECT_TRUE(reader_started_[0]);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Return data
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish placeholder Read()
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- row_futures_[0].get();
-
- auto stream_status = stream_status_future_.get();
- ASSERT_EQ(StatusCode::kInternal, stream_status.code());
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-enum class CancelMode {
- kFalseValue,
-#if GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
- kStdExcept,
- kOtherExcept,
-#endif // GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
-};
-
-/// @test Verify canceling the stream by satisfying the futures with false
-class TableAsyncReadRowsCancelMidStreamTest
- : public TableAsyncReadRowsTest,
- public WithParamInterface {};
-
-TEST_P(TableAsyncReadRowsCancelMidStreamTest, CancelMidStream) {
- auto& stream = AddReader([](btproto::ReadRowsRequest const&) {});
-
- EXPECT_CALL(stream, Read)
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- R"(
- chunks {
- row_key: "r1"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- }
- chunks {
- row_key: "r2"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- })");
- })
- .RetiresOnSaturation();
- EXPECT_CALL(stream, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- ExpectRow("r1");
- ReadRows();
-
- EXPECT_TRUE(reader_started_[0]);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
-
- EXPECT_TRUE(Unsatisfied(row_futures_[0]));
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Return data
- row_futures_[0].get();
-
- // Check that we're not asking for data unless someone is waiting for it.
- ASSERT_EQ(0U, cq_impl_->size());
-
- switch (GetParam()) {
- case CancelMode::kFalseValue:
- promises_from_user_cb_[0].set_value(false);
- break;
-#if GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
- case CancelMode::kStdExcept:
- try {
- throw std::runtime_error("user threw std::exception");
- } catch (...) {
- promises_from_user_cb_[0].set_exception(std::current_exception());
- }
- break;
- case CancelMode::kOtherExcept:
- try {
- throw 5;
- } catch (...) {
- promises_from_user_cb_[0].set_exception(std::current_exception());
- }
- break;
-#endif // GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
- }
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- EXPECT_TRUE(Unsatisfied(stream_status_future_));
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- auto stream_status = stream_status_future_.get();
- ASSERT_EQ(StatusCode::kCancelled, stream_status.code());
- switch (GetParam()) {
- case CancelMode::kFalseValue:
- ASSERT_THAT(stream_status.message(), HasSubstr("User cancelled"));
- break;
-#if GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
- case CancelMode::kStdExcept:
- ASSERT_THAT(stream_status.message(),
- HasSubstr("user threw std::exception"));
- break;
- case CancelMode::kOtherExcept:
- ASSERT_THAT(stream_status.message(), HasSubstr("unknown exception"));
- break;
-#endif // GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
- }
-
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-#if GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
-INSTANTIATE_TEST_SUITE_P(CancelMidStream, TableAsyncReadRowsCancelMidStreamTest,
- Values(CancelMode::kFalseValue, CancelMode::kStdExcept,
- CancelMode::kOtherExcept));
-#else // GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
-INSTANTIATE_TEST_SUITE_P(CancelMidStream, TableAsyncReadRowsCancelMidStreamTest,
- Values(CancelMode::kFalseValue));
-#endif // GOOGLE_CLOUD_CPP_HAVE_EXCEPTIONS
-
-/// @test Like CancelMidStream but after the underlying stream has finished.
-TEST_F(TableAsyncReadRowsTest, CancelAfterStreamFinish) {
- auto& stream = AddReader([](btproto::ReadRowsRequest const&) {});
-
- // First two rows are going to be processed, but third will cause the parser
- // to fail (row order violation). This will result in finishing the stream
- // while still keeping the two processed rows for the user.
- EXPECT_CALL(stream, Read)
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- R"(
- chunks {
- row_key: "r1"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- }
- chunks {
- row_key: "r2"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- }
- chunks {
- row_key: "r0"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- })");
- })
- .RetiresOnSaturation();
- EXPECT_CALL(stream, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- ExpectRow("r1");
- ReadRows();
-
- EXPECT_TRUE(reader_started_[0]);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
-
- EXPECT_TRUE(Unsatisfied(row_futures_[0]));
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Return data
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- EXPECT_TRUE(Unsatisfied(row_futures_[0]));
- EXPECT_TRUE(Unsatisfied(stream_status_future_));
- cq_impl_->SimulateCompletion(true); // Finish Finish()
- ASSERT_EQ(0U, cq_impl_->size());
-
- EXPECT_TRUE(Unsatisfied(stream_status_future_));
- auto row = row_futures_[0].get();
-
- // Check that we're not asking for data unless someone is waiting for it.
- ASSERT_EQ(0U, cq_impl_->size());
- promises_from_user_cb_[0].set_value(false);
-
- auto stream_status = stream_status_future_.get();
- ASSERT_FALSE(stream_status.ok());
- ASSERT_EQ(StatusCode::kCancelled, stream_status.code());
-}
-
-/// @test Verify that the recursion described in TryGiveRowToUser is bounded.
-TEST_F(TableAsyncReadRowsTest, DeepStack) {
- auto& stream = AddReader([](btproto::ReadRowsRequest const&) {});
-
- auto large_response = bigtable::testing::ReadRowsResponseFromString(
- R"(
- chunks {
- row_key: "000"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- })");
- ExpectRow("000");
- for (int i = 1; i < 101; ++i) {
- auto chunk = large_response.chunks(0);
- std::stringstream s_idx;
- s_idx << std::setfill('0') << std::setw(3) << i;
- chunk.set_row_key(s_idx.str());
- ExpectRow(chunk.row_key());
- *large_response.add_chunks() = std::move(chunk);
- }
-
- EXPECT_CALL(stream, Read)
- .WillOnce([large_response](btproto::ReadRowsResponse* r, void*) {
- *r = large_response;
- })
- .RetiresOnSaturation();
- EXPECT_CALL(stream, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- for (int i = 0; i < 101; ++i) {
- promises_from_user_cb_[i].set_value(true);
- }
- ReadRows();
-
- EXPECT_TRUE(reader_started_[0]);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
-
- EXPECT_TRUE(Unsatisfied(row_futures_[0]));
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Return data
-
- for (int i = 0; i < 100; ++i) {
- row_futures_[i].get();
- }
- ASSERT_TRUE(Unsatisfied(row_futures_[100]));
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // RunAsync
- row_futures_[100].get();
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- EXPECT_TRUE(Unsatisfied(stream_status_future_));
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- auto stream_status = stream_status_future_.get();
- ASSERT_STATUS_OK(stream_status);
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-TEST_F(TableAsyncReadRowsTest, ReadRowSuccess) {
- auto& stream = AddReader([](btproto::ReadRowsRequest const&) {});
-
- EXPECT_CALL(stream, Read)
- .WillOnce([](btproto::ReadRowsResponse* r, void*) {
- *r = bigtable::testing::ReadRowsResponseFromString(
- R"(
- chunks {
- row_key: "000"
- family_name { value: "fam" }
- qualifier { value: "col" }
- timestamp_micros: 42000
- value: "value"
- commit_row: true
- })");
- })
- .RetiresOnSaturation();
- EXPECT_CALL(stream, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- auto row_future = table_.AsyncReadRow("000", Filter::PassAllFilter());
-
- EXPECT_TRUE(reader_started_[0]);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
-
- EXPECT_TRUE(Unsatisfied(row_future));
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Return data
-
- // We return data only after the whole stream is finished.
- ASSERT_TRUE(Unsatisfied(row_future));
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- auto row = row_future.get();
- ASSERT_STATUS_OK(row);
- ASSERT_TRUE(row->first);
- ASSERT_EQ("000", row->second.row_key());
-
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-TEST_F(TableAsyncReadRowsTest, ReadRowNotFound) {
- auto& stream = AddReader([](btproto::ReadRowsRequest const&) {});
-
- EXPECT_CALL(stream, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
-
- auto row_future = table_.AsyncReadRow("000", Filter::PassAllFilter());
-
- EXPECT_TRUE(reader_started_[0]);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- EXPECT_TRUE(Unsatisfied(row_future));
-
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- auto row = row_future.get();
- ASSERT_STATUS_OK(row);
- ASSERT_FALSE(row->first);
-}
-
-TEST_F(TableAsyncReadRowsTest, ReadRowError) {
- auto& stream = AddReader([](btproto::ReadRowsRequest const&) {});
-
- EXPECT_CALL(stream, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status(grpc::StatusCode::PERMISSION_DENIED, "");
- });
-
- auto row_future = table_.AsyncReadRow("000", Filter::PassAllFilter());
-
- EXPECT_TRUE(reader_started_[0]);
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(true); // Finish Start()
-
- ASSERT_EQ(1U, cq_impl_->size());
- cq_impl_->SimulateCompletion(false); // Finish stream
- ASSERT_EQ(1U, cq_impl_->size());
- EXPECT_TRUE(Unsatisfied(row_future));
-
- cq_impl_->SimulateCompletion(true); // Finish Finish()
-
- auto row = row_future.get();
- ASSERT_FALSE(row);
- ASSERT_EQ(StatusCode::kPermissionDenied, row.status().code());
-}
-
-} // namespace
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
-} // namespace bigtable
-} // namespace cloud
-} // namespace google
diff --git a/google/cloud/bigtable/internal/legacy_async_row_sampler.cc b/google/cloud/bigtable/internal/legacy_async_row_sampler.cc
deleted file mode 100644
index 9ed3f3065b620..0000000000000
--- a/google/cloud/bigtable/internal/legacy_async_row_sampler.cc
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "google/cloud/bigtable/internal/legacy_async_row_sampler.h"
-#include "google/cloud/grpc_error_delegate.h"
-#include
-#include
-#include
-
-namespace google {
-namespace cloud {
-namespace bigtable {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-namespace internal {
-
-namespace btproto = ::google::bigtable::v2;
-
-future>> LegacyAsyncRowSampler::Create(
- CompletionQueue cq, std::shared_ptr client,
- std::unique_ptr rpc_retry_policy,
- std::unique_ptr rpc_backoff_policy,
- MetadataUpdatePolicy metadata_update_policy, std::string app_profile_id,
- std::string table_name) {
- std::shared_ptr sampler(new LegacyAsyncRowSampler(
- std::move(cq), std::move(client), std::move(rpc_retry_policy),
- std::move(rpc_backoff_policy), std::move(metadata_update_policy),
- std::move(app_profile_id), std::move(table_name)));
- sampler->StartIteration();
- return sampler->promise_.get_future();
-}
-
-LegacyAsyncRowSampler::LegacyAsyncRowSampler(
- CompletionQueue cq, std::shared_ptr client,
- std::unique_ptr rpc_retry_policy,
- std::unique_ptr rpc_backoff_policy,
- MetadataUpdatePolicy metadata_update_policy, std::string app_profile_id,
- std::string table_name)
- : cq_(std::move(cq)),
- client_(std::move(client)),
- rpc_retry_policy_(std::move(rpc_retry_policy)),
- rpc_backoff_policy_(std::move(rpc_backoff_policy)),
- metadata_update_policy_(std::move(metadata_update_policy)),
- app_profile_id_(std::move(app_profile_id)),
- table_name_(std::move(table_name)),
- promise_([this] { keep_reading_ = false; }) {}
-
-void LegacyAsyncRowSampler::StartIteration() {
- btproto::SampleRowKeysRequest request;
- request.set_app_profile_id(app_profile_id_);
- request.set_table_name(table_name_);
-
- auto context = std::make_unique();
- rpc_retry_policy_->Setup(*context);
- rpc_backoff_policy_->Setup(*context);
- metadata_update_policy_.Setup(*context);
-
- auto& client = client_;
- auto self = this->shared_from_this();
- cq_.MakeStreamingReadRpc(
- [client](grpc::ClientContext* context,
- btproto::SampleRowKeysRequest const& request,
- grpc::CompletionQueue* cq) {
- return client->PrepareAsyncSampleRowKeys(context, request, cq);
- },
- request, std::move(context),
- [self](btproto::SampleRowKeysResponse response) {
- return self->OnRead(std::move(response));
- },
- [self](Status const& status) { self->OnFinish(status); });
-}
-
-future LegacyAsyncRowSampler::OnRead(
- btproto::SampleRowKeysResponse response) {
- RowKeySample row_sample;
- row_sample.offset_bytes = response.offset_bytes();
- row_sample.row_key = std::move(*response.mutable_row_key());
- samples_.emplace_back(std::move(row_sample));
- return make_ready_future(keep_reading_.load());
-}
-
-void LegacyAsyncRowSampler::OnFinish(Status const& status) {
- if (status.ok()) {
- promise_.set_value(std::move(samples_));
- return;
- }
- if (!rpc_retry_policy_->OnFailure(status)) {
- promise_.set_value(status);
- return;
- }
-
- using TimerFuture = future>;
-
- samples_.clear();
- auto self = this->shared_from_this();
- auto delay = rpc_backoff_policy_->OnCompletion(std::move(status));
- cq_.MakeRelativeTimer(delay).then([self](TimerFuture result) {
- if (result.get()) {
- self->StartIteration();
- } else {
- self->promise_.set_value(
- Status(StatusCode::kCancelled, "call cancelled"));
- }
- });
-}
-
-} // namespace internal
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
-} // namespace bigtable
-} // namespace cloud
-} // namespace google
diff --git a/google/cloud/bigtable/internal/legacy_async_row_sampler.h b/google/cloud/bigtable/internal/legacy_async_row_sampler.h
deleted file mode 100644
index 54c4440ddb24e..0000000000000
--- a/google/cloud/bigtable/internal/legacy_async_row_sampler.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_INTERNAL_LEGACY_ASYNC_ROW_SAMPLER_H
-#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_INTERNAL_LEGACY_ASYNC_ROW_SAMPLER_H
-
-#include "google/cloud/bigtable/completion_queue.h"
-#include "google/cloud/bigtable/data_client.h"
-#include "google/cloud/bigtable/metadata_update_policy.h"
-#include "google/cloud/bigtable/row_key_sample.h"
-#include "google/cloud/bigtable/rpc_backoff_policy.h"
-#include "google/cloud/bigtable/rpc_retry_policy.h"
-#include "google/cloud/bigtable/version.h"
-#include "google/cloud/future_generic.h"
-#include "google/cloud/status.h"
-#include "google/cloud/status_or.h"
-#include "google/bigtable/v2/bigtable.pb.h"
-#include
-#include
-#include
-#include
-
-namespace google {
-namespace cloud {
-namespace bigtable {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-namespace internal {
-
-/**
- * Objects of this class represent the state of receiving row keys via
- * AsyncSampleRows.
- */
-class LegacyAsyncRowSampler
- : public std::enable_shared_from_this {
- public:
- static future>> Create(
- CompletionQueue cq, std::shared_ptr client,
- std::unique_ptr rpc_retry_policy,
- std::unique_ptr rpc_backoff_policy,
- MetadataUpdatePolicy metadata_update_policy, std::string app_profile_id,
- std::string table_name);
-
- private:
- LegacyAsyncRowSampler(CompletionQueue cq, std::shared_ptr client,
- std::unique_ptr rpc_retry_policy,
- std::unique_ptr rpc_backoff_policy,
- MetadataUpdatePolicy metadata_update_policy,
- std::string app_profile_id, std::string table_name);
-
- void StartIteration();
- future OnRead(google::bigtable::v2::SampleRowKeysResponse response);
- void OnFinish(Status const& status);
-
- CompletionQueue cq_;
- std::shared_ptr client_;
- std::unique_ptr rpc_retry_policy_;
- std::unique_ptr rpc_backoff_policy_;
- MetadataUpdatePolicy metadata_update_policy_;
- std::string app_profile_id_;
- std::string table_name_;
-
- std::atomic keep_reading_{true};
- std::vector samples_;
- promise>> promise_;
-};
-
-} // namespace internal
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
-} // namespace bigtable
-} // namespace cloud
-} // namespace google
-
-#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_INTERNAL_LEGACY_ASYNC_ROW_SAMPLER_H
diff --git a/google/cloud/bigtable/internal/legacy_async_row_sampler_test.cc b/google/cloud/bigtable/internal/legacy_async_row_sampler_test.cc
deleted file mode 100644
index f5eaab86ad4c0..0000000000000
--- a/google/cloud/bigtable/internal/legacy_async_row_sampler_test.cc
+++ /dev/null
@@ -1,462 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "google/cloud/bigtable/internal/legacy_async_row_sampler.h"
-#include "google/cloud/bigtable/testing/mock_policies.h"
-#include "google/cloud/bigtable/testing/mock_response_reader.h"
-#include "google/cloud/bigtable/testing/table_test_fixture.h"
-#include "google/cloud/testing_util/fake_completion_queue_impl.h"
-#include "google/cloud/testing_util/status_matchers.h"
-#include "google/bigtable/v2/bigtable.pb.h"
-#include
-#include
-#include
-#include
-#include
-
-namespace google {
-namespace cloud {
-namespace bigtable {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-namespace {
-
-namespace btproto = ::google::bigtable::v2;
-
-using ::google::cloud::bigtable::testing::MockBackoffPolicy;
-using ::google::cloud::bigtable::testing::MockClientAsyncReaderInterface;
-using ::google::cloud::testing_util::FakeCompletionQueueImpl;
-using ::google::cloud::testing_util::StatusIs;
-using ::testing::ElementsAre;
-using ::testing::HasSubstr;
-
-class AsyncSampleRowKeysTest : public bigtable::testing::TableTestFixture {
- protected:
- AsyncSampleRowKeysTest()
- : TableTestFixture(
- CompletionQueue(std::make_shared())),
- rpc_retry_policy_(
- bigtable::DefaultRPCRetryPolicy(internal::kBigtableLimits)),
- metadata_update_policy_("my_table", MetadataParamTypes::NAME) {}
-
- std::shared_ptr rpc_retry_policy_;
- MetadataUpdatePolicy metadata_update_policy_;
-};
-
-struct RowKeySampleVectors {
- explicit RowKeySampleVectors(std::vector samples) {
- row_keys.reserve(samples.size());
- offset_bytes.reserve(samples.size());
- for (auto& sample : samples) {
- row_keys.emplace_back(std::move(sample.row_key));
- offset_bytes.emplace_back(std::move(sample.offset_bytes));
- }
- }
-
- std::vector row_keys;
- std::vector offset_bytes;
-};
-
-TEST_F(AsyncSampleRowKeysTest, Simple) {
- EXPECT_CALL(*client_, PrepareAsyncSampleRowKeys)
- .WillOnce([](grpc::ClientContext*, btproto::SampleRowKeysRequest const&,
- grpc::CompletionQueue*) {
- auto reader = std::make_unique<
- MockClientAsyncReaderInterface>();
- EXPECT_CALL(*reader, StartCall);
- EXPECT_CALL(*reader, Read)
- .WillOnce([](btproto::SampleRowKeysResponse* r, void*) {
- {
- r->set_row_key("test1");
- r->set_offset_bytes(11);
- }
- })
- .WillOnce([](btproto::SampleRowKeysResponse* r, void*) {
- {
- r->set_row_key("test2");
- r->set_offset_bytes(22);
- }
- })
- .WillOnce([](btproto::SampleRowKeysResponse*, void*) {});
-
- EXPECT_CALL(*reader, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
- return reader;
- });
-
- auto samples_future = table_.AsyncSampleRows();
-
- // Start()
- cq_impl_->SimulateCompletion(true);
- // Return response 1
- cq_impl_->SimulateCompletion(true);
- // Return response 2
- cq_impl_->SimulateCompletion(true);
- // End stream
- cq_impl_->SimulateCompletion(false);
- // Finish()
- cq_impl_->SimulateCompletion(true);
-
- auto status = samples_future.get();
- ASSERT_STATUS_OK(status);
-
- auto samples = RowKeySampleVectors(status.value());
- EXPECT_THAT(samples.row_keys, ElementsAre("test1", "test2"));
- EXPECT_THAT(samples.offset_bytes, ElementsAre(11, 22));
-}
-
-TEST_F(AsyncSampleRowKeysTest, Retry) {
- EXPECT_CALL(*client_, PrepareAsyncSampleRowKeys)
- .WillOnce([](grpc::ClientContext*, btproto::SampleRowKeysRequest const&,
- grpc::CompletionQueue*) {
- auto reader = std::make_unique<
- MockClientAsyncReaderInterface>();
- EXPECT_CALL(*reader, StartCall);
- EXPECT_CALL(*reader, Read)
- .WillOnce([](btproto::SampleRowKeysResponse* r, void*) {
- {
- r->set_row_key("test1");
- r->set_offset_bytes(11);
- }
- })
- .WillOnce([](btproto::SampleRowKeysResponse*, void*) {});
-
- EXPECT_CALL(*reader, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status(grpc::StatusCode::UNAVAILABLE, "try again");
- });
- return reader;
- })
- .WillOnce([](grpc::ClientContext*, btproto::SampleRowKeysRequest const&,
- grpc::CompletionQueue*) {
- auto reader = std::make_unique<
- MockClientAsyncReaderInterface>();
- EXPECT_CALL(*reader, StartCall);
- EXPECT_CALL(*reader, Read)
- .WillOnce([](btproto::SampleRowKeysResponse* r, void*) {
- {
- r->set_row_key("test2");
- r->set_offset_bytes(22);
- }
- })
- .WillOnce([](btproto::SampleRowKeysResponse*, void*) {});
- EXPECT_CALL(*reader, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
- return reader;
- });
-
- auto samples_future = table_.AsyncSampleRows();
-
- // Start()
- cq_impl_->SimulateCompletion(true);
- // Return response
- cq_impl_->SimulateCompletion(true);
- // End stream
- cq_impl_->SimulateCompletion(false);
- // Finish()
- cq_impl_->SimulateCompletion(true);
- // Simulate the backoff timer
- cq_impl_->SimulateCompletion(true);
-
- ASSERT_EQ(1U, cq_impl_->size());
-
- // Start()
- cq_impl_->SimulateCompletion(true);
- // Return response
- cq_impl_->SimulateCompletion(true);
- // End stream
- cq_impl_->SimulateCompletion(false);
- // Finish()
- cq_impl_->SimulateCompletion(true);
-
- ASSERT_EQ(0U, cq_impl_->size());
-
- auto status = samples_future.get();
- ASSERT_STATUS_OK(status);
-
- auto samples = RowKeySampleVectors(status.value());
- EXPECT_THAT(samples.row_keys, ElementsAre("test2"));
- EXPECT_THAT(samples.offset_bytes, ElementsAre(22));
-}
-
-TEST_F(AsyncSampleRowKeysTest, TooManyFailures) {
- // We give up on the 3rd error.
- auto constexpr kErrorCount = 2;
- Table custom_table(client_, "foo_table",
- LimitedErrorCountRetryPolicy(kErrorCount));
-
- EXPECT_CALL(*client_, PrepareAsyncSampleRowKeys)
- .Times(kErrorCount + 1)
- .WillRepeatedly([](grpc::ClientContext*,
- btproto::SampleRowKeysRequest const&,
- grpc::CompletionQueue*) {
- auto reader = std::make_unique<
- MockClientAsyncReaderInterface>();
- EXPECT_CALL(*reader, StartCall);
- EXPECT_CALL(*reader, Read).Times(2);
- EXPECT_CALL(*reader, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status(grpc::StatusCode::UNAVAILABLE, "try again");
- });
- return reader;
- });
-
- auto samples_future = custom_table.AsyncSampleRows();
-
- for (int retry = 0; retry < kErrorCount; ++retry) {
- // Start()
- cq_impl_->SimulateCompletion(true);
- // Return response
- cq_impl_->SimulateCompletion(true);
- // End stream
- cq_impl_->SimulateCompletion(false);
- // Finish()
- cq_impl_->SimulateCompletion(true);
- // Simulate the backoff timer
- cq_impl_->SimulateCompletion(true);
-
- ASSERT_EQ(1U, cq_impl_->size());
- }
-
- // Start()
- cq_impl_->SimulateCompletion(true);
- // Return response
- cq_impl_->SimulateCompletion(true);
- // End stream
- cq_impl_->SimulateCompletion(false);
- // Finish()
- cq_impl_->SimulateCompletion(true);
-
- auto status = samples_future.get();
- ASSERT_THAT(status,
- StatusIs(StatusCode::kUnavailable, HasSubstr("try again")));
-
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-TEST_F(AsyncSampleRowKeysTest, UsesBackoff) {
- auto grpc_error = grpc::Status(grpc::StatusCode::UNAVAILABLE, "try again");
- auto error = MakeStatusFromRpcError(grpc_error);
-
- std::unique_ptr mock(new MockBackoffPolicy);
- EXPECT_CALL(*mock, Setup).Times(2);
- EXPECT_CALL(*mock, OnCompletion(error));
-
- EXPECT_CALL(*client_, PrepareAsyncSampleRowKeys)
- .WillOnce([grpc_error](grpc::ClientContext*,
- btproto::SampleRowKeysRequest const&,
- grpc::CompletionQueue*) {
- auto reader = std::make_unique<
- MockClientAsyncReaderInterface>();
- EXPECT_CALL(*reader, StartCall);
- EXPECT_CALL(*reader, Read).Times(2);
- EXPECT_CALL(*reader, Finish)
- .WillOnce([grpc_error](grpc::Status* status, void*) {
- *status = grpc_error;
- });
- return reader;
- })
- .WillOnce([](grpc::ClientContext*, btproto::SampleRowKeysRequest const&,
- grpc::CompletionQueue*) {
- auto reader = std::make_unique<
- MockClientAsyncReaderInterface>();
- EXPECT_CALL(*reader, StartCall);
- EXPECT_CALL(*reader, Read).Times(2);
- EXPECT_CALL(*reader, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
- return reader;
- });
-
- auto samples_future = internal::LegacyAsyncRowSampler::Create(
- cq_, client_, rpc_retry_policy_->clone(), std::move(mock),
- metadata_update_policy_, "my-app-profile", "my-table");
-
- // Start()
- cq_impl_->SimulateCompletion(true);
- // Return response
- cq_impl_->SimulateCompletion(true);
- // End stream
- cq_impl_->SimulateCompletion(false);
- // Finish()
- cq_impl_->SimulateCompletion(true);
- // Simulate the backoff timer
- cq_impl_->SimulateCompletion(true);
-
- ASSERT_EQ(1U, cq_impl_->size());
-
- // Start()
- cq_impl_->SimulateCompletion(true);
- // Return response
- cq_impl_->SimulateCompletion(true);
- // End stream
- cq_impl_->SimulateCompletion(false);
- // Finish()
- cq_impl_->SimulateCompletion(true);
-
- ASSERT_EQ(0U, cq_impl_->size());
-}
-
-TEST_F(AsyncSampleRowKeysTest, CancelDuringBackoff) {
- auto grpc_error = grpc::Status(grpc::StatusCode::UNAVAILABLE, "try again");
- auto error = MakeStatusFromRpcError(grpc_error);
-
- std::unique_ptr mock(new MockBackoffPolicy);
- EXPECT_CALL(*mock, Setup);
- EXPECT_CALL(*mock, OnCompletion(error));
-
- EXPECT_CALL(*client_, PrepareAsyncSampleRowKeys)
- .WillOnce([grpc_error](grpc::ClientContext*,
- btproto::SampleRowKeysRequest const&,
- grpc::CompletionQueue*) {
- auto reader = std::make_unique<
- MockClientAsyncReaderInterface>();
- EXPECT_CALL(*reader, StartCall);
- EXPECT_CALL(*reader, Read).Times(2);
- EXPECT_CALL(*reader, Finish)
- .WillOnce([grpc_error](grpc::Status* status, void*) {
- *status = grpc_error;
- });
- return reader;
- });
-
- auto samples_future = internal::LegacyAsyncRowSampler::Create(
- cq_, client_, rpc_retry_policy_->clone(), std::move(mock),
- metadata_update_policy_, "my-app-profile", "my-table");
-
- // Start()
- cq_impl_->SimulateCompletion(true);
- // Return response
- cq_impl_->SimulateCompletion(true);
- // End stream
- cq_impl_->SimulateCompletion(false);
- // Finish()
- cq_impl_->SimulateCompletion(true);
-
- ASSERT_EQ(1U, cq_impl_->size());
-
- // Cancel the pending operation.
- samples_future.cancel();
- // Simulate the backoff timer
- cq_impl_->SimulateCompletion(false);
-
- ASSERT_EQ(0U, cq_impl_->size());
-
- auto status = samples_future.get();
- ASSERT_THAT(status,
- StatusIs(StatusCode::kCancelled, HasSubstr("call cancelled")));
-}
-
-TEST_F(AsyncSampleRowKeysTest, CancelAfterSuccess) {
- EXPECT_CALL(*client_, PrepareAsyncSampleRowKeys)
- .WillOnce([](grpc::ClientContext*, btproto::SampleRowKeysRequest const&,
- grpc::CompletionQueue*) {
- auto reader = std::make_unique<
- MockClientAsyncReaderInterface>();
- EXPECT_CALL(*reader, StartCall);
- EXPECT_CALL(*reader, Read)
- .WillOnce([](btproto::SampleRowKeysResponse* r, void*) {
- {
- r->set_row_key("test1");
- r->set_offset_bytes(11);
- }
- })
- .WillOnce([](btproto::SampleRowKeysResponse*, void*) {});
-
- EXPECT_CALL(*reader, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status::OK;
- });
- return reader;
- });
-
- auto samples_future = table_.AsyncSampleRows();
- // samples_future.cancel();
-
- // Start()
- cq_impl_->SimulateCompletion(true);
- // Return response
- cq_impl_->SimulateCompletion(true);
-
- // Cancel the pending operation
- samples_future.cancel();
-
- // End stream
- cq_impl_->SimulateCompletion(false);
- // Finish()
- cq_impl_->SimulateCompletion(true);
-
- auto status = samples_future.get();
- ASSERT_STATUS_OK(status);
-
- auto samples = RowKeySampleVectors(status.value());
- EXPECT_THAT(samples.row_keys, ElementsAre("test1"));
- EXPECT_THAT(samples.offset_bytes, ElementsAre(11));
-}
-
-TEST_F(AsyncSampleRowKeysTest, CancelMidStream) {
- EXPECT_CALL(*client_, PrepareAsyncSampleRowKeys)
- .WillOnce([](grpc::ClientContext*, btproto::SampleRowKeysRequest const&,
- grpc::CompletionQueue*) {
- auto reader = std::make_unique<
- MockClientAsyncReaderInterface>();
- EXPECT_CALL(*reader, StartCall);
- EXPECT_CALL(*reader, Read)
- .WillOnce([](btproto::SampleRowKeysResponse* r, void*) {
- {
- r->set_row_key("test1");
- r->set_offset_bytes(11);
- }
- })
- .WillOnce([](btproto::SampleRowKeysResponse* r, void*) {
- {
- r->set_row_key("test2");
- r->set_offset_bytes(22);
- }
- })
- .WillOnce([](btproto::SampleRowKeysResponse* r, void*) {
- {
- r->set_row_key("test3");
- r->set_offset_bytes(33);
- }
- });
- EXPECT_CALL(*reader, Finish).WillOnce([](grpc::Status* status, void*) {
- *status = grpc::Status(grpc::StatusCode::CANCELLED, "User cancelled");
- });
- return reader;
- });
-
- auto samples_future = table_.AsyncSampleRows();
-
- // Start()
- cq_impl_->SimulateCompletion(true);
- // Return response 1
- cq_impl_->SimulateCompletion(true);
- // Cancel the pending operation
- samples_future.cancel();
- // Return response 2
- cq_impl_->SimulateCompletion(true);
- // Return response 3
- cq_impl_->SimulateCompletion(false);
- // Finish()
- cq_impl_->SimulateCompletion(true);
-
- auto status = samples_future.get();
- EXPECT_THAT(status,
- StatusIs(StatusCode::kCancelled, HasSubstr("User cancelled")));
-}
-
-} // namespace
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
-} // namespace bigtable
-} // namespace cloud
-} // namespace google
diff --git a/google/cloud/bigtable/internal/legacy_bulk_mutator_test.cc b/google/cloud/bigtable/internal/legacy_bulk_mutator_test.cc
deleted file mode 100644
index 5674b8aff7390..0000000000000
--- a/google/cloud/bigtable/internal/legacy_bulk_mutator_test.cc
+++ /dev/null
@@ -1,496 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "google/cloud/bigtable/internal/bulk_mutator.h"
-#include "google/cloud/bigtable/testing/mock_data_client.h"
-#include "google/cloud/bigtable/testing/mock_mutate_rows_reader.h"
-#include "google/cloud/bigtable/testing/mock_response_reader.h"
-#include "google/cloud/testing_util/chrono_literals.h"
-#include "google/cloud/testing_util/fake_completion_queue_impl.h"
-
-namespace google {
-namespace cloud {
-namespace bigtable {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-namespace {
-
-namespace btproto = ::google::bigtable::v2;
-using ::testing::Return;
-using ::google::cloud::testing_util::chrono_literals::operator""_ms;
-using ::google::cloud::bigtable::testing::MockMutateRowsReader;
-
-auto constexpr kTableName = "projects/blah/instances/blah2/tables/table";
-
-std::unique_ptr TestContext(
- std::string const& app_profile_id = "") {
- auto context = std::make_unique();
- bigtable_internal::MakeMetadataUpdatePolicy(kTableName, app_profile_id)
- .Setup(*context);
- return context;
-}
-
-/// @test Verify that MultipleRowsMutator handles easy cases.
-TEST(MultipleRowsMutatorTest, Simple) {
- // In this test we create a Mutation for two rows, which succeeds in the
- // first RPC request. First create the mutation.
- BulkMutation mut(
- SingleRowMutation("foo", {SetCell("fam", "col", 0_ms, "baz")}),
- SingleRowMutation("bar", {SetCell("fam", "col", 0_ms, "qux")}));
-
- // Prepare the mocks. The mutator should issue a RPC which must return a
- // stream of responses, we prepare the stream first because it is easier than
- // to create one of the fly.
- auto reader = std::make_unique(
- "google.bigtable.v2.Bigtable.MutateRows");
- EXPECT_CALL(*reader, Read)
- .WillOnce([](btproto::MutateRowsResponse* r) {
- {
- auto& e = *r->add_entries();
- e.set_index(0);
- e.mutable_status()->set_code(grpc::StatusCode::OK);
- }
- {
- auto& e = *r->add_entries();
- e.set_index(1);
- e.mutable_status()->set_code(grpc::StatusCode::OK);
- }
- return true;
- })
- .WillOnce(Return(false));
- EXPECT_CALL(*reader, Finish()).WillOnce(Return(grpc::Status::OK));
-
- // Now prepare the client for the one request.
- bigtable::testing::MockDataClient client;
- EXPECT_CALL(client, MutateRows)
- .WillOnce(reader.release()->MakeMockReturner());
-
- auto policy = DefaultIdempotentMutationPolicy();
- bigtable_internal::BulkMutator mutator(
- "", kTableName, *policy, std::move(mut),
- std::make_shared());
-
- EXPECT_TRUE(mutator.HasPendingMutations());
- auto context = TestContext();
- auto status = mutator.MakeOneRequest(client, *context);
- EXPECT_TRUE(status.ok());
- auto failures = std::move(mutator).OnRetryDone();
- EXPECT_TRUE(failures.empty());
-}
-
-/// @test Verify that MultipleRowsMutator uses app_profile_id when set.
-TEST(MultipleRowsMutatorTest, BulkApplyAppProfileId) {
- namespace btproto = ::google::bigtable::v2;
-
- // In this test we create a Mutation for two rows, which succeeds in the
- // first RPC request. First create the mutation.
- BulkMutation mut(
- SingleRowMutation("foo", {SetCell("fam", "col", 0_ms, "baz")}),
- SingleRowMutation("bar", {SetCell("fam", "col", 0_ms, "qux")}));
-
- // Prepare the mocks. The mutator should issue a RPC which must return a
- // stream of responses, we prepare the stream first because it is easier than
- // to create one of the fly.
-
- // Now prepare the client for the one request.
- std::string expected_id = "test-id";
- bigtable::testing::MockDataClient client;
- EXPECT_CALL(client, MutateRows)
- .WillOnce([expected_id](grpc::ClientContext*,
- btproto::MutateRowsRequest const& req) {
- auto reader = std::make_unique(
- "google.bigtable.v2.Bigtable.MutateRows");
- EXPECT_CALL(*reader, Read)
- .WillOnce([](btproto::MutateRowsResponse* r) {
- {
- auto& e = *r->add_entries();
- e.set_index(0);
- e.mutable_status()->set_code(grpc::StatusCode::OK);
- }
- {
- auto& e = *r->add_entries();
- e.set_index(1);
- e.mutable_status()->set_code(grpc::StatusCode::OK);
- }
- return true;
- })
- .WillOnce(Return(false));
- EXPECT_CALL(*reader, Finish()).WillOnce(Return(grpc::Status::OK));
- EXPECT_EQ(expected_id, req.app_profile_id());
- return reader;
- });
-
- auto policy = DefaultIdempotentMutationPolicy();
- bigtable_internal::BulkMutator mutator(
- "test-id", kTableName, *policy, std::move(mut),
- std::make_shared());
-
- EXPECT_TRUE(mutator.HasPendingMutations());
- auto context = TestContext("test-id");
- auto status = mutator.MakeOneRequest(client, *context);
- EXPECT_TRUE(status.ok());
- auto failures = std::move(mutator).OnRetryDone();
- EXPECT_TRUE(failures.empty());
-}
-
-/// @test Verify that MultipleRowsMutator retries partial failures.
-TEST(MultipleRowsMutatorTest, RetryPartialFailure) {
- // In this test we create a Mutation for two rows, one of which will fail.
- // First create the mutation.
- BulkMutation mut(
- SingleRowMutation("foo", {SetCell("fam", "col", 0_ms, "baz")}),
- SingleRowMutation("bar", {SetCell("fam", "col", 0_ms, "qux")}));
-
- // Prepare the mocks for the request. First create a stream response which
- // indicates a partial failure.
-
- // Setup the client to response with r1 first, and r2 next.
- bigtable::testing::MockDataClient client;
- EXPECT_CALL(client, MutateRows)
- .WillOnce(
- [](grpc::ClientContext*, btproto::MutateRowsRequest const& req) {
- EXPECT_EQ(kTableName, req.table_name());
- auto r1 = std::make_unique(
- "google.bigtable.v2.Bigtable.MutateRows");
- EXPECT_CALL(*r1, Read)
- .WillOnce([](btproto::MutateRowsResponse* r) {
- // Simulate a partial (and recoverable) failure.
- auto& e0 = *r->add_entries();
- e0.set_index(0);
- e0.mutable_status()->set_code(grpc::StatusCode::UNAVAILABLE);
- auto& e1 = *r->add_entries();
- e1.set_index(1);
- e1.mutable_status()->set_code(grpc::StatusCode::OK);
- return true;
- })
- .WillOnce(Return(false));
- EXPECT_CALL(*r1, Finish).WillOnce(Return(grpc::Status::OK));
- return r1;
- })
- .WillOnce(
- [](grpc::ClientContext*, btproto::MutateRowsRequest const& req) {
- EXPECT_EQ(kTableName, req.table_name());
- // Prepare a second stream response, because the client should retry
- // after the partial failure.
- auto r2 = std::make_unique(
- "google.bigtable.v2.Bigtable.MutateRows");
- EXPECT_CALL(*r2, Read)
- .WillOnce([](btproto::MutateRowsResponse* r) {
- {
- auto& e = *r->add_entries();
- e.set_index(0);
- e.mutable_status()->set_code(grpc::StatusCode::OK);
- }
- return true;
- })
- .WillOnce(Return(false));
- EXPECT_CALL(*r2, Finish).WillOnce(Return(grpc::Status::OK));
- return r2;
- });
-
- auto policy = DefaultIdempotentMutationPolicy();
- bigtable_internal::BulkMutator mutator(
- "", kTableName, *policy, std::move(mut),
- std::make_shared());
-
- // This work will be in BulkApply(), but this is the test for BulkMutator in
- // isolation, so call MakeOneRequest() twice, for the r1, and the r2 cases.
- for (int i = 0; i != 2; ++i) {
- EXPECT_TRUE(mutator.HasPendingMutations());
- auto context = TestContext();
- auto status = mutator.MakeOneRequest(client, *context);
- EXPECT_TRUE(status.ok());
- }
- auto failures = std::move(mutator).OnRetryDone();
- EXPECT_TRUE(failures.empty());
-}
-
-/// @test Verify that MultipleRowsMutator handles permanent failures.
-TEST(MultipleRowsMutatorTest, PermanentFailure) {
- // In this test we handle a recoverable and one unrecoverable failures.
- // Create a bulk mutation with two SetCell() mutations.
- BulkMutation mut(
- SingleRowMutation("foo", {SetCell("fam", "col", 0_ms, "baz")}),
- SingleRowMutation("bar", {SetCell("fam", "col", 0_ms, "qux")}));
-
- // Make the first RPC return one recoverable and one unrecoverable failures.
- auto r1 = std::make_unique(
- "google.bigtable.v2.Bigtable.MutateRows");
- EXPECT_CALL(*r1, Read)
- .WillOnce([](btproto::MutateRowsResponse* r) {
- // Simulate a partial failure, which is recoverable for this first
- // element.
- auto& e0 = *r->add_entries();
- e0.set_index(0);
- e0.mutable_status()->set_code(grpc::StatusCode::UNAVAILABLE);
- // Simulate an unrecoverable failure for the second element.
- auto& e1 = *r->add_entries();
- e1.set_index(1);
- e1.mutable_status()->set_code(grpc::StatusCode::OUT_OF_RANGE);
- return true;
- })
- .WillOnce(Return(false));
- EXPECT_CALL(*r1, Finish()).WillOnce(Return(grpc::Status::OK));
-
- // The BulkMutator should issue a second request, which will return success
- // for the remaining mutation.
- auto r2 = std::make_unique(
- "google.bigtable.v2.Bigtable.MutateRows");
- EXPECT_CALL(*r2, Read)
- .WillOnce([](btproto::MutateRowsResponse* r) {
- {
- auto& e = *r->add_entries();
- e.set_index(0);
- e.mutable_status()->set_code(grpc::StatusCode::OK);
- }
- return true;
- })
- .WillOnce(Return(false));
- EXPECT_CALL(*r2, Finish()).WillOnce(Return(grpc::Status::OK));
-
- bigtable::testing::MockDataClient client;
- EXPECT_CALL(client, MutateRows)
- .WillOnce(r1.release()->MakeMockReturner())
- .WillOnce(r2.release()->MakeMockReturner());
-
- auto policy = DefaultIdempotentMutationPolicy();
- bigtable_internal::BulkMutator mutator(
- "", kTableName, *policy, std::move(mut),
- std::make_shared());
-
- // This work will be in BulkApply(), but this is the test for BulkMutator in
- // isolation, so call MakeOneRequest() twice, for the r1, and the r2 cases.
- for (int i = 0; i != 2; ++i) {
- EXPECT_TRUE(mutator.HasPendingMutations());
- auto context = TestContext();
- auto status = mutator.MakeOneRequest(client, *context);
- EXPECT_TRUE(status.ok());
- }
- auto failures = std::move(mutator).OnRetryDone();
- ASSERT_EQ(1UL, failures.size());
- EXPECT_EQ(1, failures[0].original_index());
- // EXPECT_EQ("bar", failures[0].mutation().row_key());
- EXPECT_EQ(google::cloud::StatusCode::kOutOfRange,
- failures[0].status().code());
-}
-
-/// @test Verify that MultipleRowsMutator handles a stream with partial results
-TEST(MultipleRowsMutatorTest, PartialStream) {
- // We are going to test the case where the stream does not contain a response
- // for all requests. Create a BulkMutation with two entries.
- BulkMutation mut(
- SingleRowMutation("foo", {SetCell("fam", "col", 0_ms, "baz")}),
- SingleRowMutation("bar", {SetCell("fam", "col", 0_ms, "qux")}));
-
- // This will be the stream returned by the first request. It is missing
- // information about one of the mutations.
- auto r1 = std::make_unique(
- "google.bigtable.v2.Bigtable.MutateRows");
- EXPECT_CALL(*r1, Read)
- .WillOnce([](btproto::MutateRowsResponse* r) {
- auto& e0 = *r->add_entries();
- e0.set_index(0);
- e0.mutable_status()->set_code(grpc::StatusCode::OK);
- return true;
- })
- .WillOnce(Return(false));
- EXPECT_CALL(*r1, Finish()).WillOnce(Return(grpc::Status::OK));
-
- // The BulkMutation should issue a second request, this is the stream returned
- // by the second request, which indicates success for the missed mutation
- // on r1.
- auto r2 = std::make_unique(
- "google.bigtable.v2.Bigtable.MutateRows");
- EXPECT_CALL(*r2, Read)
- .WillOnce([](btproto::MutateRowsResponse* r) {
- {
- auto& e = *r->add_entries();
- e.set_index(0);
- e.mutable_status()->set_code(grpc::StatusCode::OK);
- }
- return true;
- })
- .WillOnce(Return(false));
- EXPECT_CALL(*r2, Finish()).WillOnce(Return(grpc::Status::OK));
-
- bigtable::testing::MockDataClient client;
- EXPECT_CALL(client, MutateRows)
- .WillOnce(r1.release()->MakeMockReturner())
- .WillOnce(r2.release()->MakeMockReturner());
-
- auto policy = DefaultIdempotentMutationPolicy();
- bigtable_internal::BulkMutator mutator(
- "", kTableName, *policy, std::move(mut),
- std::make_shared());
-
- // This work will be in BulkApply(), but this is the test for BulkMutator in
- // isolation, so call MakeOneRequest() twice: for the r1 and r2 cases.
- for (int i = 0; i != 2; ++i) {
- EXPECT_TRUE(mutator.HasPendingMutations());
- auto context = TestContext();
- auto status = mutator.MakeOneRequest(client, *context);
- EXPECT_TRUE(status.ok());
- }
- auto failures = std::move(mutator).OnRetryDone();
- EXPECT_TRUE(failures.empty());
-}
-
-/// @test Verify that MultipleRowsMutator only retries idempotent mutations
-TEST(MultipleRowsMutatorTest, RetryOnlyIdempotent) {
- // Create a BulkMutation with a non-idempotent mutation.
- BulkMutation mut(
- SingleRowMutation("foo", {SetCell("fam", "col", "baz")}),
- SingleRowMutation("bar", {SetCell("fam", "col", 0_ms, "qux")}),
- SingleRowMutation("baz", {SetCell("fam", "col", "v")}),
- SingleRowMutation("bar", {SetCell("fam", "col", 0_ms, "qux")}));
-
- // We will setup the mock to return recoverable failures for idempotent
- // mutations.
-
- // Verify that the second response has the right contents. It is easier (and
- // more readable) to write these in a separate small lambda expression because
- // ASSERT_EQ() has an embedded "return;" in it, which does not play well with
- // the rest of the stuff here.
- auto expect_r2 = [](btproto::MutateRowsRequest const& r) {
- ASSERT_EQ(2, r.entries_size());
- EXPECT_EQ("bar", r.entries(0).row_key());
- };
- bigtable::testing::MockDataClient client;
- EXPECT_CALL(client, MutateRows)
- .WillOnce([](grpc::ClientContext*, btproto::MutateRowsRequest const& r) {
- EXPECT_EQ(4, r.entries_size());
- auto r1 = std::make_unique(
- "google.bigtable.v2.Bigtable.MutateRows");
- EXPECT_CALL(*r1, Read)
- .WillOnce([](btproto::MutateRowsResponse* r) {
- // Simulate recoverable failures for both elements.
- auto& e0 = *r->add_entries();
- e0.set_index(0);
- e0.mutable_status()->set_code(grpc::StatusCode::UNAVAILABLE);
- auto& e1 = *r->add_entries();
- e1.set_index(1);
- e1.mutable_status()->set_code(grpc::StatusCode::UNAVAILABLE);
- return true;
- })
- .WillOnce(Return(false));
- EXPECT_CALL(*r1, Finish).WillOnce(Return(grpc::Status::OK));
- return r1;
- })
- .WillOnce([expect_r2](grpc::ClientContext*,
- btproto::MutateRowsRequest const& r) {
- expect_r2(r);
- // The BulkMutator should issue a second request, with only the
- // idempotent mutations, make the mocks return success for them.
- auto r2 = std::make_unique(
- "google.bigtable.v2.Bigtable.MutateRows");
- EXPECT_CALL(*r2, Read)
- .WillOnce([](btproto::MutateRowsResponse* r) {
- {
- auto& e = *r->add_entries();
- e.set_index(0);
- e.mutable_status()->set_code(grpc::StatusCode::OK);
- }
- return true;
- })
- .WillOnce(Return(false));
- EXPECT_CALL(*r2, Finish).WillOnce(Return(grpc::Status::OK));
-
- return r2;
- });
-
- auto policy = DefaultIdempotentMutationPolicy();
- bigtable_internal::BulkMutator mutator(
- "", kTableName, *policy, std::move(mut),
- std::make_shared());
-
- // This work will be in BulkApply(), but this is the test for BulkMutator in
- // isolation, so call MakeOneRequest() twice, for the r1, and the r2 cases.
- for (int i = 0; i != 2; ++i) {
- EXPECT_TRUE(mutator.HasPendingMutations());
- auto context = TestContext();
- auto status = mutator.MakeOneRequest(client, *context);
- EXPECT_TRUE(status.ok());
- }
- auto failures = std::move(mutator).OnRetryDone();
- ASSERT_EQ(3UL, failures.size());
- EXPECT_EQ(0, failures[0].original_index());
- // EXPECT_EQ("foo", failures[0].mutation().row_key());
- EXPECT_EQ(google::cloud::StatusCode::kUnavailable,
- failures[0].status().code());
-
- EXPECT_EQ(2, failures[1].original_index());
- // EXPECT_EQ("baz", failures[1].mutation().row_key());
- EXPECT_EQ(google::cloud::StatusCode::kInternal, failures[1].status().code());
- EXPECT_EQ(google::cloud::StatusCode::kInternal, failures[2].status().code());
-}
-
-TEST(MultipleRowsMutatorTest, UnconfirmedAreFailed) {
- // Make sure that mutations which are not confirmed are reported as UNKNOWN
- // with the proper index.
- BulkMutation mut(SingleRowMutation("foo", {SetCell("fam", "col", "baz")}),
- // this one will be unconfirmed
- SingleRowMutation("bar", {SetCell("fam", "col", "qux")}),
- SingleRowMutation("baz", {SetCell("fam", "col", "v")}));
-
- // We will setup the mock to return recoverable failures for idempotent
- // mutations.
-
- // The BulkMutator should not issue a second request because the error is
- // PERMISSION_DENIED (not retryable).
-
- bigtable::testing::MockDataClient client;
- EXPECT_CALL(client, MutateRows)
- .WillOnce([](grpc::ClientContext*, btproto::MutateRowsRequest const& r) {
- EXPECT_EQ(3, r.entries_size());
- auto r1 = std::make_unique(
- "google.bigtable.v2.Bigtable.MutateRows");
- EXPECT_CALL(*r1, Read)
- .WillOnce([](btproto::MutateRowsResponse* r) {
- auto& e0 = *r->add_entries();
- e0.set_index(0);
- e0.mutable_status()->set_code(grpc::StatusCode::OK);
- auto& e1 = *r->add_entries();
- e1.set_index(2);
- e1.mutable_status()->set_code(grpc::StatusCode::OK);
- return true;
- })
- .WillOnce(Return(false));
- EXPECT_CALL(*r1, Finish)
- .WillOnce(
- Return(grpc::Status(grpc::StatusCode::PERMISSION_DENIED, "")));
- return r1;
- });
-
- auto policy = DefaultIdempotentMutationPolicy();
- bigtable_internal::BulkMutator mutator(
- "", kTableName, *policy, std::move(mut),
- std::make_shared());
-
- EXPECT_TRUE(mutator.HasPendingMutations());
- auto context = TestContext();
- auto status = mutator.MakeOneRequest(client, *context);
- EXPECT_FALSE(status.ok());
-
- auto failures = std::move(mutator).OnRetryDone();
- ASSERT_EQ(1UL, failures.size());
- EXPECT_EQ(1, failures[0].original_index());
- // EXPECT_EQ("bar", failures[0].mutation().row_key());
- EXPECT_EQ(google::cloud::StatusCode::kPermissionDenied,
- failures.front().status().code());
-}
-
-} // anonymous namespace
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
-} // namespace bigtable
-} // namespace cloud
-} // namespace google
diff --git a/google/cloud/bigtable/internal/legacy_row_reader.cc b/google/cloud/bigtable/internal/legacy_row_reader.cc
deleted file mode 100644
index 62eeafff9ca3e..0000000000000
--- a/google/cloud/bigtable/internal/legacy_row_reader.cc
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "google/cloud/bigtable/internal/legacy_row_reader.h"
-#include "google/cloud/bigtable/table.h"
-#include "google/cloud/grpc_error_delegate.h"
-#include "google/cloud/internal/throw_delegate.h"
-#include "google/cloud/log.h"
-#include
-
-namespace google {
-namespace cloud {
-namespace bigtable_internal {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-
-LegacyRowReader::LegacyRowReader(
- std::shared_ptr client, std::string table_name,
- bigtable::RowSet row_set, std::int64_t rows_limit, bigtable::Filter filter,
- std::unique_ptr retry_policy,
- std::unique_ptr backoff_policy,
- bigtable::MetadataUpdatePolicy metadata_update_policy,
- std::unique_ptr parser_factory)
- : LegacyRowReader(std::move(client), std::string(""), std::move(table_name),
- std::move(row_set), rows_limit, std::move(filter),
- std::move(retry_policy), std::move(backoff_policy),
- std::move(metadata_update_policy),
- std::move(parser_factory)) {}
-
-LegacyRowReader::LegacyRowReader(
- std::shared_ptr client, std::string app_profile_id,
- std::string table_name, bigtable::RowSet row_set, std::int64_t rows_limit,
- bigtable::Filter filter,
- std::unique_ptr retry_policy,
- std::unique_ptr backoff_policy,
- bigtable::MetadataUpdatePolicy metadata_update_policy,
- std::unique_ptr parser_factory)
- : client_(std::move(client)),
- app_profile_id_(std::move(app_profile_id)),
- table_name_(std::move(table_name)),
- row_set_(std::move(row_set)),
- rows_limit_(rows_limit),
- filter_(std::move(filter)),
- retry_policy_(std::move(retry_policy)),
- backoff_policy_(std::move(backoff_policy)),
- metadata_update_policy_(std::move(metadata_update_policy)),
- parser_factory_(std::move(parser_factory)) {}
-
-void LegacyRowReader::MakeRequest() {
- response_ = {};
- processed_chunks_count_ = 0;
-
- google::bigtable::v2::ReadRowsRequest request;
- request.set_table_name(table_name_);
- request.set_app_profile_id(app_profile_id_);
-
- auto row_set_proto = row_set_.as_proto();
- request.mutable_rows()->Swap(&row_set_proto);
-
- auto filter_proto = filter_.as_proto();
- request.mutable_filter()->Swap(&filter_proto);
-
- if (rows_limit_ != bigtable::RowReader::NO_ROWS_LIMIT) {
- request.set_rows_limit(rows_limit_ - rows_count_);
- }
-
- context_ = std::make_unique();
- retry_policy_->Setup(*context_);
- backoff_policy_->Setup(*context_);
- metadata_update_policy_.Setup(*context_);
- stream_ = client_->ReadRows(context_.get(), request);
- stream_is_open_ = true;
-
- parser_ = parser_factory_->Create(false);
-}
-
-bool LegacyRowReader::NextChunk() {
- ++processed_chunks_count_;
- while (processed_chunks_count_ >= response_.chunks_size()) {
- processed_chunks_count_ = 0;
- bool response_is_valid = stream_->Read(&response_);
- if (!response_is_valid) {
- response_ = {};
- return false;
- }
- if (!response_.last_scanned_row_key().empty()) {
- last_read_row_key_ = std::move(*response_.mutable_last_scanned_row_key());
- }
- }
- return true;
-}
-
-absl::variant LegacyRowReader::Advance() {
- if (operation_cancelled_) {
- return Status(StatusCode::kCancelled, "Operation cancelled.");
- }
- while (true) {
- auto variant = AdvanceOrFail();
- if (absl::holds_alternative(variant)) {
- return absl::get(std::move(variant));
- }
-
- auto status = absl::get(std::move(variant));
- if (status.ok()) return Status{};
-
- // In the unlikely case when we have already reached the requested
- // number of rows and still receive an error (the parser can throw
- // an error at end of stream for example), there is no need to
- // retry and we have no good value for rows_limit anyway.
- if (rows_limit_ != bigtable::RowReader::NO_ROWS_LIMIT &&
- rows_limit_ <= rows_count_) {
- return Status{};
- }
-
- if (!last_read_row_key_.empty()) {
- // We've returned some rows and need to make sure we don't
- // request them again.
- row_set_ =
- row_set_.Intersect(bigtable::RowRange::Open(last_read_row_key_, ""));
- }
-
- // If we receive an error, but the retryable set is empty, stop.
- if (row_set_.IsEmpty()) return Status{};
-
- if (!retry_policy_->OnFailure(status)) return status;
-
- auto delay = backoff_policy_->OnCompletion(status);
- std::this_thread::sleep_for(delay);
-
- // If we reach this place, we failed and need to restart the call.
- MakeRequest();
- }
-}
-
-absl::variant LegacyRowReader::AdvanceOrFail() {
- grpc::Status status;
- if (!stream_) {
- MakeRequest();
- }
- while (!parser_->HasNext()) {
- if (NextChunk()) {
- parser_->HandleChunk(
- std::move(*(response_.mutable_chunks(processed_chunks_count_))),
- status);
- if (!status.ok()) return MakeStatusFromRpcError(status);
- continue;
- }
-
- // Here, there are no more chunks to look at. Close the stream,
- // finalize the parser and return OK with no rows unless something
- // fails during cleanup.
- stream_is_open_ = false;
- status = stream_->Finish();
- if (!status.ok()) return MakeStatusFromRpcError(status);
- parser_->HandleEndOfStream(status);
- return MakeStatusFromRpcError(status);
- }
-
- // We have a complete row in the parser.
- bigtable::Row parsed_row = parser_->Next(status);
- if (!status.ok()) return MakeStatusFromRpcError(status);
-
- ++rows_count_;
- last_read_row_key_ = parsed_row.row_key();
- return parsed_row;
-}
-
-void LegacyRowReader::Cancel() {
- operation_cancelled_ = true;
- if (!stream_is_open_) return;
- context_->TryCancel();
-
- // Also drain any data left unread
- google::bigtable::v2::ReadRowsResponse response;
- while (stream_->Read(&response)) {
- }
-
- stream_is_open_ = false;
- (void)stream_->Finish(); // ignore errors
-}
-
-LegacyRowReader::~LegacyRowReader() {
- // Make sure we don't leave open streams.
- Cancel();
-}
-
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
-} // namespace bigtable_internal
-} // namespace cloud
-} // namespace google
diff --git a/google/cloud/bigtable/internal/legacy_row_reader.h b/google/cloud/bigtable/internal/legacy_row_reader.h
deleted file mode 100644
index 3d9acc7fae36f..0000000000000
--- a/google/cloud/bigtable/internal/legacy_row_reader.h
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_INTERNAL_LEGACY_ROW_READER_H
-#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_INTERNAL_LEGACY_ROW_READER_H
-
-#include "google/cloud/bigtable/data_client.h"
-#include "google/cloud/bigtable/filters.h"
-#include "google/cloud/bigtable/internal/readrowsparser.h"
-#include "google/cloud/bigtable/internal/row_reader_impl.h"
-#include "google/cloud/bigtable/metadata_update_policy.h"
-#include "google/cloud/bigtable/row.h"
-#include "google/cloud/bigtable/row_set.h"
-#include "google/cloud/bigtable/rpc_backoff_policy.h"
-#include "google/cloud/bigtable/rpc_retry_policy.h"
-#include "google/cloud/bigtable/version.h"
-#include "absl/types/variant.h"
-#include "google/bigtable/v2/bigtable.grpc.pb.h"
-#include
-#include
-#include
-
-namespace google {
-namespace cloud {
-namespace bigtable_internal {
-GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
-
-/**
- * `RowReaderImpl` that interacts with the Bigtable service via `DataClient`.
- */
-class LegacyRowReader : public RowReaderImpl {
- public:
- LegacyRowReader(std::shared_ptr client,
- std::string table_name, bigtable::RowSet row_set,
- std::int64_t rows_limit, bigtable::Filter filter,
- std::unique_ptr retry_policy,
- std::unique_ptr backoff_policy,
- bigtable::MetadataUpdatePolicy metadata_update_policy,
- std::unique_ptr
- parser_factory);
-
- LegacyRowReader(std::shared_ptr client,
- std::string app_profile_id, std::string table_name,
- bigtable::RowSet row_set, std::int64_t rows_limit,
- bigtable::Filter filter,
- std::unique_ptr