GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 8cec0e...e8c327 )
by Amir
13:23
created

lfucache.*fsmSnapshot.Release   A

Complexity

Conditions 1

Size

Total Lines 1
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
dl 0
loc 1
rs 10
c 0
b 0
f 0
nop 0
1
// Package lfucache implements LFU (Least Frequently Used) cache
2
package lfucache
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
// LFUCache data structure
16
type LFUCache struct {
17
	sync.RWMutex
18
	cache.UnImplementedCache
19
	size     bytesize.ByteSize
20
	capacity bytesize.ByteSize
21
	node     map[string]*dlinklist.Node
22
	freq     map[int]*dlinklist.DLinkedList
23
	minFreq  int
24
}
25
26
// NewCache LFUCache constructor
27
func NewCache(capacity bytesize.ByteSize) *LFUCache {
28
	return &LFUCache{
29
		size:     0,
30
		capacity: capacity,
31
		node:     map[string]*dlinklist.Node{},
32
		freq:     map[int]*dlinklist.DLinkedList{},
33
		minFreq:  0,
34
	}
35
}
36
37
// This is a helper function that used in the following two cases:
38
//
39
// 1. when Get(key)` is called; and
40
// 2. when Put(key, value)` is called and the key exists.
41
//
42
// The common point of these two cases is that:
43
//
44
// 1. no new node comes in, and
45
// 2. the node is visited one more times -> node.freq changed ->
46
// thus the place of this node will change
47
//
48
// The logic of this function is:
49
//
50
// 1. pop the node from the old DLinkedList (with freq `f`)
51
// 2. append the node to new DLinkedList (with freq `f+1`)
52
// 3. if old DLinkedList has size 0 and minFreq is `f`,
53
// update minFreq to `f+1`
54
//
55
// All of the above operations took O(1) time.
56
func (c *LFUCache) update(node *dlinklist.Node) {
57
	freq := node.Freq
58
59
	c.freq[freq].RemoveNode(node)
60
	if v, _ := c.freq[freq]; c.minFreq == freq && v.Size() == 0 {
61
		delete(c.freq, freq)
62
		c.minFreq++
63
	}
64
65
	node.Freq++
66
	freq = node.Freq
67
	if _, ok := c.freq[freq]; !ok {
68
		c.freq[freq] = dlinklist.NewLinkedList()
69
	}
70
	c.freq[freq].AddNode(node)
71
}
72
73
// Get through checking node[key], we can get the node in O(1) time.
74
// Just performs update, then we can return the value of node.
75
func (c *LFUCache) Get(key string) (value string, ok bool) {
76
	defer c.Unlock()
77
	c.Lock()
78
79
	if _, ok := c.node[key]; !ok {
80
		metrics.Miss.Inc()
81
		return "", false
82
	}
83
84
	metrics.Hits.Inc()
85
	node := c.node[key]
86
	c.update(node)
87
	return node.Value, true
88
}
89
90
// Put If `key` already exists in self._node, we do the same operations as `get`, except
91
// updating the node.val to new value.	Otherwise
92
// 1. if the cache reaches its capacity, pop the least frequently used item. (*)
93
// 2. add new node to self._node
94
// 3. add new node to the DLinkedList with frequency 1
95
// 4. reset minFreq to 1
96
//
97
// (*) How to pop the least frequently used item? Two facts:
98
// 1. we maintain the minFreq, the minimum possible frequency in cache.
99
// 2. All cache with the same frequency are stored as a DLinkedList, with
100
// recently used order (Always append at head)
101
// 3. The tail of the DLinkedList with minFreq is the least
102
//recently used one, pop it.
103
func (c *LFUCache) Put(key, value string) (created bool) {
104
	defer c.Unlock()
105
	c.Lock()
106
	if c.capacity == 0 {
107
		return
108
	}
109
	if _, ok := c.node[key]; ok {
110
		metrics.Hits.Inc()
111
		node := c.node[key]
112
		c.update(node)
113
		node.Value = value
114
		created = false
115
	} else {
116
		c.size += bytesize.ByteSize(len(value))
117
		for c.size > c.capacity {
118
			minList, ok := c.freq[c.minFreq]
119
			metrics.Deletes.Inc()
120
			if !ok || minList.Size() == 0 {
121
				delete(c.freq, c.minFreq)
122
				c.minFreq++
123
			} else {
124
				node := minList.PopTail()
125
				freq := node.Freq
126
				if v, _ := c.freq[c.minFreq]; c.minFreq == freq && v.Size() == 0 {
127
					delete(c.freq, freq)
128
					c.minFreq++
129
				}
130
				c.size -= bytesize.ByteSize(len(node.Value))
131
				delete(c.node, node.Key)
132
			}
133
		}
134
		metrics.Adds.Inc()
135
		node := &dlinklist.Node{
136
			Key:   key,
137
			Value: value,
138
			Freq:  1,
139
		}
140
		c.node[key] = node
141
		if _, ok := c.freq[1]; !ok {
142
			c.freq[1] = dlinklist.NewLinkedList()
143
		}
144
145
		c.freq[1].AddNode(node)
146
		c.minFreq = 1
147
		created = true
148
	}
149
	return created
150
}
151
152
//Delete deletes a key from LFU cache
153
func (c *LFUCache) Delete(key string) (ok bool) {
154
	node, ok := c.node[key]
155
	if !ok {
156
		return false
157
	}
158
	freq := node.Freq
159
	if v, _ := c.freq[c.minFreq]; c.minFreq == freq && v.Size() == 0 {
160
		metrics.Deletes.Inc()
161
		delete(c.freq, freq)
162
		c.minFreq++
163
	}
164
	delete(c.node, key)
165
	return true
166
}
167
168
func (c *LFUCache) Snapshot() (raft.FSMSnapshot, error) {
169
	c.RLock()
170
	defer c.RUnlock()
171
172
	o := make(map[string]string)
173
174
	for k, v := range c.node {
175
		o[k] = v.Value
176
	}
177
178
	return &fsmSnapshot{store: o}, nil
179
180
}
181
182
func (c *LFUCache) Restore(closer io.ReadCloser) error {
183
	o := make(map[string]string)
184
	if err := json.NewDecoder(closer).Decode(&o); err != nil {
185
		return err
186
	}
187
188
	// Set the state from the snapshot, no lock required according to
189
	// Hashicorp docs.
190
	for k, v := range o {
191
		c.Put(k, v)
192
	}
193
194
	return nil
195
}
196
197
type fsmSnapshot struct {
198
	store map[string]string
199
}
200
201
func (f *fsmSnapshot) Persist(sink raft.SnapshotSink) error {
202
	err := func() error {
203
		// Encode data.
204
		b, err := json.Marshal(f.store)
205
		if err != nil {
206
			return err
207
		}
208
209
		// Write data to sink.
210
		if _, err := sink.Write(b); err != nil {
211
			return err
212
		}
213
214
		// Close the sink.
215
		return sink.Close()
216
	}()
217
218
	if err != nil {
219
		sink.Cancel()
220
	}
221
222
	return err
223
}
224
225
func (f *fsmSnapshot) Release() {}
226