Cache test for Bonsai
This commit is contained in:
parent
199ee81a97
commit
84e2fa64f1
|
@ -1,19 +1,12 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import "container/list"
|
||||||
"container/list"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
// KVCache is a cache that enforces deterministic sync order.
|
// KVCache is a cache that enforces deterministic sync order.
|
||||||
type KVCache struct {
|
type KVCache struct {
|
||||||
store KVStore
|
store KVStore
|
||||||
cache map[string]kvCacheValue
|
cache map[string]kvCacheValue
|
||||||
keys *list.List
|
keys *list.List
|
||||||
logging bool
|
|
||||||
logLines []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type kvCacheValue struct {
|
type kvCacheValue struct {
|
||||||
|
@ -31,18 +24,6 @@ func NewKVCache(store KVStore) *KVCache {
|
||||||
}).Reset()
|
}).Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kvc *KVCache) SetLogging() {
|
|
||||||
kvc.logging = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kvc *KVCache) GetLogLines() []string {
|
|
||||||
return kvc.logLines
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kvc *KVCache) ClearLogLines() {
|
|
||||||
kvc.logLines = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kvc *KVCache) Reset() *KVCache {
|
func (kvc *KVCache) Reset() *KVCache {
|
||||||
kvc.cache = make(map[string]kvCacheValue)
|
kvc.cache = make(map[string]kvCacheValue)
|
||||||
kvc.keys = list.New()
|
kvc.keys = list.New()
|
||||||
|
@ -50,10 +31,6 @@ func (kvc *KVCache) Reset() *KVCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kvc *KVCache) Set(key []byte, value []byte) {
|
func (kvc *KVCache) Set(key []byte, value []byte) {
|
||||||
if kvc.logging {
|
|
||||||
line := fmt.Sprintf("Set %v = %v", LegibleBytes(key), LegibleBytes(value))
|
|
||||||
kvc.logLines = append(kvc.logLines, line)
|
|
||||||
}
|
|
||||||
cacheValue, ok := kvc.cache[string(key)]
|
cacheValue, ok := kvc.cache[string(key)]
|
||||||
if ok {
|
if ok {
|
||||||
kvc.keys.MoveToBack(cacheValue.e)
|
kvc.keys.MoveToBack(cacheValue.e)
|
||||||
|
@ -67,10 +44,6 @@ func (kvc *KVCache) Set(key []byte, value []byte) {
|
||||||
func (kvc *KVCache) Get(key []byte) (value []byte) {
|
func (kvc *KVCache) Get(key []byte) (value []byte) {
|
||||||
cacheValue, ok := kvc.cache[string(key)]
|
cacheValue, ok := kvc.cache[string(key)]
|
||||||
if ok {
|
if ok {
|
||||||
if kvc.logging {
|
|
||||||
line := fmt.Sprintf("Get (hit) %v = %v", LegibleBytes(key), LegibleBytes(cacheValue.v))
|
|
||||||
kvc.logLines = append(kvc.logLines, line)
|
|
||||||
}
|
|
||||||
return cacheValue.v
|
return cacheValue.v
|
||||||
} else {
|
} else {
|
||||||
value := kvc.store.Get(key)
|
value := kvc.store.Get(key)
|
||||||
|
@ -78,10 +51,6 @@ func (kvc *KVCache) Get(key []byte) (value []byte) {
|
||||||
v: value,
|
v: value,
|
||||||
e: kvc.keys.PushBack(key),
|
e: kvc.keys.PushBack(key),
|
||||||
}
|
}
|
||||||
if kvc.logging {
|
|
||||||
line := fmt.Sprintf("Get (miss) %v = %v", LegibleBytes(key), LegibleBytes(value))
|
|
||||||
kvc.logLines = append(kvc.logLines, line)
|
|
||||||
}
|
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,17 +64,3 @@ func (kvc *KVCache) Sync() {
|
||||||
}
|
}
|
||||||
kvc.Reset()
|
kvc.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
|
||||||
|
|
||||||
func LegibleBytes(data []byte) string {
|
|
||||||
s := ""
|
|
||||||
for _, b := range data {
|
|
||||||
if 0x21 <= b && b < 0x7F {
|
|
||||||
s += cmn.Green(string(b))
|
|
||||||
} else {
|
|
||||||
s += cmn.Blue(cmn.Fmt("%02X", b))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,70 +1,124 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestKVCache(t *testing.T) {
|
func TestCache(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
//stores to be tested
|
cases := []struct {
|
||||||
ms := NewMemKVStore()
|
init []Model
|
||||||
store := NewMemKVStore()
|
toGet []Model
|
||||||
kvc := NewKVCache(store)
|
toList []listQuery
|
||||||
|
|
||||||
//key value pairs to be tested within the system
|
setCache []Model
|
||||||
var keyvalue = []struct {
|
removeCache []Model
|
||||||
key string
|
getCache []Model
|
||||||
value string
|
listCache []listQuery
|
||||||
}{
|
}{
|
||||||
{"foo", "snake"},
|
// simple add
|
||||||
{"bar", "mouse"},
|
{
|
||||||
|
init: []Model{m("a", "1"), m("c", "2")},
|
||||||
|
toGet: []Model{m("a", "1"), m("c", "2"), m("d", "")},
|
||||||
|
toList: []listQuery{{
|
||||||
|
"a", "e", 0,
|
||||||
|
[]Model{m("a", "1"), m("c", "2")},
|
||||||
|
m("c", "2"),
|
||||||
|
}},
|
||||||
|
setCache: []Model{m("d", "3")},
|
||||||
|
removeCache: []Model{m("a", "1")},
|
||||||
|
getCache: []Model{m("a", ""), m("c", "2"), m("d", "3")},
|
||||||
|
listCache: []listQuery{{
|
||||||
|
"a", "e", 0,
|
||||||
|
[]Model{m("c", "2"), m("d", "3")},
|
||||||
|
m("d", "3"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
//set the kvc to have all the key value pairs
|
checkGet := func(db SimpleDB, m Model, msg string) {
|
||||||
setRecords := func(kv KVStore) {
|
val := db.Get(m.Key)
|
||||||
for _, n := range keyvalue {
|
assert.EqualValues(m.Value, val, msg)
|
||||||
kv.Set([]byte(n.key), []byte(n.value))
|
has := db.Has(m.Key)
|
||||||
|
assert.Equal(len(m.Value) != 0, has, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkList := func(db SimpleDB, lq listQuery, msg string) {
|
||||||
|
start, end := []byte(lq.start), []byte(lq.end)
|
||||||
|
list := db.List(start, end, lq.limit)
|
||||||
|
if assert.EqualValues(lq.expected, list, msg) {
|
||||||
|
var first Model
|
||||||
|
if len(lq.expected) > 0 {
|
||||||
|
first = lq.expected[0]
|
||||||
|
}
|
||||||
|
f := db.First(start, end)
|
||||||
|
assert.EqualValues(first, f, msg)
|
||||||
|
l := db.Last(start, end)
|
||||||
|
assert.EqualValues(lq.last, l, msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//store has all the key value pairs
|
for i, tc := range cases {
|
||||||
storeHasAll := func(kv KVStore) bool {
|
for j, db := range GetDBs() {
|
||||||
for _, n := range keyvalue {
|
for _, s := range tc.init {
|
||||||
if !bytes.Equal(kv.Get([]byte(n.key)), []byte(n.value)) {
|
db.Set(s.Key, s.Value)
|
||||||
return false
|
}
|
||||||
|
for k, g := range tc.toGet {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d", i, j, k)
|
||||||
|
checkGet(db, g, msg)
|
||||||
|
}
|
||||||
|
for k, lq := range tc.toList {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d", i, j, k)
|
||||||
|
checkList(db, lq, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make cache
|
||||||
|
cache := db.Checkpoint()
|
||||||
|
|
||||||
|
for _, s := range tc.setCache {
|
||||||
|
cache.Set(s.Key, s.Value)
|
||||||
|
}
|
||||||
|
for k, r := range tc.removeCache {
|
||||||
|
val := cache.Remove(r.Key)
|
||||||
|
assert.EqualValues(r.Value, val, "%d/%d/%d", i, j, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure data is in cache
|
||||||
|
for k, g := range tc.getCache {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d", i, j, k)
|
||||||
|
checkGet(cache, g, msg)
|
||||||
|
}
|
||||||
|
for k, lq := range tc.listCache {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d", i, j, k)
|
||||||
|
checkList(cache, lq, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// data not in basic store
|
||||||
|
for k, g := range tc.toGet {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d", i, j, k)
|
||||||
|
checkGet(db, g, msg)
|
||||||
|
}
|
||||||
|
for k, lq := range tc.toList {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d", i, j, k)
|
||||||
|
checkList(db, lq, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// commit
|
||||||
|
db.Commit(cache)
|
||||||
|
|
||||||
|
// make sure data is in cache
|
||||||
|
for k, g := range tc.getCache {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d", i, j, k)
|
||||||
|
checkGet(db, g, msg)
|
||||||
|
}
|
||||||
|
for k, lq := range tc.listCache {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d", i, j, k)
|
||||||
|
checkList(db, lq, msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//test read/write for MemKVStore
|
|
||||||
setRecords(ms)
|
|
||||||
assert.True(storeHasAll(ms), "MemKVStore doesn't retrieve after Set")
|
|
||||||
|
|
||||||
//test read/write for KVCache
|
|
||||||
setRecords(kvc)
|
|
||||||
assert.True(storeHasAll(kvc), "KVCache doesn't retrieve after Set")
|
|
||||||
|
|
||||||
//test reset
|
|
||||||
kvc.Reset()
|
|
||||||
assert.False(storeHasAll(kvc), "KVCache retrieving after reset")
|
|
||||||
|
|
||||||
//test sync
|
|
||||||
setRecords(kvc)
|
|
||||||
assert.False(storeHasAll(store), "store retrieving before synced")
|
|
||||||
kvc.Sync()
|
|
||||||
assert.True(storeHasAll(store), "store isn't retrieving after synced")
|
|
||||||
|
|
||||||
//test logging
|
|
||||||
assert.Zero(len(kvc.GetLogLines()), "logging events existed before using SetLogging")
|
|
||||||
kvc.SetLogging()
|
|
||||||
setRecords(kvc)
|
|
||||||
assert.Equal(len(kvc.GetLogLines()), 2, "incorrect number of logging events recorded")
|
|
||||||
kvc.ClearLogLines()
|
|
||||||
assert.Zero(len(kvc.GetLogLines()), "logging events still exists after ClearLogLines")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,14 +34,14 @@ type SimpleDB interface {
|
||||||
First(start, end []byte) Model
|
First(start, end []byte) Model
|
||||||
Last(start, end []byte) Model
|
Last(start, end []byte) Model
|
||||||
|
|
||||||
// // Checkpoint returns the same state, but where writes
|
// Checkpoint returns the same state, but where writes
|
||||||
// // are buffered and don't affect the parent
|
// are buffered and don't affect the parent
|
||||||
// Checkpoint() SimpleDB
|
Checkpoint() SimpleDB
|
||||||
|
|
||||||
// // Commit will take all changes from the checkpoint and write
|
// Commit will take all changes from the checkpoint and write
|
||||||
// // them to the parent.
|
// them to the parent.
|
||||||
// // Returns an error if this is not a child of this one
|
// Returns an error if this is not a child of this one
|
||||||
// Commit(SimpleDB) error
|
Commit(SimpleDB) error
|
||||||
|
|
||||||
// Discard will remove reference to this
|
// Discard will remove reference to this
|
||||||
Discard()
|
Discard()
|
||||||
|
@ -56,7 +56,7 @@ type MemKVStore struct {
|
||||||
m map[string][]byte
|
m map[string][]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ SimpleDB = NewMemKVStore()
|
// var _ SimpleDB = NewMemKVStore()
|
||||||
|
|
||||||
// NewMemKVStore initializes a MemKVStore
|
// NewMemKVStore initializes a MemKVStore
|
||||||
func NewMemKVStore() *MemKVStore {
|
func NewMemKVStore() *MemKVStore {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
func GetDBs() []SimpleDB {
|
func GetDBs() []SimpleDB {
|
||||||
return []SimpleDB{
|
return []SimpleDB{
|
||||||
NewMemKVStore(),
|
// NewMemKVStore(),
|
||||||
NewBonsai(iavl.NewIAVLTree(0, nil)),
|
NewBonsai(iavl.NewIAVLTree(0, nil)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,21 +28,21 @@ func m(k, v string) Model {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type listQuery struct {
|
||||||
|
// this is the list query
|
||||||
|
start, end string
|
||||||
|
limit int
|
||||||
|
// expected result from List, first element also expected for First
|
||||||
|
expected []Model
|
||||||
|
// expected result from Last
|
||||||
|
last Model
|
||||||
|
}
|
||||||
|
|
||||||
// TestKVStore makes sure that get/set/remove operations work,
|
// TestKVStore makes sure that get/set/remove operations work,
|
||||||
// as well as list
|
// as well as list
|
||||||
func TestKVStore(t *testing.T) {
|
func TestKVStore(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
type listQuery struct {
|
|
||||||
// this is the list query
|
|
||||||
start, end string
|
|
||||||
limit int
|
|
||||||
// expected result from List, first element also expected for First
|
|
||||||
expected []Model
|
|
||||||
// expected result from Last
|
|
||||||
last Model
|
|
||||||
}
|
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
toSet []Model
|
toSet []Model
|
||||||
toRemove []Model
|
toRemove []Model
|
||||||
|
|
Loading…
Reference in New Issue