From c30053a2336837b979ebbbd759f520fc61c7615b Mon Sep 17 00:00:00 2001 From: Vaibhav Pratap Date: Tue, 16 Jun 2026 18:28:30 +0000 Subject: [PATCH] feat(storage): introduce core feature adoption tracking bitmask and options (#2657) --- .../storage/google_cloud_cpp_storage.bzl | 2 + .../storage/google_cloud_cpp_storage.cmake | 3 + .../cloud/storage/internal/feature_tracker.cc | 58 ++++++++++++ .../cloud/storage/internal/feature_tracker.h | 88 +++++++++++++++++++ google/cloud/storage/options.h | 15 +++- .../storage/storage_client_unit_tests.bzl | 1 + 6 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 google/cloud/storage/internal/feature_tracker.cc create mode 100644 google/cloud/storage/internal/feature_tracker.h diff --git a/google/cloud/storage/google_cloud_cpp_storage.bzl b/google/cloud/storage/google_cloud_cpp_storage.bzl index ed40ddb04ab5f..ab70919fcfa6f 100644 --- a/google/cloud/storage/google_cloud_cpp_storage.bzl +++ b/google/cloud/storage/google_cloud_cpp_storage.bzl @@ -61,6 +61,7 @@ google_cloud_cpp_storage_hdrs = [ "internal/curl/request_builder.h", "internal/default_object_acl_requests.h", "internal/empty_response.h", + "internal/feature_tracker.h", "internal/generate_message_boundary.h", "internal/generic_object_request.h", "internal/generic_request.h", @@ -173,6 +174,7 @@ google_cloud_cpp_storage_srcs = [ "internal/crc32c.cc", "internal/default_object_acl_requests.cc", "internal/empty_response.cc", + "internal/feature_tracker.cc", "internal/generate_message_boundary.cc", "internal/generic_stub_adapter.cc", "internal/generic_stub_factory.cc", diff --git a/google/cloud/storage/google_cloud_cpp_storage.cmake b/google/cloud/storage/google_cloud_cpp_storage.cmake index f3d70b6766817..9b41dc09b646c 100644 --- a/google/cloud/storage/google_cloud_cpp_storage.cmake +++ b/google/cloud/storage/google_cloud_cpp_storage.cmake @@ -95,6 +95,8 @@ add_library( internal/default_object_acl_requests.h internal/empty_response.cc internal/empty_response.h + internal/feature_tracker.cc + internal/feature_tracker.h internal/generate_message_boundary.cc internal/generate_message_boundary.h internal/generic_object_request.h @@ -437,6 +439,7 @@ if (BUILD_TESTING) internal/const_buffer_test.cc internal/crc32c_test.cc internal/default_object_acl_requests_test.cc + internal/feature_tracker_test.cc internal/generate_message_boundary_test.cc internal/generic_request_test.cc internal/hash_function_impl_test.cc diff --git a/google/cloud/storage/internal/feature_tracker.cc b/google/cloud/storage/internal/feature_tracker.cc new file mode 100644 index 0000000000000..e9ca0e9ee409e --- /dev/null +++ b/google/cloud/storage/internal/feature_tracker.cc @@ -0,0 +1,58 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "google/cloud/storage/internal/feature_tracker.h" +#include "google/cloud/storage/internal/base64.h" +#include "google/cloud/storage/options.h" +#include "google/cloud/options.h" +#include +#include + +namespace google { +namespace cloud { +namespace storage { +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN +namespace internal { + +std::string EncodeFeatureTrackerBitmask(std::uint32_t mask) { + if (mask == 0) return {}; + + std::vector bytes; + for (int i = 0; i < 4; ++i) { + auto b = static_cast((mask >> (24 - i * 8)) & 0xFF); + if (!bytes.empty() || b != 0) { + bytes.push_back(b); + } + } + return Base64Encode(bytes); +} + +Options SetupFeatureTracker(Options opts) { + if (opts.has() && + !opts.get()) { + return std::move(opts); + } + if (opts.has()) return std::move(opts); + + std::uint32_t mask = 0; + // Add checks for configuration-driven options here as they are introduced. + opts.set(std::make_shared(mask)); + return std::move(opts); +} + +} // namespace internal +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END +} // namespace storage +} // namespace cloud +} // namespace google diff --git a/google/cloud/storage/internal/feature_tracker.h b/google/cloud/storage/internal/feature_tracker.h new file mode 100644 index 0000000000000..20c68c2fd7d4a --- /dev/null +++ b/google/cloud/storage/internal/feature_tracker.h @@ -0,0 +1,88 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_FEATURE_TRACKER_H +#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_FEATURE_TRACKER_H + +#include "google/cloud/storage/version.h" +#include "google/cloud/options.h" +#include +#include +#include +#include + +namespace google { +namespace cloud { +namespace storage { +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN +namespace internal { + +inline constexpr auto kFeatureTrackerHeaderName = "x-goog-storage-cpp-features"; + +// Tracked features represented as bit positions in a bitmask. +enum class TrackedFeature : std::uint32_t { + // Operation-Driven Optimizations + kMultiStreamInMRD = 0, + kPCU = 1, + + // Configuration-Driven Options + kGrpcDirectPathEnforced = 2, + kJsonReads = 3, +}; + +// Converts a feature bitmask to a Base64-encoded string, stripping leading +// zero bytes to maintain compact representation. +std::string EncodeFeatureTrackerBitmask(std::uint32_t mask); + +// A thread-safe state object that can be shared between operation connections +// (like ObjectDescriptorImpl) and RPC factories (like OpenStreamFactory) +// to track which features are adopted during an operation. +class FeatureTracker { + public: + FeatureTracker() = default; + explicit FeatureTracker(std::uint32_t initial) : mask_(initial) {} + + void RegisterFeature(TrackedFeature feature) { + mask_.fetch_or(1U << static_cast(feature), + std::memory_order_relaxed); + } + + std::uint32_t GetMask() const { + return mask_.load(std::memory_order_relaxed); + } + + std::string HeaderValue() const { + return EncodeFeatureTrackerBitmask(GetMask()); + } + + private: + std::atomic mask_{0}; +}; + +struct FeatureTrackerOption { + using Type = std::shared_ptr; +}; + +// Evaluates client configuration options, creates a shared FeatureTracker +// initialized with configuration-driven feature flags (if any), and stores it +// into the Options list under FeatureTrackerOption. +Options SetupFeatureTracker(Options opts); + +} // namespace internal +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END +} // namespace storage +} // namespace cloud +} // namespace google + +#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_FEATURE_TRACKER_H diff --git a/google/cloud/storage/options.h b/google/cloud/storage/options.h index f64f5d1f28e51..3684d4d07956d 100644 --- a/google/cloud/storage/options.h +++ b/google/cloud/storage/options.h @@ -317,6 +317,19 @@ struct IdempotencyPolicyOption { using Type = std::shared_ptr; }; +/** + * Enable adoption reporting of client-side optimizations and configuration + * choices. + * + * When this option is enabled (the default), the GCS client sends a bitmask in + * the `x-goog-storage-cpp-features` header. + * + * @ingroup storage-options + */ +struct EnableFeatureReportsOption { + using Type = bool; +}; + /// The complete list of options accepted by `storage::Client`. using ClientOptionList = ::google::cloud::OptionList< RestEndpointOption, IamEndpointOption, ProjectIdOption, ProjectIdOption, @@ -324,7 +337,7 @@ using ClientOptionList = ::google::cloud::OptionList< EnableCurlSslLockingOption, EnableCurlSigpipeHandlerOption, MaximumCurlSocketRecvSizeOption, MaximumCurlSocketSendSizeOption, TransferStallTimeoutOption, RetryPolicyOption, BackoffPolicyOption, - IdempotencyPolicyOption, CARootsFilePathOption, + IdempotencyPolicyOption, CARootsFilePathOption, EnableFeatureReportsOption, storage_experimental::HttpVersionOption>; GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END diff --git a/google/cloud/storage/storage_client_unit_tests.bzl b/google/cloud/storage/storage_client_unit_tests.bzl index 54c1c64a555b6..5899fa1f3acbd 100644 --- a/google/cloud/storage/storage_client_unit_tests.bzl +++ b/google/cloud/storage/storage_client_unit_tests.bzl @@ -60,6 +60,7 @@ storage_client_unit_tests = [ "internal/const_buffer_test.cc", "internal/crc32c_test.cc", "internal/default_object_acl_requests_test.cc", + "internal/feature_tracker_test.cc", "internal/generate_message_boundary_test.cc", "internal/generic_request_test.cc", "internal/hash_function_impl_test.cc",