From d906d8573ee373393d2f5d4223b7b372952c5be7 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Thu, 20 Oct 2022 10:33:56 +0300 Subject: [PATCH] lib/workingsetcache: drop the previous cache whenever it recieves less than 5% of requests comparing to the current cache This means that the majority of requests are successfully served from the current cache, so the previous cache can be reset in order to free up memory. --- lib/workingsetcache/cache.go | 46 ++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/lib/workingsetcache/cache.go b/lib/workingsetcache/cache.go index a6091ef51..c56dccd71 100644 --- a/lib/workingsetcache/cache.go +++ b/lib/workingsetcache/cache.go @@ -131,14 +131,14 @@ func (c *Cache) runWatchers(expireDuration time.Duration) { func (c *Cache) expirationWatcher(expireDuration time.Duration) { expireDuration += timeJitter(expireDuration / 10) t := time.NewTicker(expireDuration) + defer t.Stop() + var csCurr, csPrev fastcache.Stats for { select { case <-c.stopCh: - t.Stop() return case <-t.C: } - c.mu.Lock() if atomic.LoadUint32(&c.mode) != split { // Stop the expirationWatcher on non-split mode. @@ -148,16 +148,52 @@ func (c *Cache) expirationWatcher(expireDuration time.Duration) { // Reset prev cache and swap it with the curr cache. prev := c.prev.Load().(*fastcache.Cache) curr := c.curr.Load().(*fastcache.Cache) + csCurr.Reset() + curr.UpdateStats(&csCurr) + csPrev.Reset() + prev.UpdateStats(&csPrev) c.prev.Store(curr) - var cs fastcache.Stats - prev.UpdateStats(&cs) - updateCacheStatsHistory(&c.csHistory, &cs) + prevGetCalls := csCurr.GetCalls + updateCacheStatsHistory(&c.csHistory, &csPrev) prev.Reset() c.curr.Store(prev) c.mu.Unlock() + + // Watch for the usage of the prev cache and drop it whenever it receives + // less than 5% of get calls comparing to the curr cache. + // This allows saving memory. + prev, curr = curr, prev + checkInterval := 10 * time.Second + checkerT := time.NewTicker(checkInterval) + checkerDeadline := time.Now().Add(expireDuration - checkInterval) + for time.Now().Before(checkerDeadline) { + select { + case <-c.stopCh: + break + case <-checkerT.C: + } + c.mu.Lock() + if atomic.LoadUint32(&c.mode) != split { + // Do nothing in non-split mode. + c.mu.Unlock() + break + } + csCurr.Reset() + curr.UpdateStats(&csCurr) + csPrev.Reset() + prev.UpdateStats(&csPrev) + getCalls := csPrev.GetCalls - prevGetCalls + if float64(getCalls) < 0.05*float64(csCurr.GetCalls) { + // The majority of requests are served from the curr cache, + // so the prev cache can be deleted. + prev.Reset() + break + } + } + checkerT.Stop() } }