wip: tests and fixes for kvstore iteration
This commit is contained in:
parent
66e6225d13
commit
72b0ed004b
|
@ -3,63 +3,494 @@ package store
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
dbm "github.com/tendermint/tmlibs/db"
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func newCacheKVStore() CacheKVStore {
|
||||||
|
mem := dbm.NewMemDB()
|
||||||
|
return NewCacheKVStore(mem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyFmt(i int) []byte { return bz(cmn.Fmt("key%0.8d", i)) }
|
||||||
|
func valFmt(i int) []byte { return bz(cmn.Fmt("value%0.8d", i)) }
|
||||||
|
|
||||||
func TestCacheKVStore(t *testing.T) {
|
func TestCacheKVStore(t *testing.T) {
|
||||||
mem := dbm.NewMemDB()
|
mem := dbm.NewMemDB()
|
||||||
st := NewCacheKVStore(mem)
|
st := NewCacheKVStore(mem)
|
||||||
|
|
||||||
require.Empty(t, st.Get(bz("key1")), "Expected `key1` to be empty")
|
require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty")
|
||||||
|
|
||||||
mem.Set(bz("key1"), bz("value1"))
|
// put something in mem and in cache
|
||||||
st.Set(bz("key1"), bz("value1"))
|
mem.Set(keyFmt(1), valFmt(1))
|
||||||
require.Equal(t, bz("value1"), st.Get(bz("key1")))
|
st.Set(keyFmt(1), valFmt(1))
|
||||||
|
require.Equal(t, valFmt(1), st.Get(keyFmt(1)))
|
||||||
|
|
||||||
st.Set(bz("key1"), bz("value2"))
|
// update it in cache, shoudn't change mem
|
||||||
require.Equal(t, bz("value2"), st.Get(bz("key1")))
|
st.Set(keyFmt(1), valFmt(2))
|
||||||
require.Equal(t, bz("value1"), mem.Get(bz("key1")))
|
require.Equal(t, valFmt(2), st.Get(keyFmt(1)))
|
||||||
|
require.Equal(t, valFmt(1), mem.Get(keyFmt(1)))
|
||||||
|
|
||||||
|
// write it. should change mem
|
||||||
st.Write()
|
st.Write()
|
||||||
require.Equal(t, bz("value2"), mem.Get(bz("key1")))
|
require.Equal(t, valFmt(2), mem.Get(keyFmt(1)))
|
||||||
|
require.Equal(t, valFmt(2), st.Get(keyFmt(1)))
|
||||||
|
|
||||||
|
// more writes and checks
|
||||||
st.Write()
|
st.Write()
|
||||||
st.Write()
|
st.Write()
|
||||||
require.Equal(t, bz("value2"), mem.Get(bz("key1")))
|
require.Equal(t, valFmt(2), mem.Get(keyFmt(1)))
|
||||||
|
require.Equal(t, valFmt(2), st.Get(keyFmt(1)))
|
||||||
|
|
||||||
|
// make a new one, check it
|
||||||
st = NewCacheKVStore(mem)
|
st = NewCacheKVStore(mem)
|
||||||
st.Delete(bz("key1"))
|
require.Equal(t, valFmt(2), st.Get(keyFmt(1)))
|
||||||
require.Empty(t, st.Get(bz("key1")))
|
|
||||||
require.Equal(t, mem.Get(bz("key1")), bz("value2"))
|
|
||||||
|
|
||||||
|
// make a new one and delete - should not be removed from mem
|
||||||
|
st = NewCacheKVStore(mem)
|
||||||
|
st.Delete(keyFmt(1))
|
||||||
|
require.Empty(t, st.Get(keyFmt(1)))
|
||||||
|
require.Equal(t, mem.Get(keyFmt(1)), valFmt(2))
|
||||||
|
|
||||||
|
// Write. should now be removed from both
|
||||||
st.Write()
|
st.Write()
|
||||||
require.Empty(t, st.Get(bz("key1")), "Expected `key1` to be empty")
|
require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty")
|
||||||
require.Empty(t, mem.Get(bz("key1")), "Expected `key1` to be empty")
|
require.Empty(t, mem.Get(keyFmt(1)), "Expected `key1` to be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCacheKVStoreNested(t *testing.T) {
|
func TestCacheKVStoreNested(t *testing.T) {
|
||||||
mem := dbm.NewMemDB()
|
mem := dbm.NewMemDB()
|
||||||
st := NewCacheKVStore(mem)
|
st := NewCacheKVStore(mem)
|
||||||
st.Set(bz("key1"), bz("value1"))
|
|
||||||
|
|
||||||
require.Empty(t, mem.Get(bz("key1")))
|
// set. check its there on st and not on mem.
|
||||||
require.Equal(t, bz("value1"), st.Get(bz("key1")))
|
st.Set(keyFmt(1), valFmt(1))
|
||||||
|
require.Empty(t, mem.Get(keyFmt(1)))
|
||||||
|
require.Equal(t, valFmt(1), st.Get(keyFmt(1)))
|
||||||
|
|
||||||
|
// make a new from st and check
|
||||||
st2 := NewCacheKVStore(st)
|
st2 := NewCacheKVStore(st)
|
||||||
require.Equal(t, bz("value1"), st2.Get(bz("key1")))
|
require.Equal(t, valFmt(1), st2.Get(keyFmt(1)))
|
||||||
|
|
||||||
st2.Set(bz("key1"), bz("VALUE2"))
|
// update the value on st2, check it only effects st2
|
||||||
require.Equal(t, []byte(nil), mem.Get(bz("key1")))
|
st2.Set(keyFmt(1), valFmt(3))
|
||||||
require.Equal(t, bz("value1"), st.Get(bz("key1")))
|
require.Equal(t, []byte(nil), mem.Get(keyFmt(1)))
|
||||||
require.Equal(t, bz("VALUE2"), st2.Get(bz("key1")))
|
require.Equal(t, valFmt(1), st.Get(keyFmt(1)))
|
||||||
|
require.Equal(t, valFmt(3), st2.Get(keyFmt(1)))
|
||||||
|
|
||||||
|
// st2 writes to its parent, st. doesnt effect mem
|
||||||
st2.Write()
|
st2.Write()
|
||||||
require.Equal(t, []byte(nil), mem.Get(bz("key1")))
|
require.Equal(t, []byte(nil), mem.Get(keyFmt(1)))
|
||||||
require.Equal(t, bz("VALUE2"), st.Get(bz("key1")))
|
require.Equal(t, valFmt(3), st.Get(keyFmt(1)))
|
||||||
|
|
||||||
|
// updates mem
|
||||||
st.Write()
|
st.Write()
|
||||||
require.Equal(t, bz("VALUE2"), mem.Get(bz("key1")))
|
require.Equal(t, valFmt(3), mem.Get(keyFmt(1)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCacheKVIteratorBounds(t *testing.T) {
|
||||||
|
st := newCacheKVStore()
|
||||||
|
|
||||||
|
// set some items
|
||||||
|
nItems := 5
|
||||||
|
for i := 0; i < nItems; i++ {
|
||||||
|
st.Set(keyFmt(i), valFmt(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate over all of them
|
||||||
|
itr := st.Iterator(nil, nil)
|
||||||
|
var i = 0
|
||||||
|
for ; itr.Valid(); itr.Next() {
|
||||||
|
k, v := itr.Key(), itr.Value()
|
||||||
|
assert.Equal(t, keyFmt(i), k)
|
||||||
|
assert.Equal(t, valFmt(i), v)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
assert.Equal(t, nItems, i)
|
||||||
|
|
||||||
|
// iterate over none
|
||||||
|
itr = st.Iterator(bz("money"), nil)
|
||||||
|
i = 0
|
||||||
|
for ; itr.Valid(); itr.Next() {
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
assert.Equal(t, 0, i)
|
||||||
|
|
||||||
|
// iterate over lower
|
||||||
|
itr = st.Iterator(keyFmt(0), keyFmt(3))
|
||||||
|
i = 0
|
||||||
|
for ; itr.Valid(); itr.Next() {
|
||||||
|
k, v := itr.Key(), itr.Value()
|
||||||
|
assert.Equal(t, keyFmt(i), k)
|
||||||
|
assert.Equal(t, valFmt(i), v)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
assert.Equal(t, 3, i)
|
||||||
|
|
||||||
|
// iterate over upper
|
||||||
|
itr = st.Iterator(keyFmt(2), keyFmt(4))
|
||||||
|
i = 2
|
||||||
|
for ; itr.Valid(); itr.Next() {
|
||||||
|
k, v := itr.Key(), itr.Value()
|
||||||
|
assert.Equal(t, keyFmt(i), k)
|
||||||
|
assert.Equal(t, valFmt(i), v)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
assert.Equal(t, 4, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCacheKVMergeIteratorBasics(t *testing.T) {
|
||||||
|
st := newCacheKVStore()
|
||||||
|
|
||||||
|
// set and delete an item in the cache, iterator should be empty
|
||||||
|
k, v := keyFmt(0), valFmt(0)
|
||||||
|
st.Set(k, v)
|
||||||
|
st.Delete(k)
|
||||||
|
assertIterateDomain(t, st, 0)
|
||||||
|
|
||||||
|
// now set it and assert its there
|
||||||
|
st.Set(k, v)
|
||||||
|
assertIterateDomain(t, st, 1)
|
||||||
|
|
||||||
|
// write it and assert its there
|
||||||
|
st.Write()
|
||||||
|
assertIterateDomain(t, st, 1)
|
||||||
|
|
||||||
|
// remove it in cache and assert its not
|
||||||
|
st.Delete(k)
|
||||||
|
assertIterateDomain(t, st, 0)
|
||||||
|
|
||||||
|
// write the delete and assert its not there
|
||||||
|
st.Write()
|
||||||
|
assertIterateDomain(t, st, 0)
|
||||||
|
|
||||||
|
// add two keys and assert theyre there
|
||||||
|
k1, v1 := keyFmt(1), valFmt(1)
|
||||||
|
st.Set(k, v)
|
||||||
|
st.Set(k1, v1)
|
||||||
|
assertIterateDomain(t, st, 2)
|
||||||
|
|
||||||
|
// write it and assert theyre there
|
||||||
|
st.Write()
|
||||||
|
assertIterateDomain(t, st, 2)
|
||||||
|
|
||||||
|
// remove one in cache and assert its not
|
||||||
|
st.Delete(k1)
|
||||||
|
assertIterateDomain(t, st, 1)
|
||||||
|
|
||||||
|
// write the delete and assert its not there
|
||||||
|
st.Write()
|
||||||
|
assertIterateDomain(t, st, 1)
|
||||||
|
|
||||||
|
// delete the other key in cache and asserts its empty
|
||||||
|
st.Delete(k)
|
||||||
|
assertIterateDomain(t, st, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCacheKVMergeIteratorDeleteLast(t *testing.T) {
|
||||||
|
st := newCacheKVStore()
|
||||||
|
|
||||||
|
// set some items and write them
|
||||||
|
nItems := 5
|
||||||
|
for i := 0; i < nItems; i++ {
|
||||||
|
st.Set(keyFmt(i), valFmt(i))
|
||||||
|
}
|
||||||
|
st.Write()
|
||||||
|
|
||||||
|
// set some more items and leave dirty
|
||||||
|
for i := nItems; i < nItems*2; i++ {
|
||||||
|
st.Set(keyFmt(i), valFmt(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate over all of them
|
||||||
|
assertIterateDomain(t, st, nItems*2)
|
||||||
|
|
||||||
|
// delete them all
|
||||||
|
for i := 0; i < nItems*2; i++ {
|
||||||
|
last := nItems*2 - 1 - i
|
||||||
|
st.Delete(keyFmt(last))
|
||||||
|
assertIterateDomain(t, st, last)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCacheKVMergeIteratorDeletes(t *testing.T) {
|
||||||
|
st := newCacheKVStore()
|
||||||
|
truth := dbm.NewMemDB()
|
||||||
|
|
||||||
|
// set some items and write them
|
||||||
|
nItems := 10
|
||||||
|
for i := 0; i < nItems; i++ {
|
||||||
|
doOp(st, truth, opSet, i)
|
||||||
|
}
|
||||||
|
st.Write()
|
||||||
|
|
||||||
|
// delete every other item, starting from 0
|
||||||
|
for i := 0; i < nItems; i += 2 {
|
||||||
|
doOp(st, truth, opDel, i)
|
||||||
|
assertIterateDomainCompare(t, st, truth)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset
|
||||||
|
st = newCacheKVStore()
|
||||||
|
truth = dbm.NewMemDB()
|
||||||
|
|
||||||
|
// set some items and write them
|
||||||
|
for i := 0; i < nItems; i++ {
|
||||||
|
doOp(st, truth, opSet, i)
|
||||||
|
}
|
||||||
|
st.Write()
|
||||||
|
|
||||||
|
// delete every other item, starting from 1
|
||||||
|
for i := 1; i < nItems; i += 2 {
|
||||||
|
doOp(st, truth, opDel, i)
|
||||||
|
assertIterateDomainCompare(t, st, truth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCacheKVMergeIteratorChunks(t *testing.T) {
|
||||||
|
st := newCacheKVStore()
|
||||||
|
|
||||||
|
// Use the truth to check values on the merge iterator
|
||||||
|
truth := dbm.NewMemDB()
|
||||||
|
|
||||||
|
// sets to the parent
|
||||||
|
setRange(st, truth, 0, 20)
|
||||||
|
setRange(st, truth, 40, 60)
|
||||||
|
st.Write()
|
||||||
|
|
||||||
|
// sets to the cache
|
||||||
|
setRange(st, truth, 20, 40)
|
||||||
|
setRange(st, truth, 60, 80)
|
||||||
|
assertIterateDomainCheck(t, st, truth, []keyRange{{0, 80}})
|
||||||
|
|
||||||
|
// remove some parents and some cache
|
||||||
|
deleteRange(st, truth, 15, 25)
|
||||||
|
assertIterateDomainCheck(t, st, truth, []keyRange{{0, 15}, {25, 80}})
|
||||||
|
|
||||||
|
// remove some parents and some cache
|
||||||
|
deleteRange(st, truth, 35, 45)
|
||||||
|
assertIterateDomainCheck(t, st, truth, []keyRange{{0, 15}, {25, 35}, {45, 80}})
|
||||||
|
|
||||||
|
// write, add more to the cache, and delete some cache
|
||||||
|
st.Write()
|
||||||
|
setRange(st, truth, 38, 42)
|
||||||
|
deleteRange(st, truth, 40, 43)
|
||||||
|
assertIterateDomainCheck(t, st, truth, []keyRange{{0, 15}, {25, 35}, {38, 40}, {45, 80}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCacheKVMergeIteratorRandom(t *testing.T) {
|
||||||
|
st := newCacheKVStore()
|
||||||
|
truth := dbm.NewMemDB()
|
||||||
|
|
||||||
|
start, end := 25, 75
|
||||||
|
max := 100
|
||||||
|
setRange(st, truth, start, end)
|
||||||
|
|
||||||
|
// do an op, test the iterator
|
||||||
|
for i := 0; i < 2000; i++ {
|
||||||
|
doRandomOp(st, truth, max)
|
||||||
|
assertIterateDomainCompare(t, st, truth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------------
|
||||||
|
// do some random ops
|
||||||
|
|
||||||
|
const (
|
||||||
|
opSet = 0
|
||||||
|
opSetRange = 1
|
||||||
|
opDel = 2
|
||||||
|
opDelRange = 3
|
||||||
|
opWrite = 4
|
||||||
|
|
||||||
|
totalOps = 5 // number of possible operations
|
||||||
|
)
|
||||||
|
|
||||||
|
func randInt(n int) int {
|
||||||
|
return cmn.RandInt() % n
|
||||||
|
}
|
||||||
|
|
||||||
|
// useful for replaying a error case if we find one
|
||||||
|
func doOp(st CacheKVStore, truth dbm.DB, op int, args ...int) {
|
||||||
|
switch op {
|
||||||
|
case opSet:
|
||||||
|
k := args[0]
|
||||||
|
st.Set(keyFmt(k), valFmt(k))
|
||||||
|
truth.Set(keyFmt(k), valFmt(k))
|
||||||
|
case opSetRange:
|
||||||
|
start := args[0]
|
||||||
|
end := args[1]
|
||||||
|
setRange(st, truth, start, end)
|
||||||
|
case opDel:
|
||||||
|
k := args[0]
|
||||||
|
st.Delete(keyFmt(k))
|
||||||
|
truth.Delete(keyFmt(k))
|
||||||
|
case opDelRange:
|
||||||
|
start := args[0]
|
||||||
|
end := args[1]
|
||||||
|
deleteRange(st, truth, start, end)
|
||||||
|
case opWrite:
|
||||||
|
st.Write()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func doRandomOp(st CacheKVStore, truth dbm.DB, maxKey int) {
|
||||||
|
r := randInt(totalOps)
|
||||||
|
switch r {
|
||||||
|
case opSet:
|
||||||
|
k := randInt(maxKey)
|
||||||
|
st.Set(keyFmt(k), valFmt(k))
|
||||||
|
truth.Set(keyFmt(k), valFmt(k))
|
||||||
|
case opSetRange:
|
||||||
|
start := randInt(maxKey - 2)
|
||||||
|
end := randInt(maxKey-start) + start
|
||||||
|
setRange(st, truth, start, end)
|
||||||
|
case opDel:
|
||||||
|
k := randInt(maxKey)
|
||||||
|
st.Delete(keyFmt(k))
|
||||||
|
truth.Delete(keyFmt(k))
|
||||||
|
case opDelRange:
|
||||||
|
start := randInt(maxKey - 2)
|
||||||
|
end := randInt(maxKey-start) + start
|
||||||
|
deleteRange(st, truth, start, end)
|
||||||
|
case opWrite:
|
||||||
|
st.Write()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// iterate over whole domain
|
||||||
|
func assertIterateDomain(t *testing.T, st KVStore, expectedN int) {
|
||||||
|
itr := st.Iterator(nil, nil)
|
||||||
|
var i = 0
|
||||||
|
for ; itr.Valid(); itr.Next() {
|
||||||
|
k, v := itr.Key(), itr.Value()
|
||||||
|
assert.Equal(t, keyFmt(i), k)
|
||||||
|
assert.Equal(t, valFmt(i), v)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
assert.Equal(t, expectedN, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertIterateDomainCheck(t *testing.T, st KVStore, mem dbm.DB, r []keyRange) {
|
||||||
|
// iterate over each and check they match the other
|
||||||
|
itr := st.Iterator(nil, nil)
|
||||||
|
itr2 := mem.Iterator(nil, nil) // ground truth
|
||||||
|
|
||||||
|
krc := newKeyRangeCounter(r)
|
||||||
|
i := 0
|
||||||
|
|
||||||
|
for ; krc.valid(); krc.next() {
|
||||||
|
assert.True(t, itr.Valid())
|
||||||
|
assert.True(t, itr2.Valid())
|
||||||
|
|
||||||
|
// check the key/val matches the ground truth
|
||||||
|
k, v := itr.Key(), itr.Value()
|
||||||
|
k2, v2 := itr2.Key(), itr2.Value()
|
||||||
|
assert.Equal(t, k, k2)
|
||||||
|
assert.Equal(t, v, v2)
|
||||||
|
|
||||||
|
// check they match the counter
|
||||||
|
assert.Equal(t, k, keyFmt(krc.key()))
|
||||||
|
|
||||||
|
itr.Next()
|
||||||
|
itr2.Next()
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.False(t, itr.Valid())
|
||||||
|
assert.False(t, itr2.Valid())
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertIterateDomainCompare(t *testing.T, st KVStore, mem dbm.DB) {
|
||||||
|
// iterate over each and check they match the other
|
||||||
|
itr := st.Iterator(nil, nil)
|
||||||
|
itr2 := mem.Iterator(nil, nil) // ground truth
|
||||||
|
checkIterators(t, itr, itr2)
|
||||||
|
checkIterators(t, itr2, itr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkIterators(t *testing.T, itr, itr2 Iterator) {
|
||||||
|
for ; itr.Valid(); itr.Next() {
|
||||||
|
assert.True(t, itr2.Valid())
|
||||||
|
k, v := itr.Key(), itr.Value()
|
||||||
|
k2, v2 := itr2.Key(), itr2.Value()
|
||||||
|
assert.Equal(t, k, k2)
|
||||||
|
assert.Equal(t, v, v2)
|
||||||
|
itr2.Next()
|
||||||
|
}
|
||||||
|
assert.False(t, itr.Valid())
|
||||||
|
assert.False(t, itr2.Valid())
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------
|
||||||
|
|
||||||
|
func setRange(st KVStore, mem dbm.DB, start, end int) {
|
||||||
|
for i := start; i < end; i++ {
|
||||||
|
st.Set(keyFmt(i), valFmt(i))
|
||||||
|
mem.Set(keyFmt(i), valFmt(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteRange(st KVStore, mem dbm.DB, start, end int) {
|
||||||
|
for i := start; i < end; i++ {
|
||||||
|
st.Delete(keyFmt(i))
|
||||||
|
mem.Delete(keyFmt(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------
|
||||||
|
|
||||||
|
type keyRange struct {
|
||||||
|
start int
|
||||||
|
end int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kr keyRange) len() int {
|
||||||
|
return kr.end - kr.start
|
||||||
|
}
|
||||||
|
|
||||||
|
func newKeyRangeCounter(kr []keyRange) *keyRangeCounter {
|
||||||
|
return &keyRangeCounter{keyRanges: kr}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can iterate over this and make sure our real iterators have all the right keys
|
||||||
|
type keyRangeCounter struct {
|
||||||
|
rangeIdx int
|
||||||
|
idx int
|
||||||
|
keyRanges []keyRange
|
||||||
|
}
|
||||||
|
|
||||||
|
func (krc *keyRangeCounter) valid() bool {
|
||||||
|
maxRangeIdx := len(krc.keyRanges) - 1
|
||||||
|
maxRange := krc.keyRanges[maxRangeIdx]
|
||||||
|
|
||||||
|
// if we're not in the max range, we're valid
|
||||||
|
if krc.rangeIdx <= maxRangeIdx &&
|
||||||
|
krc.idx < maxRange.len() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (krc *keyRangeCounter) next() {
|
||||||
|
thisKeyRange := krc.keyRanges[krc.rangeIdx]
|
||||||
|
if krc.idx == thisKeyRange.len()-1 {
|
||||||
|
krc.rangeIdx += 1
|
||||||
|
krc.idx = 0
|
||||||
|
} else {
|
||||||
|
krc.idx += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (krc *keyRangeCounter) key() int {
|
||||||
|
thisKeyRange := krc.keyRanges[krc.rangeIdx]
|
||||||
|
return thisKeyRange.start + krc.idx
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------
|
||||||
|
|
||||||
func bz(s string) []byte { return []byte(s) }
|
func bz(s string) []byte { return []byte(s) }
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package store
|
package store
|
||||||
|
|
||||||
import "bytes"
|
import (
|
||||||
|
"bytes"
|
||||||
|
)
|
||||||
|
|
||||||
// cacheMergeIterator merges a parent Iterator and a cache Iterator.
|
// cacheMergeIterator merges a parent Iterator and a cache Iterator.
|
||||||
// The cache iterator may return nil keys to signal that an item
|
// The cache iterator may return nil keys to signal that an item
|
||||||
|
@ -46,15 +48,7 @@ func (iter *cacheMergeIterator) Domain() (start, end []byte) {
|
||||||
|
|
||||||
// Valid implements Iterator.
|
// Valid implements Iterator.
|
||||||
func (iter *cacheMergeIterator) Valid() bool {
|
func (iter *cacheMergeIterator) Valid() bool {
|
||||||
|
return iter.skipUntilExistsOrInvalid()
|
||||||
// If parent is valid, this is valid.
|
|
||||||
if iter.parent.Valid() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise depends on child.
|
|
||||||
iter.skipCacheDeletes(nil)
|
|
||||||
return iter.cache.Valid()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next implements Iterator
|
// Next implements Iterator
|
||||||
|
@ -76,8 +70,7 @@ func (iter *cacheMergeIterator) Next() {
|
||||||
|
|
||||||
// Both are valid. Compare keys.
|
// Both are valid. Compare keys.
|
||||||
keyP, keyC := iter.parent.Key(), iter.cache.Key()
|
keyP, keyC := iter.parent.Key(), iter.cache.Key()
|
||||||
cmp := iter.compare(keyP, keyC)
|
switch iter.compare(keyP, keyC) {
|
||||||
switch cmp {
|
|
||||||
case -1: // parent < cache
|
case -1: // parent < cache
|
||||||
iter.parent.Next()
|
iter.parent.Next()
|
||||||
case 0: // parent == cache
|
case 0: // parent == cache
|
||||||
|
@ -148,10 +141,10 @@ func (iter *cacheMergeIterator) Value() []byte {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release implements Iterator
|
// Close implements Iterator
|
||||||
func (iter *cacheMergeIterator) Release() {
|
func (iter *cacheMergeIterator) Close() {
|
||||||
iter.parent.Release()
|
iter.parent.Close()
|
||||||
iter.cache.Release()
|
iter.cache.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Like bytes.Compare but opposite if not ascending.
|
// Like bytes.Compare but opposite if not ascending.
|
||||||
|
@ -164,44 +157,46 @@ func (iter *cacheMergeIterator) compare(a, b []byte) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip all delete-items from the cache w/ `key < until`. After this function,
|
// Skip all delete-items from the cache w/ `key < until`. After this function,
|
||||||
// current item is a non-delete-item, or `until <= key`.
|
// current cache item is a non-delete-item, or `until <= key`.
|
||||||
// If the current item is not a delete item, does noting.
|
// If the current cache item is not a delete item, does nothing.
|
||||||
// If `until` is nil, there is no limit.
|
// If `until` is nil, there is no limit, and cache may end up invalid.
|
||||||
// CONTRACT: cache is valid.
|
// CONTRACT: cache is valid.
|
||||||
func (iter *cacheMergeIterator) skipCacheDeletes(until []byte) {
|
func (iter *cacheMergeIterator) skipCacheDeletes(until []byte) {
|
||||||
for (until == nil || iter.compare(iter.cache.Key(), until) < 0) &&
|
for iter.cache.Valid() &&
|
||||||
iter.cache.Value() == nil {
|
iter.cache.Value() == nil &&
|
||||||
|
(until == nil || iter.compare(iter.cache.Key(), until) < 0) {
|
||||||
|
|
||||||
iter.cache.Next()
|
iter.cache.Next()
|
||||||
if !iter.cache.Valid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast forwards cache (or parent+cache in case of deleted items) until current
|
// Fast forwards cache (or parent+cache in case of deleted items) until current
|
||||||
// item exists, or until iterator becomes invalid.
|
// item exists, or until iterator becomes invalid.
|
||||||
func (iter *cacheMergeIterator) skipUntilExistsOrInvalid() {
|
// Returns whether the iterator is valid.
|
||||||
|
func (iter *cacheMergeIterator) skipUntilExistsOrInvalid() bool {
|
||||||
for {
|
for {
|
||||||
|
|
||||||
// Invalid.
|
// If parent is invalid, fast-forward cache.
|
||||||
if !iter.Valid() {
|
if !iter.parent.Valid() {
|
||||||
return
|
iter.skipCacheDeletes(nil)
|
||||||
|
return iter.cache.Valid()
|
||||||
}
|
}
|
||||||
|
// Parent is valid.
|
||||||
|
|
||||||
// Parent and Cache items exist.
|
if !iter.cache.Valid() {
|
||||||
keyP, keyC := iter.parent.Key(), iter.cache.Key()
|
return true
|
||||||
cmp := iter.compare(keyP, keyC)
|
}
|
||||||
switch cmp {
|
// Parent is valid, cache is valid.
|
||||||
|
|
||||||
// parent < cache
|
// Compare parent and cache.
|
||||||
case -1:
|
keyP := iter.parent.Key()
|
||||||
|
keyC := iter.cache.Key()
|
||||||
|
switch iter.compare(keyP, keyC) {
|
||||||
|
|
||||||
// Parent exists.
|
case -1: // parent < cache.
|
||||||
return
|
return true
|
||||||
|
|
||||||
// parent == cache
|
case 0: // parent == cache.
|
||||||
case 0:
|
|
||||||
|
|
||||||
// Skip over if cache item is a delete.
|
// Skip over if cache item is a delete.
|
||||||
valueC := iter.cache.Value()
|
valueC := iter.cache.Value()
|
||||||
|
@ -210,11 +205,11 @@ func (iter *cacheMergeIterator) skipUntilExistsOrInvalid() {
|
||||||
iter.cache.Next()
|
iter.cache.Next()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Child shadows parent.
|
// Cache is not a delete.
|
||||||
return
|
|
||||||
|
|
||||||
// parent > cache
|
return true // cache exists.
|
||||||
case 1:
|
|
||||||
|
case 1: // cache < parent
|
||||||
|
|
||||||
// Skip over if cache item is a delete.
|
// Skip over if cache item is a delete.
|
||||||
valueC := iter.cache.Value()
|
valueC := iter.cache.Value()
|
||||||
|
@ -222,8 +217,9 @@ func (iter *cacheMergeIterator) skipUntilExistsOrInvalid() {
|
||||||
iter.skipCacheDeletes(keyP)
|
iter.skipCacheDeletes(keyP)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Child exists.
|
// Cache is not a delete.
|
||||||
return
|
|
||||||
|
return true // cache exists.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ func First(st KVStore, start, end []byte) (kv KVPair, ok bool) {
|
||||||
if !iter.Valid() {
|
if !iter.Valid() {
|
||||||
return kv, false
|
return kv, false
|
||||||
}
|
}
|
||||||
defer iter.Release()
|
defer iter.Close()
|
||||||
|
|
||||||
return KVPair{iter.Key(), iter.Value()}, true
|
return KVPair{iter.Key(), iter.Value()}, true
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ func Last(st KVStore, start, end []byte) (kv KVPair, ok bool) {
|
||||||
return kv, false
|
return kv, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defer iter.Release()
|
defer iter.Close()
|
||||||
|
|
||||||
if bytes.Equal(iter.Key(), end) {
|
if bytes.Equal(iter.Key(), end) {
|
||||||
// Skip this one, end is exclusive.
|
// Skip this one, end is exclusive.
|
||||||
|
|
|
@ -234,8 +234,8 @@ func (iter *iavlIterator) Value() []byte {
|
||||||
return iter.value
|
return iter.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release implements Iterator
|
// Close implements Iterator
|
||||||
func (iter *iavlIterator) Release() {
|
func (iter *iavlIterator) Close() {
|
||||||
close(iter.quitCh)
|
close(iter.quitCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package store
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
|
)
|
||||||
|
|
||||||
// Iterates over iterKVCache items.
|
// Iterates over iterKVCache items.
|
||||||
// if key is nil, means it was deleted.
|
// if key is nil, means it was deleted.
|
||||||
// Implements Iterator.
|
// Implements Iterator.
|
||||||
|
@ -9,10 +13,17 @@ type memIterator struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMemIterator(start, end []byte, items []KVPair) *memIterator {
|
func newMemIterator(start, end []byte, items []KVPair) *memIterator {
|
||||||
|
itemsInDomain := make([]KVPair, 0)
|
||||||
|
for _, item := range items {
|
||||||
|
ascending := keyCompare(start, end) < 0
|
||||||
|
if dbm.IsKeyInDomain(item.Key, start, end, !ascending) {
|
||||||
|
itemsInDomain = append(itemsInDomain, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
return &memIterator{
|
return &memIterator{
|
||||||
start: start,
|
start: start,
|
||||||
end: end,
|
end: end,
|
||||||
items: items,
|
items: itemsInDomain,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +56,7 @@ func (mi *memIterator) Value() []byte {
|
||||||
return mi.items[0].Value
|
return mi.items[0].Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mi *memIterator) Release() {
|
func (mi *memIterator) Close() {
|
||||||
mi.start = nil
|
mi.start = nil
|
||||||
mi.end = nil
|
mi.end = nil
|
||||||
mi.items = nil
|
mi.items = nil
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
"github.com/tendermint/go-wire/data"
|
"github.com/tendermint/go-wire/data"
|
||||||
"github.com/tendermint/tmlibs/db"
|
"github.com/tendermint/tmlibs/db"
|
||||||
)
|
)
|
||||||
|
@ -128,3 +130,14 @@ type CommitID struct {
|
||||||
func (cid CommitID) IsZero() bool {
|
func (cid CommitID) IsZero() bool {
|
||||||
return cid.Version == 0 && len(cid.Hash) == 0
|
return cid.Version == 0 && len(cid.Hash) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bytes.Compare but bounded on both sides by nil.
|
||||||
|
// both (k1, nil) and (nil, k2) return -1
|
||||||
|
func keyCompare(k1, k2 []byte) int {
|
||||||
|
if k1 == nil && k2 == nil {
|
||||||
|
return 0
|
||||||
|
} else if k1 == nil || k2 == nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return bytes.Compare(k1, k2)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue