github.com/cockroachdb/pebble@v0.0.0-20231214172447-ab4952c5f87b/internal/keyspan/interleaving_iter_test.go (about)

     1  // Copyright 2021 The LevelDB-Go and Pebble Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  package keyspan
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"fmt"
    11  	"io"
    12  	"sort"
    13  	"strings"
    14  	"testing"
    15  
    16  	"github.com/cockroachdb/datadriven"
    17  	"github.com/cockroachdb/pebble/internal/base"
    18  	"github.com/cockroachdb/pebble/internal/testkeys"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  func TestInterleavingIter(t *testing.T) {
    23  	runInterleavingIterTest(t, "testdata/interleaving_iter")
    24  }
    25  
    26  func TestInterleavingIter_Masking(t *testing.T) {
    27  	runInterleavingIterTest(t, "testdata/interleaving_iter_masking")
    28  }
    29  
    30  type maskingHooks struct {
    31  	log        io.Writer
    32  	cmp        base.Compare
    33  	split      base.Split
    34  	threshold  []byte
    35  	maskSuffix []byte
    36  }
    37  
    38  func (m *maskingHooks) SpanChanged(s *Span) {
    39  	if m.log != nil {
    40  		if s == nil {
    41  			fmt.Fprintln(m.log, "-- SpanChanged(nil)")
    42  		} else {
    43  			fmt.Fprintf(m.log, "-- SpanChanged(%s)\n", s)
    44  		}
    45  	}
    46  
    47  	// Find the smallest suffix of a key contained within the Span, excluding
    48  	// suffixes less than m.threshold.
    49  	m.maskSuffix = nil
    50  	if s == nil || m.threshold == nil || len(s.Keys) == 0 {
    51  		return
    52  	}
    53  	for i := range s.Keys {
    54  		if s.Keys[i].Suffix == nil {
    55  			continue
    56  		}
    57  		if m.cmp(s.Keys[i].Suffix, m.threshold) < 0 {
    58  			continue
    59  		}
    60  		if m.maskSuffix == nil || m.cmp(m.maskSuffix, s.Keys[i].Suffix) > 0 {
    61  			m.maskSuffix = s.Keys[i].Suffix
    62  		}
    63  	}
    64  }
    65  
    66  func (m *maskingHooks) SkipPoint(userKey []byte) bool {
    67  	pointSuffix := userKey[m.split(userKey):]
    68  	return m.maskSuffix != nil && len(pointSuffix) > 0 && m.cmp(m.maskSuffix, pointSuffix) < 0
    69  }
    70  
    71  func runInterleavingIterTest(t *testing.T, filename string) {
    72  	cmp := testkeys.Comparer.Compare
    73  	var keyspanIter MergingIter
    74  	var pointIter pointIterator
    75  	var iter InterleavingIter
    76  	var buf bytes.Buffer
    77  	hooks := maskingHooks{
    78  		log:   &buf,
    79  		cmp:   testkeys.Comparer.Compare,
    80  		split: testkeys.Comparer.Split,
    81  	}
    82  
    83  	var prevKey *base.InternalKey
    84  	formatKey := func(k *base.InternalKey, _ base.LazyValue) {
    85  		if k == nil {
    86  			fmt.Fprint(&buf, ".")
    87  			return
    88  		}
    89  		prevKey = k
    90  		s := iter.Span()
    91  		fmt.Fprintf(&buf, "PointKey: %s\n", k.String())
    92  		if s != nil {
    93  			fmt.Fprintf(&buf, "Span: %s\n-", s)
    94  		} else {
    95  			fmt.Fprintf(&buf, "Span: %s\n-", Span{})
    96  		}
    97  	}
    98  
    99  	datadriven.RunTest(t, filename, func(t *testing.T, td *datadriven.TestData) string {
   100  		buf.Reset()
   101  		switch td.Cmd {
   102  		case "set-masking-threshold":
   103  			hooks.threshold = []byte(strings.TrimSpace(td.Input))
   104  			return "OK"
   105  		case "define-rangekeys":
   106  			var spans []Span
   107  			lines := strings.Split(strings.TrimSpace(td.Input), "\n")
   108  			for _, line := range lines {
   109  				spans = append(spans, ParseSpan(line))
   110  			}
   111  			keyspanIter.Init(cmp, noopTransform, new(MergingBuffers), NewIter(cmp, spans))
   112  			hooks.maskSuffix = nil
   113  			iter.Init(testkeys.Comparer, &pointIter, &keyspanIter,
   114  				InterleavingIterOpts{Mask: &hooks})
   115  			return "OK"
   116  		case "define-pointkeys":
   117  			var points []base.InternalKey
   118  			lines := strings.Split(strings.TrimSpace(td.Input), "\n")
   119  			for _, line := range lines {
   120  				points = append(points, base.ParseInternalKey(line))
   121  			}
   122  			pointIter = pointIterator{cmp: cmp, keys: points}
   123  			hooks.maskSuffix = nil
   124  			iter.Init(testkeys.Comparer, &pointIter, &keyspanIter,
   125  				InterleavingIterOpts{Mask: &hooks})
   126  			return "OK"
   127  		case "iter":
   128  			buf.Reset()
   129  			// Clear any previous bounds.
   130  			iter.SetBounds(nil, nil)
   131  			prevKey = nil
   132  			lines := strings.Split(strings.TrimSpace(td.Input), "\n")
   133  			for _, line := range lines {
   134  				bufLen := buf.Len()
   135  				line = strings.TrimSpace(line)
   136  				i := strings.IndexByte(line, ' ')
   137  				iterCmd := line
   138  				if i > 0 {
   139  					iterCmd = string(line[:i])
   140  				}
   141  				switch iterCmd {
   142  				case "first":
   143  					formatKey(iter.First())
   144  				case "last":
   145  					formatKey(iter.Last())
   146  				case "next":
   147  					formatKey(iter.Next())
   148  				case "next-prefix":
   149  					succKey := testkeys.Comparer.ImmediateSuccessor(nil, prevKey.UserKey[:testkeys.Comparer.Split(prevKey.UserKey)])
   150  					formatKey(iter.NextPrefix(succKey))
   151  				case "prev":
   152  					formatKey(iter.Prev())
   153  				case "seek-ge":
   154  					formatKey(iter.SeekGE([]byte(strings.TrimSpace(line[i:])), base.SeekGEFlagsNone))
   155  				case "seek-prefix-ge":
   156  					key := []byte(strings.TrimSpace(line[i:]))
   157  					prefix := key[:testkeys.Comparer.Split(key)]
   158  					formatKey(iter.SeekPrefixGE(prefix, key, base.SeekGEFlagsNone))
   159  				case "seek-lt":
   160  					formatKey(iter.SeekLT([]byte(strings.TrimSpace(line[i:])), base.SeekLTFlagsNone))
   161  				case "set-bounds":
   162  					bounds := strings.Fields(line[i:])
   163  					if len(bounds) != 2 {
   164  						return fmt.Sprintf("set-bounds expects 2 bounds, got %d", len(bounds))
   165  					}
   166  					l, u := []byte(bounds[0]), []byte(bounds[1])
   167  					if bounds[0] == "." {
   168  						l = nil
   169  					}
   170  					if bounds[1] == "." {
   171  						u = nil
   172  					}
   173  					iter.SetBounds(l, u)
   174  				default:
   175  					return fmt.Sprintf("unrecognized iter command %q", iterCmd)
   176  				}
   177  				require.NoError(t, iter.Error())
   178  				if buf.Len() > bufLen {
   179  					fmt.Fprintln(&buf)
   180  				}
   181  			}
   182  			return strings.TrimSpace(buf.String())
   183  		default:
   184  			return fmt.Sprintf("unrecognized command %q", td.Cmd)
   185  		}
   186  	})
   187  	require.NoError(t, iter.Close())
   188  }
   189  
   190  type pointIterator struct {
   191  	cmp   base.Compare
   192  	keys  []base.InternalKey
   193  	lower []byte
   194  	upper []byte
   195  	index int
   196  }
   197  
   198  var _ base.InternalIterator = &pointIterator{}
   199  
   200  func (i *pointIterator) SeekGE(
   201  	key []byte, flags base.SeekGEFlags,
   202  ) (*base.InternalKey, base.LazyValue) {
   203  	i.index = sort.Search(len(i.keys), func(j int) bool {
   204  		return i.cmp(i.keys[j].UserKey, key) >= 0
   205  	})
   206  	if i.index < 0 || i.index >= len(i.keys) {
   207  		return nil, base.LazyValue{}
   208  	}
   209  	if i.upper != nil && i.cmp(i.keys[i.index].UserKey, i.upper) >= 0 {
   210  		return nil, base.LazyValue{}
   211  	}
   212  	return &i.keys[i.index], base.LazyValue{}
   213  }
   214  
   215  func (i *pointIterator) SeekPrefixGE(
   216  	prefix, key []byte, flags base.SeekGEFlags,
   217  ) (*base.InternalKey, base.LazyValue) {
   218  	return i.SeekGE(key, flags)
   219  }
   220  
   221  func (i *pointIterator) SeekLT(
   222  	key []byte, flags base.SeekLTFlags,
   223  ) (*base.InternalKey, base.LazyValue) {
   224  	i.index = sort.Search(len(i.keys), func(j int) bool {
   225  		return i.cmp(i.keys[j].UserKey, key) >= 0
   226  	})
   227  	i.index--
   228  	if i.index < 0 || i.index >= len(i.keys) {
   229  		return nil, base.LazyValue{}
   230  	}
   231  	if i.lower != nil && i.cmp(i.keys[i.index].UserKey, i.lower) < 0 {
   232  		return nil, base.LazyValue{}
   233  	}
   234  	return &i.keys[i.index], base.LazyValue{}
   235  }
   236  
   237  func (i *pointIterator) First() (*base.InternalKey, base.LazyValue) {
   238  	i.index = 0
   239  	if i.index < 0 || i.index >= len(i.keys) {
   240  		return nil, base.LazyValue{}
   241  	}
   242  	if i.upper != nil && i.cmp(i.keys[i.index].UserKey, i.upper) >= 0 {
   243  		return nil, base.LazyValue{}
   244  	}
   245  	return &i.keys[i.index], base.LazyValue{}
   246  }
   247  
   248  func (i *pointIterator) Last() (*base.InternalKey, base.LazyValue) {
   249  	i.index = len(i.keys) - 1
   250  	if i.index < 0 || i.index >= len(i.keys) {
   251  		return nil, base.LazyValue{}
   252  	}
   253  	if i.lower != nil && i.cmp(i.keys[i.index].UserKey, i.lower) < 0 {
   254  		return nil, base.LazyValue{}
   255  	}
   256  	return &i.keys[i.index], base.LazyValue{}
   257  }
   258  
   259  func (i *pointIterator) Next() (*base.InternalKey, base.LazyValue) {
   260  	i.index++
   261  	if i.index < 0 || i.index >= len(i.keys) {
   262  		return nil, base.LazyValue{}
   263  	}
   264  	if i.upper != nil && i.cmp(i.keys[i.index].UserKey, i.upper) >= 0 {
   265  		return nil, base.LazyValue{}
   266  	}
   267  	return &i.keys[i.index], base.LazyValue{}
   268  }
   269  
   270  func (i *pointIterator) NextPrefix(succKey []byte) (*base.InternalKey, base.LazyValue) {
   271  	return i.SeekGE(succKey, base.SeekGEFlagsNone)
   272  }
   273  
   274  func (i *pointIterator) Prev() (*base.InternalKey, base.LazyValue) {
   275  	i.index--
   276  	if i.index < 0 || i.index >= len(i.keys) {
   277  		return nil, base.LazyValue{}
   278  	}
   279  	if i.lower != nil && i.cmp(i.keys[i.index].UserKey, i.lower) < 0 {
   280  		return nil, base.LazyValue{}
   281  	}
   282  	return &i.keys[i.index], base.LazyValue{}
   283  }
   284  
   285  func (i *pointIterator) Close() error   { return nil }
   286  func (i *pointIterator) Error() error   { return nil }
   287  func (i *pointIterator) String() string { return "test-point-iterator" }
   288  func (i *pointIterator) SetBounds(lower, upper []byte) {
   289  	i.lower, i.upper = lower, upper
   290  }
   291  func (i *pointIterator) SetContext(_ context.Context) {}