mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 15:16:43 +00:00
344 lines
8.2 KiB
Go
344 lines
8.2 KiB
Go
package blockstm
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
"math/rand"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
)
|
|
|
|
var randomness = rand.Intn(10) + 10
|
|
|
|
// create test data for a given txIdx and incarnation
|
|
func valueFor(txIdx, inc int) []byte {
|
|
return []byte(fmt.Sprintf("%ver:%ver:%ver", txIdx*5, txIdx+inc, inc*5))
|
|
}
|
|
|
|
func getCommonAddress(i int) common.Address {
|
|
return common.BigToAddress(big.NewInt(int64(i % randomness)))
|
|
}
|
|
|
|
func TestHelperFunctions(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ap1 := NewAddressKey(getCommonAddress(1))
|
|
ap2 := NewAddressKey(getCommonAddress(2))
|
|
|
|
mvh := MakeMVHashMap()
|
|
|
|
mvh.Write(ap1, Version{0, 1}, valueFor(0, 1))
|
|
mvh.Write(ap1, Version{0, 2}, valueFor(0, 2))
|
|
res := mvh.Read(ap1, 0)
|
|
require.Equal(t, -1, res.DepIdx())
|
|
require.Equal(t, -1, res.Incarnation())
|
|
require.Equal(t, 2, res.Status())
|
|
|
|
mvh.Write(ap2, Version{1, 1}, valueFor(1, 1))
|
|
mvh.Write(ap2, Version{1, 2}, valueFor(1, 2))
|
|
res = mvh.Read(ap2, 1)
|
|
require.Equal(t, -1, res.DepIdx())
|
|
require.Equal(t, -1, res.Incarnation())
|
|
require.Equal(t, 2, res.Status())
|
|
|
|
mvh.Write(ap1, Version{2, 1}, valueFor(2, 1))
|
|
mvh.Write(ap1, Version{2, 2}, valueFor(2, 2))
|
|
res = mvh.Read(ap1, 2)
|
|
require.Equal(t, 0, res.DepIdx())
|
|
require.Equal(t, 2, res.Incarnation())
|
|
require.Equal(t, valueFor(0, 2), res.Value().([]byte))
|
|
require.Equal(t, 0, res.Status())
|
|
}
|
|
|
|
func TestFlushMVWrite(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ap1 := NewAddressKey(getCommonAddress(1))
|
|
ap2 := NewAddressKey(getCommonAddress(2))
|
|
|
|
mvh := MakeMVHashMap()
|
|
|
|
var res MVReadResult
|
|
|
|
wd := []WriteDescriptor{}
|
|
|
|
wd = append(wd, WriteDescriptor{
|
|
Path: ap1,
|
|
V: Version{0, 1},
|
|
Val: valueFor(0, 1),
|
|
})
|
|
wd = append(wd, WriteDescriptor{
|
|
Path: ap1,
|
|
V: Version{0, 2},
|
|
Val: valueFor(0, 2),
|
|
})
|
|
wd = append(wd, WriteDescriptor{
|
|
Path: ap2,
|
|
V: Version{1, 1},
|
|
Val: valueFor(1, 1),
|
|
})
|
|
wd = append(wd, WriteDescriptor{
|
|
Path: ap2,
|
|
V: Version{1, 2},
|
|
Val: valueFor(1, 2),
|
|
})
|
|
wd = append(wd, WriteDescriptor{
|
|
Path: ap1,
|
|
V: Version{2, 1},
|
|
Val: valueFor(2, 1),
|
|
})
|
|
wd = append(wd, WriteDescriptor{
|
|
Path: ap1,
|
|
V: Version{2, 2},
|
|
Val: valueFor(2, 2),
|
|
})
|
|
|
|
mvh.FlushMVWriteSet(wd)
|
|
|
|
res = mvh.Read(ap1, 0)
|
|
require.Equal(t, -1, res.DepIdx())
|
|
require.Equal(t, -1, res.Incarnation())
|
|
require.Equal(t, 2, res.Status())
|
|
|
|
res = mvh.Read(ap2, 1)
|
|
require.Equal(t, -1, res.DepIdx())
|
|
require.Equal(t, -1, res.Incarnation())
|
|
require.Equal(t, 2, res.Status())
|
|
|
|
res = mvh.Read(ap1, 2)
|
|
require.Equal(t, 0, res.DepIdx())
|
|
require.Equal(t, 2, res.Incarnation())
|
|
require.Equal(t, valueFor(0, 2), res.Value().([]byte))
|
|
require.Equal(t, 0, res.Status())
|
|
}
|
|
|
|
// TODO - handle panic
|
|
func TestLowerIncarnation(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ap1 := NewAddressKey(getCommonAddress(1))
|
|
|
|
mvh := MakeMVHashMap()
|
|
|
|
mvh.Write(ap1, Version{0, 2}, valueFor(0, 2))
|
|
mvh.Read(ap1, 0)
|
|
mvh.Write(ap1, Version{1, 2}, valueFor(1, 2))
|
|
mvh.Write(ap1, Version{0, 5}, valueFor(0, 5))
|
|
mvh.Write(ap1, Version{1, 5}, valueFor(1, 5))
|
|
}
|
|
|
|
func TestMarkEstimate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ap1 := NewAddressKey(getCommonAddress(1))
|
|
|
|
mvh := MakeMVHashMap()
|
|
|
|
mvh.Write(ap1, Version{7, 2}, valueFor(7, 2))
|
|
mvh.MarkEstimate(ap1, 7)
|
|
mvh.Write(ap1, Version{7, 4}, valueFor(7, 4))
|
|
}
|
|
|
|
func TestMVHashMapBasics(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// memory locations
|
|
ap1 := NewAddressKey(getCommonAddress(1))
|
|
ap2 := NewAddressKey(getCommonAddress(2))
|
|
ap3 := NewAddressKey(getCommonAddress(3))
|
|
|
|
mvh := MakeMVHashMap()
|
|
|
|
res := mvh.Read(ap1, 5)
|
|
require.Equal(t, -1, res.depIdx)
|
|
|
|
mvh.Write(ap1, Version{10, 1}, valueFor(10, 1))
|
|
|
|
res = mvh.Read(ap1, 9)
|
|
require.Equal(t, -1, res.depIdx, "reads that should go the DB return dependency -1")
|
|
res = mvh.Read(ap1, 10)
|
|
require.Equal(t, -1, res.depIdx, "Read returns entries from smaller txns, not txn 10")
|
|
|
|
// Reads for a higher txn return the entry written by txn 10.
|
|
res = mvh.Read(ap1, 15)
|
|
require.Equal(t, 10, res.depIdx, "reads for a higher txn return the entry written by txn 10.")
|
|
require.Equal(t, 1, res.incarnation)
|
|
require.Equal(t, valueFor(10, 1), res.value)
|
|
|
|
// More writes.
|
|
mvh.Write(ap1, Version{12, 0}, valueFor(12, 0))
|
|
mvh.Write(ap1, Version{8, 3}, valueFor(8, 3))
|
|
|
|
// Verify reads.
|
|
res = mvh.Read(ap1, 15)
|
|
require.Equal(t, 12, res.depIdx)
|
|
require.Equal(t, 0, res.incarnation)
|
|
require.Equal(t, valueFor(12, 0), res.value)
|
|
|
|
res = mvh.Read(ap1, 11)
|
|
require.Equal(t, 10, res.depIdx)
|
|
require.Equal(t, 1, res.incarnation)
|
|
require.Equal(t, valueFor(10, 1), res.value)
|
|
|
|
res = mvh.Read(ap1, 10)
|
|
require.Equal(t, 8, res.depIdx)
|
|
require.Equal(t, 3, res.incarnation)
|
|
require.Equal(t, valueFor(8, 3), res.value)
|
|
|
|
// Mark the entry written by 10 as an estimate.
|
|
mvh.MarkEstimate(ap1, 10)
|
|
|
|
res = mvh.Read(ap1, 11)
|
|
require.Equal(t, 10, res.depIdx)
|
|
require.Equal(t, -1, res.incarnation, "dep at tx 10 is now an estimate")
|
|
|
|
// Delete the entry written by 10, write to a different ap.
|
|
mvh.Delete(ap1, 10)
|
|
mvh.Write(ap2, Version{10, 2}, valueFor(10, 2))
|
|
|
|
// Read by txn 11 no longer observes entry from txn 10.
|
|
res = mvh.Read(ap1, 11)
|
|
require.Equal(t, 8, res.depIdx)
|
|
require.Equal(t, 3, res.incarnation)
|
|
require.Equal(t, valueFor(8, 3), res.value)
|
|
|
|
// Reads, writes for ap2 and ap3.
|
|
mvh.Write(ap2, Version{5, 0}, valueFor(5, 0))
|
|
mvh.Write(ap3, Version{20, 4}, valueFor(20, 4))
|
|
|
|
res = mvh.Read(ap2, 10)
|
|
require.Equal(t, 5, res.depIdx)
|
|
require.Equal(t, 0, res.incarnation)
|
|
require.Equal(t, valueFor(5, 0), res.value)
|
|
|
|
res = mvh.Read(ap3, 21)
|
|
require.Equal(t, 20, res.depIdx)
|
|
require.Equal(t, 4, res.incarnation)
|
|
require.Equal(t, valueFor(20, 4), res.value)
|
|
|
|
// Clear ap1 and ap3.
|
|
mvh.Delete(ap1, 12)
|
|
mvh.Delete(ap1, 8)
|
|
mvh.Delete(ap3, 20)
|
|
|
|
// Reads from ap1 and ap3 go to db.
|
|
res = mvh.Read(ap1, 30)
|
|
require.Equal(t, -1, res.depIdx)
|
|
|
|
res = mvh.Read(ap3, 30)
|
|
require.Equal(t, -1, res.depIdx)
|
|
|
|
// No-op delete at ap2 - doesn't panic because ap2 does exist
|
|
mvh.Delete(ap2, 11)
|
|
|
|
// Read entry by txn 10 at ap2.
|
|
res = mvh.Read(ap2, 15)
|
|
require.Equal(t, 10, res.depIdx)
|
|
require.Equal(t, 2, res.incarnation)
|
|
require.Equal(t, valueFor(10, 2), res.value)
|
|
}
|
|
|
|
func BenchmarkWriteTimeSameLocationDifferentTxIdx(b *testing.B) {
|
|
mvh2 := MakeMVHashMap()
|
|
ap2 := NewAddressKey(getCommonAddress(2))
|
|
|
|
randInts := []int{}
|
|
for i := 0; i < b.N; i++ {
|
|
randInts = append(randInts, rand.Intn(1000000000000000))
|
|
}
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
mvh2.Write(ap2, Version{randInts[i], 1}, valueFor(randInts[i], 1))
|
|
}
|
|
}
|
|
|
|
func BenchmarkReadTimeSameLocationDifferentTxIdx(b *testing.B) {
|
|
mvh2 := MakeMVHashMap()
|
|
ap2 := NewAddressKey(getCommonAddress(2))
|
|
txIdxSlice := []int{}
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
txIdx := rand.Intn(1000000000000000)
|
|
txIdxSlice = append(txIdxSlice, txIdx)
|
|
mvh2.Write(ap2, Version{txIdx, 1}, valueFor(txIdx, 1))
|
|
}
|
|
|
|
b.ResetTimer()
|
|
|
|
for _, value := range txIdxSlice {
|
|
mvh2.Read(ap2, value)
|
|
}
|
|
}
|
|
|
|
func TestTimeComplexity(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// for 1000000 read and write with no dependency at different memory location
|
|
mvh1 := MakeMVHashMap()
|
|
|
|
for i := 0; i < 1000000; i++ {
|
|
ap1 := NewAddressKey(getCommonAddress(i))
|
|
mvh1.Write(ap1, Version{i, 1}, valueFor(i, 1))
|
|
mvh1.Read(ap1, i)
|
|
}
|
|
|
|
// for 1000000 read and write with dependency at same memory location
|
|
mvh2 := MakeMVHashMap()
|
|
ap2 := NewAddressKey(getCommonAddress(2))
|
|
|
|
for i := 0; i < 1000000; i++ {
|
|
mvh2.Write(ap2, Version{i, 1}, valueFor(i, 1))
|
|
mvh2.Read(ap2, i)
|
|
}
|
|
}
|
|
|
|
func TestWriteTimeSameLocationDifferentTxnIdx(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
mvh1 := MakeMVHashMap()
|
|
ap1 := NewAddressKey(getCommonAddress(1))
|
|
|
|
for i := 0; i < 1000000; i++ {
|
|
mvh1.Write(ap1, Version{i, 1}, valueFor(i, 1))
|
|
}
|
|
}
|
|
|
|
func TestWriteTimeSameLocationSameTxnIdx(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
mvh1 := MakeMVHashMap()
|
|
ap1 := NewAddressKey(getCommonAddress(1))
|
|
|
|
for i := 0; i < 1000000; i++ {
|
|
mvh1.Write(ap1, Version{1, i}, valueFor(i, 1))
|
|
}
|
|
}
|
|
|
|
func TestWriteTimeDifferentLocation(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
mvh1 := MakeMVHashMap()
|
|
|
|
for i := 0; i < 1000000; i++ {
|
|
ap1 := NewAddressKey(getCommonAddress(i))
|
|
mvh1.Write(ap1, Version{i, 1}, valueFor(i, 1))
|
|
}
|
|
}
|
|
|
|
func TestReadTimeSameLocation(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
mvh1 := MakeMVHashMap()
|
|
ap1 := NewAddressKey(getCommonAddress(1))
|
|
|
|
mvh1.Write(ap1, Version{1, 1}, valueFor(1, 1))
|
|
|
|
for i := 0; i < 1000000; i++ {
|
|
mvh1.Read(ap1, 2)
|
|
}
|
|
}
|