Skip to content

feat(storage): Synchronous gRPC Interceptor #16176

Draft
v-pratap wants to merge 3 commits into
googleapis:mainfrom
v-pratap:feat/storage-grpc-feature-tracking
Draft

feat(storage): Synchronous gRPC Interceptor #16176
v-pratap wants to merge 3 commits into
googleapis:mainfrom
v-pratap:feat/storage-grpc-feature-tracking

Conversation

@v-pratap

Copy link
Copy Markdown
Contributor

No description provided.

@product-auto-label product-auto-label Bot added the api: storage Issues related to the Cloud Storage API. label Jun 16, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a FeatureTracker to monitor and report client-side optimizations and configuration choices via the x-goog-storage-cpp-features header. The code reviewer identified that statically adding the tracker during stub creation in StorageMetadata is redundant and can lead to stale headers, recommending its removal along with the associated test. Additionally, to comply with the repository's style guide, the reviewer suggested factoring out duplicated logic for retrieving the tracker's header value into a centralized helper function, FeatureTrackerHeaderValue, to be used across both gRPC and REST paths.

Comment on lines +91 to +106
std::multimap<std::string, std::string> fixed_metadata;
bool const enable_reports =
!options.has<storage::EnableFeatureReportsOption>() ||
options.get<storage::EnableFeatureReportsOption>();
if (enable_reports &&
options.has<storage::internal::FeatureTrackerOption>()) {
auto const& tracker =
options.get<storage::internal::FeatureTrackerOption>();
if (tracker) {
auto const val = tracker->HeaderValue();
if (!val.empty()) {
fixed_metadata.emplace(storage::internal::kFeatureTrackerHeaderName,
val);
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Adding the feature tracker header to fixed_metadata in StorageMetadata captures a static snapshot of the tracker's value at stub creation time. Since ApplyQueryParameters (for unary RPCs) and OpenObject::CreateRpc (for streaming RPCs) already dynamically add the up-to-date header value to the ClientContext for each request, having it in StorageMetadata is redundant and leads to duplicate x-goog-storage-cpp-features headers on the wire. Furthermore, the static value in StorageMetadata will not reflect any operation-driven features registered after stub creation, potentially sending stale/conflicting information. We should remove this block entirely.

  std::multimap<std::string, std::string> fixed_metadata;

Comment on lines +80 to +81
Options SetupFeatureTracker(Options opts);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To adhere to the repository style guide regarding factoring out duplicated code (which appears 3 or more times in non-test files), we should introduce a helper function FeatureTrackerHeaderValue to centralize the logic of checking EnableFeatureReportsOption and retrieving the header value.

Options SetupFeatureTracker(Options opts);

std::string FeatureTrackerHeaderValue(Options const& opts);
References
  1. prefers to factor out duplicated code if it appears 3 or more times in non-test files. (link)

Comment on lines +51 to +53
return opts;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Implement the helper function FeatureTrackerHeaderValue to centralize the logic of checking if feature reporting is enabled and retrieving the header value from the tracker.

  return opts;
}

std::string FeatureTrackerHeaderValue(Options const& opts) {
  if (opts.has<EnableFeatureReportsOption>() &&
      !opts.get<EnableFeatureReportsOption>()) {
    return {};
  }
  if (!opts.has<FeatureTrackerOption>()) return {};
  auto const& tracker = opts.get<FeatureTrackerOption>();
  if (!tracker) return {};
  return tracker->HeaderValue();
}

Comment on lines +76 to +89
bool const enable_reports =
!options.has<storage::EnableFeatureReportsOption>() ||
options.get<storage::EnableFeatureReportsOption>();
if (enable_reports &&
options.has<storage::internal::FeatureTrackerOption>()) {
auto const& tracker =
options.get<storage::internal::FeatureTrackerOption>();
if (tracker) {
auto const val = tracker->HeaderValue();
if (!val.empty()) {
ctx.AddMetadata(storage::internal::kFeatureTrackerHeaderName, val);
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use the centralized FeatureTrackerHeaderValue helper function to simplify the code and avoid duplication.

  auto const val = storage::internal::FeatureTrackerHeaderValue(options);
  if (!val.empty()) {
    ctx.AddMetadata(storage::internal::kFeatureTrackerHeaderName, val);
  }
References
  1. prefers to factor out duplicated code if it appears 3 or more times in non-test files. (link)

Comment on lines +122 to +133
bool const enable_reports =
!options.has<EnableFeatureReportsOption>() ||
options.get<EnableFeatureReportsOption>();
if (enable_reports && options.has<FeatureTrackerOption>()) {
auto const& tracker = options.get<FeatureTrackerOption>();
if (tracker) {
auto const val = tracker->HeaderValue();
if (!val.empty()) {
builder.AddHeader(kFeatureTrackerHeaderName, val);
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use the centralized FeatureTrackerHeaderValue helper function to simplify the code and avoid duplication.

  auto const val = FeatureTrackerHeaderValue(options);
  if (!val.empty()) {
    builder.AddHeader(kFeatureTrackerHeaderName, val);
  }
References
  1. prefers to factor out duplicated code if it appears 3 or more times in non-test files. (link)

Comment on lines +320 to +349
TEST_F(StorageStubFactory, FeatureTrackerInStorageMetadata) {
auto tracker = std::make_shared<storage::internal::FeatureTracker>();
tracker->RegisterFeature(storage::internal::TrackedFeature::kPCU);

MockFactory factory;
EXPECT_CALL(factory, Call)
.WillOnce([&](std::shared_ptr<grpc::Channel> const&) {
auto mock = std::make_shared<MockStorageStub>();
EXPECT_CALL(*mock, DeleteBucket)
.WillOnce([&](grpc::ClientContext& context, Options const&,
google::storage::v2::DeleteBucketRequest const&) {
ValidateMetadataFixture fixture;
auto metadata = fixture.GetMetadata(context);
EXPECT_THAT(metadata,
Contains(Pair(storage::internal::kFeatureTrackerHeaderName,
tracker->HeaderValue())));
return internal::AbortedError("fail");
});
return mock;
});

internal::AutomaticallyCreatedBackgroundThreads pool;
auto stub = CreateTestStub(
pool.cq(), factory.AsStdFunction(),
Options{}
.set<GrpcNumChannelsOption>(1)
.set<storage::internal::FeatureTrackerOption>(tracker));
grpc::ClientContext context;
(void)stub->DeleteBucket(context, Options{}, {});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since the feature tracker is no longer added to StorageMetadata (to avoid duplication and ensure dynamic tracking), this test is no longer applicable and should be removed.

@v-pratap v-pratap force-pushed the feat/storage-grpc-feature-tracking branch from a65e372 to c916a33 Compare June 16, 2026 18:15
@v-pratap v-pratap changed the title feat(storage): Sync/Async gRPC Transport Interceptors & Multi-Stream Flag feat(storage): Synchronous gRPC Interceptor Jun 16, 2026
@v-pratap v-pratap force-pushed the feat/storage-grpc-feature-tracking branch from c916a33 to f790d95 Compare June 16, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: storage Issues related to the Cloud Storage API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant