vibe-proxy/backend/internal/cache/bounded_lru.go
2026-08-24 00:10:41 +02:00

83 lines
2 KiB
Go

package cache
import (
"container/list"
"sync"
)
type boundedLRUEntry[K comparable, V any] struct {
key K
value V
}
// BoundedLRU stores at most capacity values and evicts the least recently used
// value when a new key crosses the bound. The optional eviction callback runs
// after the cache lock is released.
type BoundedLRU[K comparable, V any] struct {
mu sync.Mutex
capacity int
entries map[K]*list.Element
order *list.List
onEvict func(K, V)
}
func NewBoundedLRU[K comparable, V any](capacity int, onEvict func(K, V)) *BoundedLRU[K, V] {
if capacity < 1 {
capacity = 1
}
return &BoundedLRU[K, V]{
capacity: capacity,
entries: make(map[K]*list.Element, capacity),
order: list.New(),
onEvict: onEvict,
}
}
// GetOrAdd returns the cached value or creates and stores one while holding the
// cache lock. The create function must not call back into this cache.
func (cache *BoundedLRU[K, V]) GetOrAdd(key K, create func() V) V {
cache.mu.Lock()
if element, ok := cache.entries[key]; ok {
cache.order.MoveToFront(element)
value := element.Value.(boundedLRUEntry[K, V]).value
cache.mu.Unlock()
return value
}
value := create()
element := cache.order.PushFront(boundedLRUEntry[K, V]{key: key, value: value})
cache.entries[key] = element
var evicted boundedLRUEntry[K, V]
didEvict := false
if cache.order.Len() > cache.capacity {
oldest := cache.order.Back()
evicted = oldest.Value.(boundedLRUEntry[K, V])
delete(cache.entries, evicted.key)
cache.order.Remove(oldest)
didEvict = true
}
cache.mu.Unlock()
if didEvict && cache.onEvict != nil {
cache.onEvict(evicted.key, evicted.value)
}
return value
}
func (cache *BoundedLRU[K, V]) Get(key K) (V, bool) {
cache.mu.Lock()
defer cache.mu.Unlock()
if element, ok := cache.entries[key]; ok {
cache.order.MoveToFront(element)
return element.Value.(boundedLRUEntry[K, V]).value, true
}
var zero V
return zero, false
}
func (cache *BoundedLRU[K, V]) Len() int {
cache.mu.Lock()
defer cache.mu.Unlock()
return len(cache.entries)
}