Repository navigation
fix(storage): apply TransferStallTimeoutOption deadline to unary gRPC calls - #16600
Conversation
There was a problem hiding this comment.
Code Review
This pull request refactors the application of unary RPC deadlines across the storage client, centralizing the logic via ApplyUnaryRpcDeadline using TransferStallTimeoutOption. It also fixes an issue in GroupOptions where non-const lvalue Options were being moved-from. The feedback highlights a critical issue in AsyncStartResumableWrite where replacing ConfigureContext with ApplyUnaryRpcDeadline inadvertently drops other essential context configurations like credentials. Additionally, it is recommended to use a more robust and idiomatic comparison for the timeout duration to safely handle negative values and avoid direct .count() calls.
e886c61 to
2c90d06
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #16600 +/- ##
==========================================
- Coverage 92.40% 92.38% -0.03%
==========================================
Files 2266 2266
Lines 220810 220948 +138
==========================================
+ Hits 204036 204115 +79
- Misses 16774 16833 +59 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
2c90d06 to
df4f48d
Compare
df4f48d to
ae719c2
Compare
… calls Apply `TransferStallTimeoutOption` as a per-attempt deadline on `grpc::ClientContext` for unary gRPC RPCs in `GrpcStub`, `AsyncConnectionImpl`, and `RewriterConnectionImpl` when no explicit deadline has already been set, while allowing `GrpcSetupOption` to override the deadline and leaving streaming RPCs (`ReadObject`, `InsertObjectMedia`, `UploadChunk`) on their per-chunk stall timers. Also wire `GrpcStub::CreateResumableUpload`, `QueryResumableUpload`, `DeleteResumableUpload`, and `SignBlob` through `ApplyQueryParameters` so that `GrpcSetupOption` and query parameters are consistently applied.
ebf838f to
87d664e
Compare
kalragauri
left a comment
There was a problem hiding this comment.
nit: a few places in the PR description no longer match main or the current diff.
Also, TransferStallTimeoutOption (not in the diff) is documented as a no-progress "stall" timeout (
google-cloud-cpp/google/cloud/storage/options.h
Lines 453 to 472 in 608aeef
Should we add a sentence there that gRPC unary RPCs apply it as a per-attempt deadline and that std::chrono::seconds(0) disables it?
Thanks for the review! Updated the PR description (removed |
Summary
Fixes a bug where
GrpcStubunary RPCs (GetBucketMetadata,GetObjectMetadata,ListObjects,PatchObject,DeleteObject,ComposeObject,RewriteObject,CreateResumableUpload,QueryResumableUpload,DeleteResumableUpload,SignBlob, etc.) andAsyncConnectionImpl/RewriterConnectionImplunary RPCs leftgrpc::ClientContext::deadline()atgpr_inf_future(std::chrono::system_clock::time_point::max()) whenTransferStallTimeoutOptionwas configured. Streaming RPCs (ReadObject,WriteObjectas used byInsertObjectMedia,BidiWriteObject) are unchanged and continue to use their per-chunk stall timers.When an HTTP/2 connection stalls after TCP/TLS/HTTP2
SETTINGShandshake completes (or when an intermediate network element blackholes HTTP/2 response frames while TCP ACKs the request frames), unary gRPC calls without aClientContextdeadline hang indefinitely insideRetryClient::GetObjectMetadata/RestRetryLoopwithout ever firingDeadlineExceededor triggering a retry.Behavior Note
storage::Clientandstorage::AsyncClient, includingRewriteObject,ComposeObject, and IAMSignBlob) now honorTransferStallTimeoutOptionas a per-attempt deadline ongrpc::ClientContext. Because the storage default options (DefaultOptionsGrpc/DefaultOptionsAsync) set a defaultTransferStallTimeoutOption(120s), unary gRPC calls now have a finite per-attempt deadline by default.TransferStallTimeoutOption(std::chrono::seconds(0))(or any negative value). To override the deadline for specific calls, set a deadline ongrpc::ClientContextviaGrpcSetupOption(orGrpcSetupPollOptionfor poll calls).TransferStallTimeoutOptiondocumentation ingoogle/cloud/storage/options.hto note this per-attempt deadline behavior for gRPC unary RPCs.Root Causes Fixed
ApplyQueryParameters(configure_client_context.h):ApplyUnaryRpcDeadline(grpc::ClientContext& ctx, Options const& options)which appliesoptions.get<TransferStallTimeoutOption>()as a relative deadline onctxwhentimeout > std::chrono::seconds::zero()and no explicit deadline has already been set (ctx.deadline() == time_point::max()).IsStreamingRequest<Request>trait (ReadObjectRangeRequest,InsertObjectMediaRequest,UploadChunkRequest) soApplyQueryParametersonly sets aClientContextdeadline on unary RPCs (streaming RPCs continue to use their per-chunk stall timers).ApplyUnaryRpcDeadlinebeforeinternal::ConfigureContext(ctx, options)so that any user-providedGrpcSetupOptioncallback can override the deadline.ApplyQueryParametersin 4GrpcStubunary RPC methods (stub.cc):GrpcStub::CreateResumableUpload,QueryResumableUpload,DeleteResumableUpload, andSignBlobpreviously constructed a rawgrpc::ClientContextwithout callingApplyQueryParameters, bypassing bothTransferStallTimeoutOptionandGrpcSetupOption.async/connection_impl.cc,async/rewriter_connection_impl.cc):ApplyUnaryRpcDeadline(*context, *options)toAsyncConnectionImpl::GetBucket,ComposeObject,DeleteObject,StartResumableWrite,QueryWriteStatus, andRewriterConnectionImpl::Iterate.(Note: A separate issue where
GroupOptions(Options&, Tail&&...)moved from non-constlvalueOptionsobjects has been split into #16601.)Testing
configure_client_context_test: Tests unary deadline application, zero and negative (seconds(-1)) timeouts leaving deadline attime_point::max(), pre-existing deadline preservation,GrpcSetupOptionoverride precedence, and streaming request exclusion (ReadObjectRangeRequest,InsertObjectMediaRequest,UploadChunkRequest).stub_test: TestsTransferStallTimeoutOptionandGrpcSetupOptionacrossGrpcStub::GetBucketMetadata,CreateResumableUpload,QueryResumableUpload,DeleteResumableUpload, andSignBlob.async/connection_impl_test,async/connection_impl_upload_test, &async/rewriter_connection_impl_test: TestsTransferStallTimeoutOptiondeadline application on async unary RPCs (GetBucket,ComposeObject,DeleteObject,StartResumableWritewith0sand1s,QueryWriteStatus, andRewriterConnectionImpl::Iterate).us-central1GCE VM + GCS bucket):GetBucketMetadata,ListObjects,InsertObject,GetObjectMetadata,PatchObject,ComposeObject,RewriteObjectBlocking,CreateResumableUpload,QueryResumableUpload,DeleteResumableUpload,DeleteObject, and streamingReadObject, plusAsyncClientequivalents) from aus-central1GCE VM against a live GCS bucket, inspecting per-callgrpc::ClientContext::deadline().SETTINGSpreface and draining request frames without replying) that synchronous and all 6 asynchronous unary RPCs time out per attempt and returnStatusCode::kDeadlineExceededafter exhausting the retry policy instead of hanging indefinitely.