-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcache.go
137 lines (118 loc) · 3.25 KB
/
cache.go
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package go_memoize
import (
"sync"
"sync/atomic"
"time"
)
// zeroValue returns the zero value for any type T.
func zeroValue[T any]() T {
var zero T
return zero
}
// cacheGroup manages multiple caches with a shared ticker.
type cacheGroup struct {
now atomic.Value
ticker *time.Ticker
tickInterval time.Duration
done chan struct{}
}
// newCacheGroup creates a new cache group with a shared ticker.
func newCacheGroup() *cacheGroup {
group := &cacheGroup{
done: make(chan struct{}),
tickInterval: time.Millisecond,
}
group.now.Store(time.Now().Unix())
group.startTicker()
return group
}
// startTicker starts the ticker for the cache group.
func (g *cacheGroup) startTicker() {
g.ticker = time.NewTicker(g.tickInterval) // Initialize the ticker
go func() {
for {
select {
case <-g.ticker.C:
g.now.Store(time.Now().Unix())
case <-g.done:
g.ticker.Stop()
return
}
}
}()
}
// var cacheGroupInstance is a singleton instance of cacheGroup.
var cacheGroupInstance = newCacheGroup()
// entry represents a cache entry with a value and a timestamp.
type entry[V any] struct {
value V
timeStamp int64
}
// Cache is a generic cache with a time-to-live (TTL) for each entry.
type Cache[K comparable, V any] struct {
entries map[K]entry[V]
ttl int64
cacheGroup *cacheGroup
mu sync.RWMutex
zeroVal V
}
// NewCache creates a new cache with the specified TTL.
func NewCache[K comparable, V any](ttl int64) *Cache[K, V] {
return &Cache[K, V]{
entries: make(map[K]entry[V]),
cacheGroup: cacheGroupInstance,
ttl: ttl,
zeroVal: zeroValue[V](),
}
}
// NewCacheSized creates a new cache with the specified size and TTL.
func NewCacheSized[K comparable, V any](size int, ttl int64) *Cache[K, V] {
return &Cache[K, V]{
entries: make(map[K]entry[V], size),
cacheGroup: cacheGroupInstance,
ttl: ttl,
zeroVal: zeroValue[V](),
}
}
// NowUnix returns the current Unix timestamp from the cache group.
func (c *Cache[K, V]) NowUnix() int64 {
return c.cacheGroup.now.Load().(int64)
}
// GetOrCompute retrieves the value for the given key or computes it using the provided function if not present or expired.
func (c *Cache[K, V]) GetOrCompute(key K, computeFn func() V) V {
c.mu.RLock()
existingEntry, ok := c.entries[key]
c.mu.RUnlock()
now := c.NowUnix()
if ok && (c.ttl == 0 || now-existingEntry.timeStamp < c.ttl) {
return existingEntry.value
}
c.mu.Lock()
newVal := computeFn()
c.entries[key] = entry[V]{value: newVal, timeStamp: now}
c.mu.Unlock()
return newVal
}
// Delete removes the entry for the given key from the cache.
func (c *Cache[K, V]) Delete(key K) {
c.mu.Lock()
delete(c.entries, key)
c.mu.Unlock()
}
// Set adds or updates the value for the given key in the cache.
func (c *Cache[K, V]) Set(key K, value V) {
timeStamp := c.NowUnix()
c.mu.Lock()
c.entries[key] = entry[V]{value: value, timeStamp: timeStamp}
c.mu.Unlock()
}
// Get retrieves the value for the given key from the cache if present and not expired.
func (c *Cache[K, V]) Get(key K) (V, bool) {
c.mu.RLock()
entry, ok := c.entries[key]
c.mu.RUnlock()
if ok && (c.ttl == 0 || c.NowUnix()-entry.timeStamp < c.ttl) {
return entry.value, true
}
return c.zeroVal, false
}