erigon-pulse/ethdb/kv_snapshot_property_test.go
b00ris 7948eafcc2
Property tests for kv snapshot (#1928)
* model tests for snapshot kv

* cursor test

* first failing test

* fix

* fix lint

* remove fmt
2021-05-13 09:40:32 +01:00

463 lines
12 KiB
Go

package ethdb
import (
"context"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/dbutils"
"github.com/stretchr/testify/require"
"pgregory.net/rapid"
"testing"
)
func TestGetAndPut(t *testing.T) {
t.Skip("remove when it become stable for 200 rounds")
rapid.Check(t, rapid.Run(&getPutkvMachine{}))
}
type getPutkvMachine struct {
bucket string
snKV RwKV
modelKV RwKV
snapshotKeys [][20]byte
newKeys [][20]byte
allKeys [][20]byte
snTX RwTx
modelTX RwTx
}
func (m *getPutkvMachine) Init(t *rapid.T) {
m.bucket = dbutils.PlainStateBucket
m.snKV = NewMemKV()
m.modelKV = NewMemKV()
m.snapshotKeys = rapid.SliceOf(rapid.ArrayOf(20, rapid.Byte())).Filter(func(_v [][20]byte) bool {
return len(_v) > 0
}).Draw(t, "generate keys").([][20]byte)
m.newKeys = rapid.SliceOf(rapid.ArrayOf(20, rapid.Byte())).Filter(func(_v [][20]byte) bool {
return len(_v) > 0
}).Draw(t, "generate new keys").([][20]byte)
notExistingKeys := rapid.SliceOf(rapid.ArrayOf(20, rapid.Byte())).Filter(func(_v [][20]byte) bool {
return len(_v) > 0
}).Draw(t, "generate not excisting keys").([][20]byte)
m.allKeys = append(m.snapshotKeys, notExistingKeys...)
txSn, err := m.snKV.BeginRw(context.Background())
require.NoError(t, err)
txModel, err := m.modelKV.BeginRw(context.Background())
require.NoError(t, err)
defer txModel.Rollback()
for _, key := range m.snapshotKeys {
innerErr := txSn.Put(m.bucket, key[:], []byte("sn_"+common.Bytes2Hex(key[:])))
require.NoError(t, innerErr)
innerErr = txModel.Put(m.bucket, key[:], []byte("sn_"+common.Bytes2Hex(key[:])))
require.NoError(t, innerErr)
}
//save snapshot and wrap new write db
err = txSn.Commit()
require.NoError(t, err)
err = txModel.Commit()
require.NoError(t, err)
m.snKV = NewSnapshotKV().SnapshotDB([]string{m.bucket}, m.snKV).DB(NewMemKV()).Open()
}
func (m *getPutkvMachine) Cleanup() {
if m.snTX != nil {
m.snTX.Rollback()
}
if m.modelTX != nil {
m.modelTX.Rollback()
}
m.snKV.Close()
m.modelKV.Close()
}
func (m *getPutkvMachine) Check(t *rapid.T) {
}
func (m *getPutkvMachine) Get(t *rapid.T) {
if m.snTX == nil && m.modelTX == nil {
return
}
key := rapid.SampledFrom(m.allKeys).Draw(t, "get a key").([20]byte)
var (
v1, v2 []byte
err1, err2 error
)
v1, err1 = m.snTX.GetOne(m.bucket, key[:])
v2, err2 = m.modelTX.GetOne(m.bucket, key[:])
require.Equal(t, err1, err2)
require.Equal(t, v1, v2)
}
func (m *getPutkvMachine) Put(t *rapid.T) {
if len(m.newKeys) == 0 {
return
}
if m.snTX == nil && m.modelTX == nil {
return
}
key := rapid.SampledFrom(m.newKeys).Draw(t, "put a key").([20]byte)
m.allKeys = append(m.allKeys, key)
for i, v := range m.newKeys {
if v == key {
m.newKeys = append(m.newKeys[:i], m.newKeys[i+1:]...)
}
}
err := m.snTX.Put(m.bucket, key[:], []byte("put"+common.Bytes2Hex(key[:])))
require.NoError(t, err)
err = m.modelTX.Put(m.bucket, key[:], []byte("put"+common.Bytes2Hex(key[:])))
require.NoError(t, err)
}
func (m *getPutkvMachine) Delete(t *rapid.T) {
if m.snTX == nil && m.modelTX == nil {
return
}
key := rapid.SampledFrom(m.allKeys).Draw(t, "delete a key").([20]byte)
err := m.snTX.Delete(m.bucket, key[:], nil)
require.NoError(t, err)
err = m.modelTX.Put(m.bucket, key[:], nil)
require.NoError(t, err)
}
func (m *getPutkvMachine) Begin(t *rapid.T) {
if m.modelTX != nil && m.snTX != nil {
return
}
mtx, err := m.modelKV.BeginRw(context.Background())
require.NoError(t, err)
sntx, err := m.snKV.BeginRw(context.Background())
require.NoError(t, err)
m.modelTX = mtx
m.snTX = sntx
}
func (m *getPutkvMachine) Rollback(t *rapid.T) {
if m.modelTX == nil && m.snTX == nil {
return
}
m.snTX.Rollback()
m.modelTX.Rollback()
m.snTX = nil
m.modelTX = nil
}
func (m *getPutkvMachine) Commit(t *rapid.T) {
if m.modelTX == nil && m.snTX == nil {
return
}
err := m.modelTX.Commit()
require.NoError(t, err)
err = m.snTX.Commit()
require.NoError(t, err)
m.snTX = nil
m.modelTX = nil
}
type getKVMachine struct {
bucket string
snKV RwKV
modelKV RwKV
overWriteKeys [][20]byte
snKeys [][20]byte
newKeys [][20]byte
allKeys [][20]byte
}
func (m *getKVMachine) Init(t *rapid.T) {
m.bucket = dbutils.PlainStateBucket
m.snKV = NewMemKV()
m.modelKV = NewMemKV()
m.snKeys = rapid.SliceOf(rapid.ArrayOf(20, rapid.Byte())).Filter(func(_v [][20]byte) bool {
return len(_v) > 0
}).Draw(t, "generate keys").([][20]byte)
m.overWriteKeys = rapid.SliceOf(rapid.SampledFrom(m.snKeys)).Draw(t, "get snKeys").([][20]byte)
m.newKeys = rapid.SliceOf(rapid.ArrayOf(20, rapid.Byte())).Draw(t, "generate new keys").([][20]byte)
m.allKeys = append(m.snKeys, m.overWriteKeys...)
m.allKeys = append(m.allKeys, m.newKeys...)
notExistingKeys := rapid.SliceOf(rapid.ArrayOf(20, rapid.Byte())).Draw(t, "generate not excisting keys").([][20]byte)
m.allKeys = append(m.allKeys, notExistingKeys...)
txSn, err := m.snKV.BeginRw(context.Background())
require.NoError(t, err)
txModel, err := m.modelKV.BeginRw(context.Background())
require.NoError(t, err)
defer txModel.Rollback()
for _, key := range m.snKeys {
innerErr := txSn.Put(m.bucket, key[:], []byte("sn_"+common.Bytes2Hex(key[:])))
require.NoError(t, innerErr)
innerErr = txModel.Put(m.bucket, key[:], []byte("sn_"+common.Bytes2Hex(key[:])))
require.NoError(t, innerErr)
}
//save snapshot and wrap new write db
err = txSn.Commit()
require.NoError(t, err)
m.snKV = NewSnapshotKV().SnapshotDB([]string{m.bucket}, m.snKV).DB(NewMemKV()).Open()
txSn, err = m.snKV.BeginRw(context.Background())
require.NoError(t, err)
defer txSn.Rollback()
for _, key := range m.overWriteKeys {
innerErr := txSn.Put(m.bucket, key[:], []byte("overwrite_"+common.Bytes2Hex(key[:])))
require.NoError(t, innerErr)
innerErr = txModel.Put(m.bucket, key[:], []byte("overwrite_"+common.Bytes2Hex(key[:])))
require.NoError(t, innerErr)
}
for _, key := range m.newKeys {
innerErr := txSn.Put(m.bucket, key[:], []byte("new_"+common.Bytes2Hex(key[:])))
require.NoError(t, innerErr)
innerErr = txModel.Put(m.bucket, key[:], []byte("new_"+common.Bytes2Hex(key[:])))
require.NoError(t, innerErr)
}
err = txSn.Commit()
require.NoError(t, err)
err = txModel.Commit()
require.NoError(t, err)
}
func (m *getKVMachine) Cleanup() {
m.snKV.Close()
m.modelKV.Close()
}
func (m *getKVMachine) Check(t *rapid.T) {
}
func (m *getKVMachine) Get(t *rapid.T) {
key := rapid.SampledFrom(m.allKeys).Draw(t, "get a key").([20]byte)
var (
v1, v2 []byte
err1, err2 error
)
err := m.snKV.View(context.Background(), func(tx Tx) error {
v1, err1 = tx.GetOne(m.bucket, key[:])
return nil
})
require.NoError(t, err)
err = m.modelKV.View(context.Background(), func(tx Tx) error {
v2, err2 = tx.GetOne(m.bucket, key[:])
return nil
})
require.NoError(t, err)
require.Equal(t, err1, err2)
require.Equal(t, v1, v2)
}
func TestGet(t *testing.T) {
t.Skip("remove when it become stable for 200 rounds")
rapid.Check(t, rapid.Run(&getKVMachine{}))
}
func TestCursorWithTX(t *testing.T) {
t.Skip("remove when it become stable for 200 rounds")
rapid.Check(t, rapid.Run(&cursorKVMachine{}))
}
type cursorKVMachine struct {
bucket string
snKV RwKV
modelKV RwKV
snTX RwTx
modelTX RwTx
snCursor RwCursor
modelCursor RwCursor
snapshotKeys [][20]byte
newKeys [][20]byte
allKeys [][20]byte
}
func (m *cursorKVMachine) Init(t *rapid.T) {
m.bucket = dbutils.PlainStateBucket
m.snKV = NewMemKV()
m.modelKV = NewMemKV()
m.snapshotKeys = rapid.SliceOf(rapid.ArrayOf(20, rapid.Byte())).Filter(func(_v [][20]byte) bool {
return len(_v) > 0
}).Draw(t, "generate keys").([][20]byte)
m.newKeys = rapid.SliceOf(rapid.ArrayOf(20, rapid.Byte())).Filter(func(_v [][20]byte) bool {
return len(_v) > 0
}).Draw(t, "generate new keys").([][20]byte)
notExistingKeys := rapid.SliceOf(rapid.ArrayOf(20, rapid.Byte())).Filter(func(_v [][20]byte) bool {
return len(_v) > 0
}).Draw(t, "generate not excisting keys").([][20]byte)
m.allKeys = append(m.snapshotKeys, notExistingKeys...)
defer func() {
m.snTX = nil
m.modelTX = nil
}()
txSn, err := m.snKV.BeginRw(context.Background())
require.NoError(t, err)
defer txSn.Rollback()
txModel, err := m.modelKV.BeginRw(context.Background())
require.NoError(t, err)
defer txModel.Rollback()
for _, key := range m.snapshotKeys {
innerErr := txSn.Put(m.bucket, key[:], []byte("sn_"+common.Bytes2Hex(key[:])))
require.NoError(t, innerErr)
innerErr = txModel.Put(m.bucket, key[:], []byte("sn_"+common.Bytes2Hex(key[:])))
require.NoError(t, innerErr)
}
//save snapshot and wrap new write db
err = txSn.Commit()
require.NoError(t, err)
err = txModel.Commit()
require.NoError(t, err)
m.snKV = NewSnapshotKV().SnapshotDB([]string{m.bucket}, m.snKV).DB(NewMemKV()).Open()
}
func (m *cursorKVMachine) Check(t *rapid.T) {
}
func (m *cursorKVMachine) Cleanup() {
if m.snTX != nil {
m.snTX.Rollback()
}
if m.modelTX != nil {
m.modelTX.Rollback()
}
m.snKV.Close()
m.snKV = nil
m.modelKV.Close()
m.modelKV = nil
}
func (m *cursorKVMachine) Begin(t *rapid.T) {
if m.modelTX != nil && m.snTX != nil {
return
}
mtx, err := m.modelKV.BeginRw(context.Background())
require.NoError(t, err)
sntx, err := m.snKV.BeginRw(context.Background())
require.NoError(t, err)
m.modelTX = mtx
m.snTX = sntx
}
func (m *cursorKVMachine) Rollback(t *rapid.T) {
if m.modelTX == nil && m.snTX == nil {
return
}
m.snTX.Rollback()
m.modelTX.Rollback()
m.snTX = nil
m.modelTX = nil
m.snCursor = nil
m.modelCursor = nil
}
func (m *cursorKVMachine) Commit(t *rapid.T) {
if m.modelTX == nil && m.snTX == nil {
return
}
err := m.modelTX.Commit()
require.NoError(t, err)
err = m.snTX.Commit()
require.NoError(t, err)
m.snTX = nil
m.modelTX = nil
m.snCursor = nil
m.modelCursor = nil
}
func (m *cursorKVMachine) Cursor(t *rapid.T) {
if m.modelTX == nil && m.snTX == nil {
return
}
if m.modelCursor != nil && m.snCursor != nil {
return
}
var err error
m.modelCursor, err = m.modelTX.RwCursor(m.bucket)
require.NoError(t, err)
m.snCursor, err = m.snTX.RwCursor(m.bucket)
require.NoError(t, err)
}
func (m *cursorKVMachine) CloseCursor(t *rapid.T) {
if m.modelTX == nil && m.snTX == nil {
return
}
if m.modelCursor == nil && m.snCursor == nil {
return
}
m.modelCursor.Close()
m.snCursor.Close()
m.modelCursor = nil
m.snCursor = nil
}
func (m *cursorKVMachine) First(t *rapid.T) {
if m.modelCursor == nil && m.snCursor == nil {
return
}
k1, v1, err1 := m.modelCursor.First()
k2, v2, err2 := m.snCursor.First()
require.Equal(t, k1, k2)
require.Equal(t, v1, v2)
require.Equal(t, err1, err2)
}
func (m *cursorKVMachine) Last(t *rapid.T) {
if m.modelCursor == nil && m.snCursor == nil {
return
}
k1, v1, err1 := m.modelCursor.Last()
k2, v2, err2 := m.snCursor.Last()
require.Equal(t, k1, k2)
require.Equal(t, v1, v2)
require.Equal(t, err1, err2)
}
func (m *cursorKVMachine) Seek(t *rapid.T) {
if m.modelCursor == nil && m.snCursor == nil {
return
}
key := rapid.SampledFrom(m.allKeys).Draw(t, "get random key").([20]byte)
k1, v1, err1 := m.modelCursor.Seek(key[:])
k2, v2, err2 := m.snCursor.Seek(key[:])
require.Equal(t, k1, k2)
require.Equal(t, v1, v2)
require.Equal(t, err1, err2)
}
func (m *cursorKVMachine) SeekExact(t *rapid.T) {
if m.modelCursor == nil && m.snCursor == nil {
return
}
key := rapid.SampledFrom(m.allKeys).Draw(t, "get random key").([20]byte)
k1, v1, err1 := m.modelCursor.SeekExact(key[:])
k2, v2, err2 := m.snCursor.SeekExact(key[:])
require.Equal(t, k1, k2)
require.Equal(t, v1, v2)
require.Equal(t, err1, err2)
}
func (m *cursorKVMachine) Next(t *rapid.T) {
if m.modelCursor == nil && m.snCursor == nil {
return
}
k1, v1, err1 := m.modelCursor.Next()
k2, v2, err2 := m.snCursor.Next()
require.Equal(t, k1, k2)
require.Equal(t, v1, v2)
require.Equal(t, err1, err2)
}