-
Notifications
You must be signed in to change notification settings - Fork 199
/
lrucache.go
168 lines (133 loc) · 4.32 KB
/
lrucache.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package lrucache
import (
"sync"
logger "github.com/ElrondNetwork/elrond-go-logger"
"github.com/ElrondNetwork/elrond-go/storage"
"github.com/ElrondNetwork/elrond-go/storage/lrucache/capacity"
lru "github.com/hashicorp/golang-lru"
)
var _ storage.Cacher = (*lruCache)(nil)
var log = logger.GetOrCreate("storage/lrucache")
// LRUCache implements a Least Recently Used eviction cache
type lruCache struct {
cache storage.SizedLRUCacheHandler
maxsize int
mutAddedDataHandlers sync.RWMutex
mapDataHandlers map[string]func(key []byte, value interface{})
}
// NewCache creates a new LRU cache instance
func NewCache(size int) (*lruCache, error) {
cache, err := lru.New(size)
if err != nil {
return nil, err
}
c := &lruCache{
cache: &simpleLRUCacheAdapter{
LRUCacheHandler: cache,
},
maxsize: size,
mutAddedDataHandlers: sync.RWMutex{},
mapDataHandlers: make(map[string]func(key []byte, value interface{})),
}
return c, nil
}
// NewCacheWithSizeInBytes creates a new sized LRU cache instance
func NewCacheWithSizeInBytes(size int, sizeInBytes int64) (*lruCache, error) {
cache, err := capacity.NewCapacityLRU(size, sizeInBytes)
if err != nil {
return nil, err
}
c := &lruCache{
cache: cache,
maxsize: size,
mutAddedDataHandlers: sync.RWMutex{},
mapDataHandlers: make(map[string]func(key []byte, value interface{})),
}
return c, nil
}
// Clear is used to completely clear the cache.
func (c *lruCache) Clear() {
c.cache.Purge()
}
// Put adds a value to the cache. Returns true if an eviction occurred.
func (c *lruCache) Put(key []byte, value interface{}, sizeInBytes int) (evicted bool) {
evicted = c.cache.AddSized(string(key), value, int64(sizeInBytes))
c.callAddedDataHandlers(key, value)
return evicted
}
// RegisterHandler registers a new handler to be called when a new data is added
func (c *lruCache) RegisterHandler(handler func(key []byte, value interface{}), id string) {
if handler == nil {
log.Error("attempt to register a nil handler to a cacher object")
return
}
c.mutAddedDataHandlers.Lock()
c.mapDataHandlers[id] = handler
c.mutAddedDataHandlers.Unlock()
}
// UnRegisterHandler removes the handler from the list
func (c *lruCache) UnRegisterHandler(id string) {
c.mutAddedDataHandlers.Lock()
delete(c.mapDataHandlers, id)
c.mutAddedDataHandlers.Unlock()
}
// Get looks up a key's value from the cache.
func (c *lruCache) Get(key []byte) (value interface{}, ok bool) {
return c.cache.Get(string(key))
}
// Has checks if a key is in the cache, without updating the
// recent-ness or deleting it for being stale.
func (c *lruCache) Has(key []byte) bool {
return c.cache.Contains(string(key))
}
// Peek returns the key value (or undefined if not found) without updating
// the "recently used"-ness of the key.
func (c *lruCache) Peek(key []byte) (value interface{}, ok bool) {
v, ok := c.cache.Peek(string(key))
if !ok {
return nil, ok
}
return v, ok
}
// HasOrAdd checks if a key is in the cache without updating the
// recent-ness or deleting it for being stale, and if not, adds the value.
// Returns whether found and whether an eviction occurred.
func (c *lruCache) HasOrAdd(key []byte, value interface{}, sizeInBytes int) (has, added bool) {
has, _ = c.cache.AddSizedIfMissing(string(key), value, int64(sizeInBytes))
if !has {
c.callAddedDataHandlers(key, value)
}
return has, !has
}
func (c *lruCache) callAddedDataHandlers(key []byte, value interface{}) {
c.mutAddedDataHandlers.RLock()
for _, handler := range c.mapDataHandlers {
go handler(key, value)
}
c.mutAddedDataHandlers.RUnlock()
}
// Remove removes the provided key from the cache.
func (c *lruCache) Remove(key []byte) {
c.cache.Remove(string(key))
}
// Keys returns a slice of the keys in the cache, from oldest to newest.
func (c *lruCache) Keys() [][]byte {
res := c.cache.Keys()
r := make([][]byte, len(res))
for i := 0; i < len(res); i++ {
r[i] = []byte(res[i].(string))
}
return r
}
// Len returns the number of items in the cache.
func (c *lruCache) Len() int {
return c.cache.Len()
}
// MaxSize returns the maximum number of items which can be stored in cache.
func (c *lruCache) MaxSize() int {
return c.maxsize
}
// IsInterfaceNil returns true if there is no value under the interface
func (c *lruCache) IsInterfaceNil() bool {
return c == nil
}