Skip to content

impl(storage): handle redirect errors#3715

Merged
coryan merged 2 commits intogoogleapis:mainfrom
coryan:pr03-impl-storage-handle-direct
Nov 6, 2025
Merged

impl(storage): handle redirect errors#3715
coryan merged 2 commits intogoogleapis:mainfrom
coryan:pr03-impl-storage-handle-direct

Conversation

@coryan
Copy link
Copy Markdown
Collaborator

@coryan coryan commented Nov 5, 2025

In the bidi streaming protocol the stream may be closed to redirect the client to a new handle and routing header. This PR introduces a helper function to convert a grpc::Status to gax::error::Error and also update the read spec (part of the initial bidi read request) with any redirect data.

Parsing the error requires several storage-specific types. It seems better to keep this in the storage library.

Part of the work for #3626

In the bidi streaming protocol the stream may be closed to redirect the client
to a new handle and routing header. This PR introduces a helper function to
convert a `grpc::Status` to `gax::error::Error` **and** also update the read
spec (part of the initial bidi read request) with any redirect data.

Parsing the error requires several storage-specific types. It seems better to
keep this in the storage library.
@product-auto-label product-auto-label bot added the api: storage Issues related to the Cloud Storage API. label Nov 5, 2025
@coryan
Copy link
Copy Markdown
Collaborator Author

coryan commented Nov 5, 2025

/FYI: @BenWhitehead @cjc25

@codecov
Copy link
Copy Markdown

codecov bot commented Nov 5, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.17%. Comparing base (9252222) to head (d3e89f0).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3715      +/-   ##
==========================================
+ Coverage   96.15%   96.17%   +0.02%     
==========================================
  Files         131      132       +1     
  Lines        5226     5234       +8     
==========================================
+ Hits         5025     5034       +9     
+ Misses        201      200       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coryan coryan marked this pull request as ready for review November 5, 2025 20:14
@coryan coryan requested a review from a team November 5, 2025 20:14
Copy link
Copy Markdown
Member

@dbolduc dbolduc left a comment

Choose a reason for hiding this comment

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

I have some questions. I don't have anything close to the full picture in my head.

if let Ok(redirect) = d.to_msg::<BidiReadObjectRedirectedError>() {
let mut guard = spec.lock().expect("never poisoned");
guard.routing_token = redirect.routing_token;
guard.read_handle = redirect.read_handle;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

break?

I assume we expect exactly one BidiReadObjectRedirectedError.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done. I don't think we expect more than one and we save a wee-bit of time.

Comment on lines +29 to +30
guard.routing_token = redirect.routing_token;
guard.read_handle = redirect.read_handle;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Q: just checking: None is supposed to override the current value in spec?

What does None even mean?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I assume that a redirect with a None means: "go back to the CFE and start over from just the bucket and object name". Possibly it is a "can't happen" thing, but I figured we would handle the case.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@cjc25 in case he has opinions or thoughts.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

tl;dr: clearing in the request is fine if either field in the error is None.

The service should never set an empty routing token in practice, but if it does, then clearing the field in the request (and the x-goog-request-params header) is fine and we will eventually route to an endpoint that can serve the request.

The service should never set read_handle to empty in a redirect error if it already returned a read_handle in some other response. It may return an empty read_handle if no response was ever delivered. Regardless, it is always safe, but slower, to send a read retry without a read_handle in the request, so clearing here if we hit that case is safe.

@coryan coryan merged commit 7522f52 into googleapis:main Nov 6, 2025
28 checks passed
@coryan coryan deleted the pr03-impl-storage-handle-direct branch November 6, 2025 15:13
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.

3 participants