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 }