kvmemcache: tests complete
This commit is contained in:
parent
a6290db03b
commit
d53a869235
@ -47,7 +47,7 @@ func New(conf Config) *Cache {
|
||||
}
|
||||
|
||||
func (c *Cache) Get(key string) (interface{}, error) {
|
||||
val, err, ok := c.get(key)
|
||||
ok, val, err := c.get(key)
|
||||
if ok {
|
||||
return val, err
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package kvmemcache
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@ -48,15 +49,15 @@ func (c *Cache) assert(state State) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var TestError = errors.New("Hello")
|
||||
var ErrTest = errors.New("Hello")
|
||||
|
||||
func TestCache_Basic(t *testing.T) {
|
||||
func TestCache_basic(t *testing.T) {
|
||||
c := New(Config{
|
||||
MaxSize: 4,
|
||||
TTL: 50 * time.Millisecond,
|
||||
Src: func(key string) (interface{}, error) {
|
||||
if key == "err" {
|
||||
return nil, TestError
|
||||
return nil, ErrTest
|
||||
}
|
||||
return key, nil
|
||||
},
|
||||
@ -66,6 +67,7 @@ func TestCache_Basic(t *testing.T) {
|
||||
name string
|
||||
sleep time.Duration
|
||||
key string
|
||||
evict bool
|
||||
state State
|
||||
}
|
||||
|
||||
@ -127,17 +129,43 @@ func TestCache_Basic(t *testing.T) {
|
||||
Keys: []string{"b", "c", "d", "e"},
|
||||
Stats: Stats{Hits: 2, Misses: 6},
|
||||
},
|
||||
}, {
|
||||
name: "get err",
|
||||
key: "err",
|
||||
state: State{
|
||||
Keys: []string{"c", "d", "e", "err"},
|
||||
Stats: Stats{Hits: 2, Misses: 7},
|
||||
},
|
||||
}, {
|
||||
name: "get err again",
|
||||
key: "err",
|
||||
state: State{
|
||||
Keys: []string{"c", "d", "e", "err"},
|
||||
Stats: Stats{Hits: 3, Misses: 7},
|
||||
},
|
||||
}, {
|
||||
name: "evict c",
|
||||
key: "c",
|
||||
evict: true,
|
||||
state: State{
|
||||
Keys: []string{"d", "e", "err"},
|
||||
Stats: Stats{Hits: 3, Misses: 7},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
time.Sleep(tc.sleep)
|
||||
val, err := c.Get(tc.key)
|
||||
if tc.key == "err" && err != TestError {
|
||||
t.Fatal(tc.name, val)
|
||||
}
|
||||
if tc.key != "err" && val.(string) != tc.key {
|
||||
t.Fatal(tc.name, tc.key, val)
|
||||
if !tc.evict {
|
||||
val, err := c.Get(tc.key)
|
||||
if tc.key == "err" && err != ErrTest {
|
||||
t.Fatal(tc.name, val)
|
||||
}
|
||||
if tc.key != "err" && val.(string) != tc.key {
|
||||
t.Fatal(tc.name, tc.key, val)
|
||||
}
|
||||
} else {
|
||||
c.Evict(tc.key)
|
||||
}
|
||||
|
||||
if err := c.assert(tc.state); err != nil {
|
||||
@ -146,4 +174,34 @@ func TestCache_Basic(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Test thundering herd mitigation.
|
||||
func TestCache_thunderingHerd(t *testing.T) {
|
||||
c := New(Config{
|
||||
MaxSize: 4,
|
||||
Src: func(key string) (interface{}, error) {
|
||||
time.Sleep(time.Second)
|
||||
return key, nil
|
||||
},
|
||||
})
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
for i := 0; i < 1024; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
val, err := c.Get("a")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if val != "a" {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
stats := c.Stats()
|
||||
if stats.Hits != 1023 || stats.Misses != 1 {
|
||||
t.Fatal(stats)
|
||||
}
|
||||
}
|
||||
|
@ -30,24 +30,24 @@ func (c *Cache) evict(key string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) get(key string) (val interface{}, err error, ok bool) {
|
||||
func (c *Cache) get(key string) (ok bool, val interface{}, err error) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
li := c.cache[key]
|
||||
if li == nil {
|
||||
return nil, nil, false
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
item := li.Value.(lruItem)
|
||||
// Maybe evict.
|
||||
if c.ttl != 0 && time.Since(item.createdAt) > c.ttl {
|
||||
c.evict(key)
|
||||
return nil, nil, false
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
c.stats.Hits++
|
||||
return item.value, item.err, true
|
||||
return true, item.value, item.err
|
||||
}
|
||||
|
||||
func (c *Cache) load(key string) (interface{}, error) {
|
||||
@ -55,7 +55,7 @@ func (c *Cache) load(key string) (interface{}, error) {
|
||||
defer c.updateLock.Unlock(key)
|
||||
|
||||
// Check again in case we lost the update race.
|
||||
val, err, ok := c.get(key)
|
||||
ok, val, err := c.get(key)
|
||||
if ok {
|
||||
return val, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user