Skip to content

Commit 44f9276

Browse files
authored
feat(storage): add range read latency metrics and trace annotations (#16353)
1 parent c1b5efa commit 44f9276

16 files changed

Lines changed: 356 additions & 108 deletions

google/cloud/storage/google_cloud_cpp_storage_grpc.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ google_cloud_cpp_storage_grpc_hdrs = [
5858
"internal/async/read_range.h",
5959
"internal/async/reader_connection_factory.h",
6060
"internal/async/reader_connection_impl.h",
61+
"internal/async/reader_connection_telemetry.h",
6162
"internal/async/reader_connection_resume.h",
6263
"internal/async/reader_connection_tracing.h",
6364
"internal/async/rewriter_connection_impl.h",
@@ -135,6 +136,7 @@ google_cloud_cpp_storage_grpc_srcs = [
135136
"internal/async/read_range.cc",
136137
"internal/async/reader_connection_factory.cc",
137138
"internal/async/reader_connection_impl.cc",
139+
"internal/async/reader_connection_telemetry.cc",
138140
"internal/async/reader_connection_resume.cc",
139141
"internal/async/reader_connection_tracing.cc",
140142
"internal/async/rewriter_connection_impl.cc",

google/cloud/storage/google_cloud_cpp_storage_grpc.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ add_library(
137137
internal/async/reader_connection_factory.h
138138
internal/async/reader_connection_impl.cc
139139
internal/async/reader_connection_impl.h
140+
internal/async/reader_connection_telemetry.cc
141+
internal/async/reader_connection_telemetry.h
140142
internal/async/reader_connection_resume.cc
141143
internal/async/reader_connection_resume.h
142144
internal/async/reader_connection_tracing.cc

google/cloud/storage/internal/async/connection_tracing.cc

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,32 +56,32 @@ class AsyncConnectionTracing : public storage::AsyncConnection {
5656
OpenParams p) override {
5757
auto span = internal::MakeSpan("storage::AsyncConnection::Open");
5858
internal::OTelScope scope(span);
59-
return impl_->Open(std::move(p))
60-
.then([oc = opentelemetry::context::RuntimeContext::GetCurrent(),
61-
span = std::move(span)](auto f)
62-
-> StatusOr<
63-
std::shared_ptr<storage::ObjectDescriptorConnection>> {
64-
auto result = f.get();
65-
internal::DetachOTelContext(oc);
66-
if (!result) {
67-
return internal::EndSpan(*span, std::move(result).status());
68-
}
69-
return MakeTracingObjectDescriptorConnection(std::move(span),
70-
*std::move(result));
71-
});
59+
auto wrap = [oc = opentelemetry::context::RuntimeContext::GetCurrent(),
60+
bucket = p.read_spec.bucket(), span = std::move(span)](auto f)
61+
-> StatusOr<std::shared_ptr<storage::ObjectDescriptorConnection>> {
62+
StatusOr<std::shared_ptr<storage::ObjectDescriptorConnection>> result =
63+
f.get();
64+
internal::DetachOTelContext(oc);
65+
if (!result) return internal::EndSpan(*span, std::move(result).status());
66+
return MakeTracingObjectDescriptorConnection(
67+
std::move(span), *std::move(result), std::move(bucket));
68+
};
69+
return impl_->Open(std::move(p)).then(std::move(wrap));
7270
}
7371

7472
future<StatusOr<std::unique_ptr<storage::AsyncReaderConnection>>> ReadObject(
7573
ReadObjectParams p) override {
7674
auto span = internal::MakeSpan("storage::AsyncConnection::ReadObject");
7775
internal::OTelScope scope(span);
7876
auto wrap = [oc = opentelemetry::context::RuntimeContext::GetCurrent(),
79-
span = std::move(span)](auto f)
77+
bucket = p.request.bucket(), span = std::move(span)](auto f)
8078
-> StatusOr<std::unique_ptr<storage::AsyncReaderConnection>> {
81-
auto reader = f.get();
79+
StatusOr<std::unique_ptr<storage::AsyncReaderConnection>> reader =
80+
f.get();
8281
internal::DetachOTelContext(oc);
8382
if (!reader) return internal::EndSpan(*span, std::move(reader).status());
84-
return MakeTracingReaderConnection(std::move(span), *std::move(reader));
83+
return MakeTracingReaderConnection(std::move(span), *std::move(reader),
84+
std::move(bucket));
8585
};
8686
return impl_->ReadObject(std::move(p)).then(std::move(wrap));
8787
}

google/cloud/storage/internal/async/object_descriptor_connection_tracing.cc

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414

1515
#include "google/cloud/storage/internal/async/object_descriptor_connection_tracing.h"
1616
#include "google/cloud/storage/async/reader_connection.h"
17-
#include "google/cloud/storage/internal/async/reader_connection_tracing.h"
1817
#include "google/cloud/internal/opentelemetry.h"
1918
#include "google/cloud/version.h"
2019
#include <opentelemetry/semconv/incubating/thread_attributes.h>
2120
#include <memory>
21+
#include <string>
22+
#include <utility>
2223

2324
namespace google {
2425
namespace cloud {
@@ -34,8 +35,11 @@ class AsyncObjectDescriptorConnectionTracing
3435
public:
3536
explicit AsyncObjectDescriptorConnectionTracing(
3637
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> span,
37-
std::shared_ptr<storage::ObjectDescriptorConnection> impl)
38-
: span_(std::move(span)), impl_(std::move(impl)) {}
38+
std::shared_ptr<storage::ObjectDescriptorConnection> impl,
39+
std::string bucket_name)
40+
: span_(std::move(span)),
41+
impl_(std::move(impl)),
42+
bucket_name_(std::move(bucket_name)) {}
3943

4044
~AsyncObjectDescriptorConnectionTracing() override {
4145
internal::EndSpan(*span_);
@@ -55,7 +59,7 @@ class AsyncObjectDescriptorConnectionTracing
5559
{{sc::thread::kThreadId, internal::CurrentThreadId()},
5660
{"read-start", p.start},
5761
{"read-length", p.length}});
58-
return MakeTracingReaderConnection(span_, std::move(result));
62+
return result;
5963
}
6064

6165
void MakeSubsequentStream() override {
@@ -65,16 +69,18 @@ class AsyncObjectDescriptorConnectionTracing
6569
private:
6670
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> span_;
6771
std::shared_ptr<storage::ObjectDescriptorConnection> impl_;
72+
std::string bucket_name_;
6873
};
6974

7075
} // namespace
7176

7277
std::shared_ptr<storage::ObjectDescriptorConnection>
7378
MakeTracingObjectDescriptorConnection(
7479
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> span,
75-
std::shared_ptr<storage::ObjectDescriptorConnection> impl) {
80+
std::shared_ptr<storage::ObjectDescriptorConnection> impl,
81+
std::string bucket_name) {
7682
return std::make_unique<AsyncObjectDescriptorConnectionTracing>(
77-
std::move(span), std::move(impl));
83+
std::move(span), std::move(impl), std::move(bucket_name));
7884
}
7985

8086
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END

google/cloud/storage/internal/async/object_descriptor_connection_tracing.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
2626
std::shared_ptr<storage::ObjectDescriptorConnection>
2727
MakeTracingObjectDescriptorConnection(
2828
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> span,
29-
std::shared_ptr<storage::ObjectDescriptorConnection> impl);
29+
std::shared_ptr<storage::ObjectDescriptorConnection> impl,
30+
std::string bucket_name);
3031

