github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/storage/multi_iterator.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package storage
    12  
    13  import (
    14  	"bytes"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/keys"
    17  )
    18  
    19  const invalidIdxSentinel = -1
    20  
    21  // multiIterator multiplexes iteration over a number of Iterators.
    22  type multiIterator struct {
    23  	iters []SimpleIterator
    24  	// The index into `iters` of the iterator currently being pointed at.
    25  	currentIdx int
    26  	// The indexes of every iterator with the same key as the one in currentIdx.
    27  	itersWithCurrentKey []int
    28  	// The indexes of every iterator with the same key and timestamp as the one
    29  	// in currentIdx.
    30  	itersWithCurrentKeyTimestamp []int
    31  
    32  	// err, if non-nil, is an error encountered by one of the underlying
    33  	// Iterators or in some incompatibility between them. If err is non-nil,
    34  	// Valid must return false.
    35  	err error
    36  }
    37  
    38  var _ SimpleIterator = &multiIterator{}
    39  
    40  // MakeMultiIterator creates an iterator that multiplexes
    41  // SimpleIterators. The caller is responsible for closing the passed
    42  // iterators after closing the returned multiIterator.
    43  //
    44  // If two iterators have an entry with exactly the same key and timestamp, the
    45  // one with a higher index in this constructor arg is preferred. The other is
    46  // skipped.
    47  func MakeMultiIterator(iters []SimpleIterator) SimpleIterator {
    48  	return &multiIterator{
    49  		iters:                        iters,
    50  		currentIdx:                   invalidIdxSentinel,
    51  		itersWithCurrentKey:          make([]int, 0, len(iters)),
    52  		itersWithCurrentKeyTimestamp: make([]int, 0, len(iters)),
    53  	}
    54  }
    55  
    56  func (f *multiIterator) Close() {
    57  	// No-op, multiIterator doesn't close the underlying iterators.
    58  }
    59  
    60  // SeekGE advances the iterator to the first key in the engine which is >= the
    61  // provided key.
    62  func (f *multiIterator) SeekGE(key MVCCKey) {
    63  	for _, iter := range f.iters {
    64  		iter.SeekGE(key)
    65  	}
    66  	// Each of the iterators now points at the first key >= key. Set currentIdx
    67  	// and itersWithCurrentKey but don't advance any of the underlying
    68  	// iterators.
    69  	f.advance()
    70  }
    71  
    72  // Valid must be called after any call to Seek(), Next(), or similar methods. It
    73  // returns (true, nil) if the iterator points to a valid key (it is undefined to
    74  // call UnsafeKey(), UnsafeValue(), or similar methods unless Valid() has
    75  // returned (true, nil)). It returns (false, nil) if the iterator has moved past
    76  // the end of the valid range, or (false, err) if an error has occurred. Valid()
    77  // will never return true with a non-nil error.
    78  func (f *multiIterator) Valid() (bool, error) {
    79  	valid := f.currentIdx != invalidIdxSentinel && f.err == nil
    80  	return valid, f.err
    81  }
    82  
    83  // UnsafeKey returns the current key, but the memory is invalidated on the next
    84  // call to {NextKey,Seek}.
    85  func (f *multiIterator) UnsafeKey() MVCCKey {
    86  	return f.iters[f.currentIdx].UnsafeKey()
    87  }
    88  
    89  // UnsafeValue returns the current value as a byte slice, but the memory is
    90  // invalidated on the next call to {NextKey,Seek}.
    91  func (f *multiIterator) UnsafeValue() []byte {
    92  	return f.iters[f.currentIdx].UnsafeValue()
    93  }
    94  
    95  // Next advances the iterator to the next key/value in the iteration. After this
    96  // call, Valid() will be true if the iterator was not positioned at the last
    97  // key.
    98  func (f *multiIterator) Next() {
    99  	// Advance each iterator at the current key and timestamp to its next key,
   100  	// then recompute currentIdx.
   101  	for _, iterIdx := range f.itersWithCurrentKeyTimestamp {
   102  		f.iters[iterIdx].Next()
   103  	}
   104  	f.advance()
   105  }
   106  
   107  // NextKey advances the iterator to the next MVCC key. This operation is
   108  // distinct from Next which advances to the next version of the current key or
   109  // the next key if the iterator is currently located at the last version for a
   110  // key.
   111  func (f *multiIterator) NextKey() {
   112  	// Advance each iterator at the current key to its next key, then recompute
   113  	// currentIdx.
   114  	for _, iterIdx := range f.itersWithCurrentKey {
   115  		f.iters[iterIdx].NextKey()
   116  	}
   117  	f.advance()
   118  }
   119  
   120  func (f *multiIterator) advance() {
   121  	// Loop through every iterator, storing the best next value for currentIdx
   122  	// in proposedNextIdx as we go. If it's still invalidIdxSentinel at the end,
   123  	// then all the underlying iterators are exhausted and so is this one.
   124  	proposedNextIdx := invalidIdxSentinel
   125  	for iterIdx, iter := range f.iters {
   126  		// If this iterator is exhausted skip it (or error if it's errored).
   127  		// TODO(dan): Iterators that are exhausted could be removed to save
   128  		// having to check them on all future calls to advance.
   129  		if ok, err := iter.Valid(); err != nil {
   130  			f.err = err
   131  			return
   132  		} else if !ok {
   133  			continue
   134  		}
   135  
   136  		// Fill proposedMVCCKey with the mvcc key of the current best for the
   137  		// next value for currentIdx (or a sentinel that sorts after everything
   138  		// if this is the first non-exhausted iterator).
   139  		proposedMVCCKey := MVCCKey{Key: keys.MaxKey}
   140  		if proposedNextIdx != invalidIdxSentinel {
   141  			proposedMVCCKey = f.iters[proposedNextIdx].UnsafeKey()
   142  		}
   143  
   144  		iterMVCCKey := iter.UnsafeKey()
   145  		if cmp := bytes.Compare(iterMVCCKey.Key, proposedMVCCKey.Key); cmp < 0 {
   146  			// The iterator at iterIdx has a lower key than any seen so far.
   147  			// Update proposedNextIdx with it and reset itersWithCurrentKey
   148  			// (because everything seen so for must have had a higher key).
   149  			f.itersWithCurrentKey = f.itersWithCurrentKey[:0]
   150  			f.itersWithCurrentKey = append(f.itersWithCurrentKey, iterIdx)
   151  			f.itersWithCurrentKeyTimestamp = f.itersWithCurrentKeyTimestamp[:0]
   152  			f.itersWithCurrentKeyTimestamp = append(f.itersWithCurrentKeyTimestamp, iterIdx)
   153  			proposedNextIdx = iterIdx
   154  		} else if cmp == 0 {
   155  			// The iterator at iterIdx has the same key as the current best, add
   156  			// it to itersWithCurrentKey and check how the timestamps compare.
   157  			f.itersWithCurrentKey = append(f.itersWithCurrentKey, iterIdx)
   158  			if proposedMVCCKey.Timestamp == iterMVCCKey.Timestamp {
   159  				// We have two exactly equal mvcc keys (both key and timestamps
   160  				// match). The one in the later iterator takes precedence and
   161  				// the one in the earlier iterator should be omitted from
   162  				// iteration.
   163  				f.itersWithCurrentKeyTimestamp = append(f.itersWithCurrentKeyTimestamp, iterIdx)
   164  				proposedNextIdx = iterIdx
   165  			} else if iterMVCCKey.Less(proposedMVCCKey) {
   166  				// This iterator sorts before the current best in mvcc sort
   167  				// order, so update the current best.
   168  				f.itersWithCurrentKeyTimestamp = f.itersWithCurrentKeyTimestamp[:0]
   169  				f.itersWithCurrentKeyTimestamp = append(f.itersWithCurrentKeyTimestamp, iterIdx)
   170  				proposedNextIdx = iterIdx
   171  			}
   172  		}
   173  	}
   174  
   175  	// NB: proposedNextIdx will still be invalidIdxSentinel here if this
   176  	// iterator is exhausted.
   177  	f.currentIdx = proposedNextIdx
   178  }