/
cache_ttl.go
125 lines (111 loc) · 1.98 KB
/
cache_ttl.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
package jwks
import (
"sync"
"time"
)
type item struct {
sync.RWMutex
data *JWK
expiration *time.Time
}
func (i *item) touch(d time.Duration) {
i.Lock()
exp := time.Now().Add(d)
i.expiration = &exp
i.Unlock()
}
func (i *item) expired() bool {
i.RLock()
res := true
if i.expiration != nil {
res = i.expiration.Before(time.Now())
}
i.RUnlock()
return res
}
// TTLCache is a TTL bases in-memory cache.
type TTLCache struct {
mu sync.RWMutex
ttl time.Duration
stop chan struct{}
stopOnce sync.Once
items map[string]*item
}
// NewTTLCache returns a new instance of ttl cache.
func NewTTLCache(ttl time.Duration) *TTLCache {
cache := &TTLCache{
ttl: ttl,
stop: make(chan struct{}),
items: make(map[string]*item),
}
cache.run()
return cache
}
func (tc *TTLCache) cleanup() {
tc.mu.Lock()
for key, item := range tc.items {
if item.expired() {
delete(tc.items, key)
}
}
tc.mu.Unlock()
}
func (tc *TTLCache) run() {
d := tc.ttl
if d < time.Second {
d = time.Second
}
ticker := time.NewTicker(d)
go func() {
for {
select {
case <-ticker.C:
tc.cleanup()
case <-tc.stop:
ticker.Stop()
return
}
}
}()
}
// Add item into cache.
func (tc *TTLCache) Add(key *JWK) error {
tc.mu.Lock()
item := &item{data: key}
item.touch(tc.ttl)
tc.items[key.Kid] = item
tc.mu.Unlock()
return nil
}
// Get item by key.
func (tc *TTLCache) Get(kid string) (*JWK, error) {
tc.mu.RLock()
item, ok := tc.items[kid]
if !ok || item.expired() {
tc.mu.RUnlock()
return nil, ErrCacheNotFound
}
item.touch(tc.ttl)
tc.mu.RUnlock()
return item.data, nil
}
// Stop stops TTL cache.
func (tc *TTLCache) Stop() error {
tc.stopOnce.Do(func() {
close(tc.stop)
})
return nil
}
func (tc *TTLCache) remove(kid string) error {
tc.mu.Lock()
delete(tc.items, kid)
tc.mu.Unlock()
return nil
}
// Len returns current size of cache.
func (tc *TTLCache) Len() (int, error) {
tc.mu.RLock()
n := len(tc.items)
tc.mu.RUnlock()
return n, nil
}