github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/internal/rangedel/get.go (about)

     1  // Copyright 2018 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 rangedel
     6  
     7  import "github.com/petermattis/pebble/internal/base"
     8  
     9  // iterator is a subset of the pebble.internalIterator interface needed for
    10  // range deletion iteration.
    11  type iterator interface {
    12  	// SeekLT moves the iterator to the last key/value pair whose key is less
    13  	// than the given key.
    14  	SeekLT(key []byte) (*base.InternalKey, []byte)
    15  
    16  	// First moves the iterator the the first key/value pair.
    17  	First() (*base.InternalKey, []byte)
    18  
    19  	// Last moves the iterator the the last key/value pair.
    20  	Last() (*base.InternalKey, []byte)
    21  
    22  	// Next moves the iterator to the next key/value pair.
    23  	// It returns whether the iterator is exhausted.
    24  	Next() (*base.InternalKey, []byte)
    25  
    26  	// Prev moves the iterator to the previous key/value pair.
    27  	// It returns whether the iterator is exhausted.
    28  	Prev() (*base.InternalKey, []byte)
    29  }
    30  
    31  // Get returns the newest tombstone that contains the target key. If no
    32  // tombstone contains the target key, an empty tombstone is returned. The
    33  // snapshot parameter controls the visibility of tombstones (only tombstones
    34  // older than the snapshot sequence number are visible). The iterator must
    35  // contain fragmented tombstones: any overlapping tombstones must have the same
    36  // start and end key.
    37  func Get(cmp base.Compare, iter iterator, key []byte, snapshot uint64) Tombstone {
    38  	// NB: We use SeekLT in order to land on the proper tombstone for a search
    39  	// key that resides in the middle of a tombstone. Consider the scenario:
    40  	//
    41  	//     a---e
    42  	//         e---i
    43  	//
    44  	// The tombstones are indexed by their start keys `a` and `e`. If the
    45  	// search key is `c` we want to land on the tombstone [a,e). If we were to
    46  	// use SeekGE then the search key `c` would land on the tombstone [e,i) and
    47  	// we'd have to backtrack. The one complexity here is what happens for the
    48  	// search key `e`. In that case SeekLT will land us on the tombstone [a,e)
    49  	// and we'll have to move forward.
    50  	iterKey, iterValue := iter.SeekLT(key)
    51  	if iterKey == nil {
    52  		iterKey, iterValue = iter.Next()
    53  		if iterKey == nil {
    54  			// The iterator is empty.
    55  			return Tombstone{}
    56  		}
    57  		if cmp(key, iterKey.UserKey) < 0 {
    58  			// The search key lies before the first tombstone.
    59  			return Tombstone{}
    60  		}
    61  	}
    62  
    63  	// Invariant: key >= iter.Key().UserKey
    64  
    65  	if cmp(key, iterValue) < 0 {
    66  		// The current tombstone contains the search key, but SeekLT returns the
    67  		// oldest entry for a key, so backup until we hit the previous tombstone or
    68  		// an entry which is not visible.
    69  		for {
    70  			iterKey, iterValue = iter.Prev()
    71  			if iterKey == nil || cmp(key, iterValue) >= 0 || !iterKey.Visible(snapshot) {
    72  				iterKey, iterValue = iter.Next()
    73  				break
    74  			}
    75  		}
    76  	} else {
    77  		// The current tombstone lies before the search key. Advance the iterator
    78  		// as long as the search key lies past the end of the tombstone. See the
    79  		// comment at the start of this function about why this is necessary.
    80  		for {
    81  			iterKey, iterValue = iter.Next()
    82  			if iterKey == nil || cmp(key, iterKey.UserKey) < 0 {
    83  				// We've run out of tombstones or we've moved on to a tombstone which
    84  				// starts after our search key.
    85  				return Tombstone{}
    86  			}
    87  			if cmp(key, iterValue) < 0 {
    88  				break
    89  			}
    90  		}
    91  	}
    92  
    93  	for {
    94  		if start := iterKey; start.Visible(snapshot) {
    95  			// The tombstone is visible at our read sequence number.
    96  			return Tombstone{
    97  				Start: *start,
    98  				End:   iterValue,
    99  			}
   100  		}
   101  		iterKey, iterValue = iter.Next()
   102  		if iterKey == nil || cmp(key, iterKey.UserKey) < 0 {
   103  			// We've run out of tombstones or we've moved on to a tombstone which
   104  			// starts after our search key.
   105  			return Tombstone{}
   106  		}
   107  	}
   108  }