Skip to content

fix: respect context cancellation in RetryTransport - #3383

Open
yoshi-taka wants to merge 1 commit into
integrations:mainfrom
yoshi-taka:fix/retry-transport-ctx-cancellation
Open

yoshi-taka wants to merge 1 commit into
integrations:mainfrom
yoshi-taka:fix/retry-transport-ctx-cancellation

Conversation

@yoshi-taka

Copy link
Copy Markdown
  • Replace time.Sleep with context-aware sleep() in RetryTransport.RoundTrip
  • Skip the delay after the final retry attempt (retry < maxRetries)

RateLimitTransport already uses the existing sleep(ctx, dur) helper (transport.go:122), which respects context cancellation. RetryTransport was using bare time.Sleep, causing it to ignore ctx.Done() during retry delays.

This aligns RetryTransport with the existing pattern in the codebase.

  • Add TestRetryTransport_cancelled: verifies context cancellation interrupts retry delay
  • Add TestRetryTransport_no_sleep_after_last_retry: verifies no delay after final attempt

Resolves #ISSUE_NUMBER


Before the change?

  • RetryTransport used time.Sleep between retry attempts.
  • During retry delays, request context cancellation was not respected.
  • RetryTransport also slept after the final retryable attempt, even when no further retry would be made.

After the change?

  • RetryTransport now uses the existing context-aware sleep(ctx, dur) helper.
  • Retry delays can now be interrupted when the request context is cancelled.
  • RetryTransport no longer sleeps after the final retryable attempt.

Pull request checklist

  • Schema migrations have been created if needed (not needed)
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been reviewed and added / updated if needed (for bug fixes / features)

Does this introduce a breaking change?

  • Yes
  • No

@github-actions github-actions Bot added the Type: Bug Something isn't working as documented label May 1, 2026
@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown

👋 Hi! Thank you for this contribution! Just to let you know, our GitHub SDK team does a round of issue and PR reviews twice a week, every Monday and Friday! We have a process in place for prioritizing and responding to your input. Because you are a part of this community please feel free to comment, add to, or pick up any issues/PRs that are labeled with Status: Up for grabs. You & others like you are the reason all of this works! So thank you & happy coding! 🚀

@deiga

deiga commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

Please rebase this branch ☺️

@deiga
deiga requested a balanced review from Copilot August 12, 2026 18:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

These provider review instructions are being used. This PR makes retry delays context-aware and avoids sleeping after the final attempt.

Changes:

  • Uses context-aware retry sleeping.
  • Adds cancellation and final-attempt timing tests.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
github/transport.go Updates retry-delay behavior.
github/transport_test.go Tests cancellation and final retry timing.
Suppressed comments (1)

github/transport_test.go:537

  • This aliases and mutates the process-wide http.DefaultClient, leaving the retry transport installed after the test and making later tests depend on execution order. Use a dedicated client for this test.
	httpClient := http.DefaultClient
	httpClient.Transport = NewRetryTransport(http.DefaultTransport,
		WithMaxRetries(1),
		WithRetryDelay(10*time.Second),
	)

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread github/transport.go Resolved
Comment thread github/transport_test.go Outdated
Comment on lines +495 to +499
httpClient := http.DefaultClient
httpClient.Transport = NewRetryTransport(http.DefaultTransport,
WithMaxRetries(1),
WithRetryDelay(10*time.Second),
)
@deiga
deiga requested a review from stevehipwell August 13, 2026 12:26
deiga
deiga previously approved these changes Aug 13, 2026

@deiga deiga left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@stevehipwell stevehipwell left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@yoshi-taka please could you look at the Copilot review comments.

@yoshi-taka

Copy link
Copy Markdown
Author

Addressed the Copilot review comments in 280d6c6:

  • Stop retrying immediately when the request context is cancelled.
  • Use test-local HTTP clients instead of mutating http.DefaultClient.
  • Fix the final-attempt test so it only verifies that no sleep occurs after the last attempt.

The RetryTransport and related transport tests pass. Could you please take another look?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Intermediate retry response bodies can leak connections and resources.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread github/transport.go Resolved

@stevehipwell stevehipwell left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@yoshi-taka please address the Copilot review comments. If you want a reference for how this should work you can check out the new client implementation of the logic which uses the hashicorp/retriable-http package.

@yoshi-taka
yoshi-taka force-pushed the fix/retry-transport-ctx-cancellation branch from f3d177f to bb9dd12 Compare October 25, 2026 09:27
@yoshi-taka

Copy link
Copy Markdown
Author

I've rebased the branch onto the latest main (1b9c5c13) and added a commit that addresses the Copilot review comment about intermediate retry response bodies.

What changed

  • Before retrying, RetryTransport now drains and closes the discarded response body so its connection and resources are not leaked. The drain is capped at 4KB, mirroring drainResponseBody in internal/ghclient, so small GitHub error bodies allow the connection to be reused while oversized bodies are still closed safely.
  • A response returned together with a transport error is also drained and discarded, since it can never reach the caller.
  • The final response (either non-retryable or after the retries are exhausted) is still returned untouched so the caller can read it.
  • This affects the legacy transport in github/transport.go, which is still the default (legacy_client = true). The new client already handles this via internal/ghclient/retry.go.

Tests

  • TestRetryTransport_closes_intermediate_response_bodies: intermediate responses are closed, the final one is not.
  • TestRetryTransport_closes_response_returned_with_error: a response returned alongside an error is closed.

Verification

  • make test (all unit tests) passes
  • go vet, gofumpt -extra, and goimports are clean
  • The rebase was conflict-free; the branch is now 3 commits on top of main

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comment thread github/transport.go Resolved
Use context-aware retry delays and check cancellation before each attempt. Do not sleep after the final attempt, and close unsent request bodies when cancellation prevents the initial attempt.

Drain and close discarded responses before retrying, with a 4KB bound matching the new client implementation. Preserve the final response body for the caller, and discard responses returned alongside transport errors.

Add deterministic virtual-time tests for cancellation and retry delays, plus coverage for request body ownership, exhausted responses, bounded draining, and read errors.
@yoshi-taka
yoshi-taka force-pushed the fix/retry-transport-ctx-cancellation branch from bb9dd12 to 6dd7be4 Compare October 26, 2026 15:49
@stevehipwell
stevehipwell requested a balanced review from Copilot October 26, 2026 17:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Synchronous response draining can block subsequent retries indefinitely when a response body stalls and no request deadline is set.

2 open issues
1 resolved since last review

Review effort: Balanced

Comment thread github/transport.go
if resp == nil || resp.Body == nil {
return
}
_, _ = io.Copy(io.Discard, io.LimitReader(resp.Body, 4*1024))
@yoshi-taka

Copy link
Copy Markdown
Author

The 4KB limit bounds bytes read, not the time spent draining, so the stalled-body concern is valid.

This helper follows the existing drainResponseBody implementation in internal/ghclient/body.go, which has the same behavior. Would you prefer addressing this consistently in both clients as a separate change, or changing the legacy transport in this PR to close discarded bodies without draining them?

@stevehipwell

Copy link
Copy Markdown
Collaborator

@yoshi-taka there is new PR (#3703) to improve the retry logic in the new client. The drain logic is fine for now, but will no longer be required when we bump to Go 1.27.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Awaiting response Type: Bug Something isn't working as documented

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants