2021-11-19 13:21:46 +01:00
|
|
|
// Package cache defines an in-memory key-value store.
|
|
|
|
//
|
2025-01-17 10:33:39 +01:00
|
|
|
// It supports exîration dates and can store arbitrary values of any type.
|
2021-11-19 13:21:46 +01:00
|
|
|
// Keys must be strings.
|
2025-01-18 00:55:00 +01:00
|
|
|
//
|
|
|
|
// Cache values are safe to share between goroutines.
|
2021-11-19 13:21:46 +01:00
|
|
|
package cache
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2025-01-18 00:55:00 +01:00
|
|
|
type entry[V any] struct {
|
2025-01-17 10:33:39 +01:00
|
|
|
expirationDate *time.Time
|
2025-01-18 00:55:00 +01:00
|
|
|
value V
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e entry[V]) isExpired() bool {
|
|
|
|
return e.expirationDate != nil && e.expirationDate.Before(time.Now())
|
2021-11-19 13:21:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Cache is an in-memory key-value store.
|
2025-01-18 00:55:00 +01:00
|
|
|
type Cache[K comparable, V any] struct {
|
|
|
|
data map[K]entry[V]
|
2025-01-18 01:37:41 +01:00
|
|
|
mu sync.RWMutex
|
2021-11-19 13:21:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// New instantiate a new cache.
|
2025-01-18 00:55:00 +01:00
|
|
|
func New[K comparable, V any]() *Cache[K, V] {
|
|
|
|
return &Cache[K, V]{
|
|
|
|
data: make(map[K]entry[V]),
|
2021-11-19 13:21:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Put stores a value in the cache under the given key.
|
2025-01-18 00:55:00 +01:00
|
|
|
func (c *Cache[K, V]) Put(key K, val V) {
|
2021-11-19 13:21:46 +01:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
2025-01-18 00:55:00 +01:00
|
|
|
c.data[key] = entry[V]{nil, val}
|
2021-11-19 13:21:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// PutTTL stores a value in the cache under the given key. The value will
|
|
|
|
// be expired after the given ttl.
|
|
|
|
//
|
|
|
|
// A 0 ttl value disables the expiration of the value.
|
2025-01-18 00:55:00 +01:00
|
|
|
func (c *Cache[K, V]) PutTTL(key K, val V, ttl time.Duration) {
|
2021-11-19 13:21:46 +01:00
|
|
|
if ttl == 0 {
|
2025-01-18 00:55:00 +01:00
|
|
|
c.Put(key, val)
|
|
|
|
|
|
|
|
return
|
2021-11-19 13:21:46 +01:00
|
|
|
}
|
|
|
|
|
2025-01-18 00:55:00 +01:00
|
|
|
exp := time.Now().Add(ttl)
|
2021-11-19 13:21:46 +01:00
|
|
|
|
2025-01-18 01:37:41 +01:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
2025-01-18 00:55:00 +01:00
|
|
|
c.data[key] = entry[V]{&exp, val}
|
2021-11-19 13:21:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get returns the value asspciated with the given key.
|
|
|
|
// The second return values indicates if the cache hs been hit or not.
|
2025-01-18 00:55:00 +01:00
|
|
|
func (c *Cache[K, V]) Get(key K) (V, bool) {
|
2021-11-19 13:21:46 +01:00
|
|
|
c.mu.RLock()
|
2025-01-18 00:55:00 +01:00
|
|
|
entry, ok := c.data[key]
|
2021-11-19 13:21:46 +01:00
|
|
|
c.mu.RUnlock()
|
|
|
|
|
|
|
|
if !ok {
|
2025-01-18 00:55:00 +01:00
|
|
|
var t V
|
|
|
|
|
|
|
|
return t, false
|
2021-11-19 13:21:46 +01:00
|
|
|
}
|
|
|
|
|
2025-01-18 00:55:00 +01:00
|
|
|
if entry.isExpired() {
|
2021-11-19 13:21:46 +01:00
|
|
|
c.Del(key)
|
|
|
|
|
2025-01-18 00:55:00 +01:00
|
|
|
var t V
|
|
|
|
|
|
|
|
return t, false
|
2021-11-19 13:21:46 +01:00
|
|
|
}
|
|
|
|
|
2025-01-18 00:55:00 +01:00
|
|
|
return entry.value, ok
|
2021-11-19 13:21:46 +01:00
|
|
|
}
|
|
|
|
|
2025-01-18 00:55:00 +01:00
|
|
|
// Del deletes the entry for the given key.
|
2021-11-19 13:21:46 +01:00
|
|
|
// It does not fail if the key does not exist.
|
2025-01-18 00:55:00 +01:00
|
|
|
func (c *Cache[K, V]) Del(key K) {
|
2021-11-19 13:21:46 +01:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
delete(c.data, key)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Count returns the total number of entries in the cache (vamid and expired).
|
2025-01-18 00:55:00 +01:00
|
|
|
func (c *Cache[K, V]) Count() int {
|
2021-11-19 13:21:46 +01:00
|
|
|
c.mu.RLock()
|
|
|
|
defer c.mu.RUnlock()
|
|
|
|
|
|
|
|
return len(c.data)
|
|
|
|
}
|