From f485f92b009f6b1d44c77b733d7facbc98824d34 Mon Sep 17 00:00:00 2001 From: withchao <993506633@qq.com> Date: Fri, 12 Dec 2025 15:33:08 +0800 Subject: [PATCH] fix: resolve deadlock in cache eviction and improve GetBatch implementation --- pkg/localcache/lru/lru_lazy.go | 56 +++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/pkg/localcache/lru/lru_lazy.go b/pkg/localcache/lru/lru_lazy.go index b4f0377a7..4a3db46c9 100644 --- a/pkg/localcache/lru/lru_lazy.go +++ b/pkg/localcache/lru/lru_lazy.go @@ -21,25 +21,25 @@ import ( "github.com/hashicorp/golang-lru/v2/simplelru" ) -type layLruItem[V any] struct { +type lazyLruItem[V any] struct { lock sync.Mutex expires int64 err error value V } -func NewLayLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) *LayLRU[K, V] { - var cb simplelru.EvictCallback[K, *layLruItem[V]] +func NewLazyLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) *LazyLRU[K, V] { + var cb simplelru.EvictCallback[K, *lazyLruItem[V]] if onEvict != nil { - cb = func(key K, value *layLruItem[V]) { + cb = func(key K, value *lazyLruItem[V]) { onEvict(key, value.value) } } - core, err := simplelru.NewLRU[K, *layLruItem[V]](size, cb) + core, err := simplelru.NewLRU[K, *lazyLruItem[V]](size, cb) if err != nil { panic(err) } - return &LayLRU[K, V]{ + return &LazyLRU[K, V]{ core: core, successTTL: successTTL, failedTTL: failedTTL, @@ -47,15 +47,15 @@ func NewLayLRU[K comparable, V any](size int, successTTL, failedTTL time.Duratio } } -type LayLRU[K comparable, V any] struct { +type LazyLRU[K comparable, V any] struct { lock sync.Mutex - core *simplelru.LRU[K, *layLruItem[V]] + core *simplelru.LRU[K, *lazyLruItem[V]] successTTL time.Duration failedTTL time.Duration target Target } -func (x *LayLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { +func (x *LazyLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { x.lock.Lock() v, ok := x.core.Get(key) if ok { @@ -68,7 +68,7 @@ func (x *LayLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { return value, err } } else { - v = &layLruItem[V]{} + v = &lazyLruItem[V]{} x.core.Add(key, v) v.lock.Lock() x.lock.Unlock() @@ -88,15 +88,15 @@ func (x *LayLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) { return v.value, v.err } -func (x *LayLRU[K, V]) GetBatch(keys []K, fetch func(keys []K) (map[K]V, error)) (map[K]V, error) { +func (x *LazyLRU[K, V]) GetBatch(keys []K, fetch func(keys []K) (map[K]V, error)) (map[K]V, error) { var ( err error once sync.Once ) res := make(map[K]V) - queries := make([]K, 0) - setVs := make(map[K]*layLruItem[V]) + queries := make([]K, 0, len(keys)) + for _, key := range keys { x.lock.Lock() v, ok := x.core.Get(key) @@ -118,14 +118,20 @@ func (x *LayLRU[K, V]) GetBatch(keys []K, fetch func(keys []K) (map[K]V, error)) } queries = append(queries, key) } - values, err1 := fetch(queries) - if err1 != nil { + + if len(queries) == 0 { + return res, err + } + + values, fetchErr := fetch(queries) + if fetchErr != nil { once.Do(func() { - err = err1 + err = fetchErr }) } + for key, val := range values { - v := &layLruItem[V]{} + v := &lazyLruItem[V]{} v.value = val if err == nil { @@ -135,7 +141,7 @@ func (x *LayLRU[K, V]) GetBatch(keys []K, fetch func(keys []K) (map[K]V, error)) v.expires = time.Now().Add(x.failedTTL).UnixMilli() x.target.IncrGetFailed() } - setVs[key] = v + x.lock.Lock() x.core.Add(key, v) x.lock.Unlock() @@ -145,29 +151,29 @@ func (x *LayLRU[K, V]) GetBatch(keys []K, fetch func(keys []K) (map[K]V, error)) return res, err } -//func (x *LayLRU[K, V]) Has(key K) bool { +//func (x *LazyLRU[K, V]) Has(key K) bool { // x.lock.Lock() // defer x.lock.Unlock() // return x.core.Contains(key) //} -func (x *LayLRU[K, V]) Set(key K, value V) { +func (x *LazyLRU[K, V]) Set(key K, value V) { x.lock.Lock() defer x.lock.Unlock() - x.core.Add(key, &layLruItem[V]{value: value, expires: time.Now().Add(x.successTTL).UnixMilli()}) + x.core.Add(key, &lazyLruItem[V]{value: value, expires: time.Now().Add(x.successTTL).UnixMilli()}) } -func (x *LayLRU[K, V]) SetHas(key K, value V) bool { +func (x *LazyLRU[K, V]) SetHas(key K, value V) bool { x.lock.Lock() defer x.lock.Unlock() if x.core.Contains(key) { - x.core.Add(key, &layLruItem[V]{value: value, expires: time.Now().Add(x.successTTL).UnixMilli()}) + x.core.Add(key, &lazyLruItem[V]{value: value, expires: time.Now().Add(x.successTTL).UnixMilli()}) return true } return false } -func (x *LayLRU[K, V]) Del(key K) bool { +func (x *LazyLRU[K, V]) Del(key K) bool { x.lock.Lock() ok := x.core.Remove(key) x.lock.Unlock() @@ -179,6 +185,6 @@ func (x *LayLRU[K, V]) Del(key K) bool { return ok } -func (x *LayLRU[K, V]) Stop() { +func (x *LazyLRU[K, V]) Stop() { }