2024-01-15 11:42:18 +08:00

86 lines
1.8 KiB
Go

package local
import (
"github.com/hashicorp/golang-lru/v2/simplelru"
"sync"
"time"
)
type inertiaLruItem[V any] struct {
lock sync.Mutex
expires int64
err error
value V
}
func NewInertiaLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) *InertiaLRU[K, V] {
var cb simplelru.EvictCallback[K, *inertiaLruItem[V]]
if onEvict != nil {
cb = func(key K, value *inertiaLruItem[V]) {
onEvict(key, value.value)
}
}
core, err := simplelru.NewLRU[K, *inertiaLruItem[V]](size, cb)
if err != nil {
panic(err)
}
return &InertiaLRU[K, V]{
core: core,
successTTL: successTTL,
failedTTL: failedTTL,
target: target,
}
}
type InertiaLRU[K comparable, V any] struct {
lock sync.Mutex
core *simplelru.LRU[K, *inertiaLruItem[V]]
successTTL time.Duration
failedTTL time.Duration
target Target
}
func (x *InertiaLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) {
x.lock.Lock()
v, ok := x.core.Get(key)
if ok {
x.lock.Unlock()
v.lock.Lock()
expires, value, err := v.expires, v.value, v.err
if expires != 0 && expires > time.Now().UnixMilli() {
v.lock.Unlock()
x.target.IncrGetHit()
return value, err
}
} else {
v = &inertiaLruItem[V]{}
x.core.Add(key, v)
v.lock.Lock()
x.lock.Unlock()
}
defer v.lock.Unlock()
if v.expires > time.Now().UnixMilli() {
return v.value, v.err
}
v.value, v.err = fetch()
if v.err == nil {
v.expires = time.Now().Add(x.successTTL).UnixMilli()
x.target.IncrGetSuccess()
} else {
v.expires = time.Now().Add(x.failedTTL).UnixMilli()
x.target.IncrGetFailed()
}
return v.value, v.err
}
func (x *InertiaLRU[K, V]) Del(key K) bool {
x.lock.Lock()
ok := x.core.Remove(key)
x.lock.Unlock()
return ok
}
func (x *InertiaLRU[K, V]) Stop() {
}