github.com/MetalBlockchain/metalgo@v1.11.9/x/merkledb/value_node_db_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  	"testing"
     8  
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/MetalBlockchain/metalgo/database"
    12  	"github.com/MetalBlockchain/metalgo/database/memdb"
    13  	"github.com/MetalBlockchain/metalgo/utils"
    14  	"github.com/MetalBlockchain/metalgo/utils/maybe"
    15  )
    16  
    17  // Test putting, modifying, deleting, and getting key-node pairs.
    18  func TestValueNodeDB(t *testing.T) {
    19  	require := require.New(t)
    20  
    21  	baseDB := memdb.New()
    22  
    23  	cacheSize := 10_000
    24  	db := newValueNodeDB(
    25  		baseDB,
    26  		utils.NewBytesPool(),
    27  		&mockMetrics{},
    28  		cacheSize,
    29  		DefaultHasher,
    30  	)
    31  
    32  	// Getting a key that doesn't exist should return an error.
    33  	key := ToKey([]byte{0x01})
    34  	_, err := db.Get(key)
    35  	require.ErrorIs(err, database.ErrNotFound)
    36  
    37  	// Put a key-node pair.
    38  	node1 := &node{
    39  		dbNode: dbNode{
    40  			value: maybe.Some([]byte{0x01}),
    41  		},
    42  		key: key,
    43  	}
    44  	batch := db.baseDB.NewBatch()
    45  	require.NoError(db.Write(batch, key, node1))
    46  	require.NoError(batch.Write())
    47  
    48  	// Get the key-node pair.
    49  	node1Read, err := db.Get(key)
    50  	require.NoError(err)
    51  	require.Equal(node1, node1Read)
    52  
    53  	// Delete the key-node pair.
    54  	batch = db.baseDB.NewBatch()
    55  	require.NoError(db.Write(batch, key, nil))
    56  	require.NoError(batch.Write())
    57  
    58  	// Key should be gone now.
    59  	_, err = db.Get(key)
    60  	require.ErrorIs(err, database.ErrNotFound)
    61  
    62  	// Put a key-node pair and delete it in the same batch.
    63  	batch = db.baseDB.NewBatch()
    64  	require.NoError(db.Write(batch, key, node1))
    65  	require.NoError(db.Write(batch, key, nil))
    66  	require.NoError(batch.Write())
    67  
    68  	// Key should still be gone.
    69  	_, err = db.Get(key)
    70  	require.ErrorIs(err, database.ErrNotFound)
    71  
    72  	// Put a key-node pair and overwrite it in the same batch.
    73  	node2 := &node{
    74  		dbNode: dbNode{
    75  			value: maybe.Some([]byte{0x02}),
    76  		},
    77  		key: key,
    78  	}
    79  	batch = db.baseDB.NewBatch()
    80  	require.NoError(db.Write(batch, key, node1))
    81  	require.NoError(db.Write(batch, key, node2))
    82  	require.NoError(batch.Write())
    83  
    84  	// Get the key-node pair.
    85  	node2Read, err := db.Get(key)
    86  	require.NoError(err)
    87  	require.Equal(node2, node2Read)
    88  
    89  	// Overwrite the key-node pair in a subsequent batch.
    90  	batch = db.baseDB.NewBatch()
    91  	require.NoError(db.Write(batch, key, node1))
    92  	require.NoError(batch.Write())
    93  
    94  	// Get the key-node pair.
    95  	node1Read, err = db.Get(key)
    96  	require.NoError(err)
    97  	require.Equal(node1, node1Read)
    98  
    99  	// Get the key-node pair from the database, not the cache.
   100  	db.nodeCache.Flush()
   101  	node1Read, err = db.Get(key)
   102  	require.NoError(err)
   103  	// Only check value since we're not setting other node fields.
   104  	require.Equal(node1.value, node1Read.value)
   105  
   106  	// Make sure the key is prefixed in the base database.
   107  	it := baseDB.NewIteratorWithPrefix(valueNodePrefix)
   108  	defer it.Release()
   109  	require.True(it.Next())
   110  	require.False(it.Next())
   111  }
   112  
   113  func TestValueNodeDBIterator(t *testing.T) {
   114  	require := require.New(t)
   115  
   116  	baseDB := memdb.New()
   117  	cacheSize := 10
   118  	db := newValueNodeDB(
   119  		baseDB,
   120  		utils.NewBytesPool(),
   121  		&mockMetrics{},
   122  		cacheSize,
   123  		DefaultHasher,
   124  	)
   125  
   126  	// Put key-node pairs.
   127  	for i := 0; i < cacheSize; i++ {
   128  		key := ToKey([]byte{byte(i)})
   129  		node := &node{
   130  			dbNode: dbNode{
   131  				value: maybe.Some([]byte{byte(i)}),
   132  			},
   133  			key: key,
   134  		}
   135  		batch := db.baseDB.NewBatch()
   136  		require.NoError(db.Write(batch, key, node))
   137  		require.NoError(batch.Write())
   138  	}
   139  
   140  	// Iterate over the key-node pairs.
   141  	it := db.newIteratorWithStartAndPrefix(nil, nil)
   142  
   143  	i := 0
   144  	for it.Next() {
   145  		require.Equal([]byte{byte(i)}, it.Key())
   146  		require.Equal([]byte{byte(i)}, it.Value())
   147  		i++
   148  	}
   149  	require.NoError(it.Error())
   150  	require.Equal(cacheSize, i)
   151  	it.Release()
   152  
   153  	// Iterate over the key-node pairs with a start.
   154  	it = db.newIteratorWithStartAndPrefix([]byte{2}, nil)
   155  	i = 0
   156  	for it.Next() {
   157  		require.Equal([]byte{2 + byte(i)}, it.Key())
   158  		require.Equal([]byte{2 + byte(i)}, it.Value())
   159  		i++
   160  	}
   161  	require.NoError(it.Error())
   162  	require.Equal(cacheSize-2, i)
   163  	it.Release()
   164  
   165  	// Put key-node pairs with a common prefix.
   166  	key := ToKey([]byte{0xFF, 0x00})
   167  	n := &node{
   168  		dbNode: dbNode{
   169  			value: maybe.Some([]byte{0xFF, 0x00}),
   170  		},
   171  		key: key,
   172  	}
   173  	batch := db.baseDB.NewBatch()
   174  	require.NoError(db.Write(batch, key, n))
   175  	require.NoError(batch.Write())
   176  
   177  	key = ToKey([]byte{0xFF, 0x01})
   178  	n = &node{
   179  		dbNode: dbNode{
   180  			value: maybe.Some([]byte{0xFF, 0x01}),
   181  		},
   182  		key: key,
   183  	}
   184  	batch = db.baseDB.NewBatch()
   185  	require.NoError(db.Write(batch, key, n))
   186  	require.NoError(batch.Write())
   187  
   188  	// Iterate over the key-node pairs with a prefix.
   189  	it = db.newIteratorWithStartAndPrefix(nil, []byte{0xFF})
   190  	i = 0
   191  	for it.Next() {
   192  		require.Equal([]byte{0xFF, byte(i)}, it.Key())
   193  		require.Equal([]byte{0xFF, byte(i)}, it.Value())
   194  		i++
   195  	}
   196  	require.NoError(it.Error())
   197  	require.Equal(2, i)
   198  
   199  	// Iterate over the key-node pairs with a start and prefix.
   200  	it = db.newIteratorWithStartAndPrefix([]byte{0xFF, 0x01}, []byte{0xFF})
   201  	i = 0
   202  	for it.Next() {
   203  		require.Equal([]byte{0xFF, 0x01}, it.Key())
   204  		require.Equal([]byte{0xFF, 0x01}, it.Value())
   205  		i++
   206  	}
   207  	require.NoError(it.Error())
   208  	require.Equal(1, i)
   209  
   210  	// Iterate over closed database.
   211  	it = db.newIteratorWithStartAndPrefix(nil, nil)
   212  	require.True(it.Next())
   213  	require.NoError(it.Error())
   214  	db.Close()
   215  	require.False(it.Next())
   216  	err := it.Error()
   217  	require.ErrorIs(err, database.ErrClosed)
   218  }
   219  
   220  func TestValueNodeDBClear(t *testing.T) {
   221  	require := require.New(t)
   222  	cacheSize := 200
   223  	baseDB := memdb.New()
   224  	db := newValueNodeDB(
   225  		baseDB,
   226  		utils.NewBytesPool(),
   227  		&mockMetrics{},
   228  		cacheSize,
   229  		DefaultHasher,
   230  	)
   231  
   232  	batch := db.baseDB.NewBatch()
   233  	for _, b := range [][]byte{{1}, {2}, {3}} {
   234  		require.NoError(db.Write(batch, ToKey(b), newNode(ToKey(b))))
   235  	}
   236  	require.NoError(batch.Write())
   237  
   238  	// Assert the db is not empty
   239  	iter := baseDB.NewIteratorWithPrefix(valueNodePrefix)
   240  	require.True(iter.Next())
   241  	iter.Release()
   242  
   243  	require.NoError(db.Clear())
   244  
   245  	iter = baseDB.NewIteratorWithPrefix(valueNodePrefix)
   246  	defer iter.Release()
   247  	require.False(iter.Next())
   248  }