1
|
|
|
// Package lrucache implements LRU (Least Recently Used) node |
2
|
|
|
package lrucache |
3
|
|
|
|
4
|
|
|
import ( |
5
|
|
|
"encoding/json" |
6
|
|
|
"github.com/arazmj/gerdu/cache" |
7
|
|
|
"github.com/arazmj/gerdu/dlinklist" |
8
|
|
|
"github.com/arazmj/gerdu/metrics" |
9
|
|
|
"github.com/hashicorp/raft" |
10
|
|
|
"github.com/inhies/go-bytesize" |
11
|
|
|
"io" |
12
|
|
|
"sync" |
13
|
|
|
) |
14
|
|
|
|
15
|
|
|
//LRUCache data structure |
16
|
|
|
type LRUCache struct { |
17
|
|
|
sync.RWMutex |
18
|
|
|
cache.UnImplementedCache |
19
|
|
|
node map[string]*dlinklist.Node |
20
|
|
|
linklist *dlinklist.DLinkedList |
21
|
|
|
capacity bytesize.ByteSize |
22
|
|
|
size bytesize.ByteSize |
23
|
|
|
} |
24
|
|
|
|
25
|
|
|
// NewCache LRUCache constructor |
26
|
|
|
func NewCache(capacity bytesize.ByteSize) *LRUCache { |
27
|
|
|
l := &LRUCache{ |
28
|
|
|
RWMutex: sync.RWMutex{}, |
29
|
|
|
node: map[string]*dlinklist.Node{}, |
30
|
|
|
linklist: dlinklist.NewLinkedList(), |
31
|
|
|
capacity: capacity, |
32
|
|
|
size: 0, |
33
|
|
|
} |
34
|
|
|
return l |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
// Get returns the value for the key |
38
|
|
|
func (c *LRUCache) Get(key string) (value string, ok bool) { |
39
|
|
|
defer c.Unlock() |
40
|
|
|
c.Lock() |
41
|
|
|
if value, ok := c.node[key]; ok { |
42
|
|
|
metrics.Hits.Inc() |
43
|
|
|
node := value |
44
|
|
|
c.linklist.RemoveNode(node) |
45
|
|
|
c.linklist.AddNode(node) |
46
|
|
|
return node.Value, true |
47
|
|
|
} |
48
|
|
|
metrics.Miss.Inc() |
49
|
|
|
return "", false |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
// applyPut updates or insert a new entry, evicts the old entry |
53
|
|
|
// if node size is larger than capacity |
54
|
|
|
func (c *LRUCache) Put(key string, value string) (created bool) { |
55
|
|
|
defer c.Unlock() |
56
|
|
|
c.Lock() |
57
|
|
|
if v, ok := c.node[key]; ok { |
58
|
|
|
node := v |
59
|
|
|
c.linklist.RemoveNode(node) |
60
|
|
|
c.linklist.AddNode(node) |
61
|
|
|
node.Value = value |
62
|
|
|
created = false |
63
|
|
|
} else { |
64
|
|
|
node := &dlinklist.Node{Key: key, Value: value} |
65
|
|
|
c.linklist.AddNode(node) |
66
|
|
|
c.node[key] = node |
67
|
|
|
metrics.Adds.Inc() |
68
|
|
|
c.size += bytesize.ByteSize(len(value)) |
69
|
|
|
for c.size > c.capacity { |
70
|
|
|
metrics.Deletes.Inc() |
71
|
|
|
tail := c.linklist.PopTail() |
72
|
|
|
c.size -= bytesize.ByteSize(len(tail.Value)) |
73
|
|
|
delete(c.node, tail.Key) |
74
|
|
|
} |
75
|
|
|
created = true |
76
|
|
|
} |
77
|
|
|
return created |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
//applyDelete the key from the node |
81
|
|
|
func (c *LRUCache) Delete(key string) (ok bool) { |
82
|
|
|
if node, ok := c.node[key]; ok { |
83
|
|
|
metrics.Deletes.Inc() |
84
|
|
|
c.linklist.RemoveNode(node) |
85
|
|
|
delete(c.node, key) |
86
|
|
|
} else { |
87
|
|
|
return false |
88
|
|
|
} |
89
|
|
|
return true |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
func (c *LRUCache) Snapshot() (raft.FSMSnapshot, error) { |
93
|
|
|
c.RLock() |
94
|
|
|
defer c.RUnlock() |
95
|
|
|
|
96
|
|
|
o := make(map[string]string) |
97
|
|
|
|
98
|
|
|
for k, v := range c.node { |
99
|
|
|
o[k] = v.Value |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
return &fsmSnapshot{store: o}, nil |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
func (c *LRUCache) Restore(closer io.ReadCloser) error { |
106
|
|
|
o := make(map[string]string) |
107
|
|
|
if err := json.NewDecoder(closer).Decode(&o); err != nil { |
108
|
|
|
return err |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
// Set the state from the snapshot, no lock required according to |
112
|
|
|
// Hashicorp docs. |
113
|
|
|
for k, v := range o { |
114
|
|
|
c.Put(k, v) |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
return nil |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
type fsmSnapshot struct { |
121
|
|
|
store map[string]string |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
func (f *fsmSnapshot) Persist(sink raft.SnapshotSink) error { |
125
|
|
|
err := func() error { |
126
|
|
|
// Encode data. |
127
|
|
|
b, err := json.Marshal(f.store) |
128
|
|
|
if err != nil { |
129
|
|
|
return err |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
// Write data to sink. |
133
|
|
|
if _, err := sink.Write(b); err != nil { |
134
|
|
|
return err |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
// Close the sink. |
138
|
|
|
return sink.Close() |
139
|
|
|
}() |
140
|
|
|
|
141
|
|
|
if err != nil { |
142
|
|
|
sink.Cancel() |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
return err |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
func (f *fsmSnapshot) Release() {} |
149
|
|
|
|