@@ -96,6 +96,11 @@ Status PermanentError() {
9696 return Status (StatusCode::kPermissionDenied , " fail" );
9797}
9898
99+ Status QueryPlanError () {
100+ return Status (StatusCode::kFailedPrecondition ,
101+ " oops! PREPARED_QUERY_EXPIRED" );
102+ }
103+
99104bigtable::SingleRowMutation IdempotentMutation (std::string const & row_key) {
100105 return bigtable::SingleRowMutation (
101106 row_key, {bigtable::SetCell (" fam" , " col" , ms (0 ), " val" )});
@@ -204,6 +209,12 @@ DataLimitedErrorCountRetryPolicy TestRetryPolicy() {
204209 return DataLimitedErrorCountRetryPolicy (kNumRetries );
205210}
206211
212+ bigtable::experimental::QueryPlanRefreshLimitedErrorCountRetryPolicy
213+ TestQueryPlanRefreshRetryPolicy () {
214+ return bigtable::experimental::QueryPlanRefreshLimitedErrorCountRetryPolicy (
215+ kNumRetries );
216+ }
217+
207218ExponentialBackoffPolicy TestBackoffPolicy () {
208219 return ExponentialBackoffPolicy (ms (0 ), ms (0 ), 2.0 );
209220}
@@ -239,6 +250,8 @@ Options CallOptionsWithoutClientContextSetup() {
239250 Options{}
240251 .set <bigtable::AppProfileIdOption>(kAppProfile )
241252 .set <DataRetryPolicyOption>(TestRetryPolicy ().clone ())
253+ .set <bigtable::experimental::QueryPlanRefreshRetryPolicyOption>(
254+ TestQueryPlanRefreshRetryPolicy ().clone ())
242255 .set <DataBackoffPolicyOption>(TestBackoffPolicy ().clone ()));
243256}
244257
@@ -3108,6 +3121,7 @@ TEST_F(DataConnectionTest, ExecuteQueryOperationRetryExhausted) {
31083121 std::move (refresh_fn));
31093122
31103123 EXPECT_CALL (*mock, ExecuteQuery)
3124+ .Times (9 )
31113125 .WillRepeatedly ([&](auto , auto const &, auto const &) {
31123126 auto stream = std::make_unique<MockExecuteQueryStream>();
31133127 EXPECT_CALL (*stream, Read).WillOnce (Return (TransientError ()));
@@ -3129,6 +3143,136 @@ TEST_F(DataConnectionTest, ExecuteQueryOperationRetryExhausted) {
31293143 fake_cq_impl->SimulateCompletion (false );
31303144}
31313145
3146+ TEST_F (DataConnectionTest, ExecuteQuerySuccessWithQueryPlanRefresh) {
3147+ auto mock = std::make_shared<MockBigtableStub>();
3148+ auto fake_cq_impl = std::make_shared<FakeCompletionQueueImpl>();
3149+ auto mock_bg = std::make_unique<MockBackgroundThreads>();
3150+ EXPECT_CALL (*mock_bg, cq).WillRepeatedly ([&]() {
3151+ return CompletionQueue{fake_cq_impl};
3152+ });
3153+
3154+ auto constexpr kInitialResultMetadataText = R"pb(
3155+ proto_schema {
3156+ columns {
3157+ name: "row_key"
3158+ type { string_type {} }
3159+ }
3160+ columns {
3161+ name: "value"
3162+ type { string_type {} }
3163+ }
3164+ columns {
3165+ name: "other_value"
3166+ type { string_type {} }
3167+ }
3168+ }
3169+ )pb" ;
3170+ v2::PrepareQueryResponse initial_pq_response;
3171+ initial_pq_response.set_prepared_query (" test-pq-id-initial" );
3172+ ASSERT_TRUE (google::protobuf::TextFormat::ParseFromString (
3173+ kInitialResultMetadataText , initial_pq_response.mutable_metadata ()));
3174+ *initial_pq_response.mutable_valid_until () = internal::ToProtoTimestamp (
3175+ std::chrono::system_clock::now () + std::chrono::seconds (3600 ));
3176+
3177+ auto constexpr kRefreshResultMetadataText = R"pb(
3178+ proto_schema {
3179+ columns {
3180+ name: "row_key"
3181+ type { string_type {} }
3182+ }
3183+ columns {
3184+ name: "value"
3185+ type { string_type {} }
3186+ }
3187+ }
3188+ )pb" ;
3189+ v2::PrepareQueryResponse refresh_pq_response;
3190+ refresh_pq_response.set_prepared_query (" test-pq-id-refresh" );
3191+ ASSERT_TRUE (google::protobuf::TextFormat::ParseFromString (
3192+ kRefreshResultMetadataText , refresh_pq_response.mutable_metadata ()));
3193+ *refresh_pq_response.mutable_valid_until () = internal::ToProtoTimestamp (
3194+ std::chrono::system_clock::now () + std::chrono::seconds (3600 ));
3195+
3196+ using RefreshReturnType =
3197+ StatusOr<google::bigtable::v2::PrepareQueryResponse>;
3198+ MockFunction<future<RefreshReturnType>()> refresh_fn;
3199+ EXPECT_CALL (refresh_fn, Call)
3200+ .WillOnce ([&]() {
3201+ return make_ready_future (RefreshReturnType (TransientError ()));
3202+ })
3203+ .WillOnce ([&]() {
3204+ return make_ready_future (make_status_or (refresh_pq_response));
3205+ });
3206+
3207+ EXPECT_CALL (*mock, ExecuteQuery)
3208+ .WillOnce ([&](auto , auto const &,
3209+ google::bigtable::v2::ExecuteQueryRequest const & request) {
3210+ EXPECT_EQ (request.prepared_query (), " test-pq-id-initial" );
3211+ auto error_stream = std::make_unique<MockExecuteQueryStream>();
3212+ EXPECT_CALL (*error_stream, Read).WillOnce (Return (QueryPlanError ()));
3213+ return error_stream;
3214+ })
3215+ .WillOnce ([&](auto , auto const &,
3216+ google::bigtable::v2::ExecuteQueryRequest const & request) {
3217+ EXPECT_EQ (request.app_profile_id (), kAppProfile );
3218+ EXPECT_EQ (request.instance_name (),
3219+ " projects/test-project/instances/test-instance" );
3220+ EXPECT_EQ (request.prepared_query (), " test-pq-id-refresh" );
3221+
3222+ auto stream = std::make_unique<MockExecuteQueryStream>();
3223+ EXPECT_CALL (*stream, Read)
3224+ .WillOnce ([&](google::bigtable::v2::ExecuteQueryResponse* r) {
3225+ *r->mutable_metadata () = refresh_pq_response.metadata ();
3226+ return absl::nullopt ;
3227+ })
3228+ .WillOnce ([&](google::bigtable::v2::ExecuteQueryResponse* r) {
3229+ MakeResponse (*r->mutable_results (), {" r1" , " v1" }, absl::nullopt ,
3230+ false );
3231+ return absl::nullopt ;
3232+ })
3233+ .WillOnce ([&](google::bigtable::v2::ExecuteQueryResponse* r) {
3234+ MakeResponse (*r->mutable_results (), {" r2" , " v2" },
3235+ " sentinel-token" , false );
3236+ return absl::nullopt ;
3237+ })
3238+ // End of stream
3239+ .WillOnce (Return (google::cloud::Status ()));
3240+
3241+ return stream;
3242+ });
3243+
3244+ auto conn = TestConnection (std::move (mock));
3245+ internal::OptionsSpan span (CallOptions ());
3246+ Project p (" test-project" );
3247+ bigtable::SqlStatement statement (" SELECT * FROM the-table" );
3248+ bigtable::InstanceResource instance (p, " test-instance" );
3249+ auto query_plan = QueryPlan::Create (CompletionQueue (fake_cq_impl),
3250+ std::move (initial_pq_response),
3251+ refresh_fn.AsStdFunction ());
3252+ auto prepared_query =
3253+ bigtable::PreparedQuery (instance, statement, std::move (query_plan));
3254+ auto bq = prepared_query.BindParameters ({});
3255+ bigtable::ExecuteQueryParams params{std::move (bq)};
3256+ auto row_stream = conn->ExecuteQuery (std::move (params));
3257+ std::vector<StatusOr<bigtable::QueryRow>> rows;
3258+ for (auto const & row : row_stream) {
3259+ ASSERT_STATUS_OK (row);
3260+ rows.push_back (row);
3261+ }
3262+
3263+ ASSERT_EQ (rows.size (), 2 );
3264+ ASSERT_STATUS_OK (rows[0 ]);
3265+ auto const & row1 = *rows[0 ];
3266+ EXPECT_EQ (row1.columns ().at (0 ), " row_key" );
3267+ EXPECT_EQ (row1.columns ().at (1 ), " value" );
3268+ EXPECT_THAT (row1.values ().at (0 ).get <std::string>(), IsOkAndHolds (" r1" ));
3269+ EXPECT_THAT (row1.values ().at (1 ).get <std::string>(), IsOkAndHolds (" v1" ));
3270+ auto const & row2 = *rows[1 ];
3271+ EXPECT_THAT (row2.values ().at (0 ).get <std::string>(), IsOkAndHolds (" r2" ));
3272+ EXPECT_THAT (row2.values ().at (1 ).get <std::string>(), IsOkAndHolds (" v2" ));
3273+ fake_cq_impl->SimulateCompletion (false );
3274+ }
3275+
31323276} // namespace
31333277GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
31343278} // namespace bigtable_internal
0 commit comments