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 }