From 5609d12e5e6efc63cbc69c21299d8dcca4815707 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Mon, 20 Apr 2026 16:55:16 -0600 Subject: [PATCH] reverseproxy: Add ability to clear dynamic upstreams cache during retries This is an optional interface for dynamic upstream modules to implement if they cache results. TODO: More documentation; this is an experiment. --- .../caddyhttp/reverseproxy/reverseproxy.go | 16 ++++++++++++++ modules/caddyhttp/reverseproxy/upstreams.go | 21 +++++++++++++++---- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/modules/caddyhttp/reverseproxy/reverseproxy.go b/modules/caddyhttp/reverseproxy/reverseproxy.go index 3b9b56a05..c5375793f 100644 --- a/modules/caddyhttp/reverseproxy/reverseproxy.go +++ b/modules/caddyhttp/reverseproxy/reverseproxy.go @@ -574,6 +574,15 @@ func (h *Handler) proxyLoopIteration(r *http.Request, origReq *http.Request, w h // get the updated list of upstreams upstreams := h.Upstreams if h.DynamicUpstreams != nil { + if retries > 0 { + if cachingDynamicUpstreams, ok := h.DynamicUpstreams.(cachingUpstreamSource); ok { + if err := cachingDynamicUpstreams.ResetCache(r); err != nil { + if c := h.logger.Check(zapcore.ErrorLevel, "failed clearing dynamic upstream source's cache"); c != nil { + c.Write(zap.Error(err)) + } + } + } + } dUpstreams, err := h.DynamicUpstreams.GetUpstreams(r) if err != nil { if c := h.logger.Check(zapcore.ErrorLevel, "failed getting dynamic upstreams; falling back to static upstreams"); c != nil { @@ -1590,6 +1599,13 @@ type UpstreamSource interface { GetUpstreams(*http.Request) ([]*Upstream, error) } +// cachingUpstreamSource is an upstream source that caches its upstreams. +// EXPERIMENTAL: Subject to change. +type cachingUpstreamSource interface { + UpstreamSource + ResetCache(*http.Request) error +} + // Hop-by-hop headers. These are removed when sent to the backend. // As of RFC 7230, hop-by-hop headers are required to appear in the // Connection header field. These are the headers defined by the diff --git a/modules/caddyhttp/reverseproxy/upstreams.go b/modules/caddyhttp/reverseproxy/upstreams.go index e9120725a..7ada6a501 100644 --- a/modules/caddyhttp/reverseproxy/upstreams.go +++ b/modules/caddyhttp/reverseproxy/upstreams.go @@ -119,6 +119,18 @@ func (su *SRVUpstreams) Provision(ctx caddy.Context) error { return nil } +func (su *SRVUpstreams) ResetCache(r *http.Request) error { + srvsMu.Lock() + if r == nil { + srvs = make(map[string]srvLookup) + } else { + suAddr, _, _, _ := su.expandedAddr(r) + delete(srvs, suAddr) + } + srvsMu.Unlock() + return nil +} + func (su SRVUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) { suAddr, service, proto, name := su.expandedAddr(r) @@ -554,8 +566,9 @@ var ( // Interface guards var ( - _ caddy.Provisioner = (*SRVUpstreams)(nil) - _ UpstreamSource = (*SRVUpstreams)(nil) - _ caddy.Provisioner = (*AUpstreams)(nil) - _ UpstreamSource = (*AUpstreams)(nil) + _ caddy.Provisioner = (*SRVUpstreams)(nil) + _ UpstreamSource = (*SRVUpstreams)(nil) + _ cachingUpstreamSource = (*SRVUpstreams)(nil) + _ caddy.Provisioner = (*AUpstreams)(nil) + _ UpstreamSource = (*AUpstreams)(nil) )