mugit/internal/cache/cache.go(view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
package cache
import (
"errors"
"sync"
"time"
)
var ErrNotFound = errors.New("not found")
type Cacher[T any] interface {
Set(key string, val T)
Get(key string) (val T, found bool)
}
type item[T any] struct {
v T
expiry time.Time
}
func (i item[T]) isExpired() bool {
return time.Now().After(i.expiry)
}
type InMemory[T any] struct {
mu sync.RWMutex
ttl time.Duration
data map[string]item[T]
}
func NewInMemory[T any](ttl time.Duration) *InMemory[T] {
c := &InMemory[T]{
data: make(map[string]item[T]),
ttl: ttl,
}
go c.clean()
return c
}
func (m *InMemory[T]) Set(key string, val T) {
m.mu.Lock()
defer m.mu.Unlock()
m.data[key] = item[T]{
v: val,
expiry: time.Now().Add(m.ttl),
}
}
func (m *InMemory[T]) Get(key string) (T, bool) {
m.mu.Lock()
defer m.mu.Unlock()
val, found := m.data[key]
if !found || val.isExpired() {
var t T
return t, false
}
return val.v, true
}
func (m *InMemory[T]) clean() {
for range time.Tick(5 * time.Second) {
m.mu.Lock()
for k, v := range m.data {
if v.isExpired() {
delete(m.data, k)
}
}
m.mu.Unlock()
}
}
|