3132
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
3233
} // namespace storage_internal

google/cloud/storage/internal/async/object_descriptor_connection_tracing_test.cc

Lines changed: 34 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16,56 +16,39 @@
1616
#include "google/cloud/storage/async/object_descriptor_connection.h"
1717
#include "google/cloud/storage/mocks/mock_async_object_descriptor_connection.h"
1818
#include "google/cloud/storage/mocks/mock_async_reader_connection.h"
19+
#include "google/cloud/internal/opentelemetry.h"
1920
#include "google/cloud/opentelemetry_options.h"
2021
#include "google/cloud/options.h"
2122
#include "google/cloud/testing_util/opentelemetry_matchers.h"
2223
#include <gmock/gmock-matchers.h>
2324
#include <gmock/gmock.h>
2425
#include <opentelemetry/semconv/incubating/thread_attributes.h>
26+
#include <memory>
27+
#include <utility>
2528

2629
namespace google {
2730
namespace cloud {
2831
namespace storage_internal {
2932
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
3033
namespace {
3134

32-
using ReadResponse =
33-
::google::cloud::storage::AsyncReaderConnection::ReadResponse;
3435
using ::google::cloud::storage::ObjectDescriptorConnection;
35-
using ::google::cloud::storage::ReadPayload;
3636
using ::google::cloud::storage_mocks::MockAsyncObjectDescriptorConnection;
3737
using ::google::cloud::storage_mocks::MockAsyncReaderConnection;
3838
using ::google::cloud::testing_util::EventNamed;
3939
using ::google::cloud::testing_util::InstallSpanCatcher;
4040
using ::google::cloud::testing_util::OTelAttribute;
41-
using ::google::cloud::testing_util::OTelContextCaptured;
4241
using ::google::cloud::testing_util::PromiseWithOTelContext;
4342
using ::google::cloud::testing_util::SpanEventAttributesAre;
43+
using ::google::cloud::testing_util::SpanEventsAre;
44+
using ::google::cloud::testing_util::SpanHasEvents;
4445
using ::google::cloud::testing_util::SpanHasInstrumentationScope;
4546
using ::google::cloud::testing_util::SpanKindIsClient;
4647
using ::google::cloud::testing_util::SpanNamed;
4748
using ::google::cloud::testing_util::SpanWithStatus;
48-
using ::google::cloud::testing_util::ThereIsAnActiveSpan;
4949
using ::testing::_;
50-
51-
// A helper to set expectations on a mock async reader. It captures the OTel
52-
// context and returns a future that can be controlled by the test.
53-
auto expect_context = [](auto& p) {
54-
return [&p] {
55-
EXPECT_TRUE(ThereIsAnActiveSpan());
56-
EXPECT_TRUE(OTelContextCaptured());
57-
return p.get_future();
58-
};
59-
};
60-
61-
// A helper to be used in a `.then()` clause. It verifies the OTel context
62-
// has been detached before the user receives the result.
63-
auto expect_no_context = [](auto f) {
64-
auto t = f.get();
65-
EXPECT_FALSE(ThereIsAnActiveSpan());
66-
EXPECT_FALSE(OTelContextCaptured());
67-
return t;
68-
};
50+
using ::testing::AllOf;
51+
using ::testing::ElementsAre;
6952

7053
TEST(ObjectDescriptorConnectionTracing, Read) {
7154
namespace sc = ::opentelemetry::semconv;
@@ -79,7 +62,7 @@ TEST(ObjectDescriptorConnectionTracing, Read) {
7962
return std::make_unique<MockAsyncReaderConnection>();
8063
});
8164
auto actual = MakeTracingObjectDescriptorConnection(
82-
internal::MakeSpan("test-span-name"), std::move(mock));
65+
internal::MakeSpan("test-span-name"), std::move(mock), "test-bucket");
8366
auto f1 = actual->Read(ObjectDescriptorConnection::ReadParams{100, 200});
8467

8568
actual.reset();
@@ -98,30 +81,39 @@ TEST(ObjectDescriptorConnectionTracing, Read) {
9881
OTelAttribute<std::string>(sc::thread::kThreadId, _)))))));
9982
}
10083

