From 0dcd8e7bef4c5e3a51b4a4b5e569c6a17a122166 Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Wed, 13 Nov 2024 18:27:35 +0800 Subject: [PATCH] planner: evict all cached plans after disabling instance plan cache (#57341) ref pingcap/tidb#54057 --- pkg/domain/domain.go | 3 +- pkg/planner/core/plan_cache_instance.go | 11 ++++++-- pkg/planner/core/plan_cache_instance_test.go | 29 +++++++++++++++++--- pkg/sessionctx/context.go | 2 +- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/pkg/domain/domain.go b/pkg/domain/domain.go index f4e4796e4c38b..ec72d51a636d9 100644 --- a/pkg/domain/domain.go +++ b/pkg/domain/domain.go @@ -3207,7 +3207,8 @@ func (do *Domain) planCacheEvictTrigger() { case <-ticker.C: // trigger the eviction begin := time.Now() - detailInfo, numEvicted := do.instancePlanCache.Evict() + enabled := variable.EnableInstancePlanCache.Load() + detailInfo, numEvicted := do.instancePlanCache.Evict(!enabled) // evict all if the plan cache is disabled metrics2.GetPlanCacheInstanceEvict().Set(float64(numEvicted)) if numEvicted > 0 { logutil.BgLogger().Info("instance plan eviction", diff --git a/pkg/planner/core/plan_cache_instance.go b/pkg/planner/core/plan_cache_instance.go index 5cdfbab6a8de1..ecfff1d1b6613 100644 --- a/pkg/planner/core/plan_cache_instance.go +++ b/pkg/planner/core/plan_cache_instance.go @@ -134,13 +134,13 @@ func (pc *instancePlanCache) Put(key string, value, paramTypes any) (succ bool) // step 1: iterate all values to collect their last_used // step 2: estimate an eviction threshold time based on all last_used values // step 3: iterate all values again and evict qualified values -func (pc *instancePlanCache) Evict() (detailInfo string, numEvicted int) { +func (pc *instancePlanCache) Evict(evictAll bool) (detailInfo string, numEvicted int) { pc.evictMutex.Lock() // make sure only one thread to trigger eviction for safety defer pc.evictMutex.Unlock() pc.inEvict.Store(true) defer pc.inEvict.Store(false) currentTot, softLimit := pc.totCost.Load(), pc.softMemLimit.Load() - if currentTot < softLimit { + if !evictAll && currentTot < softLimit { detailInfo = fmt.Sprintf("memory usage is below the soft limit, currentTot: %v, softLimit: %v", currentTot, softLimit) return } @@ -149,7 +149,12 @@ func (pc *instancePlanCache) Evict() (detailInfo string, numEvicted int) { lastUsedTimes = append(lastUsedTimes, this.lastUsed.Load()) return false }) - threshold := pc.calcEvictionThreshold(lastUsedTimes) // step 2 + var threshold time.Time + if evictAll { + threshold = time.Now().Add(time.Hour * 24) // a future time + } else { + threshold = pc.calcEvictionThreshold(lastUsedTimes) // step 2 + } detailInfo = fmt.Sprintf("evict threshold: %v", threshold) pc.foreach(func(prev, this *instancePCNode) bool { // step 3 if !this.lastUsed.Load().After(threshold) { // if lastUsed<=threshold, evict this value diff --git a/pkg/planner/core/plan_cache_instance_test.go b/pkg/planner/core/plan_cache_instance_test.go index d9bbc7a846601..088e27a699422 100644 --- a/pkg/planner/core/plan_cache_instance_test.go +++ b/pkg/planner/core/plan_cache_instance_test.go @@ -84,7 +84,7 @@ func TestInstancePlanCacheBasic(t *testing.T) { _hit(t, pc, 1, 0) // access 1-3 to refresh their last_used _hit(t, pc, 2, 0) _hit(t, pc, 3, 0) - _, numEvicted := pc.Evict() + _, numEvicted := pc.Evict(false) require.Equal(t, numEvicted > 0, true) require.Equal(t, pc.MemUsage(), int64(300)) _hit(t, pc, 1, 0) // access 1-3 to refresh their last_used @@ -98,7 +98,7 @@ func TestInstancePlanCacheBasic(t *testing.T) { _put(pc, 1, 100, 0) _put(pc, 2, 100, 0) _put(pc, 3, 100, 0) - _, numEvicted = pc.Evict() + _, numEvicted = pc.Evict(false) require.Equal(t, numEvicted > 0, false) require.Equal(t, pc.MemUsage(), int64(300)) _hit(t, pc, 1, 0) @@ -115,7 +115,7 @@ func TestInstancePlanCacheBasic(t *testing.T) { numHeads := 0 pcImpl.heads.Range(func(k, v any) bool { numHeads++; return true }) require.Equal(t, numHeads, 3) - _, numEvicted = pc.Evict() + _, numEvicted = pc.Evict(false) require.Equal(t, numEvicted > 0, true) require.Equal(t, pc.MemUsage(), int64(0)) numHeads = 0 @@ -177,7 +177,7 @@ func TestInstancePlanCacheWithMatchOpts(t *testing.T) { _hit(t, pc, 1, 1) // refresh 1-3's last_used _hit(t, pc, 1, 2) _hit(t, pc, 1, 3) - _, numEvicted := pc.Evict() + _, numEvicted := pc.Evict(false) require.True(t, numEvicted > 0) require.Equal(t, pc.MemUsage(), int64(300)) _hit(t, pc, 1, 1) @@ -187,6 +187,27 @@ func TestInstancePlanCacheWithMatchOpts(t *testing.T) { _miss(t, pc, 1, 5) } +func TestInstancePlanCacheEvictAll(t *testing.T) { + sctx := MockContext() + defer func() { + domain.GetDomain(sctx).StatsHandle().Close() + }() + sctx.GetSessionVars().PlanCacheInvalidationOnFreshStats = true + + // same key with different statsHash + pc := NewInstancePlanCache(1000, 1000) + _put(pc, 1, 100, 1) + _put(pc, 1, 100, 2) + _put(pc, 1, 100, 3) + _, numEvicted := pc.Evict(true) + require.Equal(t, 3, numEvicted) + _miss(t, pc, 1, 1) + _miss(t, pc, 1, 2) + _miss(t, pc, 1, 3) + require.Equal(t, pc.MemUsage(), int64(0)) + require.Equal(t, pc.Size(), int64(0)) +} + func TestInstancePlanCacheConcurrentRead(t *testing.T) { sctx := MockContext() defer func() { diff --git a/pkg/sessionctx/context.go b/pkg/sessionctx/context.go index b3cc26cb95376..cf871286d56b2 100644 --- a/pkg/sessionctx/context.go +++ b/pkg/sessionctx/context.go @@ -68,7 +68,7 @@ type InstancePlanCache interface { // Put puts the key and value into the cache. Put(key string, value, paramTypes any) (succ bool) // Evict evicts some cached values. - Evict() (detailInfo string, numEvicted int) + Evict(evictAll bool) (detailInfo string, numEvicted int) // Size returns the number of cached values. Size() int64 // MemUsage returns the total memory usage of this plan cache.