/
cache.go
147 lines (123 loc) · 2.99 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
138
139
140
141
142
143
144
145
146
147
package cache
import (
"sort"
"time"
)
// T is a type for cache value
type T interface{}
// A Cache is a thread-safe store for fast item storage and retrieval
type Cache struct {
itemOps chan func(map[string]T)
expiryOps chan func(map[string]*time.Timer)
}
// New returns an empty cache
func New() *Cache {
c := &Cache{
itemOps: make(chan func(map[string]T)),
expiryOps: make(chan func(map[string]*time.Timer)),
}
go c.loopItemOps()
go c.loopExpiryOps()
return c
}
func (c *Cache) loopItemOps() {
items := map[string]T{}
for op := range c.itemOps {
op(items)
}
}
func (c *Cache) loopExpiryOps() {
expiries := map[string]*time.Timer{}
for op := range c.expiryOps {
op(expiries)
}
}
// Set will set the val into the cache at the specified key.
// If an entry already exists at the specified key, it will be overwritten.
// The options param can be used to perform logic after the entry has be inserted.
func (c *Cache) Set(key string, val T, options ...SetOption) {
c.expiryOps <- func(expiries map[string]*time.Timer) {
if timer, ok := expiries[key]; ok {
timer.Stop()
delete(expiries, key)
}
}
c.itemOps <- func(items map[string]T) {
items[key] = val
}
for _, option := range options {
option(c, key, val)
}
}
// Clear removes all entries from the cache
func (c *Cache) Clear() {
c.itemOps <- func(items map[string]T) {
for key := range items {
delete(items, key)
}
}
}
// ClearEvery clears the cache on a loop at the specified interval
func (c *Cache) ClearEvery(d time.Duration) *time.Ticker {
ticker := time.NewTicker(d)
go func() {
for range ticker.C {
c.Clear()
}
}()
return ticker
}
// Delete removes an entry from the cache at the specified key.
// If no entry exists at the specified key, no action is taken
func (c *Cache) Delete(key string) {
c.itemOps <- func(items map[string]T) {
if _, ok := items[key]; ok {
delete(items, key)
}
}
}
// Get retrieves an entry at the specified key
func (c *Cache) Get(key string) T {
result := make(chan T, 1)
c.itemOps <- func(items map[string]T) {
result <- items[key]
}
return <-result
}
// GetOK retrieves an entry at the specified key.
// Returns bool specifying if the entry exists
func (c *Cache) GetOK(key string) (T, bool) {
result := make(chan T, 1)
exists := make(chan bool, 1)
c.itemOps <- func(items map[string]T) {
v, ok := items[key]
result <- v
exists <- ok
}
return <-result, <-exists
}
// Items retrieves all entries in the cache
func (c *Cache) Items() map[string]T {
result := make(chan map[string]T, 1)
c.itemOps <- func(items map[string]T) {
cp := map[string]T{}
for key, val := range items {
cp[key] = val
}
result <- cp
}
return <-result
}
// Keys retrieves a sorted list of all keys in the cache
func (c *Cache) Keys() []string {
result := make(chan []string, 1)
c.itemOps <- func(items map[string]T) {
keys := make([]string, 0, len(items))
for k := range items {
keys = append(keys, k)
}
sort.Strings(keys)
result <- keys
}
return <-result
}