Line data Source code
1 : // Copyright 2020 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 manifest
6 :
7 : import (
8 : "bytes"
9 : "fmt"
10 : "reflect"
11 :
12 : "github.com/cockroachdb/pebble/internal/base"
13 : "github.com/cockroachdb/pebble/internal/invariants"
14 : )
15 :
16 : // LevelMetadata contains metadata for all of the files within
17 : // a level of the LSM.
18 : type LevelMetadata struct {
19 : level int
20 : totalSize uint64
21 : // NumVirtual is the number of virtual sstables in the level.
22 : NumVirtual uint64
23 : // VirtualSize is the size of the virtual sstables in the level.
24 : VirtualSize uint64
25 : tree btree
26 : }
27 :
28 : // clone makes a copy of the level metadata, implicitly increasing the ref
29 : // count of every file contained within lm.
30 1 : func (lm *LevelMetadata) clone() LevelMetadata {
31 1 : return LevelMetadata{
32 1 : level: lm.level,
33 1 : totalSize: lm.totalSize,
34 1 : NumVirtual: lm.NumVirtual,
35 1 : VirtualSize: lm.VirtualSize,
36 1 : tree: lm.tree.Clone(),
37 1 : }
38 1 : }
39 :
40 1 : func (lm *LevelMetadata) release() (obsolete []*FileBacking) {
41 1 : return lm.tree.Release()
42 1 : }
43 :
44 : // MakeLevelMetadata creates a LevelMetadata with the given files.
45 1 : func MakeLevelMetadata(cmp Compare, level int, files []*FileMetadata) LevelMetadata {
46 1 : bcmp := btreeCmpSeqNum
47 1 : if level > 0 {
48 1 : bcmp = btreeCmpSmallestKey(cmp)
49 1 : }
50 1 : var lm LevelMetadata
51 1 : lm.level = level
52 1 : lm.tree, _ = makeBTree(bcmp, files)
53 1 : for _, f := range files {
54 1 : lm.totalSize += f.Size
55 1 : if f.Virtual {
56 0 : lm.NumVirtual++
57 0 : lm.VirtualSize += f.Size
58 0 : }
59 : }
60 1 : return lm
61 : }
62 :
63 1 : func makeBTree(cmp btreeCmp, files []*FileMetadata) (btree, LevelSlice) {
64 1 : var t btree
65 1 : t.cmp = cmp
66 1 : for _, f := range files {
67 1 : t.Insert(f)
68 1 : }
69 1 : return t, newLevelSlice(t.Iter())
70 : }
71 :
72 1 : func (lm *LevelMetadata) insert(f *FileMetadata) error {
73 1 : if err := lm.tree.Insert(f); err != nil {
74 1 : return err
75 1 : }
76 1 : lm.totalSize += f.Size
77 1 : if f.Virtual {
78 1 : lm.NumVirtual++
79 1 : lm.VirtualSize += f.Size
80 1 : }
81 1 : return nil
82 : }
83 :
84 1 : func (lm *LevelMetadata) remove(f *FileMetadata) bool {
85 1 : lm.totalSize -= f.Size
86 1 : if f.Virtual {
87 1 : lm.NumVirtual--
88 1 : lm.VirtualSize -= f.Size
89 1 : }
90 1 : return lm.tree.Delete(f)
91 : }
92 :
93 : // Empty indicates whether there are any files in the level.
94 1 : func (lm *LevelMetadata) Empty() bool {
95 1 : return lm.tree.Count() == 0
96 1 : }
97 :
98 : // Len returns the number of files within the level.
99 1 : func (lm *LevelMetadata) Len() int {
100 1 : return lm.tree.Count()
101 1 : }
102 :
103 : // Size returns the cumulative size of all the files within the level.
104 1 : func (lm *LevelMetadata) Size() uint64 {
105 1 : return lm.totalSize
106 1 : }
107 :
108 : // Iter constructs a LevelIterator over the entire level.
109 1 : func (lm *LevelMetadata) Iter() LevelIterator {
110 1 : return LevelIterator{iter: lm.tree.Iter()}
111 1 : }
112 :
113 : // Slice constructs a slice containing the entire level.
114 1 : func (lm *LevelMetadata) Slice() LevelSlice {
115 1 : return newLevelSlice(lm.tree.Iter())
116 1 : }
117 :
118 : // Find finds the provided file in the level. If it exists, returns a LevelSlice
119 : // that contains just that file; otherwise, returns an empty LevelSlice.
120 1 : func (lm *LevelMetadata) Find(cmp base.Compare, m *FileMetadata) LevelSlice {
121 1 : iter := lm.Iter()
122 1 : if lm.level == 0 {
123 1 : // We only need to look at the portion of files that are "equal" to m with
124 1 : // respect to the L0 ordering.
125 1 : f := iter.seek(func(f *FileMetadata) bool {
126 1 : return f.cmpSeqNum(m) >= 0
127 1 : })
128 1 : for ; f != nil && f.cmpSeqNum(m) == 0; f = iter.Next() {
129 1 : if f == m {
130 1 : return iter.Take().slice
131 1 : }
132 : }
133 1 : } else {
134 1 : // For levels other than L0, UserKeyBounds in the level are non-overlapping
135 1 : // so we only need to check one file.
136 1 : if f := iter.SeekGE(cmp, m.UserKeyBounds().Start); f == m {
137 1 : return iter.Take().slice
138 1 : }
139 : }
140 0 : return LevelSlice{}
141 : }
142 :
143 : // Annotation lazily calculates and returns the annotation defined by
144 : // Annotator. The Annotator is used as the key for pre-calculated
145 : // values, so equal Annotators must be used to avoid duplicate computations
146 : // and cached annotations. Annotation must not be called concurrently, and in
147 : // practice this is achieved by requiring callers to hold DB.mu.
148 1 : func (lm *LevelMetadata) Annotation(annotator Annotator) interface{} {
149 1 : if lm.Empty() {
150 1 : return annotator.Zero(nil)
151 1 : }
152 1 : v, _ := lm.tree.root.Annotation(annotator)
153 1 : return v
154 : }
155 :
156 : // InvalidateAnnotation clears any cached annotations defined by Annotator. The
157 : // Annotator is used as the key for pre-calculated values, so equal Annotators
158 : // must be used to clear the appropriate cached annotation. InvalidateAnnotation
159 : // must not be called concurrently, and in practice this is achieved by
160 : // requiring callers to hold DB.mu.
161 1 : func (lm *LevelMetadata) InvalidateAnnotation(annotator Annotator) {
162 1 : if lm.Empty() {
163 0 : return
164 0 : }
165 1 : lm.tree.root.InvalidateAnnotation(annotator)
166 : }
167 :
168 : // LevelFile holds a file's metadata along with its position
169 : // within a level of the LSM.
170 : type LevelFile struct {
171 : *FileMetadata
172 : slice LevelSlice
173 : }
174 :
175 : // Slice constructs a LevelSlice containing only this file.
176 1 : func (lf LevelFile) Slice() LevelSlice {
177 1 : return lf.slice
178 1 : }
179 :
180 : // NewLevelSliceSeqSorted constructs a LevelSlice over the provided files,
181 : // sorted by the L0 sequence number sort order.
182 : // TODO(jackson): Can we improve this interface or avoid needing to export
183 : // a slice constructor like this?
184 1 : func NewLevelSliceSeqSorted(files []*FileMetadata) LevelSlice {
185 1 : tr, slice := makeBTree(btreeCmpSeqNum, files)
186 1 : tr.Release()
187 1 : slice.verifyInvariants()
188 1 : return slice
189 1 : }
190 :
191 : // NewLevelSliceKeySorted constructs a LevelSlice over the provided files,
192 : // sorted by the files smallest keys.
193 : // TODO(jackson): Can we improve this interface or avoid needing to export
194 : // a slice constructor like this?
195 1 : func NewLevelSliceKeySorted(cmp base.Compare, files []*FileMetadata) LevelSlice {
196 1 : tr, slice := makeBTree(btreeCmpSmallestKey(cmp), files)
197 1 : tr.Release()
198 1 : slice.verifyInvariants()
199 1 : return slice
200 1 : }
201 :
202 : // NewLevelSliceSpecificOrder constructs a LevelSlice over the provided files,
203 : // ordering the files by their order in the provided slice. It's used in
204 : // tests.
205 : // TODO(jackson): Update tests to avoid requiring this and remove it.
206 1 : func NewLevelSliceSpecificOrder(files []*FileMetadata) LevelSlice {
207 1 : tr, slice := makeBTree(btreeCmpSpecificOrder(files), files)
208 1 : tr.Release()
209 1 : slice.verifyInvariants()
210 1 : return slice
211 1 : }
212 :
213 : // newLevelSlice constructs a new LevelSlice backed by iter.
214 1 : func newLevelSlice(iter iterator) LevelSlice {
215 1 : s := LevelSlice{iter: iter}
216 1 : if iter.r != nil {
217 1 : s.length = iter.r.subtreeCount
218 1 : }
219 1 : s.verifyInvariants()
220 1 : return s
221 : }
222 :
223 : // newBoundedLevelSlice constructs a new LevelSlice backed by iter and bounded
224 : // by the provided start and end bounds. The provided startBound and endBound
225 : // iterators must be iterators over the same B-Tree. Both start and end bounds
226 : // are inclusive.
227 1 : func newBoundedLevelSlice(iter iterator, startBound, endBound *iterator) LevelSlice {
228 1 : s := LevelSlice{
229 1 : iter: iter,
230 1 : start: startBound,
231 1 : end: endBound,
232 1 : }
233 1 : if iter.valid() {
234 1 : s.length = endBound.countLeft() - startBound.countLeft()
235 1 : // NB: The +1 is a consequence of the end bound being inclusive.
236 1 : if endBound.valid() {
237 1 : s.length++
238 1 : }
239 : // NB: A slice that's empty due to its bounds may have an endBound
240 : // positioned before the startBound due to the inclusive bounds.
241 : // TODO(jackson): Consider refactoring the end boundary to be exclusive;
242 : // it would simplify some areas (eg, here) and complicate others (eg,
243 : // Reslice-ing to grow compactions).
244 1 : if s.length < 0 {
245 1 : s.length = 0
246 1 : }
247 : }
248 1 : s.verifyInvariants()
249 1 : return s
250 : }
251 :
252 : // LevelSlice contains a slice of the files within a level of the LSM.
253 : // A LevelSlice is immutable once created, but may be used to construct a
254 : // mutable LevelIterator over the slice's files.
255 : //
256 : // LevelSlices should be constructed through one of the existing constructors,
257 : // not manually initialized.
258 : type LevelSlice struct {
259 : iter iterator
260 : length int
261 : // start and end form the inclusive bounds of a slice of files within a
262 : // level of the LSM. They may be nil if the entire B-Tree backing iter is
263 : // accessible.
264 : start *iterator
265 : end *iterator
266 : }
267 :
268 1 : func (ls LevelSlice) verifyInvariants() {
269 1 : if invariants.Enabled {
270 1 : i := ls.Iter()
271 1 : var length int
272 1 : for f := i.First(); f != nil; f = i.Next() {
273 1 : length++
274 1 : }
275 1 : if ls.length != length {
276 0 : panic(fmt.Sprintf("LevelSlice %s has length %d value; actual length is %d", ls, ls.length, length))
277 : }
278 : }
279 : }
280 :
281 : // Each invokes fn for each element in the slice.
282 1 : func (ls LevelSlice) Each(fn func(*FileMetadata)) {
283 1 : iter := ls.Iter()
284 1 : for f := iter.First(); f != nil; f = iter.Next() {
285 1 : fn(f)
286 1 : }
287 : }
288 :
289 : // String implements fmt.Stringer.
290 1 : func (ls LevelSlice) String() string {
291 1 : var buf bytes.Buffer
292 1 : fmt.Fprintf(&buf, "%d files: ", ls.length)
293 1 : ls.Each(func(f *FileMetadata) {
294 1 : if buf.Len() > 0 {
295 1 : fmt.Fprintf(&buf, " ")
296 1 : }
297 1 : fmt.Fprint(&buf, f)
298 : })
299 1 : return buf.String()
300 : }
301 :
302 : // Empty indicates whether the slice contains any files.
303 1 : func (ls *LevelSlice) Empty() bool {
304 1 : return emptyWithBounds(ls.iter, ls.start, ls.end)
305 1 : }
306 :
307 : // Iter constructs a LevelIterator that iterates over the slice.
308 1 : func (ls *LevelSlice) Iter() LevelIterator {
309 1 : return LevelIterator{
310 1 : start: ls.start,
311 1 : end: ls.end,
312 1 : iter: ls.iter.clone(),
313 1 : }
314 1 : }
315 :
316 : // Len returns the number of files in the slice. Its runtime is constant.
317 1 : func (ls *LevelSlice) Len() int {
318 1 : return ls.length
319 1 : }
320 :
321 : // SizeSum sums the size of all files in the slice. Its runtime is linear in
322 : // the length of the slice.
323 1 : func (ls *LevelSlice) SizeSum() uint64 {
324 1 : var sum uint64
325 1 : iter := ls.Iter()
326 1 : for f := iter.First(); f != nil; f = iter.Next() {
327 1 : sum += f.Size
328 1 : }
329 1 : return sum
330 : }
331 :
332 : // NumVirtual returns the number of virtual sstables in the level. Its runtime is
333 : // linear in the length of the slice.
334 1 : func (ls *LevelSlice) NumVirtual() uint64 {
335 1 : var n uint64
336 1 : iter := ls.Iter()
337 1 : for f := iter.First(); f != nil; f = iter.Next() {
338 1 : if f.Virtual {
339 1 : n++
340 1 : }
341 : }
342 1 : return n
343 : }
344 :
345 : // VirtualSizeSum returns the sum of the sizes of the virtual sstables in the
346 : // level.
347 1 : func (ls *LevelSlice) VirtualSizeSum() uint64 {
348 1 : var sum uint64
349 1 : iter := ls.Iter()
350 1 : for f := iter.First(); f != nil; f = iter.Next() {
351 1 : if f.Virtual {
352 1 : sum += f.Size
353 1 : }
354 : }
355 1 : return sum
356 : }
357 :
358 : // Reslice constructs a new slice backed by the same underlying level, with
359 : // new start and end positions. Reslice invokes the provided function, passing
360 : // two LevelIterators: one positioned to i's inclusive start and one
361 : // positioned to i's inclusive end. The resliceFunc may move either iterator
362 : // forward or backwards, including beyond the callee's original bounds to
363 : // capture additional files from the underlying level. Reslice constructs and
364 : // returns a new LevelSlice with the final bounds of the iterators after
365 : // calling resliceFunc.
366 1 : func (ls LevelSlice) Reslice(resliceFunc func(start, end *LevelIterator)) LevelSlice {
367 1 : if ls.iter.r == nil {
368 1 : return ls
369 1 : }
370 1 : var start, end LevelIterator
371 1 : if ls.start == nil {
372 1 : start.iter = ls.iter.clone()
373 1 : start.iter.first()
374 1 : } else {
375 1 : start.iter = ls.start.clone()
376 1 : }
377 1 : if ls.end == nil {
378 1 : end.iter = ls.iter.clone()
379 1 : end.iter.last()
380 1 : } else {
381 1 : end.iter = ls.end.clone()
382 1 : }
383 1 : resliceFunc(&start, &end)
384 1 : return newBoundedLevelSlice(start.iter.clone(), &start.iter, &end.iter)
385 : }
386 :
387 : // Overlaps returns a new LevelSlice that reflects the portion of files with
388 : // boundaries that overlap with the provided bounds.
389 1 : func (ls LevelSlice) Overlaps(cmp Compare, bounds base.UserKeyBounds) LevelSlice {
390 1 : startIter := ls.Iter()
391 1 : startIter.SeekGE(cmp, bounds.Start)
392 1 :
393 1 : // Note: newBoundedLevelSlice uses inclusive bounds, so we need to position
394 1 : // endIter at the last overlapping file.
395 1 : endIter := ls.Iter()
396 1 : endIterFile := endIter.SeekGE(cmp, bounds.End.Key)
397 1 : // The first file that ends at/after bounds.End.Key might or might not overlap
398 1 : // the bounds; we need to check the start key.
399 1 : if endIterFile == nil || !bounds.End.IsUpperBoundFor(cmp, endIterFile.Smallest.UserKey) {
400 1 : endIter.Prev()
401 1 : }
402 1 : return newBoundedLevelSlice(startIter.iter.clone(), &startIter.iter, &endIter.iter)
403 : }
404 :
405 : // KeyType is used to specify the type of keys we're looking for in
406 : // LevelIterator positioning operations. Files not containing any keys of the
407 : // desired type are skipped.
408 : type KeyType int8
409 :
410 : const (
411 : // KeyTypePointAndRange denotes a search among the entire keyspace, including
412 : // both point keys and range keys. No sstables are skipped.
413 : KeyTypePointAndRange KeyType = iota
414 : // KeyTypePoint denotes a search among the point keyspace. SSTables with no
415 : // point keys will be skipped. Note that the point keyspace includes rangedels.
416 : KeyTypePoint
417 : // KeyTypeRange denotes a search among the range keyspace. SSTables with no
418 : // range keys will be skipped.
419 : KeyTypeRange
420 : )
421 :
422 : // LevelIterator iterates over a set of files' metadata. Its zero value is an
423 : // empty iterator.
424 : type LevelIterator struct {
425 : iter iterator
426 : // If set, start is an inclusive lower bound on the iterator.
427 : start *iterator
428 : // If set, end is an inclusive upper bound on the iterator.
429 : end *iterator
430 : filter KeyType
431 : }
432 :
433 0 : func (i LevelIterator) String() string {
434 0 : var buf bytes.Buffer
435 0 : iter := i.iter.clone()
436 0 : iter.first()
437 0 : iter.prev()
438 0 : if i.iter.pos == -1 {
439 0 : fmt.Fprint(&buf, "(<start>)*")
440 0 : }
441 0 : iter.next()
442 0 : for ; iter.valid(); iter.next() {
443 0 : if buf.Len() > 0 {
444 0 : fmt.Fprint(&buf, " ")
445 0 : }
446 :
447 0 : if i.start != nil && cmpIter(iter, *i.start) == 0 {
448 0 : fmt.Fprintf(&buf, " [ ")
449 0 : }
450 0 : isCurrentPos := cmpIter(iter, i.iter) == 0
451 0 : if isCurrentPos {
452 0 : fmt.Fprint(&buf, " ( ")
453 0 : }
454 0 : fmt.Fprint(&buf, iter.cur().String())
455 0 : if isCurrentPos {
456 0 : fmt.Fprint(&buf, " )*")
457 0 : }
458 0 : if i.end != nil && cmpIter(iter, *i.end) == 0 {
459 0 : fmt.Fprintf(&buf, " ]")
460 0 : }
461 : }
462 0 : if i.iter.n != nil && i.iter.pos >= i.iter.n.count {
463 0 : if buf.Len() > 0 {
464 0 : fmt.Fprint(&buf, " ")
465 0 : }
466 0 : fmt.Fprint(&buf, "(<end>)*")
467 : }
468 0 : return buf.String()
469 : }
470 :
471 : // Clone copies the iterator, returning an independent iterator at the same
472 : // position.
473 1 : func (i *LevelIterator) Clone() LevelIterator {
474 1 : if i.iter.r == nil {
475 1 : return *i
476 1 : }
477 : // The start and end iterators are not cloned and are treated as
478 : // immutable.
479 1 : return LevelIterator{
480 1 : iter: i.iter.clone(),
481 1 : start: i.start,
482 1 : end: i.end,
483 1 : filter: i.filter,
484 1 : }
485 : }
486 :
487 1 : func (i *LevelIterator) empty() bool {
488 1 : return emptyWithBounds(i.iter, i.start, i.end)
489 1 : }
490 :
491 : // Filter clones the iterator and sets the desired KeyType as the key to filter
492 : // files on.
493 1 : func (i *LevelIterator) Filter(keyType KeyType) LevelIterator {
494 1 : l := i.Clone()
495 1 : l.filter = keyType
496 1 : return l
497 1 : }
498 :
499 1 : func emptyWithBounds(i iterator, start, end *iterator) bool {
500 1 : // If i.r is nil, the iterator was constructed from an empty btree.
501 1 : // If the end bound is before the start bound, the bounds represent an
502 1 : // empty slice of the B-Tree.
503 1 : return i.r == nil || (start != nil && end != nil && cmpIter(*end, *start) < 0)
504 1 : }
505 :
506 : // First seeks to the first file in the iterator and returns it.
507 1 : func (i *LevelIterator) First() *FileMetadata {
508 1 : if i.empty() {
509 1 : return nil
510 1 : }
511 1 : if i.start != nil {
512 1 : i.iter = i.start.clone()
513 1 : } else {
514 1 : i.iter.first()
515 1 : }
516 1 : if !i.iter.valid() {
517 1 : return nil
518 1 : }
519 1 : return i.skipFilteredForward(i.iter.cur())
520 : }
521 :
522 : // Last seeks to the last file in the iterator and returns it.
523 1 : func (i *LevelIterator) Last() *FileMetadata {
524 1 : if i.empty() {
525 1 : return nil
526 1 : }
527 1 : if i.end != nil {
528 1 : i.iter = i.end.clone()
529 1 : } else {
530 1 : i.iter.last()
531 1 : }
532 1 : if !i.iter.valid() {
533 0 : return nil
534 0 : }
535 1 : return i.skipFilteredBackward(i.iter.cur())
536 : }
537 :
538 : // Next advances the iterator to the next file and returns it.
539 1 : func (i *LevelIterator) Next() *FileMetadata {
540 1 : if i.iter.r == nil {
541 0 : return nil
542 0 : }
543 1 : if invariants.Enabled && (i.iter.pos >= i.iter.n.count || (i.end != nil && cmpIter(i.iter, *i.end) > 0)) {
544 0 : panic("pebble: cannot next forward-exhausted iterator")
545 : }
546 1 : i.iter.next()
547 1 : if !i.iter.valid() {
548 1 : return nil
549 1 : }
550 1 : return i.skipFilteredForward(i.iter.cur())
551 : }
552 :
553 : // Prev moves the iterator the previous file and returns it.
554 1 : func (i *LevelIterator) Prev() *FileMetadata {
555 1 : if i.iter.r == nil {
556 1 : return nil
557 1 : }
558 1 : if invariants.Enabled && (i.iter.pos < 0 || (i.start != nil && cmpIter(i.iter, *i.start) < 0)) {
559 0 : panic("pebble: cannot prev backward-exhausted iterator")
560 : }
561 1 : i.iter.prev()
562 1 : if !i.iter.valid() {
563 1 : return nil
564 1 : }
565 1 : return i.skipFilteredBackward(i.iter.cur())
566 : }
567 :
568 : // SeekGE seeks to the first file with a largest key (of the desired type) that
569 : // is an upper bound for the given user key. This is the first file that could
570 : // contain a user key that is greater than or equal to userKey.
571 : //
572 : // More specifically, userKey is less than the file's largest.UserKey or they
573 : // are equal and largest is not an exclusive sentinel.
574 : //
575 : // The iterator must have been constructed from L1+ or from a single sublevel of
576 : // L0, because it requires the underlying files to be sorted by user keys and
577 : // non-overlapping.
578 1 : func (i *LevelIterator) SeekGE(cmp Compare, userKey []byte) *FileMetadata {
579 1 : if i.iter.r == nil {
580 1 : return nil
581 1 : }
582 1 : i.assertNotL0Cmp()
583 1 : m := i.seek(func(m *FileMetadata) bool {
584 1 : return m.Largest.IsUpperBoundFor(cmp, userKey)
585 1 : })
586 1 : if i.filter != KeyTypePointAndRange && m != nil {
587 1 : b, ok := m.LargestBound(i.filter)
588 1 : if !ok || !b.IsUpperBoundFor(cmp, userKey) {
589 1 : // The file does not contain any keys of desired key types
590 1 : // that are >= userKey.
591 1 : return i.Next()
592 1 : }
593 : }
594 1 : return i.skipFilteredForward(m)
595 : }
596 :
597 : // SeekLT seeks to the last file with a smallest key (of the desired type) that
598 : // is less than the given user key. This is the last file that could contain a
599 : // key less than userKey.
600 : //
601 : // The iterator must have been constructed from L1+ or from a single sublevel of
602 : // L0, because it requires the underlying files to be sorted by user keys and
603 : // non-overlapping.
604 1 : func (i *LevelIterator) SeekLT(cmp Compare, userKey []byte) *FileMetadata {
605 1 : if i.iter.r == nil {
606 1 : return nil
607 1 : }
608 1 : i.assertNotL0Cmp()
609 1 : i.seek(func(m *FileMetadata) bool {
610 1 : return cmp(m.Smallest.UserKey, userKey) >= 0
611 1 : })
612 1 : m := i.Prev()
613 1 : // Although i.Prev() guarantees that the current file contains keys of the
614 1 : // relevant type, it doesn't guarantee that the keys of the relevant type
615 1 : // are < userKey. For example, say that we have these two files:
616 1 : // f1: [a, f) with keys of the desired type in the range [c, d)
617 1 : // f2: [h, k)
618 1 : // and userKey is b. The seek call above will position us at f2 and Prev will
619 1 : // position us at f1.
620 1 : if i.filter != KeyTypePointAndRange && m != nil {
621 1 : b, ok := m.SmallestBound(i.filter)
622 1 : if !ok {
623 0 : panic("unreachable")
624 : }
625 1 : if cmp(b.UserKey, userKey) >= 0 {
626 1 : // This file does not contain any keys of desired key types
627 1 : // that are <= userKey.
628 1 : return i.Prev()
629 1 : }
630 : }
631 1 : return m
632 : }
633 :
634 : // assertNotL0Cmp verifies that the btree associated with the iterator is
635 : // ordered by Smallest key (i.e. L1+ or L0 sublevel) and not by LargestSeqNum
636 : // (L0).
637 1 : func (i *LevelIterator) assertNotL0Cmp() {
638 1 : if invariants.Enabled {
639 1 : if reflect.ValueOf(i.iter.cmp).Pointer() == reflect.ValueOf(btreeCmpSeqNum).Pointer() {
640 0 : panic("Seek used with btreeCmpSeqNum")
641 : }
642 : }
643 : }
644 :
645 : // skipFilteredForward takes the file metadata at the iterator's current
646 : // position, and skips forward if the current key-type filter (i.filter)
647 : // excludes the file. It skips until it finds an unfiltered file or exhausts the
648 : // level. If lower is != nil, skipFilteredForward skips any files that do not
649 : // contain keys with the provided key-type ≥ lower.
650 : //
651 : // skipFilteredForward also enforces the upper bound, returning nil if at any
652 : // point the upper bound is exceeded.
653 1 : func (i *LevelIterator) skipFilteredForward(meta *FileMetadata) *FileMetadata {
654 1 : for meta != nil && !meta.ContainsKeyType(i.filter) {
655 1 : i.iter.next()
656 1 : if !i.iter.valid() {
657 1 : meta = nil
658 1 : } else {
659 1 : meta = i.iter.cur()
660 1 : }
661 : }
662 1 : if meta != nil && i.end != nil && cmpIter(i.iter, *i.end) > 0 {
663 1 : // Exceeded upper bound.
664 1 : meta = nil
665 1 : }
666 1 : return meta
667 : }
668 :
669 : // skipFilteredBackward takes the file metadata at the iterator's current
670 : // position, and skips backward if the current key-type filter (i.filter)
671 : // excludes the file. It skips until it finds an unfiltered file or exhausts the
672 : // level. If upper is != nil, skipFilteredBackward skips any files that do not
673 : // contain keys with the provided key-type < upper.
674 : //
675 : // skipFilteredBackward also enforces the lower bound, returning nil if at any
676 : // point the lower bound is exceeded.
677 1 : func (i *LevelIterator) skipFilteredBackward(meta *FileMetadata) *FileMetadata {
678 1 : for meta != nil && !meta.ContainsKeyType(i.filter) {
679 1 : i.iter.prev()
680 1 : if !i.iter.valid() {
681 1 : meta = nil
682 1 : } else {
683 1 : meta = i.iter.cur()
684 1 : }
685 : }
686 1 : if meta != nil && i.start != nil && cmpIter(i.iter, *i.start) < 0 {
687 1 : // Exceeded lower bound.
688 1 : meta = nil
689 1 : }
690 1 : return meta
691 : }
692 :
693 : // seek repositions the iterator over the first file for which fn returns true,
694 : // mirroring the semantics of the standard library's sort.Search function: fn
695 : // returns false for some (possibly empty) prefix of the tree's files, and then
696 : // true for the (possibly empty) remainder.
697 1 : func (i *LevelIterator) seek(fn func(*FileMetadata) bool) *FileMetadata {
698 1 : i.iter.seek(fn)
699 1 :
700 1 : // i.iter.seek seeked in the unbounded underlying B-Tree. If the iterator
701 1 : // has start or end bounds, we may have exceeded them. Reset to the bounds
702 1 : // if necessary.
703 1 : //
704 1 : // NB: The LevelIterator and LevelSlice semantics require that a bounded
705 1 : // LevelIterator/LevelSlice containing files x0, x1, ..., xn behave
706 1 : // identically to an unbounded LevelIterator/LevelSlice of a B-Tree
707 1 : // containing x0, x1, ..., xn. In other words, any files outside the
708 1 : // LevelIterator's bounds should not influence the iterator's behavior.
709 1 : // When seeking, this means a SeekGE that seeks beyond the end bound,
710 1 : // followed by a Prev should return the last element within bounds.
711 1 : if i.end != nil && cmpIter(i.iter, *i.end) > 0 {
712 1 : i.iter = i.end.clone()
713 1 : // Since seek(fn) positioned beyond i.end, we know there is nothing to
714 1 : // return within bounds.
715 1 : i.iter.next()
716 1 : return nil
717 1 : } else if i.start != nil && cmpIter(i.iter, *i.start) < 0 {
718 1 : i.iter = i.start.clone()
719 1 : }
720 1 : if !i.iter.valid() {
721 1 : return nil
722 1 : }
723 1 : return i.iter.cur()
724 : }
725 :
726 : // Take constructs a LevelFile containing the file at the iterator's current
727 : // position. Take panics if the iterator is not currently positioned over a
728 : // file.
729 1 : func (i *LevelIterator) Take() LevelFile {
730 1 : if !i.iter.valid() ||
731 1 : (i.end != nil && cmpIter(i.iter, *i.end) > 0) ||
732 1 : (i.start != nil && cmpIter(i.iter, *i.start) < 0) {
733 0 : panic("Take called on invalid LevelIterator")
734 : }
735 1 : m := i.iter.cur()
736 1 : // LevelSlice's start and end fields are immutable and are positioned to
737 1 : // the same position for a LevelFile because they're inclusive, so we can
738 1 : // share one iterator stack between the two bounds.
739 1 : boundsIter := i.iter.clone()
740 1 : s := newBoundedLevelSlice(i.iter.clone(), &boundsIter, &boundsIter)
741 1 : return LevelFile{
742 1 : FileMetadata: m,
743 1 : slice: s,
744 1 : }
745 : }
|