github.com/MetalBlockchain/metalgo@v1.11.9/x/merkledb/view_iterator_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package merkledb
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"math/rand"
    10  	"sort"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/stretchr/testify/require"
    15  	"golang.org/x/exp/maps"
    16  
    17  	"github.com/MetalBlockchain/metalgo/database"
    18  	"github.com/MetalBlockchain/metalgo/utils/maybe"
    19  )
    20  
    21  func Test_View_Iterator(t *testing.T) {
    22  	require := require.New(t)
    23  
    24  	key1 := []byte("hello1")
    25  	value1 := []byte("world1")
    26  
    27  	key2 := []byte("hello2")
    28  	value2 := []byte("world2")
    29  
    30  	db, err := getBasicDB()
    31  	require.NoError(err)
    32  
    33  	require.NoError(db.Put(key1, value1))
    34  	require.NoError(db.Put(key2, value2))
    35  
    36  	view, err := db.NewView(context.Background(), ViewChanges{})
    37  	require.NoError(err)
    38  	iterator := view.NewIterator()
    39  	require.NotNil(iterator)
    40  
    41  	defer iterator.Release()
    42  
    43  	require.True(iterator.Next())
    44  	require.Equal(key1, iterator.Key())
    45  	require.Equal(value1, iterator.Value())
    46  
    47  	require.True(iterator.Next())
    48  	require.Equal(key2, iterator.Key())
    49  	require.Equal(value2, iterator.Value())
    50  
    51  	require.False(iterator.Next())
    52  	require.Nil(iterator.Key())
    53  	require.Nil(iterator.Value())
    54  	require.NoError(iterator.Error())
    55  }
    56  
    57  func Test_View_Iterator_DBClosed(t *testing.T) {
    58  	require := require.New(t)
    59  
    60  	key1 := []byte("hello1")
    61  	value1 := []byte("world1")
    62  
    63  	db, err := getBasicDB()
    64  	require.NoError(err)
    65  
    66  	require.NoError(db.Put(key1, value1))
    67  
    68  	view, err := db.NewView(context.Background(), ViewChanges{})
    69  	require.NoError(err)
    70  	iterator := view.NewIterator()
    71  	require.NotNil(iterator)
    72  
    73  	defer iterator.Release()
    74  
    75  	require.NoError(db.Close())
    76  
    77  	require.False(iterator.Next())
    78  	require.Nil(iterator.Key())
    79  	require.Nil(iterator.Value())
    80  	err = iterator.Error()
    81  	require.ErrorIs(err, ErrInvalid)
    82  }
    83  
    84  // Test_View_IteratorStart tests to make sure the iterator can be configured to
    85  // start midway through the database.
    86  func Test_View_IteratorStart(t *testing.T) {
    87  	require := require.New(t)
    88  	db, err := getBasicDB()
    89  	require.NoError(err)
    90  
    91  	key1 := []byte("hello1")
    92  	value1 := []byte("world1")
    93  
    94  	key2 := []byte("hello2")
    95  	value2 := []byte("world2")
    96  
    97  	require.NoError(db.Put(key1, value1))
    98  	require.NoError(db.Put(key2, value2))
    99  
   100  	view, err := db.NewView(context.Background(), ViewChanges{})
   101  	require.NoError(err)
   102  	iterator := view.NewIteratorWithStart(key2)
   103  	require.NotNil(iterator)
   104  
   105  	defer iterator.Release()
   106  
   107  	require.True(iterator.Next())
   108  	require.Equal(key2, iterator.Key())
   109  	require.Equal(value2, iterator.Value())
   110  
   111  	require.False(iterator.Next())
   112  	require.Nil(iterator.Key())
   113  	require.Nil(iterator.Value())
   114  	require.NoError(iterator.Error())
   115  }
   116  
   117  // Test_View_IteratorPrefix tests to make sure the iterator can be configured to skip
   118  // keys missing the provided prefix.
   119  func Test_View_IteratorPrefix(t *testing.T) {
   120  	require := require.New(t)
   121  	db, err := getBasicDB()
   122  	require.NoError(err)
   123  
   124  	key1 := []byte("hello")
   125  	value1 := []byte("world1")
   126  
   127  	key2 := []byte("goodbye")
   128  	value2 := []byte("world2")
   129  
   130  	key3 := []byte("joy")
   131  	value3 := []byte("world3")
   132  
   133  	require.NoError(db.Put(key1, value1))
   134  	require.NoError(db.Put(key2, value2))
   135  	require.NoError(db.Put(key3, value3))
   136  
   137  	view, err := db.NewView(context.Background(), ViewChanges{})
   138  	require.NoError(err)
   139  	iterator := view.NewIteratorWithPrefix([]byte("h"))
   140  	require.NotNil(iterator)
   141  
   142  	defer iterator.Release()
   143  
   144  	require.True(iterator.Next())
   145  	require.Equal(key1, iterator.Key())
   146  	require.Equal(value1, iterator.Value())
   147  
   148  	require.False(iterator.Next())
   149  	require.Nil(iterator.Key())
   150  	require.Nil(iterator.Value())
   151  	require.NoError(iterator.Error())
   152  }
   153  
   154  // Test_View_IteratorStartPrefix tests to make sure that the iterator can start
   155  // midway through the database while skipping a prefix.
   156  func Test_View_IteratorStartPrefix(t *testing.T) {
   157  	require := require.New(t)
   158  	db, err := getBasicDB()
   159  	require.NoError(err)
   160  
   161  	key1 := []byte("hello1")
   162  	value1 := []byte("world1")
   163  
   164  	key2 := []byte("z")
   165  	value2 := []byte("world2")
   166  
   167  	key3 := []byte("hello3")
   168  	value3 := []byte("world3")
   169  
   170  	require.NoError(db.Put(key1, value1))
   171  	require.NoError(db.Put(key2, value2))
   172  	require.NoError(db.Put(key3, value3))
   173  
   174  	view, err := db.NewView(context.Background(), ViewChanges{})
   175  	require.NoError(err)
   176  	iterator := view.NewIteratorWithStartAndPrefix(key1, []byte("h"))
   177  	require.NotNil(iterator)
   178  
   179  	defer iterator.Release()
   180  
   181  	require.True(iterator.Next())
   182  	require.Equal(key1, iterator.Key())
   183  	require.Equal(value1, iterator.Value())
   184  
   185  	require.True(iterator.Next())
   186  	require.Equal(key3, iterator.Key())
   187  	require.Equal(value3, iterator.Value())
   188  
   189  	require.False(iterator.Next())
   190  	require.Nil(iterator.Key())
   191  	require.Nil(iterator.Value())
   192  	require.NoError(iterator.Error())
   193  }
   194  
   195  // Test view iteration by creating a stack of views,
   196  // inserting random key/value pairs into them, and
   197  // iterating over the last view.
   198  func Test_View_Iterator_Random(t *testing.T) {
   199  	require := require.New(t)
   200  	now := time.Now().UnixNano()
   201  	t.Logf("seed: %d", now)
   202  	rand := rand.New(rand.NewSource(now)) // #nosec G404
   203  
   204  	var (
   205  		numKeyChanges = 5_000
   206  		maxKeyLen     = 16
   207  		maxValLen     = 16
   208  	)
   209  
   210  	keyChanges := []KeyChange{}
   211  	for i := 0; i < numKeyChanges; i++ {
   212  		key := make([]byte, rand.Intn(maxKeyLen))
   213  		_, _ = rand.Read(key)
   214  		value := make([]byte, rand.Intn(maxValLen))
   215  		_, _ = rand.Read(value)
   216  		keyChanges = append(keyChanges, KeyChange{
   217  			Key:   key,
   218  			Value: maybe.Some(value),
   219  		})
   220  	}
   221  
   222  	db, err := getBasicDB()
   223  	require.NoError(err)
   224  
   225  	for i := 0; i < numKeyChanges/4; i++ {
   226  		require.NoError(db.Put(keyChanges[i].Key, keyChanges[i].Value.Value()))
   227  	}
   228  
   229  	ops := make([]database.BatchOp, 0, numKeyChanges/4)
   230  	for i := numKeyChanges / 4; i < 2*numKeyChanges/4; i++ {
   231  		ops = append(ops, database.BatchOp{Key: keyChanges[i].Key, Value: keyChanges[i].Value.Value()})
   232  	}
   233  
   234  	view1, err := db.NewView(context.Background(), ViewChanges{BatchOps: ops})
   235  	require.NoError(err)
   236  
   237  	ops = make([]database.BatchOp, 0, numKeyChanges/4)
   238  	for i := 2 * numKeyChanges / 4; i < 3*numKeyChanges/4; i++ {
   239  		ops = append(ops, database.BatchOp{Key: keyChanges[i].Key, Value: keyChanges[i].Value.Value()})
   240  	}
   241  
   242  	view2, err := view1.NewView(context.Background(), ViewChanges{BatchOps: ops})
   243  	require.NoError(err)
   244  
   245  	ops = make([]database.BatchOp, 0, numKeyChanges/4)
   246  	for i := 3 * numKeyChanges / 4; i < numKeyChanges; i++ {
   247  		ops = append(ops, database.BatchOp{Key: keyChanges[i].Key, Value: keyChanges[i].Value.Value()})
   248  	}
   249  
   250  	view3, err := view2.NewView(context.Background(), ViewChanges{BatchOps: ops})
   251  	require.NoError(err)
   252  
   253  	// Might have introduced duplicates, so only expect the latest value.
   254  	uniqueKeyChanges := make(map[string][]byte)
   255  	for _, keyChange := range keyChanges {
   256  		uniqueKeyChanges[string(keyChange.Key)] = keyChange.Value.Value()
   257  	}
   258  
   259  	iter := view3.NewIterator()
   260  	uniqueKeys := maps.Keys(uniqueKeyChanges)
   261  	sort.Strings(uniqueKeys)
   262  	i := 0
   263  	for iter.Next() {
   264  		expectedKey := uniqueKeys[i]
   265  		expectedValue := uniqueKeyChanges[expectedKey]
   266  		require.True(bytes.Equal([]byte(expectedKey), iter.Key()))
   267  		if len(expectedValue) == 0 {
   268  			// Don't differentiate between nil and []byte{}
   269  			require.Empty(iter.Value())
   270  		} else {
   271  			require.Equal(expectedValue, iter.Value())
   272  		}
   273  		i++
   274  	}
   275  	require.Len(uniqueKeys, i)
   276  	iter.Release()
   277  	require.NoError(iter.Error())
   278  
   279  	// Test with start and prefix.
   280  	prefix := []byte{128}
   281  	start := []byte{128, 5}
   282  	iter = view3.NewIteratorWithStartAndPrefix(start, prefix)
   283  	startPrefixUniqueKeys := []string{}
   284  	// Remove keys that don't have the prefix/are before the start.
   285  	for i := 0; i < len(uniqueKeys); i++ {
   286  		if bytes.HasPrefix([]byte(uniqueKeys[i]), prefix) && bytes.Compare([]byte(uniqueKeys[i]), start) >= 0 {
   287  			startPrefixUniqueKeys = append(startPrefixUniqueKeys, uniqueKeys[i])
   288  		}
   289  	}
   290  	require.NotEmpty(startPrefixUniqueKeys) // Sanity check to make sure we have some keys to test.
   291  	i = 0
   292  	for iter.Next() {
   293  		expectedKey := startPrefixUniqueKeys[i]
   294  		expectedValue := uniqueKeyChanges[expectedKey]
   295  		require.Equal([]byte(expectedKey), iter.Key())
   296  		if len(expectedValue) == 0 {
   297  			// Don't differentiate between nil and []byte{}
   298  			require.Empty(iter.Value())
   299  		} else {
   300  			require.Equal(expectedValue, iter.Value())
   301  		}
   302  		i++
   303  	}
   304  	require.Len(startPrefixUniqueKeys, i)
   305  	iter.Release()
   306  	require.NoError(iter.Error())
   307  }