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  }