cache/cache.go

102 lines
1.9 KiB
Go

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