more platform stuff
This commit is contained in:
1 parent
4b9a882018
commit
fc5eefbe5e
7 files changed
+756
No files matched your search
Vendored
+87
@@ -0,0 +1,87 @@
|
||||
// Package cache provides a small, concurrency-safe TTL + LRU cache. It's a
|
||||
// generic building block for sharing an expensive computation across requests
|
||||
// (e.g. the reader shares one relevance match between /api/stream and
|
||||
// /api/facets): a hit returns the stored value, a miss recomputes. The TTL
|
||||
// bounds staleness; the capacity bounds memory.
|
||||
package cache
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type entry[K comparable, V any] struct {
|
||||
key K
|
||||
val V
|
||||
at time.Time
|
||||
}
|
||||
|
||||
// TTLLRU is a fixed-capacity cache that evicts the least-recently-used entry on
|
||||
// overflow and treats entries older than ttl as misses. The zero value is not
|
||||
// usable — construct with New. All methods are safe for concurrent use.
|
||||
type TTLLRU[K comparable, V any] struct {
|
||||
mu sync.Mutex
|
||||
ll *list.List // front = most-recently-used
|
||||
m map[K]*list.Element
|
||||
cap int
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
// New returns a cache holding up to capacity entries, each valid for ttl.
|
||||
func New[K comparable, V any](capacity int, ttl time.Duration) *TTLLRU[K, V] {
|
||||
if capacity < 1 {
|
||||
capacity = 1
|
||||
}
|
||||
return &TTLLRU[K, V]{
|
||||
ll: list.New(),
|
||||
m: make(map[K]*list.Element, capacity),
|
||||
cap: capacity,
|
||||
ttl: ttl,
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the cached value for key. The boolean is false on a miss or when
|
||||
// the entry has expired (in which case it is also evicted). A hit refreshes the
|
||||
// entry's recency.
|
||||
func (c *TTLLRU[K, V]) Get(key K) (V, bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
el, ok := c.m[key]
|
||||
if !ok {
|
||||
var zero V
|
||||
return zero, false
|
||||
}
|
||||
ent := el.Value.(*entry[K, V])
|
||||
if time.Since(ent.at) > c.ttl {
|
||||
c.ll.Remove(el)
|
||||
delete(c.m, key)
|
||||
var zero V
|
||||
return zero, false
|
||||
}
|
||||
c.ll.MoveToFront(el)
|
||||
return ent.val, true
|
||||
}
|
||||
|
||||
// Put stores val under key, refreshing its timestamp and recency, evicting the
|
||||
// least-recently-used entry if that exceeds the capacity.
|
||||
func (c *TTLLRU[K, V]) Put(key K, val V) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if el, ok := c.m[key]; ok {
|
||||
ent := el.Value.(*entry[K, V])
|
||||
ent.val = val
|
||||
ent.at = time.Now()
|
||||
c.ll.MoveToFront(el)
|
||||
return
|
||||
}
|
||||
c.m[key] = c.ll.PushFront(&entry[K, V]{key: key, val: val, at: time.Now()})
|
||||
for c.ll.Len() > c.cap {
|
||||
back := c.ll.Back()
|
||||
if back == nil {
|
||||
break
|
||||
}
|
||||
c.ll.Remove(back)
|
||||
delete(c.m, back.Value.(*entry[K, V]).key)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user