-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmem.go
110 lines (92 loc) · 1.92 KB
/
mem.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
package mem
import (
"sync"
"time"
)
type Cache[K comparable, V any] struct {
expireGetter func(key K) (V, time.Time, error)
getter func(key K) (V, error)
rotateInterval time.Duration
MaxEntries int
list List[K]
syncMap sync.Map
}
func NewCache[K comparable, V any](
getter func(key K) (V, time.Time, error)) *Cache[K, V] {
return &Cache[K, V]{
expireGetter: getter,
MaxEntries: 1000,
}
}
func NewRotateCache[K comparable, V any](rotate time.Duration,
getter func(key K) (V, error)) *Cache[K, V] {
return &Cache[K, V]{
getter: getter,
rotateInterval: rotate,
MaxEntries: 1000,
}
}
type entry[V any] struct {
value V
err error
rotateAt time.Time
expireAt time.Time
rw sync.RWMutex
}
func (c *Cache[K, V]) Get(key K) (V, error) {
now := time.Now()
v, ok := c.syncMap.Load(key)
if ok {
e := v.(*entry[V])
if e.expireAt.After(now) {
return e.value, e.err
}
}
e := &entry[V]{}
if val, ok := c.syncMap.LoadOrStore(key, e); ok {
e = val.(*entry[V])
} else {
c.list.PushFront(key)
}
c.fulfill(key, e)
c.gc(now)
return e.value, e.err
}
func (c *Cache[K, V]) fulfill(key K, e *entry[V]) {
if !e.rw.TryLock() {
e.rw.RLock() // wait for unlock
e.rw.RUnlock()
return
}
defer e.rw.Unlock()
if c.expireGetter != nil {
e.value, e.expireAt, e.err = c.expireGetter(key)
} else {
e.value, e.err = c.getter(key)
e.expireAt = time.Now().Add(c.rotateInterval)
}
if e.err == nil {
c.list.MoveToFront(&Element[K]{Value: key})
}
}
func (c *Cache[K, V]) gc(now time.Time) {
ele := c.list.Back()
if ele == nil {
return
}
if c.list.len > c.MaxEntries {
c.list.Remove(ele)
return
}
if val, ok := c.syncMap.Load(ele.Value); ok {
if val.(*entry[V]).expireAt.Before(now) {
c.syncMap.Delete(ele.Value)
c.list.Remove(ele)
c.gc(now)
}
}
}
func (c *Cache[K, V]) Remove(key K) {
c.list.Remove(&Element[K]{Value: key})
c.syncMap.Delete(key)
}