github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/kv/kvtest/iterators.go (about)

     1  package kvtest
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"sync/atomic"
     7  	"testing"
     8  
     9  	"github.com/go-test/deep"
    10  	"github.com/treeverse/lakefs/pkg/graveler"
    11  	"github.com/treeverse/lakefs/pkg/kv"
    12  )
    13  
    14  const (
    15  	firstPartitionKey  = "m"
    16  	secondPartitionKey = "ma"
    17  )
    18  
    19  type StoreWithCounter struct {
    20  	kv.Store
    21  	ScanCalls int64
    22  }
    23  
    24  // MaxPageSize is the maximum page size for pagination tests
    25  const MaxPageSize = 10
    26  
    27  func NewStoreWithCounter(store kv.Store) *StoreWithCounter {
    28  	return &StoreWithCounter{Store: store}
    29  }
    30  
    31  func (swc *StoreWithCounter) Scan(ctx context.Context, partitionKey []byte, options kv.ScanOptions) (kv.EntriesIterator, error) {
    32  	atomic.AddInt64(&swc.ScanCalls, 1)
    33  	return swc.Store.Scan(ctx, partitionKey, options)
    34  }
    35  
    36  func testPartitionIterator(t *testing.T, ms MakeStore) {
    37  	ctx := context.Background()
    38  	store := ms(t, ctx)
    39  
    40  	// prepare data
    41  	modelNames := []string{"a", "aa", "b", "c", "d"}
    42  	for _, name := range modelNames {
    43  		model := TestModel{Name: []byte(name)}
    44  		for _, partitionKey := range []string{firstPartitionKey, secondPartitionKey} {
    45  			err := kv.SetMsg(ctx, store, partitionKey, model.Name, &model)
    46  			if err != nil {
    47  				t.Fatalf("failed to set model (partition %s, name %s): %s", partitionKey, name, err)
    48  			}
    49  		}
    50  	}
    51  
    52  	t.Run("listing all values of partition", testPartitionIteratorListAll(ctx, store))
    53  	t.Run("listing values SeekGE", listPartitionIteratorWithSeekGE(ctx, store))
    54  	t.Run("count scans on successive SeekGE operations", testPartitionIteratorCountScansOnSeekGE(store, ctx))
    55  	t.Run("failed SeekGE partition not found", testPartitionIteratorSeekGEOnPartitionNotFound(ctx, store))
    56  	t.Run("SeekGE past end", testPartitionIteratorSeekGEPastEnd(ctx, store))
    57  	t.Run("SeekGE seek back", testPartitionIteratorSeekGESeekBack(ctx, store))
    58  	t.Run("listing values SeekGE after pagination", testPartitionIteratorSeekGEWithPagination(ctx, store))
    59  }
    60  
    61  func testPartitionIteratorSeekGEWithPagination(ctx context.Context, store kv.Store) func(t *testing.T) {
    62  	return func(t *testing.T) {
    63  		// load much more data
    64  		moreModelNames := []string{
    65  			"da", "db", "dc", "dd", "de", "df", "dg", "dh", "di", "dj",
    66  			"dk", "dl", "dm", "dn", "do", "dp", "dq", "dr", "ds", "dt",
    67  			"du", "dv", "dw", "dx", "dy", "dz",
    68  			"z",
    69  		}
    70  		for _, name := range moreModelNames {
    71  			model := TestModel{Name: []byte(name)}
    72  			for _, partitionKey := range []string{firstPartitionKey, secondPartitionKey} {
    73  				err := kv.SetMsg(ctx, store, partitionKey, model.Name, &model)
    74  				if err != nil {
    75  					t.Fatalf("failed to set model (partition %s, name %s): %s", partitionKey, name, err)
    76  				}
    77  			}
    78  		}
    79  
    80  		itr := kv.NewPartitionIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(), secondPartitionKey, 0)
    81  		if itr == nil {
    82  			t.Fatalf("failed to create partition iterator")
    83  		}
    84  		defer itr.Close()
    85  
    86  		if !itr.Next() {
    87  			t.Fatal("expected Next to be true")
    88  		}
    89  
    90  		itr.SeekGE([]byte("b"))
    91  		for i := 0; i < MaxPageSize+1; i++ {
    92  			// force pagination
    93  			if !itr.Next() {
    94  				t.Fatal("expected Next to be true")
    95  			}
    96  		}
    97  
    98  		itr.SeekGE([]byte("z"))
    99  		if !itr.Next() {
   100  			t.Fatal("expected Next to be true")
   101  		}
   102  
   103  		itr.SeekGE([]byte("d1"))
   104  		names := scanPartitionIterator(t, itr, func(_ []byte, model *TestModel) string { return string(model.Name) })
   105  		if diffs := deep.Equal(names, moreModelNames); diffs != nil {
   106  			t.Fatalf("got wrong list of names: %v", diffs)
   107  		}
   108  	}
   109  }
   110  
   111  func testPartitionIteratorSeekGESeekBack(ctx context.Context, store kv.Store) func(t *testing.T) {
   112  	return func(t *testing.T) {
   113  		itr := kv.NewPartitionIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(), firstPartitionKey, 0)
   114  		if itr == nil {
   115  			t.Fatalf("failed to create partition iterator")
   116  		}
   117  		defer itr.Close()
   118  		itr.SeekGE([]byte("z"))
   119  		if itr.Next() {
   120  			t.Fatal("expected Next to be false")
   121  		}
   122  		if err := itr.Err(); err != nil {
   123  			t.Fatalf("unexpected error: %s", err)
   124  		}
   125  		itr.SeekGE([]byte("a"))
   126  		if !itr.Next() {
   127  			t.Fatalf("expected Next to be true")
   128  		}
   129  		if err := itr.Err(); err != nil {
   130  			t.Fatalf("unexpected error: %s", err)
   131  		}
   132  		e := itr.Entry()
   133  		model := e.Value.(*TestModel)
   134  		if string(model.Name) != "a" {
   135  			t.Fatalf("expected value a from iterator")
   136  		}
   137  	}
   138  }
   139  
   140  func testPartitionIteratorSeekGEPastEnd(ctx context.Context, store kv.Store) func(t *testing.T) {
   141  	return func(t *testing.T) {
   142  		itr := kv.NewPartitionIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(), firstPartitionKey, 0)
   143  		if itr == nil {
   144  			t.Fatalf("failed to create partition iterator")
   145  		}
   146  		defer itr.Close()
   147  		itr.SeekGE([]byte("b"))
   148  		if !itr.Next() {
   149  			t.Fatal("expected Next to be true")
   150  		}
   151  		e := itr.Entry()
   152  		model := e.Value.(*TestModel)
   153  		if string(model.Name) != "b" {
   154  			t.Fatalf("expected value b from iterator")
   155  		}
   156  		itr.SeekGE(graveler.UpperBoundForPrefix([]byte("d1")))
   157  		if itr.Next() {
   158  			t.Fatalf("expected Next to be false")
   159  		}
   160  		if err := itr.Err(); err != nil {
   161  			t.Fatalf("unexpected error: %v", err)
   162  		}
   163  	}
   164  }
   165  
   166  func testPartitionIteratorSeekGEOnPartitionNotFound(ctx context.Context, store kv.Store) func(t *testing.T) {
   167  	return func(t *testing.T) {
   168  		itr := kv.NewPartitionIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(), "", 0)
   169  		if itr == nil {
   170  			t.Fatalf("failed to create partition iterator")
   171  		}
   172  		defer itr.Close()
   173  		itr.SeekGE([]byte("d"))
   174  		if itr.Next() {
   175  			t.Fatalf("next after seekGE expected to be false")
   176  		}
   177  
   178  		itr.SeekGE([]byte("d"))
   179  		if itr.Next() {
   180  			t.Fatalf("next after seekGE expected to be false")
   181  		}
   182  
   183  		if err := itr.Err(); !errors.Is(err, kv.ErrMissingPartitionKey) {
   184  			t.Fatalf("expected error: %s, got %v", kv.ErrMissingPartitionKey, err)
   185  		}
   186  	}
   187  }
   188  
   189  func testPartitionIteratorCountScansOnSeekGE(store kv.Store, ctx context.Context) func(t *testing.T) {
   190  	return func(t *testing.T) {
   191  		store := NewStoreWithCounter(store)
   192  		itr := kv.NewPartitionIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(), secondPartitionKey, 0)
   193  		if itr == nil {
   194  			t.Fatalf("failed to create partition iterator")
   195  		}
   196  		defer itr.Close()
   197  		for _, seekValue := range []string{"b", "c", "d"} {
   198  			itr.SeekGE([]byte(seekValue))
   199  			if !itr.Next() {
   200  				t.Fatalf("Expected iterator to have a value")
   201  			}
   202  			if err := itr.Err(); err != nil {
   203  				t.Fatalf("unexpected error: %v", err)
   204  			}
   205  			k := itr.Entry().Key
   206  			if string(k) != seekValue {
   207  				t.Fatalf("Expected to find value %s. Found %s", seekValue, k)
   208  			}
   209  		}
   210  		scanCalls := atomic.LoadInt64(&store.ScanCalls)
   211  		if scanCalls != 1 {
   212  			t.Fatalf("Expected exactly 1 call to Scan. got: %d", scanCalls)
   213  		}
   214  	}
   215  }
   216  
   217  func listPartitionIteratorWithSeekGE(ctx context.Context, store kv.Store) func(t *testing.T) {
   218  	return func(t *testing.T) {
   219  		itr := kv.NewPartitionIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(), secondPartitionKey, 0)
   220  		if itr == nil {
   221  			t.Fatalf("failed to create partition iterator")
   222  		}
   223  		defer itr.Close()
   224  		for _, seekValue := range []string{"b", "aaa", "b"} {
   225  			itr.SeekGE([]byte(seekValue))
   226  			names := scanPartitionIterator(t, itr, func(_ []byte, model *TestModel) string { return string(model.Name) })
   227  			if diffs := deep.Equal(names, []string{"b", "c", "d"}); diffs != nil {
   228  				t.Fatalf("got wrong list of names: %v", diffs)
   229  			}
   230  		}
   231  	}
   232  }
   233  
   234  func testPartitionIteratorListAll(ctx context.Context, store kv.Store) func(t *testing.T) {
   235  	return func(t *testing.T) {
   236  		itr := kv.NewPartitionIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(), firstPartitionKey, 0)
   237  		if itr == nil {
   238  			t.Fatalf("failed to create partition iterator")
   239  		}
   240  		defer itr.Close()
   241  		names := scanPartitionIterator(t, itr, func(_ []byte, model *TestModel) string { return string(model.Name) })
   242  		if diffs := deep.Equal(names, []string{"a", "aa", "b", "c", "d"}); diffs != nil {
   243  			t.Fatalf("got wrong list of names: %v", diffs)
   244  		}
   245  	}
   246  }
   247  
   248  // scanPartitionIterator scans the iterator and returns a slice of the results of applying fn to each model.
   249  // slice element type is based on the callback 'fn' function return type.
   250  func scanPartitionIterator[E any](t *testing.T, itr kv.MessageIterator, fn func(key []byte, model *TestModel) E) []E {
   251  	t.Helper()
   252  	result := make([]E, 0)
   253  	for itr.Next() {
   254  		e := itr.Entry()
   255  		model := e.Value.(*TestModel)
   256  		result = append(result, fn(e.Key, model))
   257  	}
   258  	if err := itr.Err(); err != nil {
   259  		t.Fatalf("While scan partition iterator unexpected error: %v", err)
   260  	}
   261  	return result
   262  }
   263  
   264  func testPrimaryIterator(t *testing.T, ms MakeStore) {
   265  	ctx := context.Background()
   266  	store := ms(t, ctx)
   267  
   268  	// prepare data
   269  	modelNames := []string{"a", "aa", "b", "c", "d"}
   270  	for _, name := range modelNames {
   271  		model := TestModel{Name: []byte(name)}
   272  		for _, partitionKey := range []string{firstPartitionKey, secondPartitionKey} {
   273  			err := kv.SetMsg(ctx, store, partitionKey, model.Name, &model)
   274  			if err != nil {
   275  				t.Fatalf("failed to set model (partition %s, name %s): %s", partitionKey, name, err)
   276  			}
   277  		}
   278  	}
   279  
   280  	t.Run("listing all values of partition", func(t *testing.T) {
   281  		itr, err := kv.NewPrimaryIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(),
   282  			firstPartitionKey, []byte(""), kv.IteratorOptionsFrom([]byte("")))
   283  		if err != nil {
   284  			t.Fatalf("failed to create primary iterator: %v", err)
   285  		}
   286  		defer itr.Close()
   287  		names := scanPartitionIterator(t, itr, func(_ []byte, model *TestModel) string { return string(model.Name) })
   288  		if diffs := deep.Equal(names, []string{"a", "aa", "b", "c", "d"}); diffs != nil {
   289  			t.Fatalf("got wrong list of names: %v", diffs)
   290  		}
   291  	})
   292  
   293  	t.Run("listing with prefix", func(t *testing.T) {
   294  		itr, err := kv.NewPrimaryIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(),
   295  			secondPartitionKey, []byte("a"), kv.IteratorOptionsFrom([]byte("")))
   296  		if err != nil {
   297  			t.Fatalf("failed to create primary iterator: %v", err)
   298  		}
   299  		defer itr.Close()
   300  		names := scanPartitionIterator(t, itr, func(_ []byte, model *TestModel) string { return string(model.Name) })
   301  		if diffs := deep.Equal(names, []string{"a", "aa"}); diffs != nil {
   302  			t.Fatalf("got wrong list of names: %v", diffs)
   303  		}
   304  	})
   305  
   306  	t.Run("listing empty value", func(t *testing.T) {
   307  		itr, err := kv.NewPrimaryIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(),
   308  			secondPartitionKey, []byte(""), kv.IteratorOptionsAfter([]byte("d")))
   309  		if err != nil {
   310  			t.Fatalf("failed to create primary iterator: %v", err)
   311  		}
   312  		defer itr.Close()
   313  
   314  		if itr.Next() {
   315  			t.Fatalf("next should return false")
   316  		}
   317  		if err := itr.Err(); err != nil {
   318  			t.Fatalf("unexpected error: %v", err)
   319  		}
   320  	})
   321  
   322  	t.Run("listing from", func(t *testing.T) {
   323  		itr, err := kv.NewPrimaryIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(),
   324  			firstPartitionKey, []byte(""), kv.IteratorOptionsFrom([]byte("b")))
   325  		if err != nil {
   326  			t.Fatalf("failed to create primary iterator: %v", err)
   327  		}
   328  		defer itr.Close()
   329  		names := scanPartitionIterator(t, itr, func(_ []byte, model *TestModel) string { return string(model.Name) })
   330  		if diffs := deep.Equal(names, []string{"b", "c", "d"}); diffs != nil {
   331  			t.Fatalf("got wrong list of names: %v", diffs)
   332  		}
   333  	})
   334  
   335  	t.Run("listing after", func(t *testing.T) {
   336  		itr, err := kv.NewPrimaryIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(),
   337  			firstPartitionKey, []byte(""), kv.IteratorOptionsAfter([]byte("b")))
   338  		if err != nil {
   339  			t.Fatalf("failed to create primary iterator: %v", err)
   340  		}
   341  		defer itr.Close()
   342  		names := scanPartitionIterator(t, itr, func(_ []byte, model *TestModel) string { return string(model.Name) })
   343  		if diffs := deep.Equal(names, []string{"c", "d"}); diffs != nil {
   344  			t.Fatalf("got wrong list of names: %v", diffs)
   345  		}
   346  	})
   347  }
   348  
   349  func testSecondaryIterator(t *testing.T, ms MakeStore) {
   350  	ctx := context.Background()
   351  	store := ms(t, ctx)
   352  
   353  	// prepare data
   354  	m := []TestModel{
   355  		{Name: []byte("a"), JustAString: "one"},
   356  		{Name: []byte("b"), JustAString: "two"},
   357  		{Name: []byte("c"), JustAString: "three"},
   358  		{Name: []byte("d"), JustAString: "four"},
   359  		{Name: []byte("e"), JustAString: "five"},
   360  	}
   361  	for i := 0; i < len(m); i++ {
   362  		primaryKey := append([]byte("name/"), m[i].Name...)
   363  		err := kv.SetMsg(ctx, store, firstPartitionKey, primaryKey, &m[i])
   364  		if err != nil {
   365  			t.Fatal("failed to set model (primary key)", err)
   366  		}
   367  		secondaryKey := append([]byte("just_a_string/"), m[i].JustAString...)
   368  		err = kv.SetMsg(ctx, store, firstPartitionKey, secondaryKey, &kv.SecondaryIndex{PrimaryKey: primaryKey})
   369  		if err != nil {
   370  			t.Fatal("failed to set model (secondary key)", err)
   371  		}
   372  	}
   373  	err := kv.SetMsg(ctx, store, secondPartitionKey, []byte("just_a_string/e"), &kv.SecondaryIndex{})
   374  	if err != nil {
   375  		t.Fatal("failed to set key", err)
   376  	}
   377  	err = kv.SetMsg(ctx, store, secondPartitionKey, []byte("just_a_string/f"), &kv.SecondaryIndex{PrimaryKey: []byte("name/no-name")})
   378  	if err != nil {
   379  		t.Fatal("failed to set model", err)
   380  	}
   381  
   382  	t.Run("listing all values of partition", func(t *testing.T) {
   383  		itr, err := kv.NewSecondaryIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(),
   384  			firstPartitionKey, []byte("just_a_string/"), []byte(""))
   385  		if err != nil {
   386  			t.Fatalf("failed to create secondary iterator: %v", err)
   387  		}
   388  		defer itr.Close()
   389  
   390  		type KeyNameValue struct {
   391  			Key   []byte
   392  			Name  []byte
   393  			Value string
   394  		}
   395  		msgs := scanPartitionIterator(t, itr, func(key []byte, model *TestModel) KeyNameValue {
   396  			return KeyNameValue{
   397  				Key:   key,
   398  				Name:  model.Name,
   399  				Value: model.JustAString,
   400  			}
   401  		})
   402  		expectedMsgs := []KeyNameValue{
   403  			{Key: []byte("just_a_string/five"), Name: []byte("e"), Value: "five"},
   404  			{Key: []byte("just_a_string/four"), Name: []byte("d"), Value: "four"},
   405  			{Key: []byte("just_a_string/one"), Name: []byte("a"), Value: "one"},
   406  			{Key: []byte("just_a_string/three"), Name: []byte("c"), Value: "three"},
   407  			{Key: []byte("just_a_string/two"), Name: []byte("b"), Value: "two"},
   408  		}
   409  		if diffs := deep.Equal(msgs, expectedMsgs); diffs != nil {
   410  			t.Fatalf("Diff expected result: %v", diffs)
   411  		}
   412  	})
   413  
   414  	t.Run("listing with prefix", func(t *testing.T) {
   415  		itr, err := kv.NewSecondaryIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(),
   416  			firstPartitionKey, []byte("just_a_string/f"), []byte(""))
   417  		if err != nil {
   418  			t.Fatalf("failed to create secondary iterator: %v", err)
   419  		}
   420  		defer itr.Close()
   421  		var msgs []*TestModel
   422  		for itr.Next() {
   423  			e := itr.Entry()
   424  			model := e.Value.(*TestModel)
   425  			msgs = append(msgs, model)
   426  		}
   427  		if err := itr.Err(); err != nil {
   428  			t.Fatalf("unexpected error: %v", err)
   429  		}
   430  
   431  		expectedMsgs := []*TestModel{
   432  			{Name: []byte("e"), JustAString: "five"},
   433  			{Name: []byte("d"), JustAString: "four"},
   434  		}
   435  		if diffs := deep.Equal(msgs, expectedMsgs); diffs != nil {
   436  			t.Fatalf("Diff expected result: %v", diffs)
   437  		}
   438  	})
   439  
   440  	t.Run("listing after", func(t *testing.T) {
   441  		itr, err := kv.NewSecondaryIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(),
   442  			firstPartitionKey, []byte("just_a_string/"), []byte("just_a_string/four"))
   443  		if err != nil {
   444  			t.Fatalf("failed to create secondary iterator: %v", err)
   445  		}
   446  		defer itr.Close()
   447  		var msgs []*TestModel
   448  		for itr.Next() {
   449  			e := itr.Entry()
   450  			model := e.Value.(*TestModel)
   451  			msgs = append(msgs, model)
   452  		}
   453  		if err := itr.Err(); err != nil {
   454  			t.Fatalf("unexpected error: %v", err)
   455  		}
   456  
   457  		expectedMsgs := []*TestModel{
   458  			{Name: []byte("a"), JustAString: "one"},
   459  			{Name: []byte("c"), JustAString: "three"},
   460  			{Name: []byte("b"), JustAString: "two"},
   461  		}
   462  		if diffs := deep.Equal(msgs, expectedMsgs); diffs != nil {
   463  			t.Fatalf("Diff expected result: %v", diffs)
   464  		}
   465  	})
   466  
   467  	t.Run("key not found", func(t *testing.T) {
   468  		itr, err := kv.NewSecondaryIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(),
   469  			secondPartitionKey, []byte("just_a_string/e"), []byte(""))
   470  		if err != nil {
   471  			t.Fatalf("failed to create secondary iterator: %v", err)
   472  		}
   473  		defer itr.Close()
   474  
   475  		if itr.Next() {
   476  			t.Fatalf("Next() should return false")
   477  		}
   478  
   479  		expectedErr := kv.ErrMissingKey
   480  		if err := itr.Err(); !errors.Is(err, expectedErr) {
   481  			t.Fatalf("expected error: %s, got %v", expectedErr, err)
   482  		}
   483  	})
   484  
   485  	t.Run("value not found", func(t *testing.T) {
   486  		itr, err := kv.NewSecondaryIterator(ctx, store, (&TestModel{}).ProtoReflect().Type(),
   487  			secondPartitionKey, []byte("just_a_string/f"), []byte(""))
   488  		if err != nil {
   489  			t.Fatalf("failed to create secondary iterator: %v", err)
   490  		}
   491  		defer itr.Close()
   492  		if itr.Next() {
   493  			t.Fatalf("next should return false")
   494  		}
   495  		if err := itr.Err(); err != nil {
   496  			t.Fatalf("unexpected error: %v", err)
   497  		}
   498  	})
   499  }