failover

package
v0.1.11 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 24, 2026 License: Apache-2.0 Imports: 15 Imported by: 1

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewRoundTripper

func NewRoundTripper(endpoints []Endpoint, fo Options, defaultBase http.RoundTripper) http.RoundTripper

NewRoundTripper creates a RoundTripper that cycles through endpoints according to fo when a request fails. Slices in fo are copied so callers can safely mutate the originals.

For each endpoint, a dedicated http.Transport is created when SkipTLSVerify or CertificateAuthData are set; otherwise defaultBase is used.

When the strategy is "none" or empty, RoundTrip passes through to defaultBase with no retry overhead.

Types

type Endpoint

type Endpoint struct {
	URL                 string
	SkipTLSVerify       bool
	CertificateAuthData string
}

Endpoint represents a server endpoint with optional per-endpoint TLS configuration. When SkipTLSVerify or CertificateAuthData are set, a dedicated http.Transport is created for that endpoint at construction time, otherwise the default base transport is shared.

type ExponentialBackoffOptions

type ExponentialBackoffOptions struct {
	// InitialInterval is the initial interval between retries. If zero, it defaults to 500ms.
	InitialInterval time.Duration `json:"initialInterval,omitempty" yaml:"initialInterval,omitempty"`

	// MaxInterval is the maximum interval between retries. If zero, it defaults to 60s.
	MaxInterval time.Duration `json:"maxInterval,omitempty" yaml:"maxInterval,omitempty"`

	// Multiplier is the factor by which the interval increases after each retry. If nil, it defaults to 1.5.
	Multiplier *float64 `json:"multiplier,omitempty" yaml:"multiplier,omitempty"`

	// RandomizationFactor is the factor used to randomize the backoff intervals. If nil, it defaults to 0.5.
	RandomizationFactor *float64 `json:"randomizationFactor,omitempty" yaml:"randomizationFactor,omitempty"`
}

ExponentialBackoffOptions controls the backoff parameters for exponential backoff. By configuring Multiplier and RandomizationFactor, it is possible to achieve a constant backoff or a linear backoff as well.

func (*ExponentialBackoffOptions) NewExponentialBackoff

func (b *ExponentialBackoffOptions) NewExponentialBackoff() boff.BackOff

NewExponentialBackoff creates a new exponential backoff instance configured with the options in b. If b is nil, defaults from cenkalti/backoff are used.

type Options

type Options struct {
	Strategy Strategy `json:"strategy,omitempty" yaml:"strategy,omitempty"`

	// RetryableMethods controls which HTTP methods are eligible for transport-level
	// failover retries. If empty/nil, the default is safe/idempotent methods.
	RetryableMethods []string `json:"retryableMethods,omitempty" yaml:"retryableMethods,omitempty"`

	// RetryOnTimeout controls whether failover retries should also happen when
	// the underlying transport experiences a timeout (for example, a network
	// timeout such as net.OpError.Timeout() / ETIMEDOUT). It does not apply to
	// context cancellation or deadline-exceeded errors (ctx.Err() != nil), which
	// always stop retries.
	RetryOnTimeout bool `json:"retryOnTimeout,omitempty" yaml:"retryOnTimeout,omitempty"`

	// FailoverOnStatusCodes controls whether the transport should fail over to the
	// next server when it receives one of these HTTP status codes.
	FailoverOnStatusCodes []int `json:"failoverOnStatusCodes,omitempty" yaml:"failoverOnStatusCodes,omitempty"`

	// MaxRetries controls how many times the transport will attempt a request using the set strategy before giving up
	// and returning the last error. If zero, it defaults to 3.
	MaxRetries int `json:"maxRetries,omitempty" yaml:"maxRetries,omitempty"`

	ExponentialBackoff *ExponentialBackoffOptions `json:"exponentialBackoff,omitempty" yaml:"exponentialBackoff,omitempty"`
}

Options controls transport-level endpoint failover behaviour. It is nested under Configuration so it can be grouped cleanly in JSON/YAML.

This layer only applies when a multiserver strategy is active (currently only RoundRobin). With None/empty or a single server, the transport passes through directly to the base http.RoundTripper.

Interaction with product-level callAPI retry loop

Product-level callAPI wraps HTTPClient.Do(), which invokes RoundTrip(). Each callAPI retry triggers a fresh RoundTrip() call that cycles through all servers from the beginning. Worst-case total attempts:

callAPI.MaxRetries × Options.MaxRetries

FailoverOnStatusCodes behaviour

Status codes listed in FailoverOnStatusCodes are handled at the transport level: the response body is drained, and the request is retried against the next server with exponential backoff. Response headers (e.g. Retry-After) are not inspected. If all servers return a listed code, RoundTrip returns an error (not an HTTP response), so callAPI receives err != nil and returns immediately — its own status-code retry logic is never reached.

Status codes NOT listed here pass through as a normal HTTP response to callAPI, which has its own retry logic for 502/503/504 (fixed backoff, no POST retry) and 429 (honors Retry-After).

type RoundTripper

type RoundTripper struct {
	// contains filtered or unexported fields
}

RoundTripper is an http.RoundTripper wrapper that retries the request against multiple configured servers when the underlying transport returns a network-level error or an HTTP status code listed in Options.FailoverOnStatusCodes.

Notes:

  • Network errors trigger retries with exponential backoff, cycling to the next server.
  • Status codes in FailoverOnStatusCodes also trigger retries: the response body is drained and the request is sent to the next server. Response headers (e.g. Retry-After) are not inspected at this layer.
  • If the request carries a body, the request must have GetBody set (the SDK generates requests in a way that supports this).
  • For non-idempotent requests (POST, PATCH, etc.), enabling failover on timeouts can produce duplicates when RetryOnTimeout is set. Context cancellation always stops retries immediately.

The request URL is rewritten by swapping scheme/host with each server URL, preserving path and query.

Endpoints, transports, and options are snapshotted at construction time; subsequent changes to the original slices or struct do not affect this instance.

func (*RoundTripper) RoundTrip

func (t *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip implements http.RoundTripper. It validates preconditions and dispatches to a strategy-specific method based on the configured Strategy:

  • RoundRobin: delegates to orderedRoundTrip, which cycles through servers sequentially.
  • None / empty: passes through to the base transport with no retry logic.
  • Unknown strategy: returns an error immediately (configuration error).

New strategies extend the switch in this method — each case builds its own server-selection function (or calls an entirely different method).

This method is called by HTTPClient.Do() inside product-level callAPI. Each callAPI retry triggers a fresh RoundTrip invocation that cycles through all servers from the beginning.

type Strategy

type Strategy string

Strategy selects the endpoint failover behaviour. It is a string type so it serializes nicely to JSON/YAML config files.

Supported values:

  • None ("none") or "": default behaviour (no endpoint failover)
  • RoundRobin ("roundRobin"): on network-level errors, retry the request against the next server in Servers

Note: comparisons should be case-insensitive.

const (
	None       Strategy = "none"
	RoundRobin Strategy = "roundRobin"
)

func NormalizeStrategy

func NormalizeStrategy(s Strategy) Strategy

NormalizeStrategy returns the strategy in lower-case with surrounding whitespace removed, so comparisons are case-insensitive.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL