reverseproxy: Add ability to clear dynamic upstreams cache during retries (#7662)
* 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. * Add some godoc * Export interface; update godocpull/7344/head^2
parent
2d33271482
commit
4d6945769d
|
|
@ -574,6 +574,17 @@ func (h *Handler) proxyLoopIteration(r *http.Request, origReq *http.Request, w h
|
||||||
// get the updated list of upstreams
|
// get the updated list of upstreams
|
||||||
upstreams := h.Upstreams
|
upstreams := h.Upstreams
|
||||||
if h.DynamicUpstreams != nil {
|
if h.DynamicUpstreams != nil {
|
||||||
|
if retries > 0 {
|
||||||
|
// after a failure (and thus during a retry), give dynamic upstream modules an opportunity
|
||||||
|
// to purge their relevant cache entries so we don't keep retrying bad upstreams
|
||||||
|
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)
|
dUpstreams, err := h.DynamicUpstreams.GetUpstreams(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if c := h.logger.Check(zapcore.ErrorLevel, "failed getting dynamic upstreams; falling back to static upstreams"); c != nil {
|
if c := h.logger.Check(zapcore.ErrorLevel, "failed getting dynamic upstreams; falling back to static upstreams"); c != nil {
|
||||||
|
|
@ -1640,10 +1651,28 @@ type Selector interface {
|
||||||
// may be called during each retry, multiple times per request, and as
|
// may be called during each retry, multiple times per request, and as
|
||||||
// such, needs to be instantaneous. The returned slice will not be
|
// such, needs to be instantaneous. The returned slice will not be
|
||||||
// modified.
|
// modified.
|
||||||
|
//
|
||||||
|
// For upstream sources that cache results, implement the
|
||||||
|
// [CachingUpstreamSource] interface for optimal performance.
|
||||||
type UpstreamSource interface {
|
type UpstreamSource interface {
|
||||||
GetUpstreams(*http.Request) ([]*Upstream, error)
|
GetUpstreams(*http.Request) ([]*Upstream, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CachingUpstreamSource is an upstream source that caches its upstreams.
|
||||||
|
// The relevant cache entry can be cleared/reset for a given request during
|
||||||
|
// retries if a request fails. This can help ensure that failing backends
|
||||||
|
// are not retried.
|
||||||
|
//
|
||||||
|
// EXPERIMENTAL: Subject to change.
|
||||||
|
type CachingUpstreamSource interface {
|
||||||
|
UpstreamSource
|
||||||
|
|
||||||
|
// ResetCache clears any cache entry related to the given request.
|
||||||
|
// The next time GetUpstreams is called, it should have new upstream
|
||||||
|
// information for the given request.
|
||||||
|
ResetCache(*http.Request) error
|
||||||
|
}
|
||||||
|
|
||||||
// Hop-by-hop headers. These are removed when sent to the backend.
|
// 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
|
// As of RFC 7230, hop-by-hop headers are required to appear in the
|
||||||
// Connection header field. These are the headers defined by the
|
// Connection header field. These are the headers defined by the
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,18 @@ func (su *SRVUpstreams) Provision(ctx caddy.Context) error {
|
||||||
return nil
|
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) {
|
func (su SRVUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
|
||||||
suAddr, service, proto, name := su.expandedAddr(r)
|
suAddr, service, proto, name := su.expandedAddr(r)
|
||||||
|
|
||||||
|
|
@ -554,8 +566,9 @@ var (
|
||||||
|
|
||||||
// Interface guards
|
// Interface guards
|
||||||
var (
|
var (
|
||||||
_ caddy.Provisioner = (*SRVUpstreams)(nil)
|
_ caddy.Provisioner = (*SRVUpstreams)(nil)
|
||||||
_ UpstreamSource = (*SRVUpstreams)(nil)
|
_ UpstreamSource = (*SRVUpstreams)(nil)
|
||||||
_ caddy.Provisioner = (*AUpstreams)(nil)
|
_ CachingUpstreamSource = (*SRVUpstreams)(nil)
|
||||||
_ UpstreamSource = (*AUpstreams)(nil)
|
_ caddy.Provisioner = (*AUpstreams)(nil)
|
||||||
|
_ UpstreamSource = (*AUpstreams)(nil)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue