github.com/koko1123/flow-go-1@v0.29.6/storage/badger/operation/common_test.go (about) 1 // (c) 2019 Dapper Labs - ALL RIGHTS RESERVED 2 3 package operation 4 5 import ( 6 "bytes" 7 "fmt" 8 "math/rand" 9 "reflect" 10 "testing" 11 "time" 12 13 "github.com/dgraph-io/badger/v3" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 "github.com/vmihailenco/msgpack/v4" 17 18 "github.com/koko1123/flow-go-1/model/flow" 19 "github.com/koko1123/flow-go-1/storage" 20 "github.com/koko1123/flow-go-1/utils/unittest" 21 ) 22 23 func init() { 24 rand.Seed(time.Now().UnixNano()) 25 } 26 27 type Entity struct { 28 ID uint64 29 } 30 31 type UnencodeableEntity Entity 32 33 var errCantEncode = fmt.Errorf("encoding not supported") 34 var errCantDecode = fmt.Errorf("decoding not supported") 35 36 func (a UnencodeableEntity) MarshalJSON() ([]byte, error) { 37 return nil, errCantEncode 38 } 39 40 func (a *UnencodeableEntity) UnmarshalJSON(b []byte) error { 41 return errCantDecode 42 } 43 44 func (a UnencodeableEntity) MarshalMsgpack() ([]byte, error) { 45 return nil, errCantEncode 46 } 47 48 func (a UnencodeableEntity) UnmarshalMsgpack(b []byte) error { 49 return errCantDecode 50 } 51 52 func TestInsertValid(t *testing.T) { 53 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 54 e := Entity{ID: 1337} 55 key := []byte{0x01, 0x02, 0x03} 56 val, _ := msgpack.Marshal(e) 57 58 err := db.Update(insert(key, e)) 59 require.NoError(t, err) 60 61 var act []byte 62 _ = db.View(func(tx *badger.Txn) error { 63 item, err := tx.Get(key) 64 require.NoError(t, err) 65 act, err = item.ValueCopy(nil) 66 require.NoError(t, err) 67 return nil 68 }) 69 70 assert.Equal(t, val, act) 71 }) 72 } 73 74 func TestInsertDuplicate(t *testing.T) { 75 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 76 e := Entity{ID: 1337} 77 key := []byte{0x01, 0x02, 0x03} 78 val, _ := msgpack.Marshal(e) 79 80 // persist first time 81 err := db.Update(insert(key, e)) 82 require.NoError(t, err) 83 84 e2 := Entity{ID: 1338} 85 86 // persist again 87 err = db.Update(insert(key, e2)) 88 require.Error(t, err) 89 require.ErrorIs(t, err, storage.ErrAlreadyExists) 90 91 // ensure old value did not update 92 var act []byte 93 _ = db.View(func(tx *badger.Txn) error { 94 item, err := tx.Get(key) 95 require.NoError(t, err) 96 act, err = item.ValueCopy(nil) 97 require.NoError(t, err) 98 return nil 99 }) 100 101 assert.Equal(t, val, act) 102 }) 103 } 104 105 func TestInsertEncodingError(t *testing.T) { 106 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 107 e := Entity{ID: 1337} 108 key := []byte{0x01, 0x02, 0x03} 109 110 err := db.Update(insert(key, UnencodeableEntity(e))) 111 112 require.ErrorIs(t, err, errCantEncode) 113 }) 114 } 115 116 func TestUpdateValid(t *testing.T) { 117 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 118 e := Entity{ID: 1337} 119 key := []byte{0x01, 0x02, 0x03} 120 val, _ := msgpack.Marshal(e) 121 122 _ = db.Update(func(tx *badger.Txn) error { 123 err := tx.Set(key, []byte{}) 124 require.NoError(t, err) 125 return nil 126 }) 127 128 err := db.Update(update(key, e)) 129 require.NoError(t, err) 130 131 var act []byte 132 _ = db.View(func(tx *badger.Txn) error { 133 item, err := tx.Get(key) 134 require.NoError(t, err) 135 act, err = item.ValueCopy(nil) 136 require.NoError(t, err) 137 return nil 138 }) 139 140 assert.Equal(t, val, act) 141 }) 142 } 143 144 func TestUpdateMissing(t *testing.T) { 145 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 146 e := Entity{ID: 1337} 147 key := []byte{0x01, 0x02, 0x03} 148 149 err := db.Update(update(key, e)) 150 require.ErrorIs(t, err, storage.ErrNotFound) 151 152 // ensure nothing was written 153 _ = db.View(func(tx *badger.Txn) error { 154 _, err := tx.Get(key) 155 require.Equal(t, badger.ErrKeyNotFound, err) 156 return nil 157 }) 158 }) 159 } 160 161 func TestUpdateEncodingError(t *testing.T) { 162 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 163 e := Entity{ID: 1337} 164 key := []byte{0x01, 0x02, 0x03} 165 val, _ := msgpack.Marshal(e) 166 167 _ = db.Update(func(tx *badger.Txn) error { 168 err := tx.Set(key, val) 169 require.NoError(t, err) 170 return nil 171 }) 172 173 err := db.Update(update(key, UnencodeableEntity(e))) 174 require.ErrorIs(t, err, errCantEncode) 175 176 // ensure value did not change 177 var act []byte 178 _ = db.View(func(tx *badger.Txn) error { 179 item, err := tx.Get(key) 180 require.NoError(t, err) 181 act, err = item.ValueCopy(nil) 182 require.NoError(t, err) 183 return nil 184 }) 185 186 assert.Equal(t, val, act) 187 }) 188 } 189 190 func TestUpsertEntry(t *testing.T) { 191 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 192 e := Entity{ID: 1337} 193 key := []byte{0x01, 0x02, 0x03} 194 val, _ := msgpack.Marshal(e) 195 196 // first upsert an non-existed entry 197 err := db.Update(insert(key, e)) 198 require.NoError(t, err) 199 200 var act []byte 201 _ = db.View(func(tx *badger.Txn) error { 202 item, err := tx.Get(key) 203 require.NoError(t, err) 204 act, err = item.ValueCopy(nil) 205 require.NoError(t, err) 206 return nil 207 }) 208 209 assert.Equal(t, val, act) 210 211 // next upsert the value with the same key 212 newEntity := Entity{ID: 1338} 213 newVal, _ := msgpack.Marshal(newEntity) 214 err = db.Update(upsert(key, newEntity)) 215 require.NoError(t, err) 216 217 _ = db.View(func(tx *badger.Txn) error { 218 item, err := tx.Get(key) 219 require.NoError(t, err) 220 act, err = item.ValueCopy(nil) 221 require.NoError(t, err) 222 return nil 223 }) 224 225 assert.Equal(t, newVal, act) 226 }) 227 } 228 229 func TestRetrieveValid(t *testing.T) { 230 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 231 e := Entity{ID: 1337} 232 key := []byte{0x01, 0x02, 0x03} 233 val, _ := msgpack.Marshal(e) 234 235 _ = db.Update(func(tx *badger.Txn) error { 236 err := tx.Set(key, val) 237 require.NoError(t, err) 238 return nil 239 }) 240 241 var act Entity 242 err := db.View(retrieve(key, &act)) 243 require.NoError(t, err) 244 245 assert.Equal(t, e, act) 246 }) 247 } 248 249 func TestRetrieveMissing(t *testing.T) { 250 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 251 key := []byte{0x01, 0x02, 0x03} 252 253 var act Entity 254 err := db.View(retrieve(key, &act)) 255 require.ErrorIs(t, err, storage.ErrNotFound) 256 }) 257 } 258 259 func TestRetrieveUnencodeable(t *testing.T) { 260 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 261 e := Entity{ID: 1337} 262 key := []byte{0x01, 0x02, 0x03} 263 val, _ := msgpack.Marshal(e) 264 265 _ = db.Update(func(tx *badger.Txn) error { 266 err := tx.Set(key, val) 267 require.NoError(t, err) 268 return nil 269 }) 270 271 var act *UnencodeableEntity 272 err := db.View(retrieve(key, &act)) 273 require.ErrorIs(t, err, errCantDecode) 274 }) 275 } 276 277 func TestLookup(t *testing.T) { 278 expected := []flow.Identifier{ 279 {0x01}, 280 {0x02}, 281 {0x03}, 282 {0x04}, 283 } 284 actual := []flow.Identifier{} 285 286 iterationFunc := lookup(&actual) 287 288 for _, e := range expected { 289 checkFunc, createFunc, handleFunc := iterationFunc() 290 assert.True(t, checkFunc([]byte{0x00})) 291 target := createFunc() 292 assert.IsType(t, &flow.Identifier{}, target) 293 294 // set the value to target. Need to use reflection here since target is not strongly typed 295 reflect.ValueOf(target).Elem().Set(reflect.ValueOf(e)) 296 297 assert.NoError(t, handleFunc()) 298 } 299 300 assert.Equal(t, expected, actual) 301 } 302 303 func TestIterate(t *testing.T) { 304 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 305 keys := [][]byte{{0x00}, {0x12}, {0xf0}, {0xff}} 306 vals := []bool{false, false, true, true} 307 expected := []bool{false, true} 308 309 _ = db.Update(func(tx *badger.Txn) error { 310 for i, key := range keys { 311 enc, err := msgpack.Marshal(vals[i]) 312 require.NoError(t, err) 313 err = tx.Set(key, enc) 314 require.NoError(t, err) 315 } 316 return nil 317 }) 318 319 actual := make([]bool, 0, len(keys)) 320 iterationFunc := func() (checkFunc, createFunc, handleFunc) { 321 check := func(key []byte) bool { 322 return !bytes.Equal(key, []byte{0x12}) 323 } 324 var val bool 325 create := func() interface{} { 326 return &val 327 } 328 handle := func() error { 329 actual = append(actual, val) 330 return nil 331 } 332 return check, create, handle 333 } 334 335 err := db.View(iterate(keys[0], keys[2], iterationFunc)) 336 require.Nil(t, err) 337 338 assert.Equal(t, expected, actual) 339 }) 340 } 341 342 func TestTraverse(t *testing.T) { 343 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 344 keys := [][]byte{{0x42, 0x00}, {0xff}, {0x42, 0x56}, {0x00}, {0x42, 0xff}} 345 vals := []bool{false, false, true, false, true} 346 expected := []bool{false, true} 347 348 _ = db.Update(func(tx *badger.Txn) error { 349 for i, key := range keys { 350 enc, err := msgpack.Marshal(vals[i]) 351 require.NoError(t, err) 352 err = tx.Set(key, enc) 353 require.NoError(t, err) 354 } 355 return nil 356 }) 357 358 actual := make([]bool, 0, len(keys)) 359 iterationFunc := func() (checkFunc, createFunc, handleFunc) { 360 check := func(key []byte) bool { 361 return !bytes.Equal(key, []byte{0x42, 0x56}) 362 } 363 var val bool 364 create := func() interface{} { 365 return &val 366 } 367 handle := func() error { 368 actual = append(actual, val) 369 return nil 370 } 371 return check, create, handle 372 } 373 374 err := db.View(traverse([]byte{0x42}, iterationFunc)) 375 require.Nil(t, err) 376 377 assert.Equal(t, expected, actual) 378 }) 379 } 380 381 func TestRemove(t *testing.T) { 382 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 383 e := Entity{ID: 1337} 384 key := []byte{0x01, 0x02, 0x03} 385 val, _ := msgpack.Marshal(e) 386 387 _ = db.Update(func(tx *badger.Txn) error { 388 err := tx.Set(key, val) 389 require.NoError(t, err) 390 return nil 391 }) 392 393 t.Run("should be able to remove", func(t *testing.T) { 394 _ = db.Update(func(txn *badger.Txn) error { 395 err := remove(key)(txn) 396 assert.NoError(t, err) 397 398 _, err = txn.Get(key) 399 assert.ErrorIs(t, err, badger.ErrKeyNotFound) 400 401 return nil 402 }) 403 }) 404 405 t.Run("should error when removing non-existing value", func(t *testing.T) { 406 nonexistantKey := append(key, 0x01) 407 _ = db.Update(func(txn *badger.Txn) error { 408 err := remove(nonexistantKey)(txn) 409 assert.ErrorIs(t, err, storage.ErrNotFound) 410 assert.Error(t, err) 411 return nil 412 }) 413 }) 414 }) 415 } 416 417 func TestRemoveByPrefix(t *testing.T) { 418 t.Run("should no-op when removing non-existing value", func(t *testing.T) { 419 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 420 e := Entity{ID: 1337} 421 key := []byte{0x01, 0x02, 0x03} 422 val, _ := msgpack.Marshal(e) 423 424 _ = db.Update(func(tx *badger.Txn) error { 425 err := tx.Set(key, val) 426 assert.NoError(t, err) 427 return nil 428 }) 429 430 nonexistantKey := append(key, 0x01) 431 err := db.Update(removeByPrefix(nonexistantKey)) 432 assert.NoError(t, err) 433 434 var act Entity 435 err = db.View(retrieve(key, &act)) 436 require.NoError(t, err) 437 438 assert.Equal(t, e, act) 439 }) 440 }) 441 442 t.Run("should be able to remove", func(t *testing.T) { 443 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 444 e := Entity{ID: 1337} 445 key := []byte{0x01, 0x02, 0x03} 446 val, _ := msgpack.Marshal(e) 447 448 _ = db.Update(func(tx *badger.Txn) error { 449 err := tx.Set(key, val) 450 assert.NoError(t, err) 451 return nil 452 }) 453 454 _ = db.Update(func(txn *badger.Txn) error { 455 prefix := []byte{0x01, 0x02} 456 err := removeByPrefix(prefix)(txn) 457 assert.NoError(t, err) 458 459 _, err = txn.Get(key) 460 assert.Error(t, err) 461 assert.IsType(t, badger.ErrKeyNotFound, err) 462 463 return nil 464 }) 465 }) 466 }) 467 468 t.Run("should be able to remove by key", func(t *testing.T) { 469 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 470 e := Entity{ID: 1337} 471 key := []byte{0x01, 0x02, 0x03} 472 val, _ := msgpack.Marshal(e) 473 474 _ = db.Update(func(tx *badger.Txn) error { 475 err := tx.Set(key, val) 476 assert.NoError(t, err) 477 return nil 478 }) 479 480 _ = db.Update(func(txn *badger.Txn) error { 481 err := removeByPrefix(key)(txn) 482 assert.NoError(t, err) 483 484 _, err = txn.Get(key) 485 assert.Error(t, err) 486 assert.IsType(t, badger.ErrKeyNotFound, err) 487 488 return nil 489 }) 490 }) 491 }) 492 } 493 494 func TestIterateBoundaries(t *testing.T) { 495 496 // create range of keys covering all boundaries around our start/end values 497 start := []byte{0x10} 498 end := []byte{0x20} 499 keys := [][]byte{ 500 // before start -> not included in range 501 {0x09, 0xff}, 502 // shares prefix with start -> included in range 503 {0x10, 0x00}, 504 {0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 505 {0x10, 0xff}, 506 {0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 507 // prefix between start and end -> included in range 508 {0x11, 0x00}, 509 {0x19, 0xff}, 510 // shares prefix with end -> included in range 511 {0x20, 0x00}, 512 {0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 513 {0x20, 0xff}, 514 {0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 515 // after end -> not included in range 516 {0x21, 0x00}, 517 } 518 519 // set the maximum current DB key range 520 for _, key := range keys { 521 if uint32(len(key)) > max { 522 max = uint32(len(key)) 523 } 524 } 525 526 // keys within the expected range 527 keysInRange := keys[1:11] 528 529 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 530 531 // insert the keys into the database 532 _ = db.Update(func(tx *badger.Txn) error { 533 for _, key := range keys { 534 err := tx.Set(key, []byte{0x00}) 535 if err != nil { 536 return err 537 } 538 } 539 return nil 540 }) 541 542 // define iteration function that simply appends all traversed keys 543 var found [][]byte 544 iteration := func() (checkFunc, createFunc, handleFunc) { 545 check := func(key []byte) bool { 546 found = append(found, key) 547 return false 548 } 549 create := func() interface{} { 550 return nil 551 } 552 handle := func() error { 553 return fmt.Errorf("shouldn't handle anything") 554 } 555 return check, create, handle 556 } 557 558 // iterate forward and check boundaries are included correctly 559 found = nil 560 err := db.View(iterate(start, end, iteration)) 561 for i, f := range found { 562 t.Logf("forward %d: %x", i, f) 563 } 564 require.NoError(t, err, "should iterate forward without error") 565 assert.ElementsMatch(t, keysInRange, found, "forward iteration should go over correct keys") 566 567 // iterate backward and check boundaries are included correctly 568 found = nil 569 err = db.View(iterate(end, start, iteration)) 570 for i, f := range found { 571 t.Logf("backward %d: %x", i, f) 572 } 573 require.NoError(t, err, "should iterate backward without error") 574 assert.ElementsMatch(t, keysInRange, found, "backward iteration should go over correct keys") 575 }) 576 }