Classify transport errors precisely, make base errors unwrappable, and fix backoff jitter

Aperta
#174 0 commenti 0 reazioni 1 assegnatario Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@ffumero2003 ci sta già lavorando.

Dal 8/11/2026.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
35/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
go

Direzione di ricerca

Start with the named sections in base/base.go and internal/retry/retry.go, then inspect b2/backend.go where positive backoff values are handled. Read the existing retry and error classification behavior first; the acceptance criteria specify the required error wrapping, retry decisions, Retry-After handling, and jitter tests.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

bug

Context

base turns every non-HTTP failure other than a certificate verification error into the same b2err{retry: 1} and keeps only the error text. Effects:

  1. Cause is lost. b2err has no Unwrap, so errors.Is(err, context.DeadlineExceeded), errors.As(err, &netErr) and similar checks are false for errors from base and b2.
  2. Wrapped context errors are retried. Cancellation is detected by identity comparison, so a RoundTripper that wraps errors (tracing, metrics) turns a deadline into a retryable error.
  3. Deterministic failures are retried five times: invalid header values (CR/LF in UserAgent) and unsupported schemes.
  4. Fixed 1 s delay for network errors, and internal/retry Jitter adds only about 1-3%.
  5. Retry-After makes any status retryable when numeric (clamped to 1-30 s), and the HTTP-date form is ignored.
  6. Type assertions (err.(b2err)) in Action, Code, MsgCode, MaxRetries, MaxReuploads and Backoff classify a wrapped b2err as Punt with code 0.

Certificate verification failures already return the original error and are not retried (Backblaze/blazer#139). A 408 on a non-upload call is retried since Backblaze/blazer#167 (merged).

Current behavior

At a5e0d13: base/base.go:518-558 (transport error handling; identity check at 521-525, retry: 1 at 552-556), :48-61 (no Unwrap), :69-74 (any retry > 0 is Retry), :166-174 and :184-196 (Retry-After parsed as integer seconds only), :230-242 (Backoff); type assertions at :65, :111, :120, :201, :214, :231; b2/backend.go:1066-1076 (positive backoff replaces the exponential delay); internal/retry/retry.go:73-78 (Jitter).

Reproduced locally at a5e0d13:

refused connection:           As(net.Error)=false  Action=Retry  Backoff=1s
unsupported scheme (ftp):     Action=Retry  MaxRetries=5
UserAgent with CR/LF:         invalid header field value  Action=Retry
wrapping transport, deadline: Is(DeadlineExceeded)=false  Action=Retry
400 with Retry-After: 3:      Action=Retry
fmt.Errorf("%w", 503 b2err):  Action=Punt
Expected behavior

Callers can use errors.Is/errors.As; configuration errors fail at once; network retries back off with real jitter.

Acceptance criteria

  • R1. A deadline through a wrapping RoundTripper satisfies errors.Is(err, context.DeadlineExceeded) and is Punt.
  • R2. A refused connection satisfies errors.As(err, &netErr); an invalid header value or unsupported scheme is not retried.
  • R3. An HTTP-date Retry-After on a 503 is honored (capped); Retry-After on a 400 does not make it retryable.
  • R4. A b2err wrapped with fmt.Errorf("%w") keeps its classification.
  • R5. New internal/retry tests show growing, capped delays that differ across calls by more than 10%.

In scope

Proposed change
  • Add Unwrap() error to b2err, use errors.Is for context checks, and export a typed error (for example *base.APIError with Status, Code, Message, Method).
  • Use errors.As in all classification helpers.
  • Classify context and request-construction errors as Punt; keep dial errors, resets and timeouts retryable, subject to whether the method is safe to repeat.
  • Keep retry for a real Retry-After, parse both forms (keeping the 30 s cap), and honor it only for 429 and 503.
  • Use full or equal jitter in internal/retry and the same exponential schedule for network errors.
  • Validate UserAgent when the option is set.

Out of scope

  • Unrelated work outside the components and behavior described in In scope.

Verification

Run the repository regression checks with live credentials unset:

unset B2_APPLICATION_KEY_ID B2_APPLICATION_KEY B2_ACCOUNT_ID B2_ACCOUNT_KEY
go build ./...
go vet ./...
go test -race -count=1 ./base ./b2/... ./internal/retry
  • Exercise wrapped deadlines, refused connections, invalid headers/schemes, wrapped API errors, and the stated Retry-After status/date cases; assert errors.Is/errors.As and literal classifications.
  • Measure the retry schedule through the existing test controls and assert growth, cap, and the stated >10% jitter variation; demonstrate each targeted regression red then green.
  • Expected result: the checks above succeed and the observable outcomes in Acceptance criteria are demonstrated. If a check cannot run, report Could not run because: ...; if it starts but fails, report Could not complete because: .... A skipped check is not a pass.

Links

Backblaze/blazer#101 (401 classification). Backblaze/blazer#18 and #31 bounded the retry count. Backblaze/blazer#139 stopped retrying certificate verification failures. Backblaze/blazer#167 (merged, closes #165) made 408 retryable on non-upload calls and did not change retry counts or backoff timing.

Lingua principale
Go
Stelle
66
Fork
18
Merge medio
1g 10h
PR unite (30g)
132

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di Backblaze/blazer

Tutte le issue di Backblaze/blazer

Issue simili

Altre issue su Go

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.