101-
TEST(ObjectDescriptorConnectionTracing, ReadThenRead) {
84+
TEST(ObjectDescriptorConnectionTracing,
85+
SingleReadRangeCompletedDoesNotEndOpenSpan) {
10286
namespace sc = ::opentelemetry::semconv;
10387
auto span_catcher = InstallSpanCatcher();
10488

10589
auto mock_connection =
10690
std::make_shared<MockAsyncObjectDescriptorConnection>();
107-
auto* mock_reader_ptr = new MockAsyncReaderConnection;
108-
PromiseWithOTelContext<ReadResponse> p;
109-
EXPECT_CALL(*mock_reader_ptr, Read).WillOnce(expect_context(p));
91+
auto mock_reader = std::make_unique<MockAsyncReaderConnection>();
92+
PromiseWithOTelContext<storage::AsyncReaderConnection::ReadResponse> p;
93+
EXPECT_CALL(*mock_reader, Read).WillOnce([&p] { return p.get_future(); });
11094

11195
EXPECT_CALL(*mock_connection, Read)
112-
.WillOnce([&](ObjectDescriptorConnection::ReadParams) {
113-
return std::unique_ptr<storage::AsyncReaderConnection>(mock_reader_ptr);
96+
.WillOnce([&, r = std::move(mock_reader)](
97+
ObjectDescriptorConnection::ReadParams p) mutable {
98+
EXPECT_EQ(p.start, 100);
99+
EXPECT_EQ(p.length, 200);
100+
return std::move(r);
114101
});
115102

116103
auto connection = MakeTracingObjectDescriptorConnection(
117-
internal::MakeSpan("test-span"), std::move(mock_connection));
104+
internal::MakeSpan("test-span"), std::move(mock_connection),
105+
"test-bucket");
118106

119-
auto reader = connection->Read({});
120-
auto f = reader->Read().then(expect_no_context);
121-
p.set_value(ReadPayload("test-payload").set_offset(123));
107+
auto reader = connection->Read({100, 200});
108+
auto f = reader->Read();
109+
// Simulate stream completion (EOF)
110+
p.set_value(Status{});
122111
(void)f.get();
123112

124-
connection.reset(); // End the span
113+
// Before resetting the connection, the Open span must NOT be ended yet.
114+
EXPECT_THAT(span_catcher->GetSpans(), ::testing::IsEmpty());
115+
116+
connection.reset(); // End the span now
125117

126118
auto spans = span_catcher->GetSpans();
127119
EXPECT_THAT(
@@ -130,21 +122,12 @@ TEST(ObjectDescriptorConnectionTracing, ReadThenRead) {
130122
SpanNamed("test-span"),
131123
SpanWithStatus(opentelemetry::trace::StatusCode::kOk),
132124
SpanHasInstrumentationScope(), SpanKindIsClient(),
133-
SpanEventsAre(
134-
AllOf(EventNamed("gl-cpp.open.read"),
135-
SpanEventAttributesAre(
136-
OTelAttribute<std::int64_t>("read-length", 0),
137-
OTelAttribute<std::int64_t>("read-start", 0),
138-
OTelAttribute<std::string>(sc::thread::kThreadId, _))),
139-
AllOf(EventNamed("gl-cpp.read"),
140-
SpanEventAttributesAre(
141-
OTelAttribute<std::int64_t>("message.starting_offset",
142-
123),
143-
OTelAttribute<std::string>(sc::thread::kThreadId, _),
144-
OTelAttribute<std::int64_t>("rpc.message.id", 1),
145-
// THIS WAS THE MISSING ATTRIBUTE:
146-
OTelAttribute<std::string>("rpc.message.type",
147-
"RECEIVED")))))));
125+
SpanEventsAre(AllOf(
126+
EventNamed("gl-cpp.open.read"),
127+
SpanEventAttributesAre(
128+
OTelAttribute<std::int64_t>("read-length", 200),
129+
OTelAttribute<std::int64_t>("read-start", 100),
130+
OTelAttribute<std::string>(sc::thread::kThreadId, _)))))));
148131
}
149132

150133
} // namespace

google/cloud/storage/internal/async/object_descriptor_impl.cc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ std::unique_ptr<storage::AsyncReaderConnection> ObjectDescriptorImpl::Read(
201201
return std::unique_ptr<storage::AsyncReaderConnection>(
202202
std::make_unique<ObjectDescriptorReader>(std::move(range)));
203203
}
204-
return MakeTracingObjectDescriptorReader(std::move(range));
204+
return MakeTracingObjectDescriptorReader(std::move(range),
205+
read_object_spec_.bucket());
205206
}
206207

207208
auto it = stream_manager_->GetLeastBusyStream();
@@ -217,8 +218,8 @@ std::unique_ptr<storage::AsyncReaderConnection> ObjectDescriptorImpl::Read(
217218
return std::unique_ptr<storage::AsyncReaderConnection>(
218219
std::make_unique<ObjectDescriptorReader>(std::move(range)));
219220
}
220-
221-
return MakeTracingObjectDescriptorReader(std::move(range));
221+
return MakeTracingObjectDescriptorReader(std::move(range),
222+
read_object_spec_.bucket());
222223
}
223224

224225
std::shared_ptr<storage::internal::HashFunction>

0 commit comments

Comments
 (0)