Skip to content

Commit

Permalink
chore: cleanup code
Browse files Browse the repository at this point in the history
  • Loading branch information
Larvan2 committed Dec 22, 2023
1 parent ac38173 commit 147400f
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 98 deletions.
126 changes: 66 additions & 60 deletions common/arc/arc.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,15 @@ func WithSize[K comparable, V any](maxSize int) Option[K, V] {
}

type ARC[K comparable, V any] struct {
p int
c int
t1 *list.List[*entry[K, V]]
b1 *list.List[*entry[K, V]]
t2 *list.List[*entry[K, V]]
b2 *list.List[*entry[K, V]]
mutex sync.Mutex
len int
cache map[K]*entry[K, V]
staleReturn bool
p int
c int
t1 *list.List[*entry[K, V]]
b1 *list.List[*entry[K, V]]
t2 *list.List[*entry[K, V]]
b2 *list.List[*entry[K, V]]
mutex sync.Mutex
len int
cache map[K]*entry[K, V]
}

// New returns a new Adaptive Replacement Cache (ARC).
Expand Down Expand Up @@ -74,20 +73,22 @@ func (a *ARC[K, V]) SetWithExpire(key K, value V, expires time.Time) {

func (a *ARC[K, V]) setWithExpire(key K, value V, expires time.Time) {
ent, ok := a.cache[key]
if ok != true {
if !ok {
a.len++
ent := &entry[K, V]{key: key, value: value, ghost: false, expires: expires.Unix()}
a.req(ent)
a.cache[key] = ent
} else {
if ent.ghost {
a.len++
}
ent.value = value
ent.ghost = false
ent.expires = expires.Unix()
a.req(ent)
return
}

if ent.ghost {
a.len++
}

ent.value = value
ent.ghost = false
ent.expires = expires.Unix()
a.req(ent)
}

// Get retrieves a previously via Set inserted entry.
Expand All @@ -97,25 +98,25 @@ func (a *ARC[K, V]) Get(key K) (value V, ok bool) {
defer a.mutex.Unlock()

ent, ok := a.get(key)
if ok {
return ent.value, true
if !ok {
return lo.Empty[V](), false
}
return lo.Empty[V](), false
return ent.value, true
}

func (a *ARC[K, V]) get(key K) (e *entry[K, V], ok bool) {
ent, ok := a.cache[key]
if ok {
a.req(ent)
return ent, !ent.ghost
if !ok {
return ent, false
}
return ent, false
a.req(ent)
return ent, !ent.ghost
}

// GetWithExpire returns any representation of a cached response,
// a time.Time Give expected expires,
// and a bool set to true if the key was found.
// This method will NOT check the maxAge of element and will NOT update the expires.
// This method will NOT update the expires.
func (a *ARC[K, V]) GetWithExpire(key K) (V, time.Time, bool) {
a.mutex.Lock()
defer a.mutex.Unlock()
Expand All @@ -138,10 +139,11 @@ func (a *ARC[K, V]) Len() int {
}

func (a *ARC[K, V]) req(ent *entry[K, V]) {
if ent.ll == a.t1 || ent.ll == a.t2 {
switch {
case ent.ll == a.t1 || ent.ll == a.t2:
// Case I
ent.setMRU(a.t2)
} else if ent.ll == a.b1 {
case ent.ll == a.b1:
// Case II
// Cache Miss in t1 and t2

Expand All @@ -152,16 +154,11 @@ func (a *ARC[K, V]) req(ent *entry[K, V]) {
} else {
d = a.b2.Len() / a.b1.Len()
}

// a.p = min(a.p+d, a.c)
a.p = a.p + d
if a.c < a.p {
a.p = a.c
}
a.p = min(a.p+d, a.c)

a.replace(ent)
ent.setMRU(a.t2)
} else if ent.ll == a.b2 {
case ent.ll == a.b2:
// Case III
// Cache Miss in t1 and t2

Expand All @@ -172,35 +169,30 @@ func (a *ARC[K, V]) req(ent *entry[K, V]) {
} else {
d = a.b1.Len() / a.b2.Len()
}
//a.p = max(a.p-d, 0)
a.p = a.p - d
if a.p < 0 {
a.p = 0
}
a.p = max(a.p-d, 0)

a.replace(ent)
ent.setMRU(a.t2)
} else if ent.ll == nil {
// Case IV

if a.t1.Len()+a.b1.Len() == a.c {
// Case A
if a.t1.Len() < a.c {
a.delLRU(a.b1)
a.replace(ent)
} else {
a.delLRU(a.t1)
}
} else if a.t1.Len()+a.b1.Len() < a.c {
// Case B
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() >= a.c {
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() == 2*a.c {
a.delLRU(a.b2)
}
a.replace(ent)
case ent.ll == nil && a.t1.Len()+a.b1.Len() == a.c:
// Case IV A
if a.t1.Len() < a.c {
a.delLRU(a.b1)
a.replace(ent)
} else {
a.delLRU(a.t1)
}
ent.setMRU(a.t1)
case ent.ll == nil && a.t1.Len()+a.b1.Len() < a.c:
// Case IV B
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() >= a.c {
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() == 2*a.c {
a.delLRU(a.b2)
}
a.replace(ent)
}

ent.setMRU(a.t1)
case ent.ll == nil:
// Case IV, not A nor B
ent.setMRU(a.t1)
}
}
Expand All @@ -227,3 +219,17 @@ func (a *ARC[K, V]) replace(ent *entry[K, V]) {
lru.setMRU(a.b2)
}
}

func min(a, b int) int {
if a < b {
return a
}
return b
}

func max(a int, b int) int {
if a < b {
return b
}
return a
}
114 changes: 80 additions & 34 deletions common/arc/arc_test.go
Original file line number Diff line number Diff line change
@@ -1,59 +1,105 @@
package arc

import "testing"
import (
"testing"
)

func TestBasic(t *testing.T) {
func TestInsertion(t *testing.T) {
cache := New[string, string](WithSize[string, string](3))
if cache.Len() != 0 {
t.Error("Empty cache should have length 0")
if got, want := cache.Len(), 0; got != want {
t.Errorf("empty cache.Len(): got %d want %d", cache.Len(), want)
}

cache.Set("Hello", "World")
if cache.Len() != 1 {
t.Error("Cache should have length 1")
}
const (
k1 = "Hello"
k2 = "Hallo"
k3 = "Ciao"
k4 = "Salut"

var val interface{}
var ok bool
v1 = "World"
v2 = "Worlds"
v3 = "Welt"
)

if val, ok = cache.Get("Hello"); val != "World" || ok != true {
t.Error("Didn't set \"Hello\" to \"World\"")
// Insert the first value
cache.Set(k1, v1)
if got, want := cache.Len(), 1; got != want {
t.Errorf("insertion of key #%d: cache.Len(): got %d want %d", want, cache.Len(), want)
}
if got, ok := cache.Get(k1); !ok || got != v1 {
t.Errorf("cache.Get(%v): got (%v,%t) want (%v,true)", k1, got, ok, v1)
}

cache.Set("Hello", "World1")
if cache.Len() != 1 {
t.Error("Inserting the same entry multiple times shouldn't increase cache size")
// Replace existing value for a given key
cache.Set(k1, v2)
if got, want := cache.Len(), 1; got != want {
t.Errorf("re-insertion: cache.Len(): got %d want %d", cache.Len(), want)
}
if got, ok := cache.Get(k1); !ok || got != v2 {
t.Errorf("re-insertion: cache.Get(%v): got (%v,%t) want (%v,true)", k1, got, ok, v2)
}

if val, ok = cache.Get("Hello"); val != "World1" || ok != true {
t.Error("Didn't update \"Hello\" to \"World1\"")
// Add a second different key
cache.Set(k2, v3)
if got, want := cache.Len(), 2; got != want {
t.Errorf("insertion of key #%d: cache.Len(): got %d want %d", want, cache.Len(), want)
}
if got, ok := cache.Get(k1); !ok || got != v2 {
t.Errorf("cache.Get(%v): got (%v,%t) want (%v,true)", k1, got, ok, v2)
}
if got, ok := cache.Get(k2); !ok || got != v3 {
t.Errorf("cache.Get(%v): got (%v,%t) want (%v,true)", k2, got, ok, v3)
}

cache.Set("Hallo", "Welt")
if cache.Len() != 2 {
t.Error("Inserting two different entries should result into lenght=2")
// Fill cache
cache.Set(k3, v1)
if got, want := cache.Len(), 3; got != want {
t.Errorf("insertion of key #%d: cache.Len(): got %d want %d", want, cache.Len(), want)
}

if val, ok = cache.Get("Hallo"); val != "Welt" || ok != true {
t.Error("Didn't set \"Hallo\" to \"Welt\"")
// Exceed size, this should not exceed size:
cache.Set(k4, v1)
if got, want := cache.Len(), 3; got != want {
t.Errorf("insertion of key out of size: cache.Len(): got %d want %d", cache.Len(), want)
}
}

func TestBasicReplace(t *testing.T) {
cache := New[string, string](WithSize[string, string](3))
func TestEviction(t *testing.T) {
size := 3
cache := New[string, string](WithSize[string, string](size))
if got, want := cache.Len(), 0; got != want {
t.Errorf("empty cache.Len(): got %d want %d", cache.Len(), want)
}

cache.Set("Hello", "Hallo")
cache.Set("World", "Welt")
cache.Get("World")
cache.Set("Cache", "Cache")
cache.Set("Replace", "Ersetzen")
tests := []struct {
k, v string
}{
{"k1", "v1"},
{"k2", "v2"},
{"k3", "v3"},
{"k4", "v4"},
}
for i, tt := range tests[:size] {
cache.Set(tt.k, tt.v)
if got, want := cache.Len(), i+1; got != want {
t.Errorf("insertion of key #%d: cache.Len(): got %d want %d", want, cache.Len(), want)
}
}

// Exceed size and check we don't outgrow it:
cache.Set(tests[size].k, tests[size].v)
if got := cache.Len(); got != size {
t.Errorf("insertion of overflow key #%d: cache.Len(): got %d want %d", 4, cache.Len(), size)
}

value, ok := cache.Get("World")
if !ok || value != "Welt" {
t.Error("ARC should have replaced \"Hello\"")
// Check that LRU got evicted:
if got, ok := cache.Get(tests[0].k); ok || got != "" {
t.Errorf("cache.Get(%v): got (%v,%t) want (<nil>,true)", tests[0].k, got, ok)
}

if cache.Len() != 3 {
t.Error("ARC should have a maximum size of 3")
for _, tt := range tests[1:] {
if got, ok := cache.Get(tt.k); !ok || got != tt.v {
t.Errorf("cache.Get(%v): got (%v,%t) want (%v,true)", tt.k, got, ok, tt.v)
}
}
}
8 changes: 4 additions & 4 deletions common/lru/lrucache.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func New[K comparable, V any](options ...Option[K, V]) *LruCache[K, V] {
return lc
}

// Get returns the any representation of a cached response and a bool
// Get returns any representation of a cached response and a bool
// set to true if the key was found.
func (c *LruCache[K, V]) Get(key K) (V, bool) {
c.mu.Lock()
Expand Down Expand Up @@ -110,7 +110,7 @@ func (c *LruCache[K, V]) GetOrStore(key K, constructor func() V) (V, bool) {
return value, true
}

// GetWithExpire returns the any representation of a cached response,
// GetWithExpire returns any representation of a cached response,
// a time.Time Give expected expires,
// and a bool set to true if the key was found.
// This method will NOT check the maxAge of element and will NOT update the expires.
Expand All @@ -135,7 +135,7 @@ func (c *LruCache[K, V]) Exist(key K) bool {
return ok
}

// Set stores the any representation of a response for a given key.
// Set stores any representation of a response for a given key.
func (c *LruCache[K, V]) Set(key K, value V) {
c.mu.Lock()
defer c.mu.Unlock()
Expand All @@ -151,7 +151,7 @@ func (c *LruCache[K, V]) set(key K, value V) {
c.setWithExpire(key, value, time.Unix(expires, 0))
}

// SetWithExpire stores the any representation of a response for a given key and given expires.
// SetWithExpire stores any representation of a response for a given key and given expires.
// The expires time will round to second.
func (c *LruCache[K, V]) SetWithExpire(key K, value V, expires time.Time) {
c.mu.Lock()
Expand Down

0 comments on commit 147400f

Please sign in to comment.