Resilient HTTP client with circuit breaking, exponential-backoff retry, X-Request-ID propagation, and a generic typed JSON helper. What's included: - Client interface with Do(req) method; New(logger, cfg) and NewWithDefaults(logger) constructors - Config struct with env-tag support for timeout, dial timeout, retry, and circuit breaker parameters - Retry via avast/retry-go/v4 with BackOffDelay; triggers only on network errors and HTTP 5xx - Circuit breaker via sony/gobreaker wrapping the full retry loop; open circuit → xerrors.ErrUnavailable - X-Request-ID header propagated automatically from context via logz.GetRequestID on every attempt - DoJSON[T](ctx, client, req) generic helper for typed JSON request/response with xerrors error mapping - MapStatusToError(code, msg) exported function mapping HTTP status codes to xerrors types Tested-via: todo-api POC integration Reviewed-against: docs/adr/
3.2 KiB
3.2 KiB
Changelog
All notable changes to this module will be documented in this file.
The format is based on Keep a Changelog, and this module adheres to Semantic Versioning.
0.9.0 - 2026-03-18
Added
Clientinterface:Do(req *http.Request) (*http.Response, error).Configstruct: fieldsName,Timeout(default30s),DialTimeout(default5s),MaxRetries(default3),RetryDelay(default1s),CBThreshold(default10),CBTimeout(default1m); settable viaHTTP_*environment variables.DefaultConfig() Config: returns aConfigpopulated with all default values.New(logger logz.Logger, cfg Config) Client: constructs a client with agobreakercircuit breaker wrapping anavast/retry-goretry loop. The circuit breaker trips afterCBThresholdconsecutive failures and resets afterCBTimeout.NewWithDefaults(logger logz.Logger) Client: convenience constructor; equivalent toNew(logger, DefaultConfig()).- Retry behaviour: up to
MaxRetriesattempts with exponential backoff (retry.BackOffDelay). Only network errors and HTTP 5xx responses are retried; 4xx responses are not. - Circuit breaker behaviour: the breaker wraps the full retry sequence, so one fully-exhausted retry sequence counts as one failure. State transitions are logged at
Warnlevel withname,from, andtofields. - Request ID propagation:
logz.GetRequestID(ctx)is called on each attempt; if a request ID is present, it is forwarded as theX-Request-IDrequest header. - Per-request structured logging: successful requests emit an
Infolog withmethod,url,status, andlatencyfields; failed individual attempts emit aDebuglog. - Error mapping on
Do: open circuit →ErrUnavailable; post-retry HTTP error response →MapStatusToError; network/timeout error →ErrInternal. DoJSON[T any](ctx context.Context, client Client, req *http.Request) (*T, error): generic free function that executes a request and decodes the JSON response body intoT; returns*Ton success and a xerrors-typed error for network failures, HTTP 4xx/5xx responses, unreadable bodies, or JSON decode failures.MapStatusToError(code int, msg string) error(exported): maps HTTP status codes to xerrors codes —404→ErrNotFound,400→ErrInvalidInput,401→ErrUnauthorized,403→ErrPermissionDenied,409→ErrAlreadyExists,429→ErrUnavailable, all others →ErrInternal.- Dial timeout applied via a custom
net.Dialeron thehttp.Transport, independent of the per-requestTimeout.
Design Notes
- The circuit breaker wraps the retry loop rather than individual attempts; a full set of exhausted retries registers as a single failure against the breaker threshold, preventing overly aggressive tripping.
DoJSONis a free generic function rather than a method so it works with anyClientimplementation, including mocks, without requiring a concrete type.- The module has no lifecycle (no
OnInit/OnStart/OnStop) and does not depend onlauncherorhealth; it is a stateless constructor suitable for use at any tier.