mirror of https://github.com/tikv/client-go.git
840 lines
20 KiB
Go
840 lines
20 KiB
Go
// Copyright 2021 TiKV Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// NOTE: The code in this file is based on code from the
|
|
// TiDB project, licensed under the Apache License v 2.0
|
|
//
|
|
// https://github.com/pingcap/tidb/tree/cc5e161ac06827589c4966674597c137cc9e809c/store/tikv/unionstore/memdb_test.go
|
|
//
|
|
|
|
// Copyright 2020 PingCAP, Inc.
|
|
//
|
|
// Copyright 2015 Wenbin Xiao
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package unionstore
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"testing"
|
|
|
|
leveldb "github.com/pingcap/goleveldb/leveldb/memdb"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/tikv/client-go/v2/kv"
|
|
)
|
|
|
|
type KeyFlags = kv.KeyFlags
|
|
|
|
func init() {
|
|
testMode = true
|
|
}
|
|
|
|
// DeleteKey is used in test to verify the `deleteNode` used in `vlog.revertToCheckpoint`.
|
|
func (db *MemDB) DeleteKey(key []byte) {
|
|
x := db.traverse(key, false)
|
|
if x.isNull() {
|
|
return
|
|
}
|
|
db.size -= len(db.vlog.getValue(x.vptr))
|
|
db.deleteNode(x)
|
|
}
|
|
|
|
func TestGetSet(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
const cnt = 10000
|
|
p := fillDB(cnt)
|
|
|
|
var buf [4]byte
|
|
for i := 0; i < cnt; i++ {
|
|
binary.BigEndian.PutUint32(buf[:], uint32(i))
|
|
v, err := p.Get(buf[:])
|
|
require.Nil(err)
|
|
require.Equal(v, buf[:])
|
|
}
|
|
}
|
|
|
|
func TestBigKV(t *testing.T) {
|
|
assert := assert.New(t)
|
|
db := newMemDB()
|
|
db.Set([]byte{1}, make([]byte, 80<<20))
|
|
assert.Equal(db.vlog.blockSize, maxBlockSize)
|
|
assert.Equal(len(db.vlog.blocks), 1)
|
|
h := db.Staging()
|
|
db.Set([]byte{2}, make([]byte, 127<<20))
|
|
db.Release(h)
|
|
assert.Equal(db.vlog.blockSize, maxBlockSize)
|
|
assert.Equal(len(db.vlog.blocks), 2)
|
|
assert.PanicsWithValue("alloc size is larger than max block size", func() { db.Set([]byte{3}, make([]byte, maxBlockSize+1)) })
|
|
}
|
|
|
|
func TestIterator(t *testing.T) {
|
|
assert := assert.New(t)
|
|
const cnt = 10000
|
|
db := fillDB(cnt)
|
|
|
|
var buf [4]byte
|
|
var i int
|
|
|
|
for it, _ := db.Iter(nil, nil); it.Valid(); it.Next() {
|
|
binary.BigEndian.PutUint32(buf[:], uint32(i))
|
|
assert.Equal(it.Key(), buf[:])
|
|
assert.Equal(it.Value(), buf[:])
|
|
i++
|
|
}
|
|
assert.Equal(i, cnt)
|
|
|
|
i--
|
|
for it, _ := db.IterReverse(nil); it.Valid(); it.Next() {
|
|
binary.BigEndian.PutUint32(buf[:], uint32(i))
|
|
assert.Equal(it.Key(), buf[:])
|
|
assert.Equal(it.Value(), buf[:])
|
|
i--
|
|
}
|
|
assert.Equal(i, -1)
|
|
}
|
|
|
|
func TestDiscard(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
const cnt = 10000
|
|
db := newMemDB()
|
|
base := deriveAndFill(0, cnt, 0, db)
|
|
sz := db.Size()
|
|
|
|
db.Cleanup(deriveAndFill(0, cnt, 1, db))
|
|
assert.Equal(db.Len(), cnt)
|
|
assert.Equal(db.Size(), sz)
|
|
|
|
var buf [4]byte
|
|
|
|
for i := 0; i < cnt; i++ {
|
|
binary.BigEndian.PutUint32(buf[:], uint32(i))
|
|
v, err := db.Get(buf[:])
|
|
assert.Nil(err)
|
|
assert.Equal(v, buf[:])
|
|
}
|
|
|
|
var i int
|
|
for it, _ := db.Iter(nil, nil); it.Valid(); it.Next() {
|
|
binary.BigEndian.PutUint32(buf[:], uint32(i))
|
|
assert.Equal(it.Key(), buf[:])
|
|
assert.Equal(it.Value(), buf[:])
|
|
i++
|
|
}
|
|
assert.Equal(i, cnt)
|
|
|
|
i--
|
|
for it, _ := db.IterReverse(nil); it.Valid(); it.Next() {
|
|
binary.BigEndian.PutUint32(buf[:], uint32(i))
|
|
assert.Equal(it.Key(), buf[:])
|
|
assert.Equal(it.Value(), buf[:])
|
|
i--
|
|
}
|
|
assert.Equal(i, -1)
|
|
|
|
db.Cleanup(base)
|
|
for i := 0; i < cnt; i++ {
|
|
binary.BigEndian.PutUint32(buf[:], uint32(i))
|
|
_, err := db.Get(buf[:])
|
|
assert.NotNil(err)
|
|
}
|
|
it1, _ := db.Iter(nil, nil)
|
|
it := it1.(*MemdbIterator)
|
|
it.seekToFirst()
|
|
assert.False(it.Valid())
|
|
it.seekToLast()
|
|
assert.False(it.Valid())
|
|
it.seek([]byte{0xff})
|
|
assert.False(it.Valid())
|
|
}
|
|
|
|
func TestFlushOverwrite(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
const cnt = 10000
|
|
db := newMemDB()
|
|
db.Release(deriveAndFill(0, cnt, 0, db))
|
|
sz := db.Size()
|
|
|
|
db.Release(deriveAndFill(0, cnt, 1, db))
|
|
|
|
assert.Equal(db.Len(), cnt)
|
|
assert.Equal(db.Size(), sz)
|
|
|
|
var kbuf, vbuf [4]byte
|
|
|
|
for i := 0; i < cnt; i++ {
|
|
binary.BigEndian.PutUint32(kbuf[:], uint32(i))
|
|
binary.BigEndian.PutUint32(vbuf[:], uint32(i+1))
|
|
v, err := db.Get(kbuf[:])
|
|
assert.Nil(err)
|
|
assert.Equal(v, vbuf[:])
|
|
}
|
|
|
|
var i int
|
|
for it, _ := db.Iter(nil, nil); it.Valid(); it.Next() {
|
|
binary.BigEndian.PutUint32(kbuf[:], uint32(i))
|
|
binary.BigEndian.PutUint32(vbuf[:], uint32(i+1))
|
|
assert.Equal(it.Key(), kbuf[:])
|
|
assert.Equal(it.Value(), vbuf[:])
|
|
i++
|
|
}
|
|
assert.Equal(i, cnt)
|
|
|
|
i--
|
|
for it, _ := db.IterReverse(nil); it.Valid(); it.Next() {
|
|
binary.BigEndian.PutUint32(kbuf[:], uint32(i))
|
|
binary.BigEndian.PutUint32(vbuf[:], uint32(i+1))
|
|
assert.Equal(it.Key(), kbuf[:])
|
|
assert.Equal(it.Value(), vbuf[:])
|
|
i--
|
|
}
|
|
assert.Equal(i, -1)
|
|
}
|
|
|
|
func TestComplexUpdate(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
const (
|
|
keep = 3000
|
|
overwrite = 6000
|
|
insert = 9000
|
|
)
|
|
|
|
db := newMemDB()
|
|
db.Release(deriveAndFill(0, overwrite, 0, db))
|
|
assert.Equal(db.Len(), overwrite)
|
|
db.Release(deriveAndFill(keep, insert, 1, db))
|
|
assert.Equal(db.Len(), insert)
|
|
|
|
var kbuf, vbuf [4]byte
|
|
|
|
for i := 0; i < insert; i++ {
|
|
binary.BigEndian.PutUint32(kbuf[:], uint32(i))
|
|
binary.BigEndian.PutUint32(vbuf[:], uint32(i))
|
|
if i >= keep {
|
|
binary.BigEndian.PutUint32(vbuf[:], uint32(i+1))
|
|
}
|
|
v, err := db.Get(kbuf[:])
|
|
assert.Nil(err)
|
|
assert.Equal(v, vbuf[:])
|
|
}
|
|
}
|
|
|
|
func TestNestedSandbox(t *testing.T) {
|
|
assert := assert.New(t)
|
|
db := newMemDB()
|
|
h0 := deriveAndFill(0, 200, 0, db)
|
|
h1 := deriveAndFill(0, 100, 1, db)
|
|
h2 := deriveAndFill(50, 150, 2, db)
|
|
h3 := deriveAndFill(100, 120, 3, db)
|
|
h4 := deriveAndFill(0, 150, 4, db)
|
|
db.Cleanup(h4) // Discard (0..150 -> 4)
|
|
db.Release(h3) // Flush (100..120 -> 3)
|
|
db.Cleanup(h2) // Discard (100..120 -> 3) & (50..150 -> 2)
|
|
db.Release(h1) // Flush (0..100 -> 1)
|
|
db.Release(h0) // Flush (0..100 -> 1) & (0..200 -> 0)
|
|
// The final result should be (0..100 -> 1) & (101..200 -> 0)
|
|
|
|
var kbuf, vbuf [4]byte
|
|
|
|
for i := 0; i < 200; i++ {
|
|
binary.BigEndian.PutUint32(kbuf[:], uint32(i))
|
|
binary.BigEndian.PutUint32(vbuf[:], uint32(i))
|
|
if i < 100 {
|
|
binary.BigEndian.PutUint32(vbuf[:], uint32(i+1))
|
|
}
|
|
v, err := db.Get(kbuf[:])
|
|
assert.Nil(err)
|
|
assert.Equal(v, vbuf[:])
|
|
}
|
|
|
|
var i int
|
|
|
|
for it, _ := db.Iter(nil, nil); it.Valid(); it.Next() {
|
|
binary.BigEndian.PutUint32(kbuf[:], uint32(i))
|
|
binary.BigEndian.PutUint32(vbuf[:], uint32(i))
|
|
if i < 100 {
|
|
binary.BigEndian.PutUint32(vbuf[:], uint32(i+1))
|
|
}
|
|
assert.Equal(it.Key(), kbuf[:])
|
|
assert.Equal(it.Value(), vbuf[:])
|
|
i++
|
|
}
|
|
assert.Equal(i, 200)
|
|
|
|
i--
|
|
for it, _ := db.IterReverse(nil); it.Valid(); it.Next() {
|
|
binary.BigEndian.PutUint32(kbuf[:], uint32(i))
|
|
binary.BigEndian.PutUint32(vbuf[:], uint32(i))
|
|
if i < 100 {
|
|
binary.BigEndian.PutUint32(vbuf[:], uint32(i+1))
|
|
}
|
|
assert.Equal(it.Key(), kbuf[:])
|
|
assert.Equal(it.Value(), vbuf[:])
|
|
i--
|
|
}
|
|
assert.Equal(i, -1)
|
|
}
|
|
|
|
func TestOverwrite(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
const cnt = 10000
|
|
db := fillDB(cnt)
|
|
var buf [4]byte
|
|
|
|
sz := db.Size()
|
|
for i := 0; i < cnt; i += 3 {
|
|
var newBuf [4]byte
|
|
binary.BigEndian.PutUint32(buf[:], uint32(i))
|
|
binary.BigEndian.PutUint32(newBuf[:], uint32(i*10))
|
|
db.Set(buf[:], newBuf[:])
|
|
}
|
|
assert.Equal(db.Len(), cnt)
|
|
assert.Equal(db.Size(), sz)
|
|
|
|
for i := 0; i < cnt; i++ {
|
|
binary.BigEndian.PutUint32(buf[:], uint32(i))
|
|
val, _ := db.Get(buf[:])
|
|
v := binary.BigEndian.Uint32(val)
|
|
if i%3 == 0 {
|
|
assert.Equal(v, uint32(i*10))
|
|
} else {
|
|
assert.Equal(v, uint32(i))
|
|
}
|
|
}
|
|
|
|
var i int
|
|
|
|
for it, _ := db.Iter(nil, nil); it.Valid(); it.Next() {
|
|
binary.BigEndian.PutUint32(buf[:], uint32(i))
|
|
assert.Equal(it.Key(), buf[:])
|
|
v := binary.BigEndian.Uint32(it.Value())
|
|
if i%3 == 0 {
|
|
assert.Equal(v, uint32(i*10))
|
|
} else {
|
|
assert.Equal(v, uint32(i))
|
|
}
|
|
i++
|
|
}
|
|
assert.Equal(i, cnt)
|
|
|
|
i--
|
|
for it, _ := db.IterReverse(nil); it.Valid(); it.Next() {
|
|
binary.BigEndian.PutUint32(buf[:], uint32(i))
|
|
assert.Equal(it.Key(), buf[:])
|
|
v := binary.BigEndian.Uint32(it.Value())
|
|
if i%3 == 0 {
|
|
assert.Equal(v, uint32(i*10))
|
|
} else {
|
|
assert.Equal(v, uint32(i))
|
|
}
|
|
i--
|
|
}
|
|
assert.Equal(i, -1)
|
|
}
|
|
|
|
func TestKVLargeThanBlock(t *testing.T) {
|
|
assert := assert.New(t)
|
|
db := newMemDB()
|
|
db.Set([]byte{1}, make([]byte, 1))
|
|
db.Set([]byte{2}, make([]byte, 4096))
|
|
assert.Equal(len(db.vlog.blocks), 2)
|
|
db.Set([]byte{3}, make([]byte, 3000))
|
|
assert.Equal(len(db.vlog.blocks), 2)
|
|
val, err := db.Get([]byte{3})
|
|
assert.Nil(err)
|
|
assert.Equal(len(val), 3000)
|
|
}
|
|
|
|
func TestEmptyDB(t *testing.T) {
|
|
assert := assert.New(t)
|
|
db := newMemDB()
|
|
_, err := db.Get([]byte{0})
|
|
assert.NotNil(err)
|
|
it1, _ := db.Iter(nil, nil)
|
|
it := it1.(*MemdbIterator)
|
|
it.seekToFirst()
|
|
assert.False(it.Valid())
|
|
it.seekToLast()
|
|
assert.False(it.Valid())
|
|
it.seek([]byte{0xff})
|
|
assert.False(it.Valid())
|
|
}
|
|
|
|
func TestReset(t *testing.T) {
|
|
assert := assert.New(t)
|
|
db := fillDB(1000)
|
|
db.Reset()
|
|
_, err := db.Get([]byte{0, 0, 0, 0})
|
|
assert.NotNil(err)
|
|
it1, _ := db.Iter(nil, nil)
|
|
it := it1.(*MemdbIterator)
|
|
it.seekToFirst()
|
|
assert.False(it.Valid())
|
|
it.seekToLast()
|
|
assert.False(it.Valid())
|
|
it.seek([]byte{0xff})
|
|
assert.False(it.Valid())
|
|
}
|
|
|
|
func TestInspectStage(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
db := newMemDB()
|
|
h1 := deriveAndFill(0, 1000, 0, db)
|
|
h2 := deriveAndFill(500, 1000, 1, db)
|
|
for i := 500; i < 1500; i++ {
|
|
var kbuf [4]byte
|
|
// don't update in place
|
|
var vbuf [5]byte
|
|
binary.BigEndian.PutUint32(kbuf[:], uint32(i))
|
|
binary.BigEndian.PutUint32(vbuf[:], uint32(i+2))
|
|
db.Set(kbuf[:], vbuf[:])
|
|
}
|
|
h3 := deriveAndFill(1000, 2000, 3, db)
|
|
|
|
db.InspectStage(h3, func(key []byte, _ KeyFlags, val []byte) {
|
|
k := int(binary.BigEndian.Uint32(key))
|
|
v := int(binary.BigEndian.Uint32(val))
|
|
|
|
assert.True(k >= 1000 && k < 2000)
|
|
assert.Equal(v-k, 3)
|
|
})
|
|
|
|
db.InspectStage(h2, func(key []byte, _ KeyFlags, val []byte) {
|
|
k := int(binary.BigEndian.Uint32(key))
|
|
v := int(binary.BigEndian.Uint32(val))
|
|
|
|
assert.True(k >= 500 && k < 2000)
|
|
if k < 1000 {
|
|
assert.Equal(v-k, 2)
|
|
} else {
|
|
assert.Equal(v-k, 3)
|
|
}
|
|
})
|
|
|
|
db.Cleanup(h3)
|
|
db.Release(h2)
|
|
|
|
db.InspectStage(h1, func(key []byte, _ KeyFlags, val []byte) {
|
|
k := int(binary.BigEndian.Uint32(key))
|
|
v := int(binary.BigEndian.Uint32(val))
|
|
|
|
assert.True(k >= 0 && k < 1500)
|
|
if k < 500 {
|
|
assert.Equal(v-k, 0)
|
|
} else {
|
|
assert.Equal(v-k, 2)
|
|
}
|
|
})
|
|
|
|
db.Release(h1)
|
|
}
|
|
|
|
func TestDirty(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
db := newMemDB()
|
|
db.Set([]byte{1}, []byte{1})
|
|
assert.True(db.Dirty())
|
|
|
|
db = newMemDB()
|
|
h := db.Staging()
|
|
db.Set([]byte{1}, []byte{1})
|
|
db.Cleanup(h)
|
|
assert.False(db.Dirty())
|
|
|
|
h = db.Staging()
|
|
db.Set([]byte{1}, []byte{1})
|
|
db.Release(h)
|
|
assert.True(db.Dirty())
|
|
|
|
// persistent flags will make memdb dirty.
|
|
db = newMemDB()
|
|
h = db.Staging()
|
|
db.SetWithFlags([]byte{1}, []byte{1}, kv.SetKeyLocked)
|
|
db.Cleanup(h)
|
|
assert.True(db.Dirty())
|
|
|
|
// non-persistent flags will not make memdb dirty.
|
|
db = newMemDB()
|
|
h = db.Staging()
|
|
db.SetWithFlags([]byte{1}, []byte{1}, kv.SetPresumeKeyNotExists)
|
|
db.Cleanup(h)
|
|
assert.False(db.Dirty())
|
|
}
|
|
|
|
func TestFlags(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
const cnt = 10000
|
|
db := newMemDB()
|
|
h := db.Staging()
|
|
for i := uint32(0); i < cnt; i++ {
|
|
var buf [4]byte
|
|
binary.BigEndian.PutUint32(buf[:], i)
|
|
if i%2 == 0 {
|
|
db.SetWithFlags(buf[:], buf[:], kv.SetPresumeKeyNotExists, kv.SetKeyLocked)
|
|
} else {
|
|
db.SetWithFlags(buf[:], buf[:], kv.SetPresumeKeyNotExists)
|
|
}
|
|
}
|
|
db.Cleanup(h)
|
|
|
|
for i := uint32(0); i < cnt; i++ {
|
|
var buf [4]byte
|
|
binary.BigEndian.PutUint32(buf[:], i)
|
|
_, err := db.Get(buf[:])
|
|
assert.NotNil(err)
|
|
flags, err := db.GetFlags(buf[:])
|
|
if i%2 == 0 {
|
|
assert.Nil(err)
|
|
assert.True(flags.HasLocked())
|
|
assert.False(flags.HasPresumeKeyNotExists())
|
|
} else {
|
|
assert.NotNil(err)
|
|
}
|
|
}
|
|
|
|
assert.Equal(db.Len(), 5000)
|
|
assert.Equal(db.Size(), 20000)
|
|
|
|
it1, _ := db.Iter(nil, nil)
|
|
it := it1.(*MemdbIterator)
|
|
assert.False(it.Valid())
|
|
|
|
it.includeFlags = true
|
|
it.init()
|
|
|
|
for ; it.Valid(); it.Next() {
|
|
k := binary.BigEndian.Uint32(it.Key())
|
|
assert.True(k%2 == 0)
|
|
}
|
|
|
|
for i := uint32(0); i < cnt; i++ {
|
|
var buf [4]byte
|
|
binary.BigEndian.PutUint32(buf[:], i)
|
|
db.UpdateFlags(buf[:], kv.DelKeyLocked)
|
|
}
|
|
for i := uint32(0); i < cnt; i++ {
|
|
var buf [4]byte
|
|
binary.BigEndian.PutUint32(buf[:], i)
|
|
_, err := db.Get(buf[:])
|
|
assert.NotNil(err)
|
|
|
|
// UpdateFlags will create missing node.
|
|
flags, err := db.GetFlags(buf[:])
|
|
assert.Nil(err)
|
|
assert.False(flags.HasLocked())
|
|
}
|
|
}
|
|
|
|
func checkConsist(t *testing.T, p1 *MemDB, p2 *leveldb.DB) {
|
|
assert := assert.New(t)
|
|
|
|
assert.Equal(p1.Len(), p2.Len())
|
|
assert.Equal(p1.Size(), p2.Size())
|
|
|
|
it1, _ := p1.Iter(nil, nil)
|
|
it2 := p2.NewIterator(nil)
|
|
|
|
var prevKey, prevVal []byte
|
|
for it2.First(); it2.Valid(); it2.Next() {
|
|
v, err := p1.Get(it2.Key())
|
|
assert.Nil(err)
|
|
assert.Equal(v, it2.Value())
|
|
|
|
assert.Equal(it1.Key(), it2.Key())
|
|
assert.Equal(it1.Value(), it2.Value())
|
|
|
|
it, _ := p1.Iter(it2.Key(), nil)
|
|
assert.Equal(it.Key(), it2.Key())
|
|
assert.Equal(it.Value(), it2.Value())
|
|
|
|
if prevKey != nil {
|
|
it, _ = p1.IterReverse(it2.Key())
|
|
assert.Equal(it.Key(), prevKey)
|
|
assert.Equal(it.Value(), prevVal)
|
|
}
|
|
|
|
it1.Next()
|
|
prevKey = it2.Key()
|
|
prevVal = it2.Value()
|
|
}
|
|
|
|
it1, _ = p1.IterReverse(nil)
|
|
for it2.Last(); it2.Valid(); it2.Prev() {
|
|
assert.Equal(it1.Key(), it2.Key())
|
|
assert.Equal(it1.Value(), it2.Value())
|
|
it1.Next()
|
|
}
|
|
}
|
|
|
|
func fillDB(cnt int) *MemDB {
|
|
db := newMemDB()
|
|
h := deriveAndFill(0, cnt, 0, db)
|
|
db.Release(h)
|
|
return db
|
|
}
|
|
|
|
func deriveAndFill(start, end, valueBase int, db *MemDB) int {
|
|
h := db.Staging()
|
|
var kbuf, vbuf [4]byte
|
|
for i := start; i < end; i++ {
|
|
binary.BigEndian.PutUint32(kbuf[:], uint32(i))
|
|
binary.BigEndian.PutUint32(vbuf[:], uint32(i+valueBase))
|
|
db.Set(kbuf[:], vbuf[:])
|
|
}
|
|
return h
|
|
}
|
|
|
|
const (
|
|
startIndex = 0
|
|
testCount = 2
|
|
indexStep = 2
|
|
)
|
|
|
|
func insertData(t *testing.T, buffer *MemDB) {
|
|
for i := startIndex; i < testCount; i++ {
|
|
val := encodeInt(i * indexStep)
|
|
err := buffer.Set(val, val)
|
|
assert.Nil(t, err)
|
|
}
|
|
}
|
|
|
|
func encodeInt(n int) []byte {
|
|
return []byte(fmt.Sprintf("%010d", n))
|
|
}
|
|
|
|
func decodeInt(s []byte) int {
|
|
var n int
|
|
fmt.Sscanf(string(s), "%010d", &n)
|
|
return n
|
|
}
|
|
|
|
func valToStr(iter Iterator) string {
|
|
val := iter.Value()
|
|
return string(val)
|
|
}
|
|
|
|
func checkNewIterator(t *testing.T, buffer *MemDB) {
|
|
assert := assert.New(t)
|
|
for i := startIndex; i < testCount; i++ {
|
|
val := encodeInt(i * indexStep)
|
|
iter, err := buffer.Iter(val, nil)
|
|
assert.Nil(err)
|
|
assert.Equal(iter.Key(), val)
|
|
assert.Equal(decodeInt([]byte(valToStr(iter))), i*indexStep)
|
|
iter.Close()
|
|
}
|
|
|
|
// Test iterator Next()
|
|
for i := startIndex; i < testCount-1; i++ {
|
|
val := encodeInt(i * indexStep)
|
|
iter, err := buffer.Iter(val, nil)
|
|
assert.Nil(err)
|
|
assert.Equal(iter.Key(), val)
|
|
assert.Equal(valToStr(iter), string(val))
|
|
|
|
err = iter.Next()
|
|
assert.Nil(err)
|
|
assert.True(iter.Valid())
|
|
|
|
val = encodeInt((i + 1) * indexStep)
|
|
assert.Equal(iter.Key(), val)
|
|
assert.Equal(valToStr(iter), string(val))
|
|
iter.Close()
|
|
}
|
|
|
|
// Non exist and beyond maximum seek test
|
|
iter, err := buffer.Iter(encodeInt(testCount*indexStep), nil)
|
|
assert.Nil(err)
|
|
assert.False(iter.Valid())
|
|
|
|
// Non exist but between existing keys seek test,
|
|
// it returns the smallest key that larger than the one we are seeking
|
|
inBetween := encodeInt((testCount-1)*indexStep - 1)
|
|
last := encodeInt((testCount - 1) * indexStep)
|
|
iter, err = buffer.Iter(inBetween, nil)
|
|
assert.Nil(err)
|
|
assert.True(iter.Valid())
|
|
assert.NotEqual(iter.Key(), inBetween)
|
|
assert.Equal(iter.Key(), last)
|
|
iter.Close()
|
|
}
|
|
|
|
func mustGet(t *testing.T, buffer *MemDB) {
|
|
for i := startIndex; i < testCount; i++ {
|
|
s := encodeInt(i * indexStep)
|
|
val, err := buffer.Get(s)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, string(val), string(s))
|
|
}
|
|
}
|
|
|
|
func TestKVGetSet(t *testing.T) {
|
|
buffer := newMemDB()
|
|
insertData(t, buffer)
|
|
mustGet(t, buffer)
|
|
}
|
|
|
|
func TestNewIterator(t *testing.T) {
|
|
assert := assert.New(t)
|
|
buffer := newMemDB()
|
|
// should be invalid
|
|
iter, err := buffer.Iter(nil, nil)
|
|
assert.Nil(err)
|
|
assert.False(iter.Valid())
|
|
|
|
insertData(t, buffer)
|
|
checkNewIterator(t, buffer)
|
|
}
|
|
|
|
// FnKeyCmp is the function for iterator the keys
|
|
type FnKeyCmp func(key []byte) bool
|
|
|
|
// TODO: remove it since it is duplicated with kv.NextUtil
|
|
// NextUntil applies FnKeyCmp to each entry of the iterator until meets some condition.
|
|
// It will stop when fn returns true, or iterator is invalid or an error occurs.
|
|
func NextUntil(it Iterator, fn FnKeyCmp) error {
|
|
var err error
|
|
for it.Valid() && !fn(it.Key()) {
|
|
err = it.Next()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func TestIterNextUntil(t *testing.T) {
|
|
assert := assert.New(t)
|
|
buffer := newMemDB()
|
|
insertData(t, buffer)
|
|
|
|
iter, err := buffer.Iter(nil, nil)
|
|
assert.Nil(err)
|
|
|
|
err = NextUntil(iter, func(k []byte) bool {
|
|
return false
|
|
})
|
|
assert.Nil(err)
|
|
assert.False(iter.Valid())
|
|
}
|
|
|
|
func TestBasicNewIterator(t *testing.T) {
|
|
assert := assert.New(t)
|
|
buffer := newMemDB()
|
|
it, err := buffer.Iter([]byte("2"), nil)
|
|
assert.Nil(err)
|
|
assert.False(it.Valid())
|
|
}
|
|
|
|
func TestNewIteratorMin(t *testing.T) {
|
|
assert := assert.New(t)
|
|
kvs := []struct {
|
|
key string
|
|
value string
|
|
}{
|
|
{"DATA_test_main_db_tbl_tbl_test_record__00000000000000000001", "lock-version"},
|
|
{"DATA_test_main_db_tbl_tbl_test_record__00000000000000000001_0002", "1"},
|
|
{"DATA_test_main_db_tbl_tbl_test_record__00000000000000000001_0003", "hello"},
|
|
{"DATA_test_main_db_tbl_tbl_test_record__00000000000000000002", "lock-version"},
|
|
{"DATA_test_main_db_tbl_tbl_test_record__00000000000000000002_0002", "2"},
|
|
{"DATA_test_main_db_tbl_tbl_test_record__00000000000000000002_0003", "hello"},
|
|
}
|
|
buffer := newMemDB()
|
|
for _, kv := range kvs {
|
|
err := buffer.Set([]byte(kv.key), []byte(kv.value))
|
|
assert.Nil(err)
|
|
}
|
|
|
|
cnt := 0
|
|
it, err := buffer.Iter(nil, nil)
|
|
assert.Nil(err)
|
|
for it.Valid() {
|
|
cnt++
|
|
err := it.Next()
|
|
assert.Nil(err)
|
|
}
|
|
assert.Equal(cnt, 6)
|
|
|
|
it, err = buffer.Iter([]byte("DATA_test_main_db_tbl_tbl_test_record__00000000000000000000"), nil)
|
|
assert.Nil(err)
|
|
assert.Equal(string(it.Key()), "DATA_test_main_db_tbl_tbl_test_record__00000000000000000001")
|
|
}
|
|
|
|
func TestMemDBStaging(t *testing.T) {
|
|
assert := assert.New(t)
|
|
buffer := newMemDB()
|
|
err := buffer.Set([]byte("x"), make([]byte, 2))
|
|
assert.Nil(err)
|
|
|
|
h1 := buffer.Staging()
|
|
err = buffer.Set([]byte("x"), make([]byte, 3))
|
|
assert.Nil(err)
|
|
|
|
h2 := buffer.Staging()
|
|
err = buffer.Set([]byte("yz"), make([]byte, 1))
|
|
assert.Nil(err)
|
|
|
|
v, _ := buffer.Get([]byte("x"))
|
|
assert.Equal(len(v), 3)
|
|
|
|
buffer.Release(h2)
|
|
|
|
v, _ = buffer.Get([]byte("yz"))
|
|
assert.Equal(len(v), 1)
|
|
|
|
buffer.Cleanup(h1)
|
|
|
|
v, _ = buffer.Get([]byte("x"))
|
|
assert.Equal(len(v), 2)
|
|
}
|
|
|
|
func TestBufferLimit(t *testing.T) {
|
|
assert := assert.New(t)
|
|
buffer := newMemDB()
|
|
buffer.bufferSizeLimit = 1000
|
|
buffer.entrySizeLimit = 500
|
|
|
|
err := buffer.Set([]byte("x"), make([]byte, 500))
|
|
assert.NotNil(err) // entry size limit
|
|
|
|
err = buffer.Set([]byte("x"), make([]byte, 499))
|
|
assert.Nil(err)
|
|
err = buffer.Set([]byte("yz"), make([]byte, 499))
|
|
assert.NotNil(err) // buffer size limit
|
|
|
|
err = buffer.Delete(make([]byte, 499))
|
|
assert.Nil(err)
|
|
|
|
err = buffer.Delete(make([]byte, 500))
|
|
assert.NotNil(err)
|
|
}
|