github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/core/blockstm/mvhashmap_test.go (about)

     1  package blockstm
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"math/rand"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/ethereum/go-ethereum/common"
    12  )
    13  
    14  var randomness = rand.Intn(10) + 10
    15  
    16  // create test data for a given txIdx and incarnation
    17  func valueFor(txIdx, inc int) []byte {
    18  	return []byte(fmt.Sprintf("%ver:%ver:%ver", txIdx*5, txIdx+inc, inc*5))
    19  }
    20  
    21  func getCommonAddress(i int) common.Address {
    22  	return common.BigToAddress(big.NewInt(int64(i % randomness)))
    23  }
    24  
    25  func TestHelperFunctions(t *testing.T) {
    26  	t.Parallel()
    27  
    28  	ap1 := NewAddressKey(getCommonAddress(1))
    29  	ap2 := NewAddressKey(getCommonAddress(2))
    30  
    31  	mvh := MakeMVHashMap()
    32  
    33  	mvh.Write(ap1, Version{0, 1}, valueFor(0, 1))
    34  	mvh.Write(ap1, Version{0, 2}, valueFor(0, 2))
    35  	res := mvh.Read(ap1, 0)
    36  	require.Equal(t, -1, res.DepIdx())
    37  	require.Equal(t, -1, res.Incarnation())
    38  	require.Equal(t, 2, res.Status())
    39  
    40  	mvh.Write(ap2, Version{1, 1}, valueFor(1, 1))
    41  	mvh.Write(ap2, Version{1, 2}, valueFor(1, 2))
    42  	res = mvh.Read(ap2, 1)
    43  	require.Equal(t, -1, res.DepIdx())
    44  	require.Equal(t, -1, res.Incarnation())
    45  	require.Equal(t, 2, res.Status())
    46  
    47  	mvh.Write(ap1, Version{2, 1}, valueFor(2, 1))
    48  	mvh.Write(ap1, Version{2, 2}, valueFor(2, 2))
    49  	res = mvh.Read(ap1, 2)
    50  	require.Equal(t, 0, res.DepIdx())
    51  	require.Equal(t, 2, res.Incarnation())
    52  	require.Equal(t, valueFor(0, 2), res.Value().([]byte))
    53  	require.Equal(t, 0, res.Status())
    54  }
    55  
    56  func TestFlushMVWrite(t *testing.T) {
    57  	t.Parallel()
    58  
    59  	ap1 := NewAddressKey(getCommonAddress(1))
    60  	ap2 := NewAddressKey(getCommonAddress(2))
    61  
    62  	mvh := MakeMVHashMap()
    63  
    64  	var res MVReadResult
    65  
    66  	wd := []WriteDescriptor{}
    67  
    68  	wd = append(wd, WriteDescriptor{
    69  		Path: ap1,
    70  		V:    Version{0, 1},
    71  		Val:  valueFor(0, 1),
    72  	})
    73  	wd = append(wd, WriteDescriptor{
    74  		Path: ap1,
    75  		V:    Version{0, 2},
    76  		Val:  valueFor(0, 2),
    77  	})
    78  	wd = append(wd, WriteDescriptor{
    79  		Path: ap2,
    80  		V:    Version{1, 1},
    81  		Val:  valueFor(1, 1),
    82  	})
    83  	wd = append(wd, WriteDescriptor{
    84  		Path: ap2,
    85  		V:    Version{1, 2},
    86  		Val:  valueFor(1, 2),
    87  	})
    88  	wd = append(wd, WriteDescriptor{
    89  		Path: ap1,
    90  		V:    Version{2, 1},
    91  		Val:  valueFor(2, 1),
    92  	})
    93  	wd = append(wd, WriteDescriptor{
    94  		Path: ap1,
    95  		V:    Version{2, 2},
    96  		Val:  valueFor(2, 2),
    97  	})
    98  
    99  	mvh.FlushMVWriteSet(wd)
   100  
   101  	res = mvh.Read(ap1, 0)
   102  	require.Equal(t, -1, res.DepIdx())
   103  	require.Equal(t, -1, res.Incarnation())
   104  	require.Equal(t, 2, res.Status())
   105  
   106  	res = mvh.Read(ap2, 1)
   107  	require.Equal(t, -1, res.DepIdx())
   108  	require.Equal(t, -1, res.Incarnation())
   109  	require.Equal(t, 2, res.Status())
   110  
   111  	res = mvh.Read(ap1, 2)
   112  	require.Equal(t, 0, res.DepIdx())
   113  	require.Equal(t, 2, res.Incarnation())
   114  	require.Equal(t, valueFor(0, 2), res.Value().([]byte))
   115  	require.Equal(t, 0, res.Status())
   116  }
   117  
   118  // TODO - handle panic
   119  func TestLowerIncarnation(t *testing.T) {
   120  	t.Parallel()
   121  
   122  	ap1 := NewAddressKey(getCommonAddress(1))
   123  
   124  	mvh := MakeMVHashMap()
   125  
   126  	mvh.Write(ap1, Version{0, 2}, valueFor(0, 2))
   127  	mvh.Read(ap1, 0)
   128  	mvh.Write(ap1, Version{1, 2}, valueFor(1, 2))
   129  	mvh.Write(ap1, Version{0, 5}, valueFor(0, 5))
   130  	mvh.Write(ap1, Version{1, 5}, valueFor(1, 5))
   131  }
   132  
   133  func TestMarkEstimate(t *testing.T) {
   134  	t.Parallel()
   135  
   136  	ap1 := NewAddressKey(getCommonAddress(1))
   137  
   138  	mvh := MakeMVHashMap()
   139  
   140  	mvh.Write(ap1, Version{7, 2}, valueFor(7, 2))
   141  	mvh.MarkEstimate(ap1, 7)
   142  	mvh.Write(ap1, Version{7, 4}, valueFor(7, 4))
   143  }
   144  
   145  func TestMVHashMapBasics(t *testing.T) {
   146  	t.Parallel()
   147  
   148  	// memory locations
   149  	ap1 := NewAddressKey(getCommonAddress(1))
   150  	ap2 := NewAddressKey(getCommonAddress(2))
   151  	ap3 := NewAddressKey(getCommonAddress(3))
   152  
   153  	mvh := MakeMVHashMap()
   154  
   155  	res := mvh.Read(ap1, 5)
   156  	require.Equal(t, -1, res.depIdx)
   157  
   158  	mvh.Write(ap1, Version{10, 1}, valueFor(10, 1))
   159  
   160  	res = mvh.Read(ap1, 9)
   161  	require.Equal(t, -1, res.depIdx, "reads that should go the the DB return dependency -1")
   162  	res = mvh.Read(ap1, 10)
   163  	require.Equal(t, -1, res.depIdx, "Read returns entries from smaller txns, not txn 10")
   164  
   165  	// Reads for a higher txn return the entry written by txn 10.
   166  	res = mvh.Read(ap1, 15)
   167  	require.Equal(t, 10, res.depIdx, "reads for a higher txn return the entry written by txn 10.")
   168  	require.Equal(t, 1, res.incarnation)
   169  	require.Equal(t, valueFor(10, 1), res.value)
   170  
   171  	// More writes.
   172  	mvh.Write(ap1, Version{12, 0}, valueFor(12, 0))
   173  	mvh.Write(ap1, Version{8, 3}, valueFor(8, 3))
   174  
   175  	// Verify reads.
   176  	res = mvh.Read(ap1, 15)
   177  	require.Equal(t, 12, res.depIdx)
   178  	require.Equal(t, 0, res.incarnation)
   179  	require.Equal(t, valueFor(12, 0), res.value)
   180  
   181  	res = mvh.Read(ap1, 11)
   182  	require.Equal(t, 10, res.depIdx)
   183  	require.Equal(t, 1, res.incarnation)
   184  	require.Equal(t, valueFor(10, 1), res.value)
   185  
   186  	res = mvh.Read(ap1, 10)
   187  	require.Equal(t, 8, res.depIdx)
   188  	require.Equal(t, 3, res.incarnation)
   189  	require.Equal(t, valueFor(8, 3), res.value)
   190  
   191  	// Mark the entry written by 10 as an estimate.
   192  	mvh.MarkEstimate(ap1, 10)
   193  
   194  	res = mvh.Read(ap1, 11)
   195  	require.Equal(t, 10, res.depIdx)
   196  	require.Equal(t, -1, res.incarnation, "dep at tx 10 is now an estimate")
   197  
   198  	// Delete the entry written by 10, write to a different ap.
   199  	mvh.Delete(ap1, 10)
   200  	mvh.Write(ap2, Version{10, 2}, valueFor(10, 2))
   201  
   202  	// Read by txn 11 no longer observes entry from txn 10.
   203  	res = mvh.Read(ap1, 11)
   204  	require.Equal(t, 8, res.depIdx)
   205  	require.Equal(t, 3, res.incarnation)
   206  	require.Equal(t, valueFor(8, 3), res.value)
   207  
   208  	// Reads, writes for ap2 and ap3.
   209  	mvh.Write(ap2, Version{5, 0}, valueFor(5, 0))
   210  	mvh.Write(ap3, Version{20, 4}, valueFor(20, 4))
   211  
   212  	res = mvh.Read(ap2, 10)
   213  	require.Equal(t, 5, res.depIdx)
   214  	require.Equal(t, 0, res.incarnation)
   215  	require.Equal(t, valueFor(5, 0), res.value)
   216  
   217  	res = mvh.Read(ap3, 21)
   218  	require.Equal(t, 20, res.depIdx)
   219  	require.Equal(t, 4, res.incarnation)
   220  	require.Equal(t, valueFor(20, 4), res.value)
   221  
   222  	// Clear ap1 and ap3.
   223  	mvh.Delete(ap1, 12)
   224  	mvh.Delete(ap1, 8)
   225  	mvh.Delete(ap3, 20)
   226  
   227  	// Reads from ap1 and ap3 go to db.
   228  	res = mvh.Read(ap1, 30)
   229  	require.Equal(t, -1, res.depIdx)
   230  
   231  	res = mvh.Read(ap3, 30)
   232  	require.Equal(t, -1, res.depIdx)
   233  
   234  	// No-op delete at ap2 - doesn't panic because ap2 does exist
   235  	mvh.Delete(ap2, 11)
   236  
   237  	// Read entry by txn 10 at ap2.
   238  	res = mvh.Read(ap2, 15)
   239  	require.Equal(t, 10, res.depIdx)
   240  	require.Equal(t, 2, res.incarnation)
   241  	require.Equal(t, valueFor(10, 2), res.value)
   242  }
   243  
   244  func BenchmarkWriteTimeSameLocationDifferentTxIdx(b *testing.B) {
   245  	mvh2 := MakeMVHashMap()
   246  	ap2 := NewAddressKey(getCommonAddress(2))
   247  
   248  	randInts := []int{}
   249  	for i := 0; i < b.N; i++ {
   250  		randInts = append(randInts, rand.Intn(1000000000000000))
   251  	}
   252  
   253  	b.ResetTimer()
   254  
   255  	for i := 0; i < b.N; i++ {
   256  		mvh2.Write(ap2, Version{randInts[i], 1}, valueFor(randInts[i], 1))
   257  	}
   258  }
   259  
   260  func BenchmarkReadTimeSameLocationDifferentTxIdx(b *testing.B) {
   261  	mvh2 := MakeMVHashMap()
   262  	ap2 := NewAddressKey(getCommonAddress(2))
   263  	txIdxSlice := []int{}
   264  
   265  	for i := 0; i < b.N; i++ {
   266  		txIdx := rand.Intn(1000000000000000)
   267  		txIdxSlice = append(txIdxSlice, txIdx)
   268  		mvh2.Write(ap2, Version{txIdx, 1}, valueFor(txIdx, 1))
   269  	}
   270  
   271  	b.ResetTimer()
   272  
   273  	for _, value := range txIdxSlice {
   274  		mvh2.Read(ap2, value)
   275  	}
   276  }
   277  
   278  func TestTimeComplexity(t *testing.T) {
   279  	t.Parallel()
   280  
   281  	// for 1000000 read and write with no dependency at different memory location
   282  	mvh1 := MakeMVHashMap()
   283  
   284  	for i := 0; i < 1000000; i++ {
   285  		ap1 := NewAddressKey(getCommonAddress(i))
   286  		mvh1.Write(ap1, Version{i, 1}, valueFor(i, 1))
   287  		mvh1.Read(ap1, i)
   288  	}
   289  
   290  	// for 1000000 read and write with dependency at same memory location
   291  	mvh2 := MakeMVHashMap()
   292  	ap2 := NewAddressKey(getCommonAddress(2))
   293  
   294  	for i := 0; i < 1000000; i++ {
   295  		mvh2.Write(ap2, Version{i, 1}, valueFor(i, 1))
   296  		mvh2.Read(ap2, i)
   297  	}
   298  }
   299  
   300  func TestWriteTimeSameLocationDifferentTxnIdx(t *testing.T) {
   301  	t.Parallel()
   302  
   303  	mvh1 := MakeMVHashMap()
   304  	ap1 := NewAddressKey(getCommonAddress(1))
   305  
   306  	for i := 0; i < 1000000; i++ {
   307  		mvh1.Write(ap1, Version{i, 1}, valueFor(i, 1))
   308  	}
   309  }
   310  
   311  func TestWriteTimeSameLocationSameTxnIdx(t *testing.T) {
   312  	t.Parallel()
   313  
   314  	mvh1 := MakeMVHashMap()
   315  	ap1 := NewAddressKey(getCommonAddress(1))
   316  
   317  	for i := 0; i < 1000000; i++ {
   318  		mvh1.Write(ap1, Version{1, i}, valueFor(i, 1))
   319  	}
   320  }
   321  
   322  func TestWriteTimeDifferentLocation(t *testing.T) {
   323  	t.Parallel()
   324  
   325  	mvh1 := MakeMVHashMap()
   326  
   327  	for i := 0; i < 1000000; i++ {
   328  		ap1 := NewAddressKey(getCommonAddress(i))
   329  		mvh1.Write(ap1, Version{i, 1}, valueFor(i, 1))
   330  	}
   331  }
   332  
   333  func TestReadTimeSameLocation(t *testing.T) {
   334  	t.Parallel()
   335  
   336  	mvh1 := MakeMVHashMap()
   337  	ap1 := NewAddressKey(getCommonAddress(1))
   338  
   339  	mvh1.Write(ap1, Version{1, 1}, valueFor(1, 1))
   340  
   341  	for i := 0; i < 1000000; i++ {
   342  		mvh1.Read(ap1, 2)
   343  	}
   344  }