Skip to content

Commit 79de35f

Browse files
committed
update observability tests
1 parent 0370b94 commit 79de35f

3 files changed

Lines changed: 136 additions & 124 deletions

File tree

google/cloud/bigtable/tests/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ foreach (fname ${bigtable_client_integration_tests})
5050
gRPC::grpc++
5151
gRPC::grpc
5252
protobuf::libprotobuf)
53+
if ("${fname}" STREQUAL "observability_integration_test.cc")
54+
target_link_libraries(${target} PRIVATE otel_collector)
55+
endif ()
5356
add_test(NAME ${target} COMMAND ${target})
5457
set_tests_properties(
5558
${target} PROPERTIES LABELS

google/cloud/bigtable/tests/observability_integration_test.cc

Lines changed: 127 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "google/cloud/bigtable/options.h"
1717
#include "google/cloud/bigtable/testing/table_integration_test.h"
1818
#include "google/cloud/credentials.h"
19+
#include "google/cloud/internal/getenv.h"
1920
#include "google/cloud/testing_util/scoped_environment.h"
2021
#include "google/cloud/testing_util/status_matchers.h"
2122
#include "absl/strings/str_cat.h"
@@ -28,16 +29,55 @@
2829
namespace google {
2930
namespace cloud {
3031
namespace bigtable {
31-
namespace testing {
32+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
3233
namespace {
3334

3435
using ::google::cloud::bigtable::testing::TableTestEnvironment;
3536
using ::google::cloud::testing_util::ScopedEnvironment;
37+
using ::testing::AllOf;
38+
using ::testing::Contains;
39+
using ::testing::Each;
3640
using ::testing::Eq;
41+
using ::testing::ExplainMatchResult;
42+
using ::testing::HasSubstr;
3743
using ::testing::IsEmpty;
3844
using ::testing::Not;
45+
using ::testing::Property;
3946
using ::testing::StartsWith;
4047

48+
MATCHER_P(MetricType, matcher, "") {
49+
return ExplainMatchResult(matcher, arg.metric().type(), result_listener);
50+
}
51+
52+
MATCHER_P(ResourceType, matcher, "") {
53+
return ExplainMatchResult(matcher, arg.resource().type(), result_listener);
54+
}
55+
56+
MATCHER_P2(HasMetricLabel, key, val_matcher, "") {
57+
auto const& labels = arg.metric().labels();
58+
auto it = labels.find(key);
59+
if (it == labels.end()) {
60+
*result_listener << "no metric label '" << key << "'";
61+
return false;
62+
}
63+
return ExplainMatchResult(val_matcher, it->second, result_listener);
64+
}
65+
66+
MATCHER_P2(HasResourceLabel, key, val_matcher, "") {
67+
auto const& labels = arg.resource().labels();
68+
auto it = labels.find(key);
69+
if (it == labels.end()) {
70+
*result_listener << "no resource label '" << key << "'";
71+
return false;
72+
}
73+
return ExplainMatchResult(val_matcher, it->second, result_listener);
74+
}
75+
76+
MATCHER_P(HasTimeSeries, ts_matcher, "") {
77+
return ExplainMatchResult(Contains(ts_matcher), arg.time_series(),
78+
result_listener);
79+
}
80+
4181
class ObservabilityIntegrationTest
4282
: public ::google::cloud::bigtable::testing::TableIntegrationTest {
4383
protected:
@@ -103,11 +143,19 @@ TEST_F(ObservabilityIntegrationTest, VerifyOperationAndAttemptMetrics) {
103143
std::chrono::seconds(5));
104144

105145
auto const table_id = TableTestEnvironment::table_id();
146+
bool const is_dynamic = google::cloud::internal::GetEnv(
147+
"GOOGLE_CLOUD_CPP_BIGTABLE_TESTING_CHANNEL_POOL")
148+
.value_or("") == "dynamic";
106149

107150
// Add scoped connection to ensure metrics are flushed on destruction.
108151
{
109-
auto conn = MakeDataConnection(
110-
{InstanceResource(Project(project_id()), instance_id())}, options);
152+
std::shared_ptr<DataConnection> conn;
153+
if (is_dynamic) {
154+
conn = MakeDataConnection(
155+
{InstanceResource(Project(project_id()), instance_id())}, options);
156+
} else {
157+
conn = MakeDataConnection(options);
158+
}
111159
auto table = Table(std::move(conn),
112160
TableResource(project_id(), instance_id(), table_id));
113161

@@ -126,52 +174,38 @@ TEST_F(ObservabilityIntegrationTest, VerifyOperationAndAttemptMetrics) {
126174
}
127175

128176
auto recorded = collector_service_.recorded_metrics();
129-
ASSERT_FALSE(recorded.empty());
130-
131-
bool found_operation_latencies = false;
132-
bool found_attempt_latencies = false;
133-
134-
for (auto const& req : recorded) {
135-
EXPECT_EQ(req.name(), absl::StrCat("projects/", project_id()));
136-
137-
for (auto const& ts : req.time_series()) {
138-
auto const& metric_type = ts.metric().type();
139-
EXPECT_THAT(metric_type,
140-
StartsWith("bigtable.googleapis.com/internal/client/"));
141-
142-
if (metric_type.find("operation_latencies") != std::string::npos) {
143-
found_operation_latencies = true;
144-
}
145-
if (metric_type.find("attempt_latencies") != std::string::npos) {
146-
found_attempt_latencies = true;
147-
}
148-
149-
auto const& labels = ts.resource().labels();
150-
auto project_it = labels.find("project_id");
151-
if (project_it != labels.end()) {
152-
EXPECT_EQ(project_it->second, project_id());
153-
}
154-
auto instance_it = labels.find("instance");
155-
if (instance_it != labels.end()) {
156-
EXPECT_EQ(instance_it->second, instance_id());
157-
}
158-
auto table_it = labels.find("table");
159-
if (table_it != labels.end()) {
160-
EXPECT_EQ(table_it->second, table_id);
161-
}
162-
auto zone_it = labels.find("zone");
163-
if (zone_it != labels.end() && !TableTestEnvironment::zone_a().empty()) {
164-
std::vector<absl::string_view> parts =
165-
absl::StrSplit(TableTestEnvironment::zone_a(), '-');
166-
auto prefix = parts.size() >= 2 ? absl::StrCat(parts[0], "-", parts[1])
167-
: TableTestEnvironment::zone_a();
168-
EXPECT_THAT(zone_it->second, StartsWith(prefix));
169-
}
170-
}
177+
ASSERT_THAT(recorded, Not(IsEmpty()));
178+
EXPECT_THAT(
179+
recorded,
180+
Each(Property(&google::monitoring::v3::CreateTimeSeriesRequest::name,
181+
Eq(absl::StrCat("projects/", project_id())))));
182+
183+
auto has_resource_labels = AllOf(HasResourceLabel("project_id", project_id()),
184+
HasResourceLabel("instance", instance_id()),
185+
HasResourceLabel("table", table_id));
186+
187+
if (!TableTestEnvironment::zone_a().empty()) {
188+
std::vector<absl::string_view> parts =
189+
absl::StrSplit(TableTestEnvironment::zone_a(), '-');
190+
auto prefix = parts.size() >= 2 ? absl::StrCat(parts[0], "-", parts[1])
191+
: TableTestEnvironment::zone_a();
192+
EXPECT_THAT(recorded, Contains(HasTimeSeries(AllOf(
193+
MetricType(HasSubstr("operation_latencies")),
194+
has_resource_labels,
195+
HasResourceLabel("zone", StartsWith(prefix))))));
196+
EXPECT_THAT(recorded, Contains(HasTimeSeries(AllOf(
197+
MetricType(HasSubstr("attempt_latencies")),
198+
has_resource_labels,
199+
HasResourceLabel("zone", StartsWith(prefix))))));
200+
} else {
201+
EXPECT_THAT(recorded, Contains(HasTimeSeries(AllOf(
202+
MetricType(HasSubstr("operation_latencies")),
203+
has_resource_labels))));
204+
EXPECT_THAT(
205+
recorded,
206+
Contains(HasTimeSeries(AllOf(MetricType(HasSubstr("attempt_latencies")),
207+
has_resource_labels))));
171208
}
172-
173-
EXPECT_TRUE(found_operation_latencies);
174-
EXPECT_TRUE(found_attempt_latencies);
175209
}
176210

177211
TEST_F(ObservabilityIntegrationTest, VerifyOutstandingRpcsMetric) {
@@ -191,89 +225,58 @@ TEST_F(ObservabilityIntegrationTest, VerifyOutstandingRpcsMetric) {
191225
std::chrono::seconds(5));
192226

193227
auto const table_id = TableTestEnvironment::table_id();
228+
bool const is_dynamic = google::cloud::internal::GetEnv(
229+
"GOOGLE_CLOUD_CPP_BIGTABLE_TESTING_CHANNEL_POOL")
230+
.value_or("") == "dynamic";
231+
std::string const expected_lb_policy =
232+
is_dynamic ? "RANDOM_TWO_LEAST_USED" : "ROUND_ROBIN";
194233

195-
auto verify_pool_metric = [this, &options, &table_id](
196-
bool dynamic_pool,
197-
std::string const& expected_lb_policy) {
198-
collector_service_.Clear();
199-
{
200-
std::shared_ptr<DataConnection> conn;
201-
if (dynamic_pool) {
202-
conn = MakeDataConnection(
203-
{InstanceResource(Project(project_id()), instance_id())}, options);
204-
} else {
205-
conn = MakeDataConnection(options);
206-
}
207-
auto table = Table(std::move(conn),
208-
TableResource(project_id(), instance_id(), table_id));
209-
210-
std::string const row_key = "observability-rpc-" + expected_lb_policy;
211-
std::vector<Cell> expected{{row_key, "family4", "c0", 1000, "v1000"},
212-
{row_key, "family4", "c1", 2000, "v2000"}};
213-
214-
// Perform mutations and read calls
215-
Apply(table, row_key, expected);
216-
auto actual = ReadRows(table, Filter::RowKeysRegex(row_key));
217-
CheckEqualUnordered(expected, actual);
218-
219-
// Wait for the periodic 5-second exporter background thread to flush
220-
// metrics while conn is active
221-
std::this_thread::sleep_for(std::chrono::seconds(6));
234+
collector_service_.Clear();
235+
{
236+
std::shared_ptr<DataConnection> conn;
237+
if (is_dynamic) {
238+
conn = MakeDataConnection(
239+
{InstanceResource(Project(project_id()), instance_id())}, options);
240+
} else {
241+
conn = MakeDataConnection(options);
222242
}
243+
auto table = Table(std::move(conn),
244+
TableResource(project_id(), instance_id(), table_id));
223245

224-
auto recorded = collector_service_.recorded_metrics();
225-
ASSERT_THAT(recorded, Not(IsEmpty()));
226-
227-
bool found_outstanding_rpcs = false;
228-
for (auto const& req : recorded) {
229-
EXPECT_THAT(req.name(), Eq(absl::StrCat("projects/", project_id())));
230-
231-
for (auto const& ts : req.time_series()) {
232-
auto const& metric_type = ts.metric().type();
233-
EXPECT_THAT(metric_type,
234-
StartsWith("bigtable.googleapis.com/internal/client/"));
235-
236-
if (metric_type.find("connection_pool/outstanding_rpcs") !=
237-
std::string::npos) {
238-
found_outstanding_rpcs = true;
239-
EXPECT_THAT(ts.resource().type(),
240-
Eq("bigtable.googleapis.com/Client"));
241-
242-
auto const& res_labels = ts.resource().labels();
243-
auto project_it = res_labels.find("project_id");
244-
if (project_it != res_labels.end()) {
245-
EXPECT_THAT(project_it->second, Eq(project_id()));
246-
}
247-
auto instance_it = res_labels.find("instance");
248-
if (instance_it != res_labels.end()) {
249-
EXPECT_THAT(instance_it->second, Eq(instance_id()));
250-
}
251-
252-
auto const& metric_labels = ts.metric().labels();
253-
auto lb_policy_it = metric_labels.find("channel_pool_lb_policy");
254-
ASSERT_THAT(lb_policy_it, Not(Eq(metric_labels.end())));
255-
EXPECT_THAT(lb_policy_it->second, Eq(expected_lb_policy));
256-
257-
auto transport_it = metric_labels.find("transport_type");
258-
ASSERT_THAT(transport_it, Not(Eq(metric_labels.end())));
259-
EXPECT_THAT(transport_it->second, Not(IsEmpty()));
260-
261-
auto streaming_it = metric_labels.find("streaming");
262-
ASSERT_THAT(streaming_it, Not(Eq(metric_labels.end())));
263-
EXPECT_THAT(streaming_it->second, Not(IsEmpty()));
264-
}
265-
}
266-
}
267-
EXPECT_TRUE(found_outstanding_rpcs)
268-
<< "Failed to find outstanding_rpcs metric for " << expected_lb_policy;
269-
};
246+
std::string const row_key = "observability-rpc-" + expected_lb_policy;
247+
std::vector<Cell> expected{{row_key, "family4", "c0", 1000, "v1000"},
248+
{row_key, "family4", "c1", 2000, "v2000"}};
249+
250+
// Perform mutations and read calls
251+
Apply(table, row_key, expected);
252+
auto actual = ReadRows(table, Filter::RowKeysRegex(row_key));
253+
CheckEqualUnordered(expected, actual);
254+
255+
// Wait for the periodic 5-second exporter background thread to flush
256+
// metrics while conn is active
257+
std::this_thread::sleep_for(std::chrono::seconds(6));
258+
}
270259

271-
verify_pool_metric(/*dynamic_pool=*/true, "RANDOM_TWO_LEAST_USED");
272-
verify_pool_metric(/*dynamic_pool=*/false, "ROUND_ROBIN");
260+
auto recorded = collector_service_.recorded_metrics();
261+
ASSERT_THAT(recorded, Not(IsEmpty()));
262+
EXPECT_THAT(
263+
recorded,
264+
Each(Property(&google::monitoring::v3::CreateTimeSeriesRequest::name,
265+
Eq(absl::StrCat("projects/", project_id())))));
266+
267+
EXPECT_THAT(recorded,
268+
Contains(HasTimeSeries(AllOf(
269+
MetricType(HasSubstr("connection_pool/outstanding_rpcs")),
270+
ResourceType("bigtable.googleapis.com/Client"),
271+
HasResourceLabel("project_id", project_id()),
272+
HasResourceLabel("instance", instance_id()),
273+
HasMetricLabel("channel_pool_lb_policy", expected_lb_policy),
274+
HasMetricLabel("transport_type", Not(IsEmpty())),
275+
HasMetricLabel("streaming", Not(IsEmpty()))))));
273276
}
274277

275278
} // namespace
276-
} // namespace testing
279+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
277280
} // namespace bigtable
278281
} // namespace cloud
279282
} // namespace google

protos/google/cloud/opentelemetry/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@ endif ()
1919
if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/testing")
2020
add_subdirectory(testing)
2121
endif ()
22+
23+
if (IS_DIRECTORY "${PROJECT_SOURCE_DIR}/ci/otel_collector"
24+
AND EXISTS "${PROJECT_SOURCE_DIR}/ci/otel_collector/CMakeLists.txt")
25+
add_subdirectory("${PROJECT_SOURCE_DIR}/ci/otel_collector"
26+
"${CMAKE_CURRENT_BINARY_DIR}/ci_otel_collector")
27+
endif ()

0 commit comments

Comments
 (0)