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) {}