Skip to content

Commit d4c31f8

Browse files
committed
impl(bigtable): add DeadlineOption
1 parent 1b9b3c1 commit d4c31f8

6 files changed

Lines changed: 277 additions & 0 deletions

File tree

google/cloud/bigtable/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ add_library(
229229
mutation_branch.h
230230
mutations.cc
231231
mutations.h
232+
options.cc
232233
options.h
233234
polling_policy.cc
234235
polling_policy.h
@@ -476,6 +477,7 @@ if (BUILD_TESTING)
476477
mocks/mock_row_reader_test.cc
477478
mutation_batcher_test.cc
478479
mutations_test.cc
480+
options_test.cc
479481
polling_policy_test.cc
480482
prepared_query_test.cc
481483
query_row_test.cc

google/cloud/bigtable/bigtable_client_unit_tests.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ bigtable_client_unit_tests = [
7272
"mocks/mock_row_reader_test.cc",
7373
"mutation_batcher_test.cc",
7474
"mutations_test.cc",
75+
"options_test.cc",
7576
"polling_policy_test.cc",
7677
"prepared_query_test.cc",
7778
"query_row_test.cc",

google/cloud/bigtable/google_cloud_cpp_bigtable.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ google_cloud_cpp_bigtable_srcs = [
222222
"internal/traced_row_reader.cc",
223223
"mutation_batcher.cc",
224224
"mutations.cc",
225+
"options.cc",
225226
"polling_policy.cc",
226227
"prepared_query.cc",
227228
"query_row.cc",

google/cloud/bigtable/options.cc

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "google/cloud/bigtable/options.h"
16+
#include "google/cloud/grpc_options.h"
17+
#include "google/cloud/options.h"
18+
#include <chrono>
19+
20+
namespace google {
21+
namespace cloud {
22+
namespace bigtable_internal {
23+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
24+
25+
Options MergeOptions(Options preferred, Options alternatives) {
26+
return MergeOptions(std::move(preferred), std::move(alternatives),
27+
[] { return std::chrono::system_clock::now(); });
28+
}
29+
30+
Options MergeOptions(
31+
Options preferred, Options alternatives,
32+
std::function<std::chrono::system_clock::time_point()> now_fn) {
33+
auto merged_options =
34+
internal::MergeOptions(std::move(preferred), std::move(alternatives));
35+
36+
if (merged_options.has<bigtable::DeadlineOption>()) {
37+
auto deadline = merged_options.get<bigtable::DeadlineOption>();
38+
auto existing_fn =
39+
internal::ExtractOption<internal::GrpcSetupOption>(merged_options);
40+
if (existing_fn.has_value()) {
41+
auto apply_deadline = [existing_fn = *std::move(existing_fn),
42+
now_fn = std::move(now_fn),
43+
deadline](grpc::ClientContext& context) {
44+
existing_fn(context);
45+
context.set_deadline(now_fn() + deadline);
46+
};
47+
merged_options.set<internal::GrpcSetupOption>(apply_deadline);
48+
} else {
49+
auto apply_deadline = [now_fn = std::move(now_fn),
50+
deadline](grpc::ClientContext& context) {
51+
context.set_deadline(now_fn() + deadline);
52+
};
53+
merged_options.set<internal::GrpcSetupOption>(apply_deadline);
54+
}
55+
}
56+
return merged_options;
57+
}
58+
59+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
60+
} // namespace bigtable_internal
61+
} // namespace cloud
62+
} // namespace google

google/cloud/bigtable/options.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
#include "google/cloud/bigtable/retry_policy.h"
4545
#include "google/cloud/bigtable/version.h"
4646
#include "google/cloud/backoff_policy.h"
47+
#include "google/cloud/grpc_options.h"
48+
#include "google/cloud/internal/clock.h"
4749
#include "google/cloud/options.h"
4850
#include <chrono>
4951
#include <string>
@@ -250,6 +252,23 @@ struct DataBackoffPolicyOption {
250252
using Type = std::shared_ptr<BackoffPolicy>;
251253
};
252254

255+
/**
256+
* Option to set the per RPC attempt deadline.
257+
*
258+
* @note If used in conjunction with a LimitedTimeRetryPolicy the last RPC
259+
* attempt could cause the maximum duration of the RetryPolicy to be
260+
* extended up to the RPC attempt deadline duration.
261+
*
262+
* @note If both DeadlineOption and GrpcSetupOption are set, DeadlineOption will
263+
* be applied after GrpcSetupOption, overwriting any changes GrpcSetupOption
264+
* made to grpc::ClientContext::deadline.
265+
*
266+
* @ingroup google-cloud-bigtable-options
267+
*/
268+
struct DeadlineOption {
269+
using Type = std::chrono::milliseconds;
270+
};
271+
253272
/**
254273
* Option to configure the idempotency policy used by `Table`.
255274
*
@@ -299,6 +318,21 @@ using DataPolicyOptionList =
299318

300319
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
301320
} // namespace bigtable
321+
322+
namespace bigtable_internal {
323+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
324+
325+
/// Bigtable specific MergeOptions that has special handling for merging
326+
/// `GrpcSetupOption` and `DeadlineOption`.
327+
Options MergeOptions(Options preferred, Options alternatives);
328+
329+
/// For testing only.
330+
Options MergeOptions(
331+
Options preferred, Options alternatives,
332+
std::function<std::chrono::system_clock::time_point()> now_fn);
333+
334+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
335+
} // namespace bigtable_internal
302336
} // namespace cloud
303337
} // namespace google
304338

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "google/cloud/bigtable/options.h"
16+
#include "google/cloud/grpc_options.h"
17+
#include "google/cloud/options.h"
18+
#include "google/cloud/testing_util/fake_clock.h"
19+
#include <gmock/gmock.h>
20+
#include <chrono>
21+
#include <memory>
22+
23+
namespace google {
24+
namespace cloud {
25+
namespace bigtable_internal {
26+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
27+
namespace {
28+
29+
using ::google::cloud::bigtable::DeadlineOption;
30+
using ::google::cloud::internal::GrpcSetupOption;
31+
using ::google::cloud::testing_util::FakeSystemClock;
32+
using ::testing::Eq;
33+
34+
TEST(MergeOptions, ConnectionDeadlineOption) {
35+
FakeSystemClock clock;
36+
clock.SetTime(std::chrono::system_clock::now());
37+
auto now_fn = [&] { return clock.Now(); };
38+
39+
auto connection_opts = Options{}.set<DeadlineOption>(std::chrono::seconds(1));
40+
Options client_opts;
41+
42+
// Merge in a deadline. This should populate the GrpcSetupOption with a call
43+
// to ClientContext::set_deadline
44+
auto merged_client_opts =
45+
MergeOptions(std::move(client_opts), std::move(connection_opts), now_fn);
46+
47+
ASSERT_TRUE(merged_client_opts.has<GrpcSetupOption>());
48+
grpc::ClientContext context;
49+
merged_client_opts.get<GrpcSetupOption>()(context);
50+
EXPECT_THAT(context.deadline() - clock.Now(), Eq(std::chrono::seconds(1)));
51+
}
52+
53+
TEST(MergeOptions, ClientOverridesDeadlineOption) {
54+
FakeSystemClock clock;
55+
clock.SetTime(std::chrono::system_clock::now());
56+
auto now_fn = [&] { return clock.Now(); };
57+
58+
auto connection_opts = Options{}.set<DeadlineOption>(std::chrono::seconds(1));
59+
auto client_opts = Options{}.set<DeadlineOption>(std::chrono::seconds(2));
60+
61+
// Merge in a deadline. This should populate the GrpcSetupOption with a call
62+
// to ClientContext::set_deadline
63+
auto merged_client_opts =
64+
MergeOptions(std::move(client_opts), std::move(connection_opts), now_fn);
65+
66+
ASSERT_TRUE(merged_client_opts.has<GrpcSetupOption>());
67+
grpc::ClientContext context;
68+
merged_client_opts.get<GrpcSetupOption>()(context);
69+
EXPECT_THAT(context.deadline() - clock.Now(), Eq(std::chrono::seconds(2)));
70+
}
71+
72+
TEST(MergeOptions, CallOverridesAllOtherDeadlineOption) {
73+
FakeSystemClock clock;
74+
clock.SetTime(std::chrono::system_clock::now());
75+
auto now_fn = [&] { return clock.Now(); };
76+
77+
auto connection_opts = Options{}.set<DeadlineOption>(std::chrono::seconds(1));
78+
auto client_opts = Options{}.set<DeadlineOption>(std::chrono::seconds(2));
79+
80+
// Merge in a deadline. This should populate the GrpcSetupOption with a call
81+
// to ClientContext::set_deadline
82+
auto merged_client_opts =
83+
MergeOptions(std::move(client_opts), std::move(connection_opts), now_fn);
84+
85+
auto call_opts = Options{}.set<DeadlineOption>(std::chrono::seconds(5));
86+
87+
auto merged_call_opts =
88+
MergeOptions(std::move(call_opts), std::move(merged_client_opts), now_fn);
89+
90+
ASSERT_TRUE(merged_call_opts.has<GrpcSetupOption>());
91+
grpc::ClientContext context;
92+
merged_call_opts.get<GrpcSetupOption>()(context);
93+
EXPECT_THAT(context.deadline() - clock.Now(), Eq(std::chrono::seconds(5)));
94+
}
95+
96+
TEST(MergeOptions, OnlyDeadlineOptionSupersedesGrpcSetupOptionDeadline) {
97+
FakeSystemClock clock;
98+
clock.SetTime(std::chrono::system_clock::now());
99+
auto now_fn = [&] { return clock.Now(); };
100+
101+
auto setup_fn = [&](grpc::ClientContext& context) {
102+
EXPECT_THAT(context.compression_algorithm(), Eq(GRPC_COMPRESS_NONE));
103+
// compression_algorithm is set to verify the non-deadline aspects of the
104+
// GrpcSetupOption are preserved.
105+
context.set_compression_algorithm(GRPC_COMPRESS_GZIP);
106+
context.set_deadline(now_fn() + std::chrono::seconds(10));
107+
};
108+
109+
auto connection_opts = Options{}.set<GrpcSetupOption>(setup_fn);
110+
Options client_opts;
111+
112+
auto merged_client_opts =
113+
MergeOptions(std::move(client_opts), std::move(connection_opts), now_fn);
114+
{
115+
ASSERT_TRUE(merged_client_opts.has<GrpcSetupOption>());
116+
grpc::ClientContext context;
117+
merged_client_opts.get<GrpcSetupOption>()(context);
118+
EXPECT_THAT(context.compression_algorithm(), Eq(GRPC_COMPRESS_GZIP));
119+
EXPECT_THAT(context.deadline() - clock.Now(), Eq(std::chrono::seconds(10)));
120+
}
121+
122+
connection_opts =
123+
Options{}.set<GrpcSetupOption>(setup_fn).set<DeadlineOption>(
124+
std::chrono::seconds(1));
125+
client_opts = Options{};
126+
127+
// Merge in a deadline. This should populate the GrpcSetupOption with a call
128+
// to ClientContext::set_deadline after calling the GrpcSetupOption function.
129+
merged_client_opts =
130+
MergeOptions(std::move(client_opts), std::move(connection_opts), now_fn);
131+
132+
ASSERT_TRUE(merged_client_opts.has<GrpcSetupOption>());
133+
grpc::ClientContext context;
134+
merged_client_opts.get<GrpcSetupOption>()(context);
135+
EXPECT_THAT(context.compression_algorithm(), Eq(GRPC_COMPRESS_GZIP));
136+
EXPECT_THAT(context.deadline() - clock.Now(), Eq(std::chrono::seconds(1)));
137+
}
138+
139+
TEST(MergeOptions, ClientDeadlineOptionCallGrpcSetupOption) {
140+
FakeSystemClock clock;
141+
clock.SetTime(std::chrono::system_clock::now());
142+
auto now_fn = [&] { return clock.Now(); };
143+
144+
auto setup_fn = [&](grpc::ClientContext& context) {
145+
EXPECT_THAT(context.compression_algorithm(), Eq(GRPC_COMPRESS_NONE));
146+
// compression_algorithm is set to verify the non-deadline aspects of the
147+
// GrpcSetupOption are preserved.
148+
context.set_compression_algorithm(GRPC_COMPRESS_GZIP);
149+
context.set_deadline(now_fn() + std::chrono::seconds(10));
150+
};
151+
152+
auto call_setup_fn = [&](grpc::ClientContext& context) {
153+
EXPECT_THAT(context.compression_algorithm(), Eq(GRPC_COMPRESS_NONE));
154+
context.set_compression_algorithm(GRPC_COMPRESS_DEFLATE);
155+
};
156+
157+
auto connection_opts = Options{}.set<GrpcSetupOption>(setup_fn);
158+
auto client_opts = Options{}.set<DeadlineOption>(std::chrono::seconds(5));
159+
auto call_opts = Options{}.set<GrpcSetupOption>(call_setup_fn);
160+
161+
auto merged_client_opts =
162+
MergeOptions(std::move(client_opts), std::move(connection_opts), now_fn);
163+
auto merged_call_opts =
164+
MergeOptions(std::move(call_opts), std::move(merged_client_opts), now_fn);
165+
166+
ASSERT_TRUE(merged_call_opts.has<GrpcSetupOption>());
167+
grpc::ClientContext context;
168+
merged_call_opts.get<GrpcSetupOption>()(context);
169+
EXPECT_THAT(context.compression_algorithm(), Eq(GRPC_COMPRESS_DEFLATE));
170+
EXPECT_THAT(context.deadline() - clock.Now(), Eq(std::chrono::seconds(5)));
171+
}
172+
173+
} // namespace
174+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
175+
} // namespace bigtable_internal
176+
} // namespace cloud
177+
} // namespace google

0 commit comments

Comments
 (0)