Skip to content

feat: add retry with exponential backoff for network interactions#1342

Open
ogulcanaydogan wants to merge 1 commit intonotaryproject:mainfrom
ogulcanaydogan:feat/518-retry-network-interactions
Open

feat: add retry with exponential backoff for network interactions#1342
ogulcanaydogan wants to merge 1 commit intonotaryproject:mainfrom
ogulcanaydogan:feat/518-retry-network-interactions

Conversation

@ogulcanaydogan
Copy link

Summary

Add automatic retry with exponential backoff for all HTTP network interactions in notation. This wraps HTTP client transports with oras-go/v2/registry/remote/retry.Transport, which provides:

  • Exponential backoff with jitter: 200ms–3s, up to 5 retries
  • Retries on: 5xx server errors, 429 Too Many Requests, 408 Request Timeout, network dial timeouts
  • No new dependencies: uses oras.land/oras-go/v2 which is already in go.mod

Changes

  • internal/httputil/client.go: Add withRetry() helper that wraps the HTTP client transport with retry.NewTransport. Applied in both NewAuthClient() and NewClient().
  • internal/httputil/client_test.go: Add unit tests covering:
    • Nil client handling (default transport)
    • Custom transport preservation
    • Retry on 503 → 200 (server error triggers retry)
    • No retry on 400 (client errors are not retried)
    • User-Agent header propagation

Transport stack

Request → [userAgentTransport] → [trace.Transport (debug)] → [retry.Transport] → [http.DefaultTransport]

Test plan

  • go test ./internal/httputil/ -v — all new tests pass
  • go build ./cmd/notation/ — builds successfully
  • go test ./... — full test suite passes
  • go vet ./internal/httputil/ — no issues

Fixes #518

Copy link
Contributor

@priteshbandi priteshbandi left a comment

Choose a reason for hiding this comment

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

lgtm

Wrap HTTP transports with retry logic from oras-go/v2/registry/remote/retry
in both NewAuthClient and NewClient. The retry transport uses exponential
backoff with jitter (200ms-3s, up to 5 retries) and retries on 5xx errors,
429 Too Many Requests, 408 Request Timeout, and network dial timeouts.

- Add withRetry helper to wrap client transports with retry.Transport
- Add unit tests for retry behavior, transport wrapping, and user agent

Fixes notaryproject#518

Signed-off-by: Ogulcan Aydogan <ogulcanaydogan@hotmail.com>
@ogulcanaydogan ogulcanaydogan force-pushed the feat/518-retry-network-interactions branch from 76006e7 to 05d12b2 Compare March 9, 2026 08:54
Copy link
Member

@FeynmanZhou FeynmanZhou left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds automatic retry with exponential backoff for all HTTP network interactions in notation by wrapping HTTP client transports with oras-go/v2/registry/remote/retry.Transport. Since oras-go/v2 is already a dependency, no new dependencies are introduced. This addresses issue #518.

Changes:

  • Added withRetry() helper in internal/httputil/client.go that wraps the HTTP client transport with retry.NewTransport, and integrated it into both NewAuthClient() and NewClient()
  • Added comprehensive unit tests in internal/httputil/client_test.go covering nil/custom client handling, retry on server errors, no retry on client errors, and user-agent propagation

Reviewed changes

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

File Description
internal/httputil/client.go Added withRetry() function and integrated it into NewAuthClient() and NewClient(), updated doc comments
internal/httputil/client_test.go New test file with tests for NewAuthClient, NewClient, withRetry, retry behavior on server/client errors, and user-agent header propagation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +27 to +30
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer ts.Close()
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

The httptest.NewServer created on line 27 is never used—none of the subtests make requests to ts.URL. This is dead code that allocates a goroutine and listener unnecessarily. Remove the unused test server.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Retry support for network interactions

4 participants