From 2b5896f9722fa3b74c24c4f8c7d2cb3df9f2c24d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AB=A5=E5=89=91?= <1045931706@qq.com> Date: Sat, 8 Feb 2025 17:06:31 +0800 Subject: [PATCH 1/2] lazy gc in ttl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 童剑 <1045931706@qq.com> --- pkg/cache/ttl.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pkg/cache/ttl.go b/pkg/cache/ttl.go index ba7e3b67330..939627f3fb0 100644 --- a/pkg/cache/ttl.go +++ b/pkg/cache/ttl.go @@ -49,8 +49,6 @@ func newTTL(ctx context.Context, gcInterval time.Duration, duration time.Duratio ttl: duration, gcInterval: gcInterval, } - - go c.doGC() return c } @@ -63,7 +61,9 @@ func (c *ttlCache) put(key any, value any) { func (c *ttlCache) putWithTTL(key any, value any, ttl time.Duration) { c.Lock() defer c.Unlock() - + if len(c.items) == 0 { + go c.doGC() + } c.items[key] = ttlCacheItem{ value: value, expire: time.Now().Add(ttl), @@ -163,6 +163,11 @@ func (c *ttlCache) doGC() { } } } + if len(c.items) == 0 { + c.Unlock() + log.Debug("TTL GC items is empty exit") + return + } c.Unlock() log.Debug("TTL GC items", zap.Int("count", count)) case <-c.ctx.Done(): From 14f11015784f0fe9398fedb2f2b335fd69db1ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AB=A5=E5=89=91?= <1045931706@qq.com> Date: Sat, 8 Feb 2025 17:47:16 +0800 Subject: [PATCH 2/2] add atomic flag to check the goroutine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 童剑 <1045931706@qq.com> --- pkg/cache/ttl.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pkg/cache/ttl.go b/pkg/cache/ttl.go index 939627f3fb0..489ae3d4f74 100644 --- a/pkg/cache/ttl.go +++ b/pkg/cache/ttl.go @@ -16,6 +16,7 @@ package cache import ( "context" + "sync/atomic" "time" "go.uber.org/zap" @@ -39,15 +40,18 @@ type ttlCache struct { items map[any]ttlCacheItem ttl time.Duration gcInterval time.Duration + // isGCRunning is used to avoid running GC multiple times. + isGCRunning atomic.Bool } // NewTTL returns a new TTL cache. func newTTL(ctx context.Context, gcInterval time.Duration, duration time.Duration) *ttlCache { c := &ttlCache{ - ctx: ctx, - items: make(map[any]ttlCacheItem), - ttl: duration, - gcInterval: gcInterval, + ctx: ctx, + items: make(map[any]ttlCacheItem), + ttl: duration, + gcInterval: gcInterval, + isGCRunning: atomic.Bool{}, } return c } @@ -61,7 +65,7 @@ func (c *ttlCache) put(key any, value any) { func (c *ttlCache) putWithTTL(key any, value any, ttl time.Duration) { c.Lock() defer c.Unlock() - if len(c.items) == 0 { + if len(c.items) == 0 && c.isGCRunning.CompareAndSwap(false, true) { go c.doGC() } c.items[key] = ttlCacheItem{ @@ -163,7 +167,7 @@ func (c *ttlCache) doGC() { } } } - if len(c.items) == 0 { + if len(c.items) == 0 && c.isGCRunning.CompareAndSwap(true, false) { c.Unlock() log.Debug("TTL GC items is empty exit") return