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 }