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 }