Skip to content

Commit c91a67e

Browse files
committed
LRU implemented
1 parent 5edab73 commit c91a67e

File tree

3 files changed

+83
-19
lines changed

3 files changed

+83
-19
lines changed

keyVal.go

+79-15
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,92 @@
11
package main
22

3-
import "sync"
3+
import (
4+
"container/list"
5+
"sync"
6+
"time"
7+
)
48

59
type KeyVal struct {
6-
mu sync.RWMutex
7-
data map[string][]byte
10+
mu sync.RWMutex
11+
data map[string][]byte
12+
lruList *list.List
13+
keyToElement map[string]*list.Element
14+
memoryLimit int64
15+
currentSize int64
816
}
917

10-
func NewKeyVal() *KeyVal {
11-
return &KeyVal{
12-
data: map[string][]byte{},
18+
type lruItem struct {
19+
key string
20+
value []byte
21+
}
22+
23+
// memoryLimit should be in MB
24+
func NewKeyVal(memoryLimit int64) *KeyVal {
25+
memoryLimit = memoryLimit * 1024 * 1024 //Size in mb
26+
kv := &KeyVal{
27+
data: make(map[string][]byte),
28+
lruList: list.New(),
29+
keyToElement: make(map[string]*list.Element),
30+
memoryLimit: memoryLimit,
31+
currentSize: 0,
1332
}
33+
go kv.startEviction() // Start the background eviction goroutine
34+
return kv
1435
}
36+
func (kv *KeyVal) Set(key, val []byte) error {
37+
kv.mu.Lock()
38+
defer kv.mu.Unlock()
39+
40+
keyStr := string(key)
1541

16-
func (KeyVal *KeyVal) Set(key, val []byte) error {
17-
KeyVal.mu.Lock()
18-
defer KeyVal.mu.Unlock()
19-
KeyVal.data[string(key)] = []byte(val)
42+
if existingElem, exists := kv.keyToElement[keyStr]; exists {
43+
existingItem := existingElem.Value.(*lruItem)
44+
kv.currentSize -= int64(len(existingItem.value))
45+
existingItem.value = val
46+
kv.currentSize += int64(len(val))
47+
kv.lruList.MoveToFront(existingElem)
48+
} else {
49+
item := &lruItem{key: keyStr, value: val}
50+
element := kv.lruList.PushFront(item)
51+
kv.keyToElement[keyStr] = element
52+
kv.data[keyStr] = val
53+
kv.currentSize += int64(len(val))
54+
}
55+
kv.evictIfNeeded()
2056
return nil
2157
}
2258

23-
func (KeyVal *KeyVal) Get(key []byte) ([]byte, bool) {
24-
KeyVal.mu.RLock()
25-
defer KeyVal.mu.RUnlock()
26-
val, ok := KeyVal.data[string(key)]
27-
return val, ok
59+
func (kv *KeyVal) Get(key []byte) ([]byte, bool) {
60+
kv.mu.Lock()
61+
defer kv.mu.Unlock()
62+
63+
keyStr := string(key)
64+
if element, exists := kv.keyToElement[keyStr]; exists {
65+
kv.lruList.MoveToFront(element)
66+
return kv.data[keyStr], true
67+
}
68+
return nil, false
69+
}
70+
71+
func (kv *KeyVal) evictIfNeeded() {
72+
for kv.currentSize > kv.memoryLimit {
73+
lastElem := kv.lruList.Back()
74+
if lastElem == nil {
75+
break
76+
}
77+
item := lastElem.Value.(*lruItem)
78+
delete(kv.data, item.key)
79+
kv.lruList.Remove(lastElem)
80+
delete(kv.keyToElement, item.key)
81+
kv.currentSize -= int64(len(item.value))
82+
}
83+
}
84+
85+
func (kv *KeyVal) startEviction() {
86+
for {
87+
time.Sleep(10 * time.Second)
88+
kv.mu.Lock()
89+
kv.evictIfNeeded()
90+
kv.mu.Unlock()
91+
}
2892
}

keyVal_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
)
66

77
func TestKeyVal(t *testing.T) {
8-
KeyVal := NewKeyVal()
8+
KeyVal := NewKeyVal(2)
99
key := "mykey"
1010
val := "myval"
1111
err := KeyVal.Set([]byte(key), []byte(val))
@@ -14,9 +14,9 @@ func TestKeyVal(t *testing.T) {
1414
}
1515
val2, ok := KeyVal.Get([]byte(key))
1616
if !ok {
17-
t.Fatal(err)
17+
t.Fatal("CAN NOT GET THE VALUE")
1818
}
1919
if string(val2) != val {
20-
t.Fatal(err)
20+
t.Fatal("VALUE DOES NOT MATCH")
2121
}
2222
}

main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func NewServer(cfg Config) *Server {
4242
quitCh: make(chan struct{}),
4343
msgCh: make(chan Message),
4444
delPeerCh: make(chan *Peer),
45-
KeyVal: NewKeyVal(),
45+
KeyVal: NewKeyVal(10),
4646
}
4747
}
4848

0 commit comments

Comments
 (0)