Repository navigation
Retry GCS requests Go's HTTP/2 client ends with its connection - #67
Merged
Merged
Conversation
When Go's HTTP/2 client closes a connection, on its own write error or on the server's GOAWAY followed by a close, every open request on it fails with an error the GCS SDK's retry predicate does not match, so each of those uploads is lost. The client's predicate now adds both. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
boks1971
approved these changes
Nov 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One more fix.
Follow-up to #66, which keeps a dead HTTP/2 connection from sitting until the idle deadline. This PR is about what happens to the requests when a connection ends anyway: the GCS client now retries the two errors with which Go's HTTP/2 client fails every open request on a connection it is closing.
How this differs from #66
client connection lost, which the SDK already retries. It prevents the stall from ever reaching the idle-deadline race, but it only helps when the peer has gone quiet.session_timed_out); the peer is alive and answering pings the whole time;What changes
gcsShouldRetryisstorage.ShouldRetryplushttp2: client connection force closed via ClientConn.Closeandhttp2: server sent GOAWAY and closed the connection, set client-wide withSetRetry(WithErrorFunc). Per-object retryers (gcsRetryAlways, the conditional write) inherit it and keep their own policy and backoff; the policy still decides which operations retry at all, this only widens which errors count.Worth knowing
LastStreamID); the ones already in flight getGoAwayError, which has noTemporary()and is not in the SDK's string list. Both error types are unexported innet/http, so a string match is the only handle, as the SDK uses for its own transport errors.DoesNotExistprecondition both tolerate.Tests
TestGCSRetriesConnectionClose: a TLS HTTP/2 server that takes the request's HEADERS, answers GOAWAY with that stream still open and closes, which yields the realGoAwayErrorfrom Go's client; asserts the SDK's predicate rejects it and ours accepts it. The force-closed case uses the error asnet/httpwraps it. A nil error, a 404 and a connection reset are classified as before.🤖 Generated with Claude Code