Type Safety

Failsafe-go’s APIs are typed based on the expected execution result. For some policies and executions, this type may not matter:

retryPolicy := retrypolicy.NewWithDefaults[any]()
err := failsafe.With(retryPolicy).Run(Connect)

But for other cases you might declare a more specific result type:

retryPolicy := retrypolicy.NewBuilder[*http.Response]().
  HandleIf(func(response *http.Response, err error) bool {
    return response != nil && response.StatusCode == 500
  }).
  OnFailure(func(e failsafe.ExecutionEvent[*http.Response]) {
    logger.Error("Failed attempt", "statusCode", e.LastResult().StatusCode)
  }).
  Build()

This allows Failsafe-go to ensure that the same result type used for the policy is returned by the execution and is available in event listeners:

response, err := failsafe.With(retryPolicy).
  OnSuccess(func(e failsafe.ExecutionDoneEvent[*http.Response]) {
    logger.Info("Request sent", "statusCode", e.Result.StatusCode)
  }).
  Get(SendHttpRequest)

It also ensures that when multiple policies are composed, they all share the same result type:

circuitBreaker := circuitbreaker.NewWithDefaults[*http.Response]()
response, err := failsafe.With(retryPolicy, circuitBreaker).Get(SendHttpRequest)

Mixing Result Types

When composing shared policies, it’s common for the shared policy to have any as the result type. These can be composed inside policies with specific result types:

circuitBreaker := circuitbreaker.NewWithDefaults[any]()
retryPolicy := retrypolicy.NewWithDefaults[Connection]()

// Compose RetryPolicy[Connection] around CircuitBreaker[any]
failsafe.With(retryPolicy).ComposeAny(circuitBreaker)

Or they can be composed outside other policies:

// Compose CircuitBreaker[any] around RetryPolicy[Connection]
failsafe.WithAny[Connection](circuitBreaker).Compose(retryPolicy)