github.com/ledgerwatch/erigon-lib@v1.0.0/kv/kvcache/cache_test.go (about) 1 /* 2 Copyright 2021 Erigon contributors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 package kvcache 17 18 import ( 19 "context" 20 "encoding/binary" 21 "fmt" 22 "sync" 23 "testing" 24 25 "github.com/ledgerwatch/erigon-lib/common" 26 "github.com/ledgerwatch/erigon-lib/gointerfaces" 27 "github.com/ledgerwatch/erigon-lib/gointerfaces/remote" 28 "github.com/ledgerwatch/erigon-lib/kv" 29 "github.com/ledgerwatch/erigon-lib/kv/memdb" 30 "github.com/stretchr/testify/require" 31 ) 32 33 func TestEvictionInUnexpectedOrder(t *testing.T) { 34 // Order: View - 2, OnNewBlock - 2, View - 5, View - 6, OnNewBlock - 3, OnNewBlock - 4, View - 5, OnNewBlock - 5, OnNewBlock - 100 35 require := require.New(t) 36 cfg := DefaultCoherentConfig 37 cfg.CacheSize = 3 38 cfg.NewBlockWait = 0 39 c := New(cfg) 40 c.selectOrCreateRoot(2) 41 require.Equal(1, len(c.roots)) 42 require.Equal(0, int(c.latestStateVersionID)) 43 require.False(c.roots[2].isCanonical) 44 45 c.add([]byte{1}, nil, c.roots[2], 2) 46 require.Equal(0, c.stateEvict.Len()) 47 48 c.advanceRoot(2) 49 require.Equal(1, len(c.roots)) 50 require.Equal(2, int(c.latestStateVersionID)) 51 require.True(c.roots[2].isCanonical) 52 53 c.add([]byte{1}, nil, c.roots[2], 2) 54 require.Equal(1, c.stateEvict.Len()) 55 56 c.selectOrCreateRoot(5) 57 require.Equal(2, len(c.roots)) 58 require.Equal(2, int(c.latestStateVersionID)) 59 require.False(c.roots[5].isCanonical) 60 61 c.add([]byte{2}, nil, c.roots[5], 5) // not added to evict list 62 require.Equal(1, c.stateEvict.Len()) 63 c.add([]byte{2}, nil, c.roots[2], 2) // added to evict list, because it's latest view 64 require.Equal(2, c.stateEvict.Len()) 65 66 c.selectOrCreateRoot(6) 67 require.Equal(3, len(c.roots)) 68 require.Equal(2, int(c.latestStateVersionID)) 69 require.False(c.roots[6].isCanonical) // parrent exists, but parent has isCanonical=false 70 71 c.advanceRoot(3) 72 require.Equal(4, len(c.roots)) 73 require.Equal(3, int(c.latestStateVersionID)) 74 require.True(c.roots[3].isCanonical) 75 76 c.advanceRoot(4) 77 require.Equal(5, len(c.roots)) 78 require.Equal(4, int(c.latestStateVersionID)) 79 require.True(c.roots[4].isCanonical) 80 81 c.selectOrCreateRoot(5) 82 require.Equal(5, len(c.roots)) 83 require.Equal(4, int(c.latestStateVersionID)) 84 require.False(c.roots[5].isCanonical) 85 86 c.advanceRoot(5) 87 require.Equal(5, len(c.roots)) 88 require.Equal(5, int(c.latestStateVersionID)) 89 require.True(c.roots[5].isCanonical) 90 91 c.advanceRoot(100) 92 require.Equal(6, len(c.roots)) 93 require.Equal(100, int(c.latestStateVersionID)) 94 require.True(c.roots[100].isCanonical) 95 96 //c.add([]byte{1}, nil, c.roots[2], 2) 97 require.Equal(0, c.latestStateView.cache.Len()) 98 require.Equal(0, c.stateEvict.Len()) 99 } 100 101 func TestEviction(t *testing.T) { 102 require, ctx := require.New(t), context.Background() 103 cfg := DefaultCoherentConfig 104 cfg.CacheSize = 21 105 cfg.NewBlockWait = 0 106 c := New(cfg) 107 db := memdb.NewTestDB(t) 108 k1, k2 := [20]byte{1}, [20]byte{2} 109 110 var id uint64 111 _ = db.Update(ctx, func(tx kv.RwTx) error { 112 _ = tx.Put(kv.PlainState, k1[:], []byte{1}) 113 id = tx.ViewID() 114 var versionID [8]byte 115 binary.BigEndian.PutUint64(versionID[:], id) 116 _ = tx.Put(kv.Sequence, kv.PlainStateVersion, versionID[:]) 117 cacheView, _ := c.View(ctx, tx) 118 view := cacheView.(*CoherentView) 119 _, _ = c.Get(k1[:], tx, view.stateVersionID) 120 _, _ = c.Get([]byte{1}, tx, view.stateVersionID) 121 _, _ = c.Get([]byte{2}, tx, view.stateVersionID) 122 _, _ = c.Get([]byte{3}, tx, view.stateVersionID) 123 //require.Equal(c.roots[c.latestViewID].cache.Len(), c.stateEvict.Len()) 124 return nil 125 }) 126 require.Equal(0, c.stateEvict.Len()) 127 //require.Equal(c.roots[c.latestViewID].cache.Len(), c.stateEvict.Len()) 128 c.OnNewBlock(&remote.StateChangeBatch{ 129 StateVersionId: id + 1, 130 ChangeBatch: []*remote.StateChange{ 131 { 132 Direction: remote.Direction_FORWARD, 133 Changes: []*remote.AccountChange{{ 134 Action: remote.Action_UPSERT, 135 Address: gointerfaces.ConvertAddressToH160(k1), 136 Data: []byte{2}, 137 }}, 138 }, 139 }, 140 }) 141 require.Equal(21, c.stateEvict.Size()) 142 require.Equal(1, c.stateEvict.Len()) 143 require.Equal(c.roots[c.latestStateVersionID].cache.Len(), c.stateEvict.Len()) 144 _ = db.Update(ctx, func(tx kv.RwTx) error { 145 _ = tx.Put(kv.PlainState, k1[:], []byte{1}) 146 id = tx.ViewID() 147 cacheView, _ := c.View(ctx, tx) 148 var versionID [8]byte 149 binary.BigEndian.PutUint64(versionID[:], id) 150 _ = tx.Put(kv.Sequence, kv.PlainStateVersion, versionID[:]) 151 view := cacheView.(*CoherentView) 152 _, _ = c.Get(k1[:], tx, view.stateVersionID) 153 _, _ = c.Get(k2[:], tx, view.stateVersionID) 154 _, _ = c.Get([]byte{5}, tx, view.stateVersionID) 155 _, _ = c.Get([]byte{6}, tx, view.stateVersionID) 156 return nil 157 }) 158 require.Equal(c.roots[c.latestStateVersionID].cache.Len(), c.stateEvict.Len()) 159 require.Equal(int(cfg.CacheSize.Bytes()), c.stateEvict.Size()) 160 } 161 162 func TestAPI(t *testing.T) { 163 require := require.New(t) 164 c := New(DefaultCoherentConfig) 165 k1, k2 := [20]byte{1}, [20]byte{2} 166 db := memdb.NewTestDB(t) 167 get := func(key [20]byte, expectTxnID uint64) (res [1]chan []byte) { 168 wg := sync.WaitGroup{} 169 for i := 0; i < len(res); i++ { 170 wg.Add(1) 171 res[i] = make(chan []byte) 172 go func(out chan []byte) { 173 require.NoError(db.View(context.Background(), func(tx kv.Tx) error { 174 if expectTxnID != tx.ViewID() { 175 panic(fmt.Sprintf("epxected: %d, got: %d", expectTxnID, tx.ViewID())) 176 } 177 wg.Done() 178 cacheView, err := c.View(context.Background(), tx) 179 view := cacheView.(*CoherentView) 180 if err != nil { 181 panic(err) 182 } 183 v, err := c.Get(key[:], tx, view.stateVersionID) 184 if err != nil { 185 panic(err) 186 } 187 out <- common.Copy(v) 188 return nil 189 })) 190 }(res[i]) 191 } 192 wg.Wait() // ensure that all goroutines started their transactions 193 return res 194 } 195 put := func(k, v []byte) uint64 { 196 var txID uint64 197 require.NoError(db.Update(context.Background(), func(tx kv.RwTx) error { 198 _ = tx.Put(kv.PlainState, k, v) 199 txID = tx.ViewID() 200 var versionID [8]byte 201 binary.BigEndian.PutUint64(versionID[:], txID) 202 _ = tx.Put(kv.Sequence, kv.PlainStateVersion, versionID[:]) 203 return nil 204 })) 205 return txID 206 } 207 // block 1 - represents existing state (no notifications about this data will come to client) 208 txID1 := put(k2[:], []byte{42}) 209 210 wg := sync.WaitGroup{} 211 212 res1, res2 := get(k1, txID1), get(k2, txID1) // will return immediately 213 wg.Add(1) 214 go func() { 215 defer wg.Done() 216 for i := range res1 { 217 require.Nil(<-res1[i]) 218 } 219 for i := range res2 { 220 require.Equal([]byte{42}, <-res2[i]) 221 } 222 fmt.Printf("done1: \n") 223 }() 224 225 txID2 := put(k1[:], []byte{2}) 226 fmt.Printf("-----1 %d, %d\n", txID1, txID2) 227 res3, res4 := get(k1, txID2), get(k2, txID2) // will see View of transaction 2 228 txID3 := put(k1[:], []byte{3}) // even if core already on block 3 229 230 c.OnNewBlock(&remote.StateChangeBatch{ 231 StateVersionId: txID2, 232 PendingBlockBaseFee: 1, 233 ChangeBatch: []*remote.StateChange{ 234 { 235 Direction: remote.Direction_FORWARD, 236 BlockHeight: 2, 237 BlockHash: gointerfaces.ConvertHashToH256([32]byte{}), 238 Changes: []*remote.AccountChange{{ 239 Action: remote.Action_UPSERT, 240 Address: gointerfaces.ConvertAddressToH160(k1), 241 Data: []byte{2}, 242 }}, 243 }, 244 }, 245 }) 246 247 wg.Add(1) 248 go func() { 249 defer wg.Done() 250 for i := range res3 { 251 require.Equal([]byte{2}, <-res3[i]) 252 } 253 for i := range res4 { 254 require.Equal([]byte{42}, <-res4[i]) 255 } 256 fmt.Printf("done2: \n") 257 }() 258 fmt.Printf("-----2\n") 259 260 res5, res6 := get(k1, txID3), get(k2, txID3) // will see View of transaction 3, even if notification has not enough changes 261 c.OnNewBlock(&remote.StateChangeBatch{ 262 StateVersionId: txID3, 263 PendingBlockBaseFee: 1, 264 ChangeBatch: []*remote.StateChange{ 265 { 266 Direction: remote.Direction_FORWARD, 267 BlockHeight: 3, 268 BlockHash: gointerfaces.ConvertHashToH256([32]byte{}), 269 Changes: []*remote.AccountChange{{ 270 Action: remote.Action_UPSERT, 271 Address: gointerfaces.ConvertAddressToH160(k1), 272 Data: []byte{3}, 273 }}, 274 }, 275 }, 276 }) 277 278 wg.Add(1) 279 go func() { 280 defer wg.Done() 281 for i := range res5 { 282 require.Equal([]byte{3}, <-res5[i]) 283 } 284 fmt.Printf("-----21\n") 285 for i := range res6 { 286 require.Equal([]byte{42}, <-res6[i]) 287 } 288 fmt.Printf("done3: \n") 289 }() 290 fmt.Printf("-----3\n") 291 txID4 := put(k1[:], []byte{2}) 292 _ = txID4 293 c.OnNewBlock(&remote.StateChangeBatch{ 294 StateVersionId: txID4, 295 PendingBlockBaseFee: 1, 296 ChangeBatch: []*remote.StateChange{ 297 { 298 Direction: remote.Direction_UNWIND, 299 BlockHeight: 2, 300 BlockHash: gointerfaces.ConvertHashToH256([32]byte{}), 301 Changes: []*remote.AccountChange{{ 302 Action: remote.Action_UPSERT, 303 Address: gointerfaces.ConvertAddressToH160(k1), 304 Data: []byte{2}, 305 }}, 306 }, 307 }, 308 }) 309 fmt.Printf("-----4\n") 310 txID5 := put(k1[:], []byte{4}) // reorg to new chain 311 c.OnNewBlock(&remote.StateChangeBatch{ 312 StateVersionId: txID4, 313 PendingBlockBaseFee: 1, 314 ChangeBatch: []*remote.StateChange{ 315 { 316 Direction: remote.Direction_FORWARD, 317 BlockHeight: 3, 318 BlockHash: gointerfaces.ConvertHashToH256([32]byte{2}), 319 Changes: []*remote.AccountChange{{ 320 Action: remote.Action_UPSERT, 321 Address: gointerfaces.ConvertAddressToH160(k1), 322 Data: []byte{4}, 323 }}, 324 }, 325 }, 326 }) 327 fmt.Printf("-----5\n") 328 329 res7, res8 := get(k1, txID5), get(k2, txID5) // will see View of transaction 3, even if notification has not enough changes 330 331 wg.Add(1) 332 go func() { 333 defer wg.Done() 334 for i := range res7 { 335 require.Equal([]byte{4}, <-res7[i]) 336 } 337 for i := range res8 { 338 require.Equal([]byte{42}, <-res8[i]) 339 } 340 fmt.Printf("done4: \n") 341 }() 342 err := db.View(context.Background(), func(tx kv.Tx) error { 343 _, err := AssertCheckValues(context.Background(), tx, c) 344 require.NoError(err) 345 return nil 346 }) 347 require.NoError(err) 348 349 wg.Wait() 350 } 351 352 func TestCode(t *testing.T) { 353 require, ctx := require.New(t), context.Background() 354 c := New(DefaultCoherentConfig) 355 db := memdb.NewTestDB(t) 356 k1, k2 := [20]byte{1}, [20]byte{2} 357 358 _ = db.Update(ctx, func(tx kv.RwTx) error { 359 _ = tx.Put(kv.Code, k1[:], k2[:]) 360 cacheView, _ := c.View(ctx, tx) 361 view := cacheView.(*CoherentView) 362 363 v, err := c.GetCode(k1[:], tx, view.stateVersionID) 364 require.NoError(err) 365 require.Equal(k2[:], v) 366 367 v, err = c.GetCode(k1[:], tx, view.stateVersionID) 368 require.NoError(err) 369 require.Equal(k2[:], v) 370 371 //require.Equal(c.roots[c.latestViewID].cache.Len(), c.stateEvict.Len()) 372 return nil 373 }) 374 }