forked from cheshir/ttlcache
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathttlcache.go
More file actions
127 lines (100 loc) · 2.4 KB
/
ttlcache.go
File metadata and controls
127 lines (100 loc) · 2.4 KB
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
package ttlcache
import (
"sync"
"time"
)
const defaultCapacity = 16 // Just to avoid extra allocations in most of the cases.
// Cache represents key-value storage.
type Cache struct {
done chan struct{}
mu sync.RWMutex
items storage
}
type storage map[uint64]item
type item struct {
deadline int64 // Unix nano
value interface{}
}
// New creates key-value storage.
// resolution – configures cleanup manager.
// Cleanup operation locks storage so think twice before setting it to small value.
func New(resolution time.Duration) *Cache {
c := &Cache{
done: make(chan struct{}),
items: make(map[uint64]item, defaultCapacity),
}
go cleaner(c, resolution)
return c
}
// Get returns stored record.
// The first returned variable is a stored value.
// The second one is an existence flag like in the map.
func (c *Cache) Get(key uint64) (interface{}, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
cacheItem, ok := c.items[key]
if !ok {
return nil, false
}
return cacheItem.value, true
}
// List returns all items in the storage
func (c *Cache) List() storage {
c.mu.RLock()
defer c.mu.RUnlock()
return c.items
}
// Set adds value to the cache with given ttl.
// ttl value should be a multiple of the resolution time value.
func (c *Cache) Set(key uint64, value interface{}, ttl time.Duration) {
cacheItem := item{
deadline: time.Now().UnixNano() + int64(ttl),
value: value,
}
c.mu.Lock()
defer c.mu.Unlock()
c.items[key] = cacheItem
}
// Delete removes record from storage.
func (c *Cache) Delete(key uint64) {
c.mu.Lock()
defer c.mu.Unlock()
delete(c.items, key)
}
// Clear removes all items from storage and leaves the cleanup manager running.
func (c *Cache) Clear() {
c.mu.Lock()
defer c.mu.Unlock()
c.items = make(map[uint64]item, defaultCapacity)
}
// Close stops cleanup manager and removes records from storage.
func (c *Cache) Close() {
c.mu.Lock()
defer c.mu.Unlock()
close(c.done)
c.items = nil
}
// cleanup removes outdated items from the storage.
// It triggers stop the world for the cache.
func (c *Cache) cleanup() {
c.mu.Lock()
defer c.mu.Unlock()
now := time.Now().UnixNano()
for key, item := range c.items {
if item.deadline < now {
delete(c.items, key)
}
}
}
func cleaner(c *Cache, resolution time.Duration) {
ticker := time.NewTicker(resolution)
for {
select {
case <-ticker.C:
c.cleanup()
case <-c.done:
ticker.Stop()
return
}
}
}