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  }