github.com/ethersphere/bee/v2@v2.2.0/pkg/storage/storagetest/storage.go (about) 1 // Copyright 2022 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package storagetest 6 7 import ( 8 "bytes" 9 "context" 10 "crypto/rand" 11 "encoding/binary" 12 "encoding/json" 13 "errors" 14 "fmt" 15 "runtime" 16 "strconv" 17 "strings" 18 "sync" 19 "testing" 20 21 "github.com/ethersphere/bee/v2/pkg/encryption" 22 "github.com/ethersphere/bee/v2/pkg/storage" 23 "github.com/ethersphere/bee/v2/pkg/storage/storageutil" 24 "github.com/ethersphere/bee/v2/pkg/swarm" 25 "github.com/google/go-cmp/cmp" 26 ) 27 28 var ( 29 // MinAddressBytes represents bytes that can be used to represent a min. address. 30 MinAddressBytes = [swarm.HashSize]byte{swarm.HashSize - 1: 0x00} 31 32 // MaxAddressBytes represents bytes that can be used to represent a max. address. 33 MaxAddressBytes = [swarm.HashSize]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} 34 35 MaxEncryptedRefBytes = [encryption.ReferenceSize]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} 36 37 // MaxStampIndexBytes represents bytes that can be used to represent a max. stamp index. 38 MaxStampIndexBytes = [swarm.StampIndexSize]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} 39 40 // MaxBatchTimestampBytes represents bytes that can be used to represent a max. batch timestamp. 41 MaxBatchTimestampBytes = [swarm.StampTimestampSize]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} 42 ) 43 44 var _ storage.Item = (*ItemStub)(nil) 45 46 // ItemStub is a stub for storage.Item. 47 type ItemStub struct { 48 MarshalBuf []byte 49 MarshalErr error 50 UnmarshalBuf []byte 51 } 52 53 // ID implements the storage.Item interface. 54 func (im ItemStub) ID() string { return fmt.Sprintf("%+v", im) } 55 56 // Namespace implements the storage.Item interface. 57 func (im ItemStub) Namespace() string { return "test" } 58 59 // Marshal implements the storage.Item interface. 60 func (im ItemStub) Marshal() ([]byte, error) { 61 return im.MarshalBuf, im.MarshalErr 62 } 63 64 // Unmarshal implements the storage.Item interface. 65 func (im *ItemStub) Unmarshal(data []byte) error { 66 im.UnmarshalBuf = data 67 return nil 68 } 69 70 // Clone implements the storage.Item interface. 71 func (im *ItemStub) Clone() storage.Item { 72 if im == nil { 73 return nil 74 } 75 return &ItemStub{ 76 MarshalBuf: append([]byte(nil), im.MarshalBuf...), 77 MarshalErr: im.MarshalErr, 78 UnmarshalBuf: append([]byte(nil), im.UnmarshalBuf...), 79 } 80 } 81 82 // Clone implements the storage.Item interface. 83 func (im ItemStub) String() string { 84 return storageutil.JoinFields(im.Namespace(), im.ID()) 85 } 86 87 type obj1 struct { 88 Id string 89 SomeInt uint64 90 Buf []byte 91 } 92 93 func (o *obj1) ID() string { return o.Id } 94 95 func (obj1) Namespace() string { return "obj1" } 96 97 func (o *obj1) Marshal() ([]byte, error) { 98 buf := make([]byte, 40) 99 copy(buf[:32], o.Id) 100 binary.LittleEndian.PutUint64(buf[32:], o.SomeInt) 101 buf = append(buf, o.Buf[:]...) 102 return buf, nil 103 } 104 105 func (o *obj1) Unmarshal(buf []byte) error { 106 if len(buf) < 40 { 107 return errors.New("invalid length") 108 } 109 o.Id = strings.TrimRight(string(buf[:32]), string([]byte{0})) 110 o.SomeInt = binary.LittleEndian.Uint64(buf[32:]) 111 o.Buf = buf[40:] 112 return nil 113 } 114 115 func (o *obj1) Clone() storage.Item { 116 if o == nil { 117 return nil 118 } 119 return &obj1{ 120 Id: o.Id, 121 SomeInt: o.SomeInt, 122 Buf: append([]byte(nil), o.Buf...), 123 } 124 } 125 126 func (o obj1) String() string { 127 return storageutil.JoinFields(o.Namespace(), o.ID()) 128 } 129 130 type obj2 struct { 131 Id int 132 SomeStr string 133 SomeFloat float64 134 } 135 136 func (o *obj2) ID() string { return strconv.Itoa(o.Id) } 137 138 func (obj2) Namespace() string { return "obj2" } 139 140 func (o *obj2) Marshal() ([]byte, error) { return json.Marshal(o) } 141 142 func (o *obj2) Unmarshal(buf []byte) error { return json.Unmarshal(buf, o) } 143 144 func (o *obj2) Clone() storage.Item { 145 if o == nil { 146 return nil 147 } 148 return &obj2{ 149 Id: o.Id, 150 SomeStr: o.SomeStr, 151 SomeFloat: o.SomeFloat, 152 } 153 } 154 155 func (o obj2) String() string { 156 return storageutil.JoinFields(o.Namespace(), o.ID()) 157 } 158 159 func randBytes(count int) []byte { 160 buf := make([]byte, count) 161 _, _ = rand.Read(buf) 162 return buf 163 } 164 165 func checkTestItemEqual(t *testing.T, a, b storage.Item) { 166 t.Helper() 167 168 if a.Namespace() != b.Namespace() { 169 t.Fatalf("namespace doesn't match %s and %s", a.Namespace(), b.Namespace()) 170 } 171 172 if a.ID() != b.ID() { 173 t.Fatalf("ID doesn't match %s and %s %d %d", a.ID(), b.ID(), len(a.ID()), len(b.ID())) 174 } 175 176 buf1, err := a.Marshal() 177 if err != nil { 178 t.Fatalf("failed marshaling: %v", err) 179 } 180 181 buf2, err := b.Marshal() 182 if err != nil { 183 t.Fatalf("failed marshaling: %v", err) 184 } 185 186 if !bytes.Equal(buf1, buf2) { 187 t.Fatalf("bytes not equal for item %s/%s", a.Namespace(), a.ID()) 188 } 189 } 190 191 // TestStore provides correctness testsuite for Store interface. 192 func TestStore(t *testing.T, s storage.Store) { 193 t.Helper() 194 195 mustParseInt := func(s string) int { 196 i, err := strconv.Atoi(s) 197 if err != nil { 198 panic(err) 199 } 200 return i 201 } 202 203 const ( 204 obj1Prefix = "obj1_prefix_" 205 obj2Prefix = "1000000000" 206 ) 207 208 var ( 209 obj1Cnt int 210 obj2Cnt int 211 212 obj1WithPrefixCnt int 213 obj2WithPrefixCnt int 214 ) 215 216 testObjs := []storage.Item{ 217 &obj1{ 218 Id: obj1Prefix + "aaaaaaaaaaa", 219 SomeInt: 3, 220 Buf: randBytes(128), 221 }, 222 &obj1{ 223 Id: obj1Prefix + "bbbbbbbbbbb", 224 SomeInt: 4, 225 Buf: randBytes(64), 226 }, 227 &obj1{ 228 Id: obj1Prefix + "ccccccccccc", 229 SomeInt: 5, 230 Buf: randBytes(32), 231 }, 232 &obj1{ 233 Id: "zddddddddddd", 234 SomeInt: 6, 235 Buf: randBytes(256), 236 }, 237 &obj1{ 238 Id: "zdddddeeeeee", 239 SomeInt: 7, 240 Buf: randBytes(16), 241 }, 242 &obj2{ 243 Id: mustParseInt(obj2Prefix)*10 + 1, 244 SomeStr: "asdasdasdasdasd", 245 SomeFloat: 10000.00001, 246 }, 247 &obj2{ 248 Id: mustParseInt(obj2Prefix)*10 + 2, 249 SomeStr: "dfgdfgdfgdfgdfg", 250 SomeFloat: 200001.11123, 251 }, 252 &obj2{ 253 Id: mustParseInt(obj2Prefix)*10 + 3, 254 SomeStr: "qweqweqweqweqwe", 255 SomeFloat: 1223444.1122, 256 }, 257 &obj2{ 258 Id: 4, 259 SomeStr: "", 260 SomeFloat: 1.0, 261 }, 262 &obj2{ 263 Id: 5, 264 SomeStr: "abc", 265 SomeFloat: 121213123111.112333, 266 }, 267 } 268 269 t.Run("create new entries", func(t *testing.T) { 270 for _, item := range testObjs { 271 err := s.Put(item) 272 if err != nil { 273 t.Fatalf("failed to add new entry: %v", err) 274 } 275 276 switch item.(type) { 277 case *obj1: 278 obj1Cnt++ 279 case *obj2: 280 obj2Cnt++ 281 } 282 283 switch { 284 case strings.HasPrefix(item.ID(), obj1Prefix): 285 obj1WithPrefixCnt++ 286 case strings.HasPrefix(item.ID(), obj2Prefix): 287 obj2WithPrefixCnt++ 288 } 289 } 290 }) 291 292 t.Run("has entries", func(t *testing.T) { 293 for _, i := range testObjs { 294 found, err := s.Has(i) 295 if err != nil { 296 t.Fatalf("failed to check entry: %v", err) 297 } 298 if !found { 299 t.Fatalf("expected entry to be found %s/%s", i.Namespace(), i.ID()) 300 } 301 } 302 }) 303 304 t.Run("get entries", func(t *testing.T) { 305 for _, item := range testObjs { 306 var readObj storage.Item 307 switch item := item.(type) { 308 case *obj1: 309 readObj = &obj1{Id: item.ID()} 310 case *obj2: 311 readObj = &obj2{Id: item.Id} 312 default: 313 t.Fatalf("unknown item: %#v", item) 314 } 315 316 err := s.Get(readObj) 317 if err != nil { 318 t.Fatalf("failed to get obj %s/%s", readObj.Namespace(), readObj.ID()) 319 } 320 321 checkTestItemEqual(t, readObj, item) 322 } 323 }) 324 325 t.Run("get size", func(t *testing.T) { 326 for _, item := range testObjs { 327 var readObj storage.Item 328 switch item := item.(type) { 329 case *obj1: 330 readObj = &obj1{Id: item.ID()} 331 case *obj2: 332 readObj = &obj2{Id: item.Id} 333 default: 334 t.Fatalf("unknown item: %#v", item) 335 } 336 337 sz, err := s.GetSize(readObj) 338 if err != nil { 339 t.Fatalf("failed to get obj %s/%s", readObj.Namespace(), readObj.ID()) 340 } 341 342 buf, err := item.Marshal() 343 if err != nil { 344 t.Fatalf("failed marshaling test item: %v", err) 345 } 346 347 if sz != len(buf) { 348 t.Fatalf("sizes dont match %s/%s expected %d found %d", item.Namespace(), item.ID(), len(buf), sz) 349 } 350 } 351 }) 352 353 t.Run("count", func(t *testing.T) { 354 t.Run("obj1", func(t *testing.T) { 355 cnt, err := s.Count(new(obj1)) 356 if err != nil { 357 t.Fatalf("failed getting count: %v", err) 358 } 359 if cnt != obj1Cnt { 360 t.Fatalf("count mismatch: want %d have %d", obj1Cnt, cnt) 361 } 362 }) 363 t.Run("obj2", func(t *testing.T) { 364 cnt, err := s.Count(new(obj2)) 365 if err != nil { 366 t.Fatalf("failed getting count: %v", err) 367 } 368 if cnt != obj2Cnt { 369 t.Fatalf("count mismatch: want %d, have %d", obj2Cnt, cnt) 370 } 371 }) 372 }) 373 374 t.Run("iterate start prefix", func(t *testing.T) { 375 t.Run("obj1", func(t *testing.T) { 376 idx := 0 377 err := s.Iterate(storage.Query{ 378 Factory: func() storage.Item { return new(obj1) }, 379 Prefix: obj1Prefix + "a", 380 PrefixAtStart: true, 381 ItemProperty: storage.QueryItem, 382 }, func(r storage.Result) (bool, error) { 383 checkTestItemEqual(t, r.Entry, testObjs[idx]) 384 idx++ 385 return false, nil 386 }) 387 if err != nil { 388 t.Fatalf("unexpected error while iteration: %v", err) 389 } 390 if idx != obj1Cnt { 391 t.Fatalf("unexpected no of entries in iteration exp %d found %d", obj1Cnt, idx) 392 } 393 }) 394 }) 395 396 t.Run("iterate subset prefix", func(t *testing.T) { 397 t.Run("obj1", func(t *testing.T) { 398 idx := 1 399 err := s.Iterate(storage.Query{ 400 Factory: func() storage.Item { return new(obj1) }, 401 Prefix: obj1Prefix + "b", 402 PrefixAtStart: true, 403 ItemProperty: storage.QueryItem, 404 }, func(r storage.Result) (bool, error) { 405 checkTestItemEqual(t, r.Entry, testObjs[idx]) 406 idx++ 407 return false, nil 408 }) 409 if err != nil { 410 t.Fatalf("unexpected error while iteration: %v", err) 411 } 412 if idx-1 != obj1Cnt-1 { 413 t.Fatalf("unexpected no of entries in iteration exp %d found %d", obj1Cnt-1, idx-1) 414 } 415 }) 416 }) 417 418 t.Run("iterate prefix", func(t *testing.T) { 419 t.Run("obj1", func(t *testing.T) { 420 idx := 0 421 err := s.Iterate(storage.Query{ 422 Factory: func() storage.Item { return new(obj1) }, 423 Prefix: obj1Prefix, 424 ItemProperty: storage.QueryItem, 425 }, func(r storage.Result) (bool, error) { 426 checkTestItemEqual(t, r.Entry, testObjs[idx]) 427 idx++ 428 return false, nil 429 }) 430 if err != nil { 431 t.Fatalf("unexpected error while iteration: %v", err) 432 } 433 if idx != obj1WithPrefixCnt { 434 t.Fatalf("unexpected no of entries in iteration exp %d found %d", obj1WithPrefixCnt, idx) 435 } 436 }) 437 t.Run("obj2 descending", func(t *testing.T) { 438 idx := 7 439 err := s.Iterate(storage.Query{ 440 Factory: func() storage.Item { return new(obj2) }, 441 Prefix: obj2Prefix, 442 ItemProperty: storage.QueryItem, 443 Order: storage.KeyDescendingOrder, 444 }, func(r storage.Result) (bool, error) { 445 if idx < obj2Cnt { 446 t.Fatal("index overflow") 447 } 448 checkTestItemEqual(t, r.Entry, testObjs[idx]) 449 idx-- 450 return false, nil 451 }) 452 if err != nil { 453 t.Fatalf("unexpected error while iteration: %v", err) 454 } 455 if idx != 7-obj2WithPrefixCnt { 456 t.Fatalf("unexpected no of entries in iteration exp %d found %d", 7-obj2WithPrefixCnt, idx) 457 } 458 }) 459 }) 460 461 t.Run("iterate skip first", func(t *testing.T) { 462 t.Run("obj1", func(t *testing.T) { 463 idx := 1 464 err := s.Iterate(storage.Query{ 465 Factory: func() storage.Item { return new(obj1) }, 466 SkipFirst: true, 467 ItemProperty: storage.QueryItem, 468 }, func(r storage.Result) (bool, error) { 469 checkTestItemEqual(t, r.Entry, testObjs[idx]) 470 idx++ 471 return false, nil 472 }) 473 if err != nil { 474 t.Fatalf("unexpected error while iteration: %v", err) 475 } 476 if idx != obj1Cnt { 477 t.Fatalf("unexpected no of entries in iteration exp %d found %d", obj1Cnt, idx) 478 } 479 }) 480 t.Run("obj2 descending", func(t *testing.T) { 481 idx := 8 482 err := s.Iterate(storage.Query{ 483 Factory: func() storage.Item { return new(obj2) }, 484 SkipFirst: true, 485 ItemProperty: storage.QueryItem, 486 Order: storage.KeyDescendingOrder, 487 }, func(r storage.Result) (bool, error) { 488 if idx < obj2Cnt { 489 t.Fatal("index overflow") 490 } 491 checkTestItemEqual(t, r.Entry, testObjs[idx]) 492 idx-- 493 return false, nil 494 }) 495 if err != nil { 496 t.Fatalf("unexpected error while iteration: %v", err) 497 } 498 if idx != obj2Cnt-1 { 499 t.Fatalf("unexpected no of entries in iteration exp %d found %d", obj2Cnt-1, idx) 500 } 501 }) 502 }) 503 504 t.Run("iterate ascending", func(t *testing.T) { 505 t.Run("obj1", func(t *testing.T) { 506 idx := 0 507 err := s.Iterate(storage.Query{ 508 Factory: func() storage.Item { return new(obj1) }, 509 ItemProperty: storage.QueryItem, 510 }, func(r storage.Result) (bool, error) { 511 checkTestItemEqual(t, r.Entry, testObjs[idx]) 512 idx++ 513 return false, nil 514 }) 515 if err != nil { 516 t.Fatalf("unexpected error while iteration: %v", err) 517 } 518 if idx != obj1Cnt { 519 t.Fatalf("unexpected no of entries in iteration exp 5 found %d", idx-5) 520 } 521 }) 522 }) 523 524 t.Run("iterate descending", func(t *testing.T) { 525 t.Run("obj1", func(t *testing.T) { 526 idx := 4 527 err := s.Iterate(storage.Query{ 528 Factory: func() storage.Item { return new(obj1) }, 529 ItemProperty: storage.QueryItem, 530 Order: storage.KeyDescendingOrder, 531 }, func(r storage.Result) (bool, error) { 532 if idx < 0 { 533 t.Fatal("index overflow") 534 } 535 checkTestItemEqual(t, r.Entry, testObjs[idx]) 536 idx-- 537 return false, nil 538 }) 539 if err != nil { 540 t.Fatalf("unexpected error while iteration: %v", err) 541 } 542 if idx != -1 { 543 t.Fatalf("unexpected no of entries in iteration exp 5 found %d", 4-idx) 544 } 545 }) 546 t.Run("obj2", func(t *testing.T) { 547 idx := 9 548 err := s.Iterate(storage.Query{ 549 Factory: func() storage.Item { return new(obj2) }, 550 ItemProperty: storage.QueryItem, 551 Order: storage.KeyDescendingOrder, 552 }, func(r storage.Result) (bool, error) { 553 if idx < obj2Cnt { 554 t.Fatal("index overflow") 555 } 556 checkTestItemEqual(t, r.Entry, testObjs[idx]) 557 idx-- 558 return false, nil 559 }) 560 if err != nil { 561 t.Fatalf("unexpected error while iteration: %v", err) 562 } 563 if idx != 4 { 564 t.Fatalf("unexpected no of entries in iteration exp 5 found %d", 9-idx) 565 } 566 }) 567 }) 568 569 t.Run("iterate property", func(t *testing.T) { 570 t.Run("key only", func(t *testing.T) { 571 idx := 0 572 err := s.Iterate(storage.Query{ 573 Factory: func() storage.Item { return new(obj1) }, 574 ItemProperty: storage.QueryItemID, 575 }, func(r storage.Result) (bool, error) { 576 if r.Entry != nil { 577 t.Fatal("expected entry to be nil") 578 } 579 if r.ID != testObjs[idx].ID() { 580 t.Fatalf("invalid key order expected %s found %s", testObjs[idx].ID(), r.ID) 581 } 582 idx++ 583 return false, nil 584 }) 585 if err != nil { 586 t.Fatalf("unexpected error while iteration %v", err) 587 } 588 if idx != obj1Cnt { 589 t.Fatalf("unexpected no of entries in iteration exp 5 found %d", idx) 590 } 591 }) 592 t.Run("size only", func(t *testing.T) { 593 idx := 9 594 err := s.Iterate(storage.Query{ 595 Factory: func() storage.Item { return new(obj2) }, 596 ItemProperty: storage.QueryItemSize, 597 Order: storage.KeyDescendingOrder, 598 }, func(r storage.Result) (bool, error) { 599 if r.Entry != nil { 600 t.Fatal("expected entry to be nil") 601 } 602 if r.ID != testObjs[idx].ID() { 603 t.Fatalf("invalid key order expected %s found %s", testObjs[idx].ID(), r.ID) 604 } 605 buf, err := testObjs[idx].Marshal() 606 if err != nil { 607 t.Fatalf("failed marshaling: %v", err) 608 } 609 if r.Size != len(buf) { 610 t.Fatalf("incorrect size in query expected %d found %d id %s", len(buf), r.Size, r.ID) 611 } 612 idx-- 613 return false, nil 614 }) 615 if err != nil { 616 t.Fatalf("unexpected error while iteration: %v", err) 617 } 618 if idx != 4 { 619 t.Fatalf("unexpected no of entries in iteration exp 5 found %d", 9-idx) 620 } 621 }) 622 }) 623 624 t.Run("iterate filters", func(t *testing.T) { 625 idx := 2 626 err := s.Iterate(storage.Query{ 627 Factory: func() storage.Item { return new(obj1) }, 628 ItemProperty: storage.QueryItem, 629 Filters: []storage.Filter{ 630 func(_ string, v []byte) bool { 631 return binary.LittleEndian.Uint64(v[32:]) < 5 632 }, 633 }, 634 }, func(r storage.Result) (bool, error) { 635 checkTestItemEqual(t, r.Entry, testObjs[idx]) 636 idx++ 637 return false, nil 638 }) 639 if err != nil { 640 t.Fatalf("unexpected error while iteration: %v", err) 641 } 642 if idx != 5 { 643 t.Fatalf("unexpected no of entries in iteration exp 3 found %d", idx-2) 644 } 645 }) 646 647 t.Run("delete", func(t *testing.T) { 648 for idx, i := range testObjs { 649 if idx < 3 || idx > 7 { 650 err := s.Delete(i) 651 if err != nil { 652 t.Fatalf("failed deleting entry: %v", err) 653 } 654 found, err := s.Has(i) 655 if err != nil { 656 t.Fatalf("unexpected error in has: %v", err) 657 } 658 if found { 659 t.Fatalf("found id %s, expected to not be found", i.ID()) 660 } 661 if idx < 3 { 662 err = s.Get(&obj1{Id: i.ID()}) 663 } else { 664 err = s.Get(&obj2{Id: i.(*obj2).Id}) 665 } 666 if want, have := storage.ErrNotFound, err; !errors.Is(have, want) { 667 t.Fatalf("unexpected error: want %v, have %v", want, have) 668 } 669 if idx < 3 { 670 _, err = s.GetSize(&obj1{Id: i.ID()}) 671 } else { 672 _, err = s.GetSize(&obj2{Id: i.(*obj2).Id}) 673 } 674 if want, have := storage.ErrNotFound, err; !errors.Is(have, want) { 675 t.Fatalf("unexpected error: want %v, have %v", want, have) 676 } 677 } 678 } 679 }) 680 681 t.Run("count after delete", func(t *testing.T) { 682 t.Run("obj1", func(t *testing.T) { 683 cnt, err := s.Count(new(obj1)) 684 if err != nil { 685 t.Fatalf("failed getting count: %v", err) 686 } 687 if cnt != 2 { 688 t.Fatalf("unexpected count exp 2 found %d", cnt) 689 } 690 }) 691 t.Run("obj2", func(t *testing.T) { 692 cnt, err := s.Count(new(obj2)) 693 if err != nil { 694 t.Fatalf("failed getting count: %v", err) 695 } 696 if cnt != 3 { 697 t.Fatalf("unexpected count exp 3 found %d", cnt) 698 } 699 }) 700 }) 701 702 t.Run("iterate after delete", func(t *testing.T) { 703 t.Run("obj1", func(t *testing.T) { 704 idx := 3 705 err := s.Iterate(storage.Query{ 706 Factory: func() storage.Item { return new(obj1) }, 707 ItemProperty: storage.QueryItem, 708 }, func(r storage.Result) (bool, error) { 709 checkTestItemEqual(t, r.Entry, testObjs[idx]) 710 idx++ 711 return false, nil 712 }) 713 if err != nil { 714 t.Fatalf("unexpected error while iteration: %v", err) 715 } 716 if idx != obj1Cnt { 717 t.Fatalf("unexpected no of entries in iteration exp 2 found %d", idx-3) 718 } 719 }) 720 t.Run("obj2", func(t *testing.T) { 721 idx := obj2Cnt 722 err := s.Iterate(storage.Query{ 723 Factory: func() storage.Item { return new(obj2) }, 724 ItemProperty: storage.QueryItem, 725 }, func(r storage.Result) (bool, error) { 726 checkTestItemEqual(t, r.Entry, testObjs[idx]) 727 idx++ 728 return false, nil 729 }) 730 if err != nil { 731 t.Fatalf("unexpected error while iteration: %v", err) 732 } 733 if idx != 8 { 734 t.Fatalf("unexpected no of entries in iteration exp 3 found %d", idx-5) 735 } 736 }) 737 }) 738 739 t.Run("error during iteration", func(t *testing.T) { 740 expErr := errors.New("test error") 741 err := s.Iterate(storage.Query{ 742 Factory: func() storage.Item { return new(obj1) }, 743 ItemProperty: storage.QueryItem, 744 }, func(r storage.Result) (bool, error) { 745 return true, expErr 746 }) 747 if !errors.Is(err, expErr) { 748 t.Fatal("incorrect error returned") 749 } 750 }) 751 752 t.Run("close", func(t *testing.T) { 753 err := s.Close() 754 if err != nil { 755 t.Fatalf("failed closing: %v", err) 756 } 757 }) 758 } 759 760 // ItemMarshalAndUnmarshalTest represents a test case 761 // for the TestItemMarshalAndUnmarshal function. 762 type ItemMarshalAndUnmarshalTest struct { 763 Item storage.Item 764 Factory func() storage.Item 765 MarshalErr error // Expected error from Marshal. 766 UnmarshalErr error // Expected error from Unmarshal. 767 CmpOpts []cmp.Option 768 } 769 770 // TestItemMarshalAndUnmarshal provides correctness testsuite 771 // for storage.Item serialization and deserialization. 772 func TestItemMarshalAndUnmarshal(t *testing.T, test *ItemMarshalAndUnmarshalTest) { 773 t.Helper() 774 775 buf, err := test.Item.Marshal() 776 if !errors.Is(err, test.MarshalErr) { 777 t.Fatalf("Marshal(): want error: %v; have error: %v", test.MarshalErr, err) 778 } 779 if test.MarshalErr != nil { 780 return 781 } 782 if len(buf) == 0 { 783 t.Fatalf("Marshal(): empty buffer") 784 } 785 786 item2 := test.Factory() 787 if err := item2.Unmarshal(buf); !errors.Is(err, test.UnmarshalErr) { 788 t.Fatalf("Unmarshal(): want error: %v; have error: %v", test.UnmarshalErr, err) 789 } 790 if test.UnmarshalErr != nil { 791 return 792 } 793 794 want, have := test.Item, item2 795 if !cmp.Equal(want, have, test.CmpOpts...) { 796 t.Errorf("Marshal/Unmarshal mismatch (-want +have):\n%s", cmp.Diff(want, have, test.CmpOpts...)) 797 } 798 } 799 800 // ItemCloneTest represents a test case for the TestItemClone function. 801 type ItemCloneTest struct { 802 Item storage.Item 803 CmpOpts []cmp.Option 804 } 805 806 // TestItemClone provides correctness testsuite for storage.Item clone capabilities. 807 func TestItemClone(t *testing.T, test *ItemCloneTest) { 808 want := test.Item 809 have := test.Item.Clone() 810 811 if diff := cmp.Diff(want, have, test.CmpOpts...); diff != "" { 812 t.Errorf("Clone(...): result mismatch (-want +have):\n%s", diff) 813 } 814 } 815 816 func BenchmarkStore(b *testing.B, s storage.Store) { 817 b.Run("WriteSequential", func(b *testing.B) { 818 BenchmarkWriteSequential(b, s) 819 }) 820 b.Run("WriteRandom", func(b *testing.B) { 821 BenchmarkWriteRandom(b, s) 822 }) 823 b.Run("ReadSequential", func(b *testing.B) { 824 BenchmarkReadSequential(b, s) 825 }) 826 b.Run("ReadRandom", func(b *testing.B) { 827 BenchmarkReadRandom(b, s) 828 }) 829 b.Run("ReadRandomMissing", func(b *testing.B) { 830 BenchmarkReadRandomMissing(b, s) 831 }) 832 b.Run("ReadReverse", func(b *testing.B) { 833 BenchmarkReadReverse(b, s) 834 }) 835 b.Run("ReadRedHot", func(b *testing.B) { 836 BenchmarkReadHot(b, s) 837 }) 838 b.Run("IterateSequential", func(b *testing.B) { 839 BenchmarkIterateSequential(b, s) 840 }) 841 b.Run("IterateReverse", func(b *testing.B) { 842 BenchmarkIterateReverse(b, s) 843 }) 844 b.Run("DeleteRandom", func(b *testing.B) { 845 BenchmarkDeleteRandom(b, s) 846 }) 847 b.Run("DeleteSequential", func(b *testing.B) { 848 BenchmarkDeleteSequential(b, s) 849 }) 850 } 851 852 // BenchmarkBatchedStore provides a benchmark suite for the 853 // storage.BatchedStore. Only the Write and Delete methods are tested. 854 func BenchmarkBatchedStore(b *testing.B, bs storage.BatchStore) { 855 b.Run("WriteInBatches", func(b *testing.B) { 856 BenchmarkWriteInBatches(b, bs) 857 }) 858 b.Run("WriteInFixedSizeBatches", func(b *testing.B) { 859 BenchmarkWriteInFixedSizeBatches(b, bs) 860 }) 861 b.Run("DeleteInBatches", func(b *testing.B) { 862 BenchmarkDeleteInBatches(b, bs) 863 }) 864 b.Run("DeleteInFixedSizeBatches", func(b *testing.B) { 865 BenchmarkDeleteInFixedSizeBatches(b, bs) 866 }) 867 } 868 869 func BenchmarkReadRandom(b *testing.B, db storage.Store) { 870 g := newRandomKeyGenerator(b.N) 871 resetBenchmark(b) 872 doRead(b, db, g, false) 873 } 874 875 func BenchmarkReadRandomMissing(b *testing.B, db storage.Store) { 876 g := newRandomMissingKeyGenerator(b.N) 877 resetBenchmark(b) 878 doRead(b, db, g, true) 879 } 880 881 func BenchmarkReadSequential(b *testing.B, db storage.Store) { 882 g := newSequentialKeyGenerator(b.N) 883 populate(b, db) 884 resetBenchmark(b) 885 doRead(b, db, g, false) 886 } 887 888 func BenchmarkReadReverse(b *testing.B, db storage.Store) { 889 g := newReversedKeyGenerator(newSequentialKeyGenerator(b.N)) 890 populate(b, db) 891 resetBenchmark(b) 892 doRead(b, db, g, false) 893 } 894 895 func BenchmarkReadHot(b *testing.B, db storage.Store) { 896 k := maxInt((b.N+99)/100, 1) 897 g := newRoundKeyGenerator(newRandomKeyGenerator(k)) 898 populate(b, db) 899 resetBenchmark(b) 900 doRead(b, db, g, false) 901 } 902 903 func BenchmarkIterateSequential(b *testing.B, db storage.Store) { 904 populate(b, db) 905 resetBenchmark(b) 906 var counter int 907 fn := func(r storage.Result) (bool, error) { 908 counter++ 909 if counter > b.N { 910 return true, nil 911 } 912 return false, nil 913 } 914 q := storage.Query{ 915 Factory: func() storage.Item { return new(obj1) }, 916 Order: storage.KeyAscendingOrder, 917 } 918 if err := db.Iterate(q, fn); err != nil { 919 b.Fatal("iterate", err) 920 } 921 } 922 923 func BenchmarkIterateReverse(b *testing.B, db storage.Store) { 924 populate(b, db) 925 resetBenchmark(b) 926 var counter int 927 fn := func(storage.Result) (bool, error) { 928 counter++ 929 if counter > b.N { 930 return true, nil 931 } 932 return false, nil 933 } 934 q := storage.Query{ 935 Factory: func() storage.Item { return new(obj1) }, 936 Order: storage.KeyDescendingOrder, 937 } 938 if err := db.Iterate(q, fn); err != nil { 939 b.Fatal("iterate", err) 940 } 941 } 942 943 func BenchmarkWriteSequential(b *testing.B, db storage.Store) { 944 g := newSequentialEntryGenerator(b.N) 945 resetBenchmark(b) 946 doWrite(b, db, g) 947 } 948 949 func BenchmarkWriteInBatches(b *testing.B, bs storage.BatchStore) { 950 g := newSequentialEntryGenerator(b.N) 951 batch := bs.Batch(context.Background()) 952 resetBenchmark(b) 953 for i := 0; i < b.N; i++ { 954 key := g.Key(i) 955 item := &obj1{ 956 Id: string(key), 957 Buf: g.Value(i), 958 } 959 if err := batch.Put(item); err != nil { 960 b.Fatalf("write key '%s': %v", string(g.Key(i)), err) 961 } 962 } 963 if err := batch.Commit(); err != nil { 964 b.Fatal("commit batch", err) 965 } 966 } 967 968 func BenchmarkWriteInFixedSizeBatches(b *testing.B, bs storage.BatchStore) { 969 g := newSequentialEntryGenerator(b.N) 970 writer := newBatchDBWriter(bs) 971 resetBenchmark(b) 972 for i := 0; i < b.N; i++ { 973 writer.Put(g.Key(i), g.Value(i)) 974 } 975 } 976 977 func BenchmarkWriteRandom(b *testing.B, db storage.Store) { 978 for i, n := 1, *maxConcurrency; i <= n; i *= 2 { 979 name := fmt.Sprintf("parallelism-%d", i) 980 runtime.GC() 981 parallelism := i 982 b.Run(name, func(b *testing.B) { 983 var gens []entryGenerator 984 start, step := 0, (b.N+parallelism)/parallelism 985 n := step * parallelism 986 g := newFullRandomEntryGenerator(0, n) 987 for i := 0; i < parallelism; i++ { 988 gens = append(gens, newStartAtEntryGenerator(start, g)) 989 start += step 990 } 991 resetBenchmark(b) 992 var wg sync.WaitGroup 993 wg.Add(len(gens)) 994 for _, g := range gens { 995 go func(g entryGenerator) { 996 defer wg.Done() 997 doWrite(b, db, g) 998 }(g) 999 } 1000 wg.Wait() 1001 }) 1002 } 1003 } 1004 1005 func BenchmarkDeleteRandom(b *testing.B, db storage.Store) { 1006 g := newFullRandomEntryGenerator(0, b.N) 1007 doWrite(b, db, g) 1008 resetBenchmark(b) 1009 doDelete(b, db, g) 1010 } 1011 1012 func BenchmarkDeleteSequential(b *testing.B, db storage.Store) { 1013 g := newSequentialEntryGenerator(b.N) 1014 doWrite(b, db, g) 1015 resetBenchmark(b) 1016 doDelete(b, db, g) 1017 } 1018 1019 func BenchmarkDeleteInBatches(b *testing.B, bs storage.BatchStore) { 1020 g := newSequentialEntryGenerator(b.N) 1021 doWrite(b, bs, g) 1022 resetBenchmark(b) 1023 batch := bs.Batch(context.Background()) 1024 for i := 0; i < b.N; i++ { 1025 item := &obj1{ 1026 Id: string(g.Key(i)), 1027 } 1028 if err := batch.Delete(item); err != nil { 1029 b.Fatalf("delete key '%s': %v", string(g.Key(i)), err) 1030 } 1031 } 1032 if err := batch.Commit(); err != nil { 1033 b.Fatal("commit batch", err) 1034 } 1035 } 1036 1037 func BenchmarkDeleteInFixedSizeBatches(b *testing.B, bs storage.BatchStore) { 1038 g := newSequentialEntryGenerator(b.N) 1039 doWrite(b, bs, g) 1040 resetBenchmark(b) 1041 writer := newBatchDBWriter(bs) 1042 for i := 0; i < b.N; i++ { 1043 writer.Delete(g.Key(i)) 1044 } 1045 }