From 0c079fa70f89d0f632992bda77a1f0ed36ec78e6 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 24 Jun 2026 23:11:29 +0200 Subject: [PATCH] fix(router): skip plan cache OnEvict during graphMux shutdown Ristretto Close() clears all plan cache entries and fires OnEvict for each one, migrating them into slowplancache right before both caches are torn down. Disable OnEvict during intentional mux shutdown and wait for pending slow-cache writes first. --- router/core/graph_server.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/router/core/graph_server.go b/router/core/graph_server.go index af206997be..2866d18a51 100644 --- a/router/core/graph_server.go +++ b/router/core/graph_server.go @@ -681,6 +681,8 @@ type graphMux struct { mux *chi.Mux reused atomic.Bool + planCacheOnEvictEnabled atomic.Bool + planCache *ristretto.Cache[uint64, *planWithMetaData] planFallbackCache *slowplancache.Cache[*planWithMetaData] persistedOperationCache *ristretto.Cache[uint64, NormalizationCacheEntry] @@ -721,10 +723,14 @@ func (s *graphMux) buildOperationCaches(srv *graphServer) (computeSha256 bool, e BufferItems: 64, } if srv.cacheWarmup != nil && srv.cacheWarmup.Enabled && srv.cacheWarmup.InMemoryFallback { + s.planCacheOnEvictEnabled.Store(true) planCacheConfig.OnEvict = func(item *ristretto.Item[*planWithMetaData]) { // This could be called before planFallbackCache is set, but it's not a problem // because there is a nil guard inside, as well as items should not really be evicted // on startup + if !s.planCacheOnEvictEnabled.Load() { + return + } s.planFallbackCache.Set(item.Key, item.Value, item.Value.planningDuration) } } @@ -961,6 +967,11 @@ func (s *graphMux) Shutdown(ctx context.Context) error { // cancel the graph muxes context to close its resources like websocket connections, resolvers, etc. s.cancel() + // ristretto Close() invokes OnEvict for every entry; skip slow-cache migration during shutdown. + s.planCacheOnEvictEnabled.Store(false) + if s.planFallbackCache != nil { + s.planFallbackCache.Wait() + } s.planCache.Close() s.planFallbackCache.Close() s.persistedOperationCache.Close()