reverseproxy: Fix retries on "upstreams unavailable" error (#5841)

This commit is contained in:
Francis Lavoie 2023-10-10 18:07:20 -04:00 committed by GitHub
parent df99502977
commit 2a6859a5e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -357,7 +357,7 @@ func (h *Handler) Provision(ctx caddy.Context) error {
// set defaults on passive health checks, if necessary
if h.HealthChecks.Passive != nil {
h.HealthChecks.Passive.logger = h.logger.Named("health_checker.passive")
if h.HealthChecks.Passive.FailDuration > 0 && h.HealthChecks.Passive.MaxFails == 0 {
if h.HealthChecks.Passive.MaxFails == 0 {
h.HealthChecks.Passive.MaxFails = 1
}
}
@ -480,7 +480,7 @@ func (h *Handler) proxyLoopIteration(r *http.Request, origReq *http.Request, w h
upstream := h.LoadBalancing.SelectionPolicy.Select(upstreams, r, w)
if upstream == nil {
if proxyErr == nil {
proxyErr = caddyhttp.Error(http.StatusServiceUnavailable, fmt.Errorf("no upstreams available"))
proxyErr = caddyhttp.Error(http.StatusServiceUnavailable, noUpstreamsAvailable)
}
if !h.LoadBalancing.tryAgain(h.ctx, start, retries, proxyErr, r) {
return true, proxyErr
@ -1010,17 +1010,23 @@ func (lb LoadBalancing) tryAgain(ctx caddy.Context, start time.Time, retries int
// should be safe to retry, since without a connection, no
// HTTP request can be transmitted; but if the error is not
// specifically a dialer error, we need to be careful
if _, ok := proxyErr.(DialError); proxyErr != nil && !ok {
if proxyErr != nil {
_, isDialError := proxyErr.(DialError)
herr, isHandlerError := proxyErr.(caddyhttp.HandlerError)
// if the error occurred after a connection was established,
// we have to assume the upstream received the request, and
// retries need to be carefully decided, because some requests
// are not idempotent
if lb.RetryMatch == nil && req.Method != "GET" {
// by default, don't retry requests if they aren't GET
return false
}
if !lb.RetryMatch.AnyMatch(req) {
return false
if !isDialError && !(isHandlerError && errors.Is(herr, noUpstreamsAvailable)) {
if lb.RetryMatch == nil && req.Method != "GET" {
// by default, don't retry requests if they aren't GET
return false
}
if !lb.RetryMatch.AnyMatch(req) {
return false
}
}
}
@ -1421,6 +1427,8 @@ func (c ignoreClientGoneContext) Err() error {
// from the proxy handler.
const proxyHandleResponseContextCtxKey caddy.CtxKey = "reverse_proxy_handle_response_context"
var noUpstreamsAvailable = fmt.Errorf("no upstreams available")
// Interface guards
var (
_ caddy.Provisioner = (*Handler)(nil)