Skip to content

Commit 24c1232

Browse files
committed
added stale status support to inMemory cache
1 parent 016b9cc commit 24c1232

File tree

5 files changed

+106
-26
lines changed

5 files changed

+106
-26
lines changed

pkg/storage/memory-bench_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func generateRandomBytes(b *testing.B) []byte {
2323
func BenchmarkLRU(b *testing.B) {
2424
maxMem := 10000.0
2525
ctx := context.Background()
26-
lru := NewLRUCache(maxMem)
26+
lru := NewLRUCache(maxMem, 200)
2727
for i := 0; i < b.N; i++ {
2828
err := lru.Store(ctx, fmt.Sprintf("key%d", i), &model.Response{
2929
Status: 200,
@@ -42,7 +42,7 @@ func BenchmarkLRU(b *testing.B) {
4242
func BenchmarkLFU(b *testing.B) {
4343
maxMem := 10000.0
4444
ctx := context.Background()
45-
lfu := NewLFUCache(maxMem)
45+
lfu := NewLFUCache(maxMem, 200)
4646
for i := 0; i < b.N; i++ {
4747
err := lfu.Store(ctx, fmt.Sprintf("key%d", i), &model.Response{
4848
Status: 200,

pkg/storage/memory-lfu.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type LFUCache struct {
1818
lookupTimeout time.Duration
1919
lists map[int]*FrequencyList
2020
cache map[string]*list.Element
21+
staleDuration int64
2122
}
2223

2324
type FrequencyList struct {
@@ -26,12 +27,13 @@ type FrequencyList struct {
2627
}
2728

2829
type LfuNode struct {
29-
parent *FrequencyList
30-
value *model.Response
31-
key string
30+
parent *FrequencyList
31+
value *model.Response
32+
timeStamp int64
33+
key string
3234
}
3335

34-
func NewLFUCache(maxSizeMB float64) *LFUCache {
36+
func NewLFUCache(maxSizeMB float64, staleInSeconds int64) *LFUCache {
3537
if maxSizeMB <= 0 {
3638
maxSizeMB = 50.0
3739
}
@@ -43,6 +45,7 @@ func NewLFUCache(maxSizeMB float64) *LFUCache {
4345
sizeMB: 0,
4446
lists: make(map[int]*FrequencyList),
4547
cache: make(map[string]*list.Element),
48+
staleDuration: staleInSeconds,
4649
}
4750
}
4851

@@ -62,7 +65,9 @@ func (lfu *LFUCache) Store(ctx context.Context, key string, value *model.Respons
6265

6366
if found {
6467
bodySizeMB -= lfu.getSize(*val.Value.(*LfuNode).value)
65-
val.Value.(*LfuNode).value = value
68+
node := val.Value.(*LfuNode)
69+
node.value = value
70+
node.timeStamp = time.Now().Unix()
6671
lfu.update(val)
6772
}
6873

@@ -84,8 +89,9 @@ func (lfu *LFUCache) Store(ctx context.Context, key string, value *model.Respons
8489

8590
if !found {
8691
node := &LfuNode{
87-
key: key,
88-
value: value,
92+
key: key,
93+
value: value,
94+
timeStamp: time.Now().Unix(),
8995
}
9096

9197
addedLfuNode := lfu.moveNode(node, 1)
@@ -108,7 +114,15 @@ func (lfu *LFUCache) LookUp(ctx context.Context, key string) (*model.Response, e
108114

109115
if val, found := lfu.cache[key]; found {
110116
lfu.update(val)
111-
proc <- val.Value.(*LfuNode).value
117+
node := val.Value.(*LfuNode)
118+
response := node.value
119+
if (time.Now().Unix() - node.timeStamp) > lfu.staleDuration {
120+
response.StaleValue = 0
121+
} else {
122+
response.StaleValue = 1
123+
}
124+
125+
proc <- response
112126

113127
return
114128
}

pkg/storage/memory-lfu_test.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
)
1010

1111
func TestLFUFunctionality(t *testing.T) {
12-
cache := NewLFUCache(0.00001)
12+
cache := NewLFUCache(0.00001, 50)
1313
assert.NotNil(t, cache)
1414

1515
ctx := context.Background()
@@ -22,6 +22,7 @@ func TestLFUFunctionality(t *testing.T) {
2222
resp, err := cache.LookUp(ctx, "1")
2323
assert.Nil(t, err)
2424
assert.Equal(t, 100, resp.Status)
25+
assert.False(t, resp.IsStale())
2526

2627
err = cache.Store(ctx, "2", &model.Response{
2728
Status: 200,
@@ -31,6 +32,7 @@ func TestLFUFunctionality(t *testing.T) {
3132
resp, err = cache.LookUp(ctx, "2")
3233
assert.Nil(t, err)
3334
assert.Equal(t, 200, resp.Status)
35+
assert.False(t, resp.IsStale())
3436

3537
err = cache.Store(ctx, "3", &model.Response{
3638
Status: 300,
@@ -40,6 +42,7 @@ func TestLFUFunctionality(t *testing.T) {
4042
resp, err = cache.LookUp(ctx, "3")
4143
assert.Nil(t, err)
4244
assert.Equal(t, 300, resp.Status)
45+
assert.False(t, resp.IsStale())
4346

4447
err = cache.Store(ctx, "1", &model.Response{
4548
Status: 200,
@@ -54,6 +57,7 @@ func TestLFUFunctionality(t *testing.T) {
5457
resp, err = cache.LookUp(ctx, "4")
5558
assert.Nil(t, err)
5659
assert.Equal(t, 400, resp.Status)
60+
assert.False(t, resp.IsStale())
5761

5862
resp, err = cache.LookUp(ctx, "2")
5963
assert.Nil(t, err)
@@ -75,10 +79,11 @@ func TestLFUFunctionality(t *testing.T) {
7579
resp, err = cache.LookUp(ctx, "5")
7680
assert.Nil(t, err)
7781
assert.Equal(t, 500, resp.Status)
82+
assert.False(t, resp.IsStale())
7883
}
7984

8085
func TestLFUFunctionality2(t *testing.T) {
81-
cache := NewLFUCache(0.00001)
86+
cache := NewLFUCache(0.00001, 50)
8287
assert.NotNil(t, cache)
8388

8489
ctx := context.Background()
@@ -91,6 +96,7 @@ func TestLFUFunctionality2(t *testing.T) {
9196
resp, err := cache.LookUp(ctx, "1")
9297
assert.Nil(t, err)
9398
assert.Equal(t, 100, resp.Status)
99+
assert.False(t, resp.IsStale())
94100

95101
err = cache.Store(ctx, "2", &model.Response{
96102
Status: 200,
@@ -100,6 +106,7 @@ func TestLFUFunctionality2(t *testing.T) {
100106
resp, err = cache.LookUp(ctx, "2")
101107
assert.Nil(t, err)
102108
assert.Equal(t, 200, resp.Status)
109+
assert.False(t, resp.IsStale())
103110

104111
err = cache.Store(ctx, "3", &model.Response{
105112
Status: 300,
@@ -109,6 +116,7 @@ func TestLFUFunctionality2(t *testing.T) {
109116
resp, err = cache.LookUp(ctx, "3")
110117
assert.Nil(t, err)
111118
assert.Equal(t, 300, resp.Status)
119+
assert.False(t, resp.IsStale())
112120

113121
// body size > capacity, nothing should happen
114122
err = cache.Store(ctx, "1", &model.Response{
@@ -119,14 +127,17 @@ func TestLFUFunctionality2(t *testing.T) {
119127
resp, err = cache.LookUp(ctx, "1")
120128
assert.Nil(t, err)
121129
assert.Equal(t, 100, resp.Status)
130+
assert.False(t, resp.IsStale())
122131

123132
resp, err = cache.LookUp(ctx, "2")
124133
assert.Nil(t, err)
125134
assert.Equal(t, 200, resp.Status)
135+
assert.False(t, resp.IsStale())
126136

127137
resp, err = cache.LookUp(ctx, "3")
128138
assert.Nil(t, err)
129139
assert.Equal(t, 300, resp.Status)
140+
assert.False(t, resp.IsStale())
130141

131142
err = cache.Store(ctx, "1", &model.Response{
132143
Status: 200,
@@ -144,11 +155,12 @@ func TestLFUFunctionality2(t *testing.T) {
144155
resp, err = cache.LookUp(ctx, "1")
145156
assert.Nil(t, err)
146157
assert.Equal(t, 200, resp.Status)
158+
assert.False(t, resp.IsStale())
147159
}
148160

149161
func TestLFUCacheCommandExeeeded(t *testing.T) {
150162
oneMegaByte := 1000000.0 / 1024 / 1024
151-
lfu := NewLFUCache(oneMegaByte)
163+
lfu := NewLFUCache(oneMegaByte, 50)
152164
ctx := context.Background()
153165

154166
lfu.lookupTimeout = 0
@@ -157,3 +169,17 @@ func TestLFUCacheCommandExeeeded(t *testing.T) {
157169
assert.Nil(t, resp)
158170
assert.EqualError(t, err, "context deadline exceeded")
159171
}
172+
173+
func TestLFUStaleStatus(t *testing.T) {
174+
oneMegaByte := 1000000.0 / 1024 / 1024
175+
lfu := NewLRUCache(oneMegaByte, 0)
176+
ctx := context.Background()
177+
178+
lfu.Store(ctx, "1", &model.Response{
179+
Status: 100,
180+
Body: []byte{1, 2, 3},
181+
})
182+
183+
resp, _ := lfu.LookUp(ctx, "1")
184+
assert.True(t, resp.IsStale())
185+
}

pkg/storage/memory-lru.go

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@ type LRUCache struct {
1717
capacityMB float64
1818
sizeMB float64
1919
lookupTimeout time.Duration
20+
staleDuration int64
2021
}
2122

22-
type Node struct {
23-
key string
24-
value *model.Response
23+
type LRUNode struct {
24+
key string
25+
value *model.Response
26+
timeStamp int64
2527
}
2628

27-
func NewLRUCache(maxSizeMB float64) *LRUCache {
29+
func NewLRUCache(maxSizeMB float64, staleInSeconds int64) *LRUCache {
2830
if maxSizeMB <= 0 {
2931
maxSizeMB = 50.0
3032
}
@@ -35,6 +37,7 @@ func NewLRUCache(maxSizeMB float64) *LRUCache {
3537
lookupTimeout: lookupTimeout,
3638
responses: list.New(),
3739
cache: make(map[string]*list.Element),
40+
staleDuration: staleInSeconds,
3841
}
3942
}
4043

@@ -51,11 +54,14 @@ func (lru *LRUCache) Store(ctx context.Context, key string, value *model.Respons
5154
}
5255

5356
if val, found := lru.cache[key]; found {
54-
bodySizeMB -= lru.getSize(*val.Value.(*Node).value)
55-
val.Value.(*Node).value = value
57+
bodySizeMB -= lru.getSize(*val.Value.(*LRUNode).value)
58+
node := val.Value.(*LRUNode)
59+
node.value = value
60+
node.timeStamp = time.Now().Unix()
61+
5662
lru.responses.MoveToFront(val)
5763
} else {
58-
element := lru.responses.PushFront(&Node{value: value, key: key})
64+
element := lru.responses.PushFront(&LRUNode{value: value, key: key, timeStamp: time.Now().Unix()})
5965
lru.cache[key] = element
6066
}
6167

@@ -64,10 +70,10 @@ func (lru *LRUCache) Store(ctx context.Context, key string, value *model.Respons
6470

6571
for lru.sizeMB > lru.capacityMB {
6672
ejectedNode = lru.responses.Back()
67-
delete(lru.cache, ejectedNode.Value.(*Node).key)
73+
delete(lru.cache, ejectedNode.Value.(*LRUNode).key)
6874
lru.responses.Remove(ejectedNode)
6975

70-
lru.sizeMB -= lru.getSize(*ejectedNode.Value.(*Node).value)
76+
lru.sizeMB -= lru.getSize(*ejectedNode.Value.(*LRUNode).value)
7177
}
7278

7379
return nil
@@ -85,7 +91,15 @@ func (lru *LRUCache) LookUp(ctx context.Context, key string) (*model.Response, e
8591

8692
if value, found := lru.cache[key]; found {
8793
lru.responses.MoveToFront(value)
88-
proc <- value.Value.(*Node).value
94+
node := value.Value.(*LRUNode)
95+
response := node.value
96+
if (time.Now().Unix() - node.timeStamp) >= lru.staleDuration {
97+
response.StaleValue = 0
98+
} else {
99+
response.StaleValue = 1
100+
}
101+
102+
proc <- response
89103

90104
return
91105
}

0 commit comments

Comments
 (0)