Skip to content

Commit

Permalink
Fix bug with LRU implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
benprew committed Aug 14, 2020
1 parent 8872b65 commit 2d3d847
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 16 deletions.
26 changes: 25 additions & 1 deletion 2-race-in-cache/check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@

package main

import "testing"
import (
"strconv"
"testing"
)

func TestMain(t *testing.T) {
cache := run()
Expand All @@ -20,3 +23,24 @@ func TestMain(t *testing.T) {
t.Errorf("Incorrect pages size %v", pagesLen)
}
}

func TestLRU(t *testing.T) {
loader := Loader{
DB: GetMockDB(),
}
cache := New(&loader)

for i := 0; i < 100; i++ {
cache.Get("Test " + strconv.Itoa(i))
}

if len(cache.cache) != 100 {
t.Errorf("cache not 100: %d", len(cache.cache))
}
cache.Get("Test 0")
cache.Get("Test 101")
if _, ok := cache.cache["Test 0"]; !ok {
t.Errorf("0 evicted incorrectly: %v", cache.cache)
}

}
37 changes: 22 additions & 15 deletions 2-race-in-cache/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@ type KeyStoreCacheLoader interface {
Load(string) string
}

type Page struct {
Key string
Value string
}

// KeyStoreCache is a LRU cache for string key-value pairs
type KeyStoreCache struct {
cache map[string]string
cache map[string]*list.Element
pages list.List
load func(string) string
}
Expand All @@ -30,28 +35,30 @@ type KeyStoreCache struct {
func New(load KeyStoreCacheLoader) *KeyStoreCache {
return &KeyStoreCache{
load: load.Load,
cache: make(map[string]string),
cache: make(map[string]*list.Element),
}
}

// Get gets the key from cache, loads it from the source if needed
func (k *KeyStoreCache) Get(key string) string {
val, ok := k.cache[key]

// Miss - load from database and save it in cache
if !ok {
val = k.load(key)
k.cache[key] = val
k.pages.PushFront(key)

if e, ok := k.cache[key]; ok {
k.pages.MoveToFront(e)
return e.Value.(Page).Value
} else {
// Miss - load from database and save it in cache
page := Page{key, k.load(key)}
// if cache is full remove the least used item
if len(k.cache) > CacheSize {
delete(k.cache, k.pages.Back().Value.(string))
k.pages.Remove(k.pages.Back())
if len(k.cache) >= CacheSize {
end := k.pages.Back()
// remove from map
delete(k.cache, end.Value.(Page).Key)
// remove from list
k.pages.Remove(end)
}
k.pages.PushFront(page)
k.cache[key] = k.pages.Front()
return page.Value
}

return val
}

// Loader implements KeyStoreLoader
Expand Down

0 comments on commit 2d3d847

Please sign in to comment.