From 1af7248a6ffa3abf90bf80090ba6ede586cd3c7f Mon Sep 17 00:00:00 2001 From: William Petit Date: Tue, 18 Mar 2025 11:35:15 +0100 Subject: [PATCH] feat: reset proxy cache with sigusr2 --- internal/cache/cache.go | 1 + internal/cache/memory/cache.go | 4 ++ internal/cache/ttl/cache.go | 5 +++ internal/proxy/director/director.go | 6 +++ internal/proxy/server.go | 60 ++++++++++++++++++++++------- misc/packaging/common/config.yml | 6 +++ 6 files changed, 68 insertions(+), 14 deletions(-) diff --git a/internal/cache/cache.go b/internal/cache/cache.go index cc60dd6..049fe87 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -3,4 +3,5 @@ package cache type Cache[K comparable, V any] interface { Get(key K) (V, bool) Set(key K, value V) + Clear() } diff --git a/internal/cache/memory/cache.go b/internal/cache/memory/cache.go index 0aadab3..cf8685a 100644 --- a/internal/cache/memory/cache.go +++ b/internal/cache/memory/cache.go @@ -25,6 +25,10 @@ func (c *Cache[K, V]) Set(key K, value V) { c.store.Store(key, value) } +func (c *Cache[K, V]) Clear() { + c.store.Clear() +} + func NewCache[K comparable, V any]() *Cache[K, V] { return &Cache[K, V]{ store: new(sync.Map), diff --git a/internal/cache/ttl/cache.go b/internal/cache/ttl/cache.go index 6dfba51..3f44959 100644 --- a/internal/cache/ttl/cache.go +++ b/internal/cache/ttl/cache.go @@ -28,6 +28,11 @@ func (c *Cache[K, V]) Set(key K, value V) { c.values.Set(key, value) } +func (c *Cache[K, V]) Clear() { + c.timestamps.Clear() + c.values.Clear() +} + func NewCache[K comparable, V any](values cache.Cache[K, V], timestamps cache.Cache[K, time.Time], ttl time.Duration) *Cache[K, V] { return &Cache[K, V]{ values: values, diff --git a/internal/proxy/director/director.go b/internal/proxy/director/director.go index d34eddb..c334082 100644 --- a/internal/proxy/director/director.go +++ b/internal/proxy/director/director.go @@ -99,9 +99,12 @@ const proxiesCacheKey = "proxies" func (d *Director) getProxies(ctx context.Context) ([]*store.Proxy, error) { proxies, exists := d.proxyCache.Get(proxiesCacheKey) if exists { + logger.Debug(ctx, "using cached proxies") return proxies, nil } + logger.Debug(ctx, "querying fresh proxies") + headers, err := d.proxyRepository.QueryProxy(ctx, store.WithProxyQueryEnabled(true)) if err != nil { return nil, errors.WithStack(err) @@ -134,9 +137,12 @@ func (d *Director) getLayers(ctx context.Context, proxyName store.ProxyName) ([] layers, exists := d.layerCache.Get(cacheKey) if exists { + logger.Debug(ctx, "using cached layers") return layers, nil } + logger.Debug(ctx, "querying fresh layers") + headers, err := d.layerRepository.QueryLayers(ctx, proxyName, store.WithLayerQueryEnabled(true)) if err != nil { return nil, errors.WithStack(err) diff --git a/internal/proxy/server.go b/internal/proxy/server.go index 154e8a9..8759c57 100644 --- a/internal/proxy/server.go +++ b/internal/proxy/server.go @@ -13,8 +13,11 @@ import ( "net/http/httputil" "net/http/pprof" "net/url" + "os" + "os/signal" "path/filepath" "strconv" + "syscall" "time" "forge.cadoles.com/Cadoles/go-proxy" @@ -94,24 +97,15 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e logger.Info(ctx, "http server listening") + layerCache, proxyCache, cancel := s.createDirectorCaches(ctx) + defer cancel() + director := director.New( s.proxyRepository, s.layerRepository, director.WithLayers(s.directorLayers...), - director.WithLayerCache( - ttl.NewCache( - memory.NewCache[string, []*store.Layer](), - memory.NewCache[string, time.Time](), - s.directorCacheTTL, - ), - ), - director.WithProxyCache( - ttl.NewCache( - memory.NewCache[string, []*store.Proxy](), - memory.NewCache[string, time.Time](), - s.directorCacheTTL, - ), - ), + director.WithLayerCache(layerCache), + director.WithProxyCache(proxyCache), director.WithHandleErrorFunc(s.handleError), ) @@ -205,6 +199,44 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e logger.Info(ctx, "http server exiting") } +func (s *Server) createDirectorCaches(ctx context.Context) (*ttl.Cache[string, []*store.Layer], *ttl.Cache[string, []*store.Proxy], func()) { + layerCache := ttl.NewCache( + memory.NewCache[string, []*store.Layer](), + memory.NewCache[string, time.Time](), + s.directorCacheTTL, + ) + + proxyCache := ttl.NewCache( + memory.NewCache[string, []*store.Proxy](), + memory.NewCache[string, time.Time](), + s.directorCacheTTL, + ) + + sig := make(chan os.Signal, 1) + + signal.Notify(sig, syscall.SIGUSR2) + + go func() { + for { + _, ok := <-sig + if !ok { + return + } + + logger.Info(ctx, "received sigusr2 signal, clearing proxies and layers cache") + + layerCache.Clear() + proxyCache.Clear() + } + }() + + cancel := func() { + close(sig) + } + + return layerCache, proxyCache, cancel +} + func (s *Server) createReverseProxy(ctx context.Context, target *url.URL) *httputil.ReverseProxy { reverseProxy := httputil.NewSingleHostReverseProxy(target) diff --git a/misc/packaging/common/config.yml b/misc/packaging/common/config.yml index 745ac6a..397283e 100644 --- a/misc/packaging/common/config.yml +++ b/misc/packaging/common/config.yml @@ -131,6 +131,12 @@ proxy: # Les proxys/layers sont mis en cache local pour une durée de 30s # par défaut. Si les modifications sont rares, vous pouvez augmenter # cette valeur pour réduire la "pression" sur le serveur Redis. + # Il est possible de forcer la réinitialisation du cache en envoyant + # le signal SIGUSR2 au processus Bouncer. + # + # Exemple + # + # kill -s USR2 $(pgrep bouncer) ttl: 30s # Configuration du transport HTTP(S)