github.com/koko1123/flow-go-1@v0.29.6/ledger/complete/mtrie/trieCache_test.go (about)

     1  package mtrie
     2  
     3  // test addition
     4  // test under capacity
     5  // test on capacity
     6  // test across boundry
     7  
     8  import (
     9  	"math/rand"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/koko1123/flow-go-1/ledger"
    15  	"github.com/koko1123/flow-go-1/ledger/common/hash"
    16  	"github.com/koko1123/flow-go-1/ledger/complete/mtrie/node"
    17  	"github.com/koko1123/flow-go-1/ledger/complete/mtrie/trie"
    18  	"github.com/koko1123/flow-go-1/utils/unittest"
    19  )
    20  
    21  func TestTrieCache(t *testing.T) {
    22  	const capacity = 10
    23  
    24  	tc := NewTrieCache(capacity, nil)
    25  	require.Equal(t, 0, tc.Count())
    26  
    27  	tries := tc.Tries()
    28  	require.Equal(t, 0, len(tries))
    29  	require.Equal(t, 0, tc.Count())
    30  	require.Equal(t, 0, len(tc.lookup))
    31  
    32  	// savedTries contains all tries that are pushed to queue
    33  	var savedTries []*trie.MTrie
    34  
    35  	// Push tries to queue to fill out capacity
    36  	for i := 0; i < capacity; i++ {
    37  		trie, err := randomMTrie()
    38  		require.NoError(t, err)
    39  
    40  		tc.Push(trie)
    41  
    42  		savedTries = append(savedTries, trie)
    43  
    44  		tr := tc.Tries()
    45  		require.Equal(t, savedTries, tr)
    46  		require.Equal(t, len(savedTries), tc.Count())
    47  		require.Equal(t, len(savedTries), len(tc.lookup))
    48  
    49  		retTrie, found := tc.Get(trie.RootHash())
    50  		require.Equal(t, retTrie, trie)
    51  		require.True(t, found)
    52  
    53  		// check last added trie functionality
    54  		retTrie = tc.LastAddedTrie()
    55  		require.Equal(t, retTrie, trie)
    56  	}
    57  
    58  	// Push more tries to queue to overwrite older elements
    59  	for i := 0; i < capacity; i++ {
    60  		trie, err := randomMTrie()
    61  		require.NoError(t, err)
    62  
    63  		tc.Push(trie)
    64  
    65  		savedTries = append(savedTries, trie)
    66  
    67  		tr := tc.Tries()
    68  		require.Equal(t, capacity, len(tr))
    69  
    70  		// After queue reaches capacity in previous loop,
    71  		// queue overwrites older elements with new insertions,
    72  		// and element count is its capacity value.
    73  		// savedTries contains all elements inserted from previous loop and current loop, so
    74  		// tr (queue snapshot) matches the last C elements in savedTries (where C is capacity).
    75  		require.Equal(t, savedTries[len(savedTries)-capacity:], tr)
    76  		require.Equal(t, capacity, tc.Count())
    77  		require.Equal(t, capacity, len(tc.lookup))
    78  
    79  		// check the trie is lookable
    80  		retTrie, found := tc.Get(trie.RootHash())
    81  		require.Equal(t, retTrie, trie)
    82  		require.True(t, found)
    83  
    84  		// check the last evicted value is not kept
    85  		retTrie, found = tc.Get(savedTries[len(savedTries)-capacity-1].RootHash())
    86  		require.Nil(t, retTrie)
    87  		require.False(t, found)
    88  
    89  		// check last added trie functionality
    90  		retTrie = tc.LastAddedTrie()
    91  		require.Equal(t, retTrie, trie)
    92  	}
    93  
    94  }
    95  
    96  func TestPurge(t *testing.T) {
    97  	const capacity = 5
    98  
    99  	trie1, err := randomMTrie()
   100  	require.NoError(t, err)
   101  	trie2, err := randomMTrie()
   102  	require.NoError(t, err)
   103  	trie3, err := randomMTrie()
   104  	require.NoError(t, err)
   105  
   106  	called := 0
   107  	tc := NewTrieCache(capacity, func(tree *trie.MTrie) {
   108  		switch called {
   109  		case 0:
   110  			require.Equal(t, trie1, tree)
   111  		case 1:
   112  			require.Equal(t, trie2, tree)
   113  		case 2:
   114  			require.Equal(t, trie3, tree)
   115  		}
   116  		called++
   117  
   118  	})
   119  	tc.Push(trie1)
   120  	tc.Push(trie2)
   121  	tc.Push(trie3)
   122  
   123  	tc.Purge()
   124  	require.Equal(t, 0, tc.Count())
   125  	require.Equal(t, 0, tc.tail)
   126  	require.Equal(t, 0, len(tc.lookup))
   127  
   128  	require.Equal(t, 3, called)
   129  }
   130  
   131  func TestEvictCallBack(t *testing.T) {
   132  	const capacity = 2
   133  
   134  	trie1, err := randomMTrie()
   135  	require.NoError(t, err)
   136  
   137  	called := false
   138  	tc := NewTrieCache(capacity, func(tree *trie.MTrie) {
   139  		called = true
   140  		require.Equal(t, trie1, tree)
   141  	})
   142  	tc.Push(trie1)
   143  
   144  	trie2, err := randomMTrie()
   145  	require.NoError(t, err)
   146  	tc.Push(trie2)
   147  
   148  	trie3, err := randomMTrie()
   149  	require.NoError(t, err)
   150  	tc.Push(trie3)
   151  
   152  	require.True(t, called)
   153  }
   154  
   155  func TestConcurrentAccess(t *testing.T) {
   156  
   157  	const worker = 50
   158  	const capacity = 100 // large enough to not worry evicts
   159  
   160  	tc := NewTrieCache(capacity, nil)
   161  
   162  	unittest.Concurrently(worker, func(i int) {
   163  		trie, err := randomMTrie()
   164  		require.NoError(t, err)
   165  		tc.Push(trie)
   166  
   167  		ret, found := tc.Get(trie.RootHash())
   168  		require.True(t, found)
   169  		require.Equal(t, trie, ret)
   170  	})
   171  
   172  	require.Equal(t, worker, tc.Count())
   173  }
   174  
   175  func randomMTrie() (*trie.MTrie, error) {
   176  	var randomPath ledger.Path
   177  	rand.Read(randomPath[:])
   178  
   179  	var randomHashValue hash.Hash
   180  	rand.Read(randomHashValue[:])
   181  
   182  	root := node.NewNode(256, nil, nil, randomPath, nil, randomHashValue)
   183  
   184  	return trie.NewMTrie(root, 1, 1)
   185  }