|
14 | 14 |
|
15 | 15 | #include "google/cloud/internal/disable_deprecation_warnings.inc" |
16 | 16 | #include "google/cloud/storage/internal/connection_impl.h" |
| 17 | +#include "google/cloud/storage/internal/hedged_object_read_source.h" |
17 | 18 | #include "google/cloud/storage/internal/retry_object_read_source.h" |
18 | 19 | #include "google/cloud/storage/parallel_upload.h" |
19 | 20 | #include "google/cloud/internal/filesystem.h" |
20 | 21 | #include "google/cloud/internal/opentelemetry.h" |
21 | 22 | #include "google/cloud/internal/rest_retry_loop.h" |
22 | 23 | #include "google/cloud/log.h" |
23 | 24 | #include "absl/strings/match.h" |
| 25 | +#include <algorithm> |
24 | 26 | #include <chrono> |
25 | 27 | #include <fstream> |
26 | 28 | #include <functional> |
@@ -155,7 +157,27 @@ std::shared_ptr<StorageConnectionImpl> StorageConnectionImpl::Create( |
155 | 157 | StorageConnectionImpl::StorageConnectionImpl( |
156 | 158 | std::unique_ptr<storage_internal::GenericStub> stub, Options options) |
157 | 159 | : stub_(std::move(stub)), |
158 | | - options_(MergeOptions(std::move(options), stub_->options())) {} |
| 160 | + options_(MergeOptions(std::move(options), stub_->options())) { |
| 161 | + if (options_.get<storage_experimental::EnableReadHedgingOption>()) { |
| 162 | + // The pool only runs stream-open attempts: one primary and (at most) a few |
| 163 | + // hedges per stream being opened. Size it to the number of connections the |
| 164 | + // REST layer can use, falling back to the hardware concurrency when the |
| 165 | + // connection pool is unbounded (`ConnectionPoolSizeOption == 0`). |
| 166 | + auto pool_size = options_.get<ConnectionPoolSizeOption>(); |
| 167 | + if (pool_size == 0) { |
| 168 | + pool_size = |
| 169 | + (std::max<std::size_t>)(4, std::thread::hardware_concurrency()); |
| 170 | + } |
| 171 | + auto const max_threads = 2 * pool_size; |
| 172 | + auto const rate_limit = |
| 173 | + options_.get<storage_experimental::ReadHedgeRateLimitOption>(); |
| 174 | + auto const max_concurrent = |
| 175 | + options_.get<storage_experimental::MaxConcurrentHedgesOption>(); |
| 176 | + // Allow bursts of up to one second worth of hedges. |
| 177 | + hedge_pool_ = std::make_shared<HedgingThreadPool>( |
| 178 | + max_threads, rate_limit, rate_limit, max_concurrent); |
| 179 | + } |
| 180 | +} |
159 | 181 |
|
160 | 182 | Options StorageConnectionImpl::options() const { return options_; } |
161 | 183 |
|
@@ -392,15 +414,37 @@ StatusOr<std::unique_ptr<ObjectReadSource>> StorageConnectionImpl::ReadObject( |
392 | 414 | *current, request, where); |
393 | 415 | }; |
394 | 416 |
|
395 | | - auto retry_policy = current->get<RetryPolicyOption>()->clone(); |
396 | | - auto backoff_policy = current->get<BackoffPolicyOption>()->clone(); |
397 | | - auto child = factory(request, *retry_policy, *backoff_policy); |
398 | | - if (!child) return child; |
| 417 | + auto retry_source_factory = |
| 418 | + [factory, current, |
| 419 | + request]() -> StatusOr<std::unique_ptr<ObjectReadSource>> { |
| 420 | + auto retry_policy = current->get<RetryPolicyOption>()->clone(); |
| 421 | + auto backoff_policy = current->get<BackoffPolicyOption>()->clone(); |
| 422 | + auto child = factory(request, *retry_policy, *backoff_policy); |
| 423 | + if (!child) return child; |
| 424 | + return std::unique_ptr<ObjectReadSource>( |
| 425 | + std::make_unique<RetryObjectReadSource>( |
| 426 | + factory, current, request, *std::move(child), |
| 427 | + std::move(retry_policy), std::move(backoff_policy))); |
| 428 | + }; |
| 429 | + |
| 430 | + auto const enable_hedging = |
| 431 | + current->get<storage_experimental::EnableReadHedgingOption>(); |
| 432 | + auto const delay = current->get<storage_experimental::ReadHedgeDelayOption>(); |
| 433 | + auto const max_hedges = |
| 434 | + current->get<storage_experimental::MaxReadHedgesOption>(); |
| 435 | + auto const max_buffer = |
| 436 | + current->get<storage_experimental::MaximumHedgeBufferOption>(); |
| 437 | + |
| 438 | + if (!enable_hedging || max_hedges <= 0 || !hedge_pool_) { |
| 439 | + return retry_source_factory(); |
| 440 | + } |
399 | 441 |
|
| 442 | + // `max_buffer` bounds the size of an individual read, which is only known |
| 443 | + // when the application calls `Read()`; the source applies it there. |
400 | 444 | return std::unique_ptr<ObjectReadSource>( |
401 | | - std::make_unique<RetryObjectReadSource>( |
402 | | - std::move(factory), std::move(current), request, *std::move(child), |
403 | | - std::move(retry_policy), std::move(backoff_policy))); |
| 445 | + std::make_unique<HedgedObjectReadSource>(hedge_pool_, |
| 446 | + std::move(retry_source_factory), |
| 447 | + delay, max_hedges, max_buffer)); |
404 | 448 | } |
405 | 449 |
|
406 | 450 | StatusOr<ListObjectsResponse> StorageConnectionImpl::ListObjects( |
|
0 commit comments