0
Files
aleesa-irc-go/vendor/github.com/cockroachdb/pebble/iterator.go
2022-09-04 04:29:50 +03:00

2230 lines
83 KiB
Go

// Copyright 2011 The LevelDB-Go and Pebble Authors. All rights reserved. Use
// of this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
package pebble
import (
"bytes"
"io"
"sort"
"sync"
"sync/atomic"
"unsafe"
"github.com/cockroachdb/errors"
"github.com/cockroachdb/pebble/internal/base"
"github.com/cockroachdb/pebble/internal/fastrand"
"github.com/cockroachdb/pebble/internal/humanize"
"github.com/cockroachdb/pebble/internal/invariants"
"github.com/cockroachdb/pebble/internal/keyspan"
"github.com/cockroachdb/pebble/internal/manifest"
"github.com/cockroachdb/pebble/internal/rangekey"
"github.com/cockroachdb/pebble/sstable"
"github.com/cockroachdb/redact"
)
// iterPos describes the state of the internal iterator, in terms of whether it
// is at the position returned to the user (cur), one ahead of the position
// returned (next for forward iteration and prev for reverse iteration). The cur
// position is split into two states, for forward and reverse iteration, since
// we need to differentiate for switching directions.
//
// There is subtlety in what is considered the current position of the Iterator.
// The internal iterator exposes a sequence of internal keys. There is not
// always a single internalIterator position corresponding to the position
// returned to the user. Consider the example:
//
// a.MERGE.9 a.MERGE.8 a.MERGE.7 a.SET.6 b.DELETE.9 b.DELETE.5 b.SET.4
// \ /
// \ Iterator.Key() = 'a' /
//
// The Iterator exposes one valid position at user key 'a' and the two exhausted
// positions at the beginning and end of iteration. The underlying
// internalIterator contains 7 valid positions and 2 exhausted positions.
//
// Iterator positioning methods must set iterPos to iterPosCur{Foward,Backward}
// iff the user key at the current internalIterator position equals the
// Iterator.Key returned to the user. This guarantees that a call to nextUserKey
// or prevUserKey will advance to the next or previous iterator position.
// iterPosCur{Forward,Backward} does not make any guarantee about the internal
// iterator position among internal keys with matching user keys, and it will
// vary subtly depending on the particular key kinds encountered. In the above
// example, the iterator returning 'a' may set iterPosCurForward if the internal
// iterator is positioned at any of a.MERGE.9, a.MERGE.8, a.MERGE.7 or a.SET.6.
//
// When setting iterPos to iterPosNext or iterPosPrev, the internal iterator
// must be advanced to the first internalIterator position at a user key greater
// (iterPosNext) or less (iterPosPrev) than the key returned to the user. An
// internalIterator position that's !Valid() must also be considered greater or
// less—depending on the direction of iteration—than the last valid Iterator
// position.
type iterPos int8
const (
iterPosCurForward iterPos = 0
iterPosNext iterPos = 1
iterPosPrev iterPos = -1
iterPosCurReverse iterPos = -2
// For limited iteration. When the iterator is at iterPosCurForwardPaused
// - Next*() call should behave as if the internal iterator is already
// at next (akin to iterPosNext).
// - Prev*() call should behave as if the internal iterator is at the
// current key (akin to iterPosCurForward).
//
// Similar semantics apply to CurReversePaused.
iterPosCurForwardPaused iterPos = 2
iterPosCurReversePaused iterPos = -3
)
// Approximate gap in bytes between samples of data read during iteration.
// This is multiplied with a default ReadSamplingMultiplier of 1 << 4 to yield
// 1 << 20 (1MB). The 1MB factor comes from:
// https://github.com/cockroachdb/pebble/issues/29#issuecomment-494477985
const readBytesPeriod uint64 = 1 << 16
var errReversePrefixIteration = errors.New("pebble: unsupported reverse prefix iteration")
// IteratorMetrics holds per-iterator metrics. These do not change over the
// lifetime of the iterator.
type IteratorMetrics struct {
// The read amplification experienced by this iterator. This is the sum of
// the memtables, the L0 sublevels and the non-empty Ln levels. Higher read
// amplification generally results in slower reads, though allowing higher
// read amplification can also result in faster writes.
ReadAmp int
}
// IteratorStatsKind describes the two kind of iterator stats.
type IteratorStatsKind int8
const (
// InterfaceCall represents calls to Iterator.
InterfaceCall IteratorStatsKind = iota
// InternalIterCall represents calls by Iterator to its internalIterator.
InternalIterCall
// NumStatsKind is the number of kinds, and is used for array sizing.
NumStatsKind
)
// IteratorStats contains iteration stats.
type IteratorStats struct {
// ForwardSeekCount includes SeekGE, SeekPrefixGE, First.
ForwardSeekCount [NumStatsKind]int
// ReverseSeek includes SeekLT, Last.
ReverseSeekCount [NumStatsKind]int
// ForwardStepCount includes Next.
ForwardStepCount [NumStatsKind]int
// ReverseStepCount includes Prev.
ReverseStepCount [NumStatsKind]int
InternalStats InternalIteratorStats
}
var _ redact.SafeFormatter = &IteratorStats{}
// InternalIteratorStats contains miscellaneous stats produced by internal
// iterators.
type InternalIteratorStats = base.InternalIteratorStats
// Iterator iterates over a DB's key/value pairs in key order.
//
// An iterator must be closed after use, but it is not necessary to read an
// iterator until exhaustion.
//
// An iterator is not goroutine-safe, but it is safe to use multiple iterators
// concurrently, with each in a dedicated goroutine.
//
// It is also safe to use an iterator concurrently with modifying its
// underlying DB, if that DB permits modification. However, the resultant
// key/value pairs are not guaranteed to be a consistent snapshot of that DB
// at a particular point in time.
//
// If an iterator encounters an error during any operation, it is stored by
// the Iterator and surfaced through the Error method. All absolute
// positioning methods (eg, SeekLT, SeekGT, First, Last, etc) reset any
// accumulated error before positioning. All relative positioning methods (eg,
// Next, Prev) return without advancing if the iterator has an accumulated
// error.
type Iterator struct {
opts IterOptions
cmp Compare
equal Equal
merge Merge
split Split
iter internalIteratorWithStats
pointIter internalIteratorWithStats
readState *readState
// rangeKey holds iteration state specific to iteration over range keys.
// The range key field may be nil if the Iterator has never been configured
// to iterate over range keys. Its non-nilness cannot be used to determine
// if the Iterator is currently iterating over range keys: For that, consult
// the IterOptions using opts.rangeKeys(). If non-nil, its rangeKeyIter
// field is guaranteed to be non-nil too.
rangeKey *iteratorRangeKeyState
err error
// When iterValidityState=IterValid, key represents the current key, which
// is backed by keyBuf.
key []byte
keyBuf []byte
value []byte
valueBuf []byte
valueCloser io.Closer
// boundsBuf holds two buffers used to store the lower and upper bounds.
// Whenever the Iterator's bounds change, the new bounds are copied into
// boundsBuf[boundsBufIdx]. The two bounds share a slice to reduce
// allocations. opts.LowerBound and opts.UpperBound point into this slice.
boundsBuf [2][]byte
boundsBufIdx int
// iterKey, iterValue reflect the latest position of iter, except when
// SetBounds is called. In that case, these are explicitly set to nil.
iterKey *InternalKey
iterValue []byte
alloc *iterAlloc
getIterAlloc *getIterAlloc
prefixOrFullSeekKey []byte
readSampling readSampling
stats IteratorStats
externalReaders []*sstable.Reader
// Following fields used when constructing an iterator stack, eg, in Clone
// and SetOptions or when re-fragmenting a batch's range keys/range dels.
// Non-nil if this Iterator includes a Batch.
batch *Batch
newIters tableNewIters
newIterRangeKey keyspan.TableNewSpanIter
lazyCombinedIter lazyCombinedIter
seqNum uint64
// batchSeqNum is used by Iterators over indexed batches to detect when the
// underlying batch has been mutated. The batch beneath an indexed batch may
// be mutated while the Iterator is open, but new keys are not surfaced
// until the next call to SetOptions.
batchSeqNum uint64
// batch{PointIter,RangeDelIter,RangeKeyIter} are used when the Iterator is
// configured to read through an indexed batch. If a batch is set, these
// iterators will be included within the iterator stack regardless of
// whether the batch currently contains any keys of their kind. These
// pointers are used during a call to SetOptions to refresh the Iterator's
// view of its indexed batch.
batchPointIter batchIter
batchRangeDelIter keyspan.Iter
batchRangeKeyIter keyspan.Iter
// Keeping the bools here after all the 8 byte aligned fields shrinks the
// sizeof this struct by 24 bytes.
// INVARIANT:
// iterValidityState==IterAtLimit <=>
// pos==iterPosCurForwardPaused || pos==iterPosCurReversePaused
iterValidityState IterValidityState
// Set to true by SetBounds, SetOptions. Causes the Iterator to appear
// exhausted externally, while preserving the correct iterValidityState for
// the iterator's internal state. Preserving the correct internal validity
// is used for SeekPrefixGE(..., trySeekUsingNext), and SeekGE/SeekLT
// optimizations after "no-op" calls to SetBounds and SetOptions.
requiresReposition bool
// The position of iter. When this is iterPos{Prev,Next} the iter has been
// moved past the current key-value, which can only happen if
// iterValidityState=IterValid, i.e., there is something to return to the
// client for the current position.
pos iterPos
// Relates to the prefixOrFullSeekKey field above.
hasPrefix bool
// Used for deriving the value of SeekPrefixGE(..., trySeekUsingNext),
// and SeekGE/SeekLT optimizations
lastPositioningOp lastPositioningOpKind
// Used in some tests to disable the random disabling of seek optimizations.
forceEnableSeekOpt bool
}
// iteratorRangeKeyState holds an iterator's range key iteration state.
type iteratorRangeKeyState struct {
opts *IterOptions
cmp base.Compare
split base.Split
// rangeKeyIter holds the range key iterator stack that iterates over the
// merged spans across the entirety of the LSM.
rangeKeyIter keyspan.FragmentIterator
iiter keyspan.InterleavingIter
// activeMaskSuffix holds the suffix of a range key currently acting as a
// mask, hiding point keys with suffixes greater than it. activeMaskSuffix
// is only ever non-nil if IterOptions.RangeKeyMasking.Suffix is non-nil.
// activeMaskSuffix is updated whenever the iterator passes over a new range
// key. See Iterator.rangeKeySpanChanged.
activeMaskSuffix []byte
// rangeKeyOnly is set to true if at the current iterator position there is
// no point key, only a range key start boundary.
rangeKeyOnly bool
hasRangeKey bool
keys bySuffix
// start and end are the [start, end) boundaries of the current range keys.
start []byte
end []byte
// buf is used to save range-key data before moving the range-key iterator.
// Start and end boundaries, suffixes and values are all copied into buf.
buf []byte
// iterConfig holds fields that are used for the construction of the
// iterator stack, but do not need to be directly accessed during iteration.
// This struct is bundled within the iteratorRangeKeyState struct to reduce
// allocations.
iterConfig rangekey.UserIteratorConfig
}
func (i *iteratorRangeKeyState) init(cmp base.Compare, split base.Split, opts *IterOptions) {
i.cmp = cmp
i.keys.cmp = cmp
i.split = split
i.opts = opts
}
var iterRangeKeyStateAllocPool = sync.Pool{
New: func() interface{} {
return &iteratorRangeKeyState{}
},
}
type bySuffix struct {
cmp base.Compare
data []RangeKeyData
}
func (s *bySuffix) Len() int { return len(s.data) }
func (s *bySuffix) Less(i, j int) bool { return s.cmp(s.data[i].Suffix, s.data[j].Suffix) < 0 }
func (s *bySuffix) Swap(i, j int) { s.data[i], s.data[j] = s.data[j], s.data[i] }
// isEphemeralPosition returns true iff the current iterator position is
// ephemeral, and won't be visited during subsequent relative positioning
// operations.
//
// The iterator position resulting from a SeekGE or SeekPrefixGE that lands on a
// straddling range key without a coincident point key is such a position.
func (i *Iterator) isEphemeralPosition() bool {
return i.opts.rangeKeys() && i.rangeKey != nil && i.rangeKey.rangeKeyOnly && !i.equal(i.rangeKey.start, i.key)
}
type lastPositioningOpKind int8
const (
unknownLastPositionOp lastPositioningOpKind = iota
seekPrefixGELastPositioningOp
seekGELastPositioningOp
seekLTLastPositioningOp
)
// Limited iteration mode. Not for use with prefix iteration.
//
// SeekGE, SeekLT, Prev, Next have WithLimit variants, that pause the iterator
// at the limit in a best-effort manner. The client should behave correctly
// even if the limits are ignored. These limits are not "deep", in that they
// are not passed down to the underlying collection of internalIterators. This
// is because the limits are transient, and apply only until the next
// iteration call. They serve mainly as a way to bound the amount of work when
// two (or more) Iterators are being coordinated at a higher level.
//
// In limited iteration mode:
// - Avoid using Iterator.Valid if the last call was to a *WithLimit() method.
// The return value from the *WithLimit() method provides a more precise
// disposition.
// - The limit is exclusive for forward and inclusive for reverse.
//
//
// Limited iteration mode & range keys
//
// Limited iteration interacts with range-key iteration. When range key
// iteration is enabled, range keys are interleaved at their start boundaries.
// Limited iteration must ensure that if a range key exists within the limit,
// the iterator visits the range key.
//
// During forward limited iteration, this is trivial: An overlapping range key
// must have a start boundary less than the limit, and the range key's start
// boundary will be interleaved and found to be within the limit.
//
// During reverse limited iteration, the tail of the range key may fall within
// the limit. The range key must be surfaced even if the range key's start
// boundary is less than the limit, and if there are no point keys between the
// current iterator position and the limit. To provide this guarantee, reverse
// limited iteration ignores the limit as long as there is a range key
// overlapping the iteration position.
// IterValidityState captures the state of the Iterator.
type IterValidityState int8
const (
// IterExhausted represents an Iterator that is exhausted.
IterExhausted IterValidityState = iota
// IterValid represents an Iterator that is valid.
IterValid
// IterAtLimit represents an Iterator that has a non-exhausted
// internalIterator, but has reached a limit without any key for the
// caller.
IterAtLimit
)
// readSampling stores variables used to sample a read to trigger a read
// compaction
type readSampling struct {
bytesUntilReadSampling uint64
initialSamplePassed bool
pendingCompactions readCompactionQueue
// forceReadSampling is used for testing purposes to force a read sample on every
// call to Iterator.maybeSampleRead()
forceReadSampling bool
}
func (i *Iterator) findNextEntry(limit []byte) {
i.iterValidityState = IterExhausted
i.pos = iterPosCurForward
if i.opts.rangeKeys() && i.rangeKey != nil {
i.rangeKey.rangeKeyOnly = false
}
// Close the closer for the current value if one was open.
if i.closeValueCloser() != nil {
return
}
for i.iterKey != nil {
key := *i.iterKey
if i.hasPrefix {
if n := i.split(key.UserKey); !bytes.Equal(i.prefixOrFullSeekKey, key.UserKey[:n]) {
return
}
}
// Compare with limit every time we start at a different user key.
// Note that given the best-effort contract of limit, we could avoid a
// comparison in the common case by doing this only after
// i.nextUserKey is called for the deletes below. However that makes
// the behavior non-deterministic (since the behavior will vary based
// on what has been compacted), which makes it hard to test with the
// metamorphic test. So we forego that performance optimization.
if limit != nil && i.cmp(limit, i.iterKey.UserKey) <= 0 {
i.iterValidityState = IterAtLimit
i.pos = iterPosCurForwardPaused
return
}
switch key.Kind() {
case InternalKeyKindRangeKeySet:
// Save the current key.
i.keyBuf = append(i.keyBuf[:0], key.UserKey...)
i.key = i.keyBuf
i.value = nil
// There may also be a live point key at this userkey that we have
// not yet read. We need to find the next entry with this user key
// to find it. Save the range key so we don't lose it when we Next
// the underlying iterator.
i.saveRangeKey()
pointKeyExists := i.nextPointCurrentUserKey()
if i.err != nil {
i.iterValidityState = IterExhausted
return
}
i.rangeKey.rangeKeyOnly = !pointKeyExists
i.iterValidityState = IterValid
return
case InternalKeyKindDelete, InternalKeyKindSingleDelete:
i.nextUserKey()
continue
case InternalKeyKindSet, InternalKeyKindSetWithDelete:
i.keyBuf = append(i.keyBuf[:0], key.UserKey...)
i.key = i.keyBuf
i.value = i.iterValue
i.iterValidityState = IterValid
i.setRangeKey()
return
case InternalKeyKindMerge:
// Resolving the merge may advance us to the next point key, which
// may be covered by a different set of range keys. Save the range
// key state so we don't lose it.
i.saveRangeKey()
if i.mergeForward(key) {
i.iterValidityState = IterValid
return
}
// The merge didn't yield a valid key, either because the value
// merger indicated it should be deleted, or because an error was
// encountered.
i.iterValidityState = IterExhausted
if i.err != nil {
return
}
if i.pos != iterPosNext {
i.nextUserKey()
}
if i.closeValueCloser() != nil {
return
}
i.pos = iterPosCurForward
default:
i.err = base.CorruptionErrorf("pebble: invalid internal key kind: %d", errors.Safe(key.Kind()))
i.iterValidityState = IterExhausted
return
}
}
}
func (i *Iterator) nextPointCurrentUserKey() bool {
i.pos = iterPosCurForward
i.iterKey, i.iterValue = i.iter.Next()
i.stats.ForwardStepCount[InternalIterCall]++
if i.iterKey == nil || !i.equal(i.key, i.iterKey.UserKey) {
i.pos = iterPosNext
return false
}
key := *i.iterKey
switch key.Kind() {
case InternalKeyKindRangeKeySet:
// RangeKeySets must always be interleaved as the first internal key
// for a user key.
i.err = base.CorruptionErrorf("pebble: unexpected range key set mid-user key")
return false
case InternalKeyKindDelete, InternalKeyKindSingleDelete:
return false
case InternalKeyKindSet, InternalKeyKindSetWithDelete:
i.value = i.iterValue
return true
case InternalKeyKindMerge:
return i.mergeForward(key)
default:
i.err = base.CorruptionErrorf("pebble: invalid internal key kind: %d", errors.Safe(key.Kind()))
return false
}
}
// mergeForward resolves a MERGE key, advancing the underlying iterator forward
// to merge with subsequent keys with the same userkey. mergeForward returns a
// boolean indicating whether or not the merge yielded a valid key. A merge may
// not yield a valid key if an error occurred, in which case i.err is non-nil,
// or the user's value merger specified the key to be deleted.
//
// mergeForward does not update iterValidityState.
func (i *Iterator) mergeForward(key base.InternalKey) (valid bool) {
var valueMerger ValueMerger
valueMerger, i.err = i.merge(key.UserKey, i.iterValue)
if i.err != nil {
return false
}
i.mergeNext(key, valueMerger)
if i.err != nil {
return false
}
var needDelete bool
i.value, needDelete, i.valueCloser, i.err = finishValueMerger(
valueMerger, true /* includesBase */)
if i.err != nil {
return false
}
if needDelete {
_ = i.closeValueCloser()
return false
}
return true
}
func (i *Iterator) closeValueCloser() error {
if i.valueCloser != nil {
i.err = i.valueCloser.Close()
i.valueCloser = nil
}
return i.err
}
func (i *Iterator) nextUserKey() {
if i.iterKey == nil {
return
}
done := i.iterKey.SeqNum() == 0
if i.iterValidityState != IterValid {
i.keyBuf = append(i.keyBuf[:0], i.iterKey.UserKey...)
i.key = i.keyBuf
}
for {
i.iterKey, i.iterValue = i.iter.Next()
i.stats.ForwardStepCount[InternalIterCall]++
if done || i.iterKey == nil {
break
}
if !i.equal(i.key, i.iterKey.UserKey) {
break
}
done = i.iterKey.SeqNum() == 0
}
}
func (i *Iterator) maybeSampleRead() {
// This method is only called when a public method of Iterator is
// returning, and below we exclude the case were the iterator is paused at
// a limit. The effect of these choices is that keys that are deleted, but
// are encountered during iteration, are not accounted for in the read
// sampling and will not cause read driven compactions, even though we are
// incurring cost in iterating over them. And this issue is not limited to
// Iterator, which does not see the effect of range deletes, which may be
// causing iteration work in mergingIter. It is not clear at this time
// whether this is a deficiency worth addressing.
if i.iterValidityState != IterValid {
return
}
if i.readState == nil {
return
}
if i.readSampling.forceReadSampling {
i.sampleRead()
return
}
samplingPeriod := int32(int64(readBytesPeriod) * i.readState.db.opts.Experimental.ReadSamplingMultiplier)
if samplingPeriod <= 0 {
return
}
bytesRead := uint64(len(i.key) + len(i.value))
for i.readSampling.bytesUntilReadSampling < bytesRead {
i.readSampling.bytesUntilReadSampling += uint64(fastrand.Uint32n(2 * uint32(samplingPeriod)))
// The block below tries to adjust for the case where this is the
// first read in a newly-opened iterator. As bytesUntilReadSampling
// starts off at zero, we don't want to sample the first read of
// every newly-opened iterator, but we do want to sample some of them.
if !i.readSampling.initialSamplePassed {
i.readSampling.initialSamplePassed = true
if fastrand.Uint32n(uint32(i.readSampling.bytesUntilReadSampling)) > uint32(bytesRead) {
continue
}
}
i.sampleRead()
}
i.readSampling.bytesUntilReadSampling -= bytesRead
}
func (i *Iterator) sampleRead() {
var topFile *manifest.FileMetadata
topLevel, numOverlappingLevels := numLevels, 0
if mi, ok := i.iter.(*mergingIter); ok {
if len(mi.levels) > 1 {
mi.ForEachLevelIter(func(li *levelIter) bool {
l := manifest.LevelToInt(li.level)
if file := li.files.Current(); file != nil {
var containsKey bool
if i.pos == iterPosNext || i.pos == iterPosCurForward ||
i.pos == iterPosCurForwardPaused {
containsKey = i.cmp(file.SmallestPointKey.UserKey, i.key) <= 0
} else if i.pos == iterPosPrev || i.pos == iterPosCurReverse ||
i.pos == iterPosCurReversePaused {
containsKey = i.cmp(file.LargestPointKey.UserKey, i.key) >= 0
}
// Do nothing if the current key is not contained in file's
// bounds. We could seek the LevelIterator at this level
// to find the right file, but the performance impacts of
// doing that are significant enough to negate the benefits
// of read sampling in the first place. See the discussion
// at:
// https://github.com/cockroachdb/pebble/pull/1041#issuecomment-763226492
if containsKey {
numOverlappingLevels++
if numOverlappingLevels >= 2 {
// Terminate the loop early if at least 2 overlapping levels are found.
return true
}
topLevel = l
topFile = file
}
}
return false
})
}
}
if topFile == nil || topLevel >= numLevels {
return
}
if numOverlappingLevels >= 2 {
allowedSeeks := atomic.AddInt64(&topFile.Atomic.AllowedSeeks, -1)
if allowedSeeks == 0 {
// Since the compaction queue can handle duplicates, we can keep
// adding to the queue even once allowedSeeks hits 0.
// In fact, we NEED to keep adding to the queue, because the queue
// is small and evicts older and possibly useful compactions.
atomic.AddInt64(&topFile.Atomic.AllowedSeeks, topFile.InitAllowedSeeks)
read := readCompaction{
start: topFile.SmallestPointKey.UserKey,
end: topFile.LargestPointKey.UserKey,
level: topLevel,
fileNum: topFile.FileNum,
}
i.readSampling.pendingCompactions.add(&read, i.cmp)
}
}
}
func (i *Iterator) findPrevEntry(limit []byte) {
i.iterValidityState = IterExhausted
i.pos = iterPosCurReverse
if i.opts.rangeKeys() && i.rangeKey != nil {
i.rangeKey.rangeKeyOnly = false
}
// Close the closer for the current value if one was open.
if i.valueCloser != nil {
i.err = i.valueCloser.Close()
i.valueCloser = nil
if i.err != nil {
i.iterValidityState = IterExhausted
return
}
}
var valueMerger ValueMerger
firstLoopIter := true
rangeKeyBoundary := false
// The code below compares with limit in multiple places. As documented in
// findNextEntry, this is being done to make the behavior of limit
// deterministic to allow for metamorphic testing. It is not required by
// the best-effort contract of limit.
for i.iterKey != nil {
key := *i.iterKey
// NB: We cannot pause if the current key is covered by a range key.
// Otherwise, the user might not ever learn of a range key that covers
// the key space being iterated over in which there are no point keys.
// Since limits are best effort, ignoring the limit in this case is
// allowed by the contract of limit.
if firstLoopIter && limit != nil && i.cmp(limit, i.iterKey.UserKey) > 0 && !i.rangeKeyWithinLimit(limit) {
i.iterValidityState = IterAtLimit
i.pos = iterPosCurReversePaused
return
}
firstLoopIter = false
if i.iterValidityState == IterValid {
if !i.equal(key.UserKey, i.key) {
// We've iterated to the previous user key.
i.pos = iterPosPrev
if valueMerger != nil {
var needDelete bool
i.value, needDelete, i.valueCloser, i.err = finishValueMerger(valueMerger, true /* includesBase */)
if i.err == nil && needDelete {
// The point key at this key is deleted. If we also have
// a range key boundary at this key, we still want to
// return. Otherwise, we need to continue looking for
// a live key.
i.value = nil
if rangeKeyBoundary {
i.rangeKey.rangeKeyOnly = true
} else {
i.iterValidityState = IterExhausted
if i.closeValueCloser() == nil {
continue
}
}
}
}
if i.err != nil {
i.iterValidityState = IterExhausted
}
return
}
}
switch key.Kind() {
case InternalKeyKindRangeKeySet:
// Range key start boundary markers are interleaved with the maximum
// sequence number, so if there's a point key also at this key, we
// must've already iterated over it.
// This is the final entry at this user key, so we may return
i.rangeKey.rangeKeyOnly = i.iterValidityState != IterValid
i.keyBuf = append(i.keyBuf[:0], key.UserKey...)
i.key = i.keyBuf
i.iterValidityState = IterValid
i.saveRangeKey()
// In all other cases, previous iteration requires advancing to
// iterPosPrev in order to determine if the key is live and
// unshadowed by another key at the same user key. In this case,
// because range key start boundary markers are always interleaved
// at the maximum sequence number, we know that there aren't any
// additional keys with the same user key in the backward direction.
//
// We Prev the underlying iterator once anyways for consistency, so
// that we can maintain the invariant during backward iteration that
// i.iterPos = iterPosPrev.
i.stats.ReverseStepCount[InternalIterCall]++
i.iterKey, i.iterValue = i.iter.Prev()
// Set rangeKeyBoundary so that on the next iteration, we know to
// return the key even if the MERGE point key is deleted.
rangeKeyBoundary = true
case InternalKeyKindDelete, InternalKeyKindSingleDelete:
i.value = nil
i.iterValidityState = IterExhausted
valueMerger = nil
i.iterKey, i.iterValue = i.iter.Prev()
i.stats.ReverseStepCount[InternalIterCall]++
// Compare with the limit. We could optimize by only checking when
// we step to the previous user key, but detecting that requires a
// comparison too. Note that this position may already passed a
// number of versions of this user key, but they are all deleted,
// so the fact that a subsequent Prev*() call will not see them is
// harmless. Also note that this is the only place in the loop,
// other than the firstLoopIter case above, where we could step
// to a different user key and start processing it for returning
// to the caller.
if limit != nil && i.iterKey != nil && i.cmp(limit, i.iterKey.UserKey) > 0 && !i.rangeKeyWithinLimit(limit) {
i.iterValidityState = IterAtLimit
i.pos = iterPosCurReversePaused
return
}
continue
case InternalKeyKindSet, InternalKeyKindSetWithDelete:
i.keyBuf = append(i.keyBuf[:0], key.UserKey...)
i.key = i.keyBuf
// iterValue is owned by i.iter and could change after the Prev()
// call, so use valueBuf instead. Note that valueBuf is only used
// in this one instance; everywhere else (eg. in findNextEntry),
// we just point i.value to the unsafe i.iter-owned value buffer.
i.valueBuf = append(i.valueBuf[:0], i.iterValue...)
i.value = i.valueBuf
// TODO(jackson): We may save the same range key many times. We can
// avoid that with some help from the InterleavingIter. See also the
// TODO in saveRangeKey.
i.saveRangeKey()
i.iterValidityState = IterValid
i.iterKey, i.iterValue = i.iter.Prev()
i.stats.ReverseStepCount[InternalIterCall]++
valueMerger = nil
continue
case InternalKeyKindMerge:
if i.iterValidityState == IterExhausted {
i.keyBuf = append(i.keyBuf[:0], key.UserKey...)
i.key = i.keyBuf
i.saveRangeKey()
valueMerger, i.err = i.merge(i.key, i.iterValue)
if i.err != nil {
return
}
i.iterValidityState = IterValid
} else if valueMerger == nil {
valueMerger, i.err = i.merge(i.key, i.value)
if i.err == nil {
i.err = valueMerger.MergeNewer(i.iterValue)
}
if i.err != nil {
i.iterValidityState = IterExhausted
return
}
} else {
i.err = valueMerger.MergeNewer(i.iterValue)
if i.err != nil {
i.iterValidityState = IterExhausted
return
}
}
i.iterKey, i.iterValue = i.iter.Prev()
i.stats.ReverseStepCount[InternalIterCall]++
continue
default:
i.err = base.CorruptionErrorf("pebble: invalid internal key kind: %d", errors.Safe(key.Kind()))
i.iterValidityState = IterExhausted
return
}
}
// i.iterKey == nil, so broke out of the preceding loop.
if i.iterValidityState == IterValid {
i.pos = iterPosPrev
if valueMerger != nil {
var needDelete bool
i.value, needDelete, i.valueCloser, i.err = finishValueMerger(valueMerger, true /* includesBase */)
if i.err == nil && needDelete {
i.key = nil
i.value = nil
i.iterValidityState = IterExhausted
}
}
if i.err != nil {
i.iterValidityState = IterExhausted
}
}
}
func (i *Iterator) prevUserKey() {
if i.iterKey == nil {
return
}
if i.iterValidityState != IterValid {
// If we're going to compare against the prev key, we need to save the
// current key.
i.keyBuf = append(i.keyBuf[:0], i.iterKey.UserKey...)
i.key = i.keyBuf
}
for {
i.iterKey, i.iterValue = i.iter.Prev()
i.stats.ReverseStepCount[InternalIterCall]++
if i.iterKey == nil {
break
}
if !i.equal(i.key, i.iterKey.UserKey) {
break
}
}
}
func (i *Iterator) mergeNext(key InternalKey, valueMerger ValueMerger) {
// Save the current key.
i.keyBuf = append(i.keyBuf[:0], key.UserKey...)
i.key = i.keyBuf
// Loop looking for older values for this key and merging them.
for {
i.iterKey, i.iterValue = i.iter.Next()
i.stats.ForwardStepCount[InternalIterCall]++
if i.iterKey == nil {
i.pos = iterPosNext
return
}
key = *i.iterKey
if !i.equal(i.key, key.UserKey) {
// We've advanced to the next key.
i.pos = iterPosNext
return
}
switch key.Kind() {
case InternalKeyKindDelete, InternalKeyKindSingleDelete:
// We've hit a deletion tombstone. Return everything up to this
// point.
return
case InternalKeyKindSet, InternalKeyKindSetWithDelete:
// We've hit a Set value. Merge with the existing value and return.
i.err = valueMerger.MergeOlder(i.iterValue)
return
case InternalKeyKindMerge:
// We've hit another Merge value. Merge with the existing value and
// continue looping.
i.err = valueMerger.MergeOlder(i.iterValue)
if i.err != nil {
return
}
continue
case InternalKeyKindRangeKeySet:
// The RANGEKEYSET marker must sort before a MERGE at the same user key.
i.err = base.CorruptionErrorf("pebble: out of order range key marker")
return
default:
i.err = base.CorruptionErrorf("pebble: invalid internal key kind: %d", errors.Safe(key.Kind()))
return
}
}
}
// SeekGE moves the iterator to the first key/value pair whose key is greater
// than or equal to the given key. Returns true if the iterator is pointing at
// a valid entry and false otherwise.
func (i *Iterator) SeekGE(key []byte) bool {
return i.SeekGEWithLimit(key, nil) == IterValid
}
// SeekGEWithLimit moves the iterator to the first key/value pair whose key is
// greater than or equal to the given key.
//
// If limit is provided, it serves as a best-effort exclusive limit. If the
// first key greater than or equal to the given search key is also greater than
// or equal to limit, the Iterator may pause and return IterAtLimit. Because
// limits are best-effort, SeekGEWithLimit may return a key beyond limit.
//
// If the Iterator is configured to iterate over range keys, SeekGEWithLimit
// guarantees it will surface any range keys with bounds overlapping the
// keyspace [key, limit).
func (i *Iterator) SeekGEWithLimit(key []byte, limit []byte) IterValidityState {
lastPositioningOp := i.lastPositioningOp
// Set it to unknown, since this operation may not succeed, in which case
// the SeekGE following this should not make any assumption about iterator
// position.
i.lastPositioningOp = unknownLastPositionOp
i.requiresReposition = false
i.err = nil // clear cached iteration error
i.hasPrefix = false
i.stats.ForwardSeekCount[InterfaceCall]++
if lowerBound := i.opts.GetLowerBound(); lowerBound != nil && i.cmp(key, lowerBound) < 0 {
key = lowerBound
} else if upperBound := i.opts.GetUpperBound(); upperBound != nil && i.cmp(key, upperBound) > 0 {
key = upperBound
}
seekInternalIter := true
var flags base.SeekGEFlags
// The following noop optimization only applies when i.batch == nil, since
// an iterator over a batch is iterating over mutable data, that may have
// changed since the last seek.
if lastPositioningOp == seekGELastPositioningOp && i.batch == nil {
cmp := i.cmp(i.prefixOrFullSeekKey, key)
// If this seek is to the same or later key, and the iterator is
// already positioned there, this is a noop. This can be helpful for
// sparse key spaces that have many deleted keys, where one can avoid
// the overhead of iterating past them again and again.
if cmp <= 0 {
if i.iterValidityState == IterExhausted ||
(i.iterValidityState == IterValid && i.cmp(key, i.key) <= 0 &&
(limit == nil || i.cmp(i.key, limit) < 0)) {
// Noop
if !invariants.Enabled || !disableSeekOpt(key, uintptr(unsafe.Pointer(i))) || i.forceEnableSeekOpt {
i.lastPositioningOp = seekGELastPositioningOp
return i.iterValidityState
}
}
// cmp == 0 is not safe to optimize since
// - i.pos could be at iterPosNext, due to a merge.
// - Even if i.pos were at iterPosCurForward, we could have a DELETE,
// SET pair for a key, and the iterator would have moved past DELETE
// but stayed at iterPosCurForward. A similar situation occurs for a
// MERGE, SET pair where the MERGE is consumed and the iterator is
// at the SET.
// We also leverage the IterAtLimit <=> i.pos invariant defined in the
// comment on iterValidityState, to exclude any cases where i.pos
// is iterPosCur{Forward,Reverse}Paused. This avoids the need to
// special-case those iterator positions and their interactions with
// TrySeekUsingNext, as the main uses for TrySeekUsingNext in CockroachDB
// do not use limited Seeks in the first place.
if cmp < 0 && i.iterValidityState != IterAtLimit && limit == nil {
flags = flags.EnableTrySeekUsingNext()
}
if invariants.Enabled && flags.TrySeekUsingNext() && !i.forceEnableSeekOpt && disableSeekOpt(key, uintptr(unsafe.Pointer(i))) {
flags = flags.DisableTrySeekUsingNext()
}
if i.pos == iterPosCurForwardPaused && i.cmp(key, i.iterKey.UserKey) <= 0 {
// Have some work to do, but don't need to seek, and we can
// start doing findNextEntry from i.iterKey.
seekInternalIter = false
}
}
}
if seekInternalIter {
i.iterKey, i.iterValue = i.iter.SeekGE(key, flags)
i.stats.ForwardSeekCount[InternalIterCall]++
}
i.findNextEntry(limit)
i.maybeSampleRead()
if i.Error() == nil && i.batch == nil {
// Prepare state for a future noop optimization.
i.prefixOrFullSeekKey = append(i.prefixOrFullSeekKey[:0], key...)
i.lastPositioningOp = seekGELastPositioningOp
}
return i.iterValidityState
}
// SeekPrefixGE moves the iterator to the first key/value pair whose key is
// greater than or equal to the given key and which has the same "prefix" as
// the given key. The prefix for a key is determined by the user-defined
// Comparer.Split function. The iterator will not observe keys not matching the
// "prefix" of the search key. Calling SeekPrefixGE puts the iterator in prefix
// iteration mode. The iterator remains in prefix iteration until a subsequent
// call to another absolute positioning method (SeekGE, SeekLT, First,
// Last). Reverse iteration (Prev) is not supported when an iterator is in
// prefix iteration mode. Returns true if the iterator is pointing at a valid
// entry and false otherwise.
//
// The semantics of SeekPrefixGE are slightly unusual and designed for
// iteration to be able to take advantage of bloom filters that have been
// created on the "prefix". If you're not using bloom filters, there is no
// reason to use SeekPrefixGE.
//
// An example Split function may separate a timestamp suffix from the prefix of
// the key.
//
// Split(<key>@<timestamp>) -> <key>
//
// Consider the keys "a@1", "a@2", "aa@3", "aa@4". The prefixes for these keys
// are "a", and "aa". Note that despite "a" and "aa" sharing a prefix by the
// usual definition, those prefixes differ by the definition of the Split
// function. To see how this works, consider the following set of calls on this
// data set:
//
// SeekPrefixGE("a@0") -> "a@1"
// Next() -> "a@2"
// Next() -> EOF
//
// If you're just looking to iterate over keys with a shared prefix, as
// defined by the configured comparer, set iterator bounds instead:
//
// iter := db.NewIter(&pebble.IterOptions{
// LowerBound: []byte("prefix"),
// UpperBound: []byte("prefiy"),
// })
// for iter.First(); iter.Valid(); iter.Next() {
// // Only keys beginning with "prefix" will be visited.
// }
//
// See ExampleIterator_SeekPrefixGE for a working example.
func (i *Iterator) SeekPrefixGE(key []byte) bool {
lastPositioningOp := i.lastPositioningOp
// Set it to unknown, since this operation may not succeed, in which case
// the SeekPrefixGE following this should not make any assumption about
// iterator position.
i.lastPositioningOp = unknownLastPositionOp
i.requiresReposition = false
i.err = nil // clear cached iteration error
i.stats.ForwardSeekCount[InterfaceCall]++
if i.split == nil {
panic("pebble: split must be provided for SeekPrefixGE")
}
prefixLen := i.split(key)
keyPrefix := key[:prefixLen]
var flags base.SeekGEFlags
if lastPositioningOp == seekPrefixGELastPositioningOp {
if !i.hasPrefix {
panic("lastPositioningOpsIsSeekPrefixGE is true, but hasPrefix is false")
}
// The iterator has not been repositioned after the last SeekPrefixGE.
// See if we are seeking to a larger key, since then we can optimize
// the seek by using next. Note that we could also optimize if Next
// has been called, if the iterator is not exhausted and the current
// position is <= the seek key. We are keeping this limited for now
// since such optimizations require care for correctness, and to not
// become de-optimizations (if one usually has to do all the next
// calls and then the seek). This SeekPrefixGE optimization
// specifically benefits CockroachDB.
cmp := i.cmp(i.prefixOrFullSeekKey, keyPrefix)
// cmp == 0 is not safe to optimize since
// - i.pos could be at iterPosNext, due to a merge.
// - Even if i.pos were at iterPosCurForward, we could have a DELETE,
// SET pair for a key, and the iterator would have moved past DELETE
// but stayed at iterPosCurForward. A similar situation occurs for a
// MERGE, SET pair where the MERGE is consumed and the iterator is
// at the SET.
// In general some versions of i.prefix could have been consumed by
// the iterator, so we only optimize for cmp < 0.
if cmp < 0 {
flags = flags.EnableTrySeekUsingNext()
}
if invariants.Enabled && flags.TrySeekUsingNext() && !i.forceEnableSeekOpt && disableSeekOpt(key, uintptr(unsafe.Pointer(i))) {
flags = flags.DisableTrySeekUsingNext()
}
}
// Make a copy of the prefix so that modifications to the key after
// SeekPrefixGE returns does not affect the stored prefix.
if cap(i.prefixOrFullSeekKey) < prefixLen {
i.prefixOrFullSeekKey = make([]byte, prefixLen)
} else {
i.prefixOrFullSeekKey = i.prefixOrFullSeekKey[:prefixLen]
}
i.hasPrefix = true
copy(i.prefixOrFullSeekKey, keyPrefix)
if lowerBound := i.opts.GetLowerBound(); lowerBound != nil && i.cmp(key, lowerBound) < 0 {
if n := i.split(lowerBound); !bytes.Equal(i.prefixOrFullSeekKey, lowerBound[:n]) {
i.err = errors.New("pebble: SeekPrefixGE supplied with key outside of lower bound")
i.iterValidityState = IterExhausted
return false
}
key = lowerBound
} else if upperBound := i.opts.GetUpperBound(); upperBound != nil && i.cmp(key, upperBound) > 0 {
if n := i.split(upperBound); !bytes.Equal(i.prefixOrFullSeekKey, upperBound[:n]) {
i.err = errors.New("pebble: SeekPrefixGE supplied with key outside of upper bound")
i.iterValidityState = IterExhausted
return false
}
key = upperBound
}
i.iterKey, i.iterValue = i.iter.SeekPrefixGE(i.prefixOrFullSeekKey, key, flags)
i.stats.ForwardSeekCount[InternalIterCall]++
i.findNextEntry(nil)
i.maybeSampleRead()
if i.Error() == nil {
i.lastPositioningOp = seekPrefixGELastPositioningOp
}
return i.iterValidityState == IterValid
}
// Deterministic disabling of the seek optimization. It uses the iterator
// pointer, since we want diversity in iterator behavior for the same key.
// Used for tests.
func disableSeekOpt(key []byte, ptr uintptr) bool {
// Fibonacci hash https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/
simpleHash := (11400714819323198485 * uint64(ptr)) >> 63
return key != nil && key[0]&byte(1) == 0 && simpleHash == 0
}
// SeekLT moves the iterator to the last key/value pair whose key is less than
// the given key. Returns true if the iterator is pointing at a valid entry and
// false otherwise.
func (i *Iterator) SeekLT(key []byte) bool {
return i.SeekLTWithLimit(key, nil) == IterValid
}
// SeekLTWithLimit moves the iterator to the last key/value pair whose key is
// less than the given key.
//
// If limit is provided, it serves as a best-effort inclusive limit. If the last
// key less than the given search key is also less than limit, the Iterator may
// pause and return IterAtLimit. Because limits are best-effort, SeekLTWithLimit
// may return a key beyond limit.
//
// If the Iterator is configured to iterate over range keys, SeekLTWithLimit
// guarantees it will surface any range keys with bounds overlapping the
// keyspace up to limit.
func (i *Iterator) SeekLTWithLimit(key []byte, limit []byte) IterValidityState {
lastPositioningOp := i.lastPositioningOp
// Set it to unknown, since this operation may not succeed, in which case
// the SeekLT following this should not make any assumption about iterator
// position.
i.lastPositioningOp = unknownLastPositionOp
i.requiresReposition = false
i.err = nil // clear cached iteration error
i.hasPrefix = false
i.stats.ReverseSeekCount[InterfaceCall]++
if upperBound := i.opts.GetUpperBound(); upperBound != nil && i.cmp(key, upperBound) > 0 {
key = upperBound
} else if lowerBound := i.opts.GetLowerBound(); lowerBound != nil && i.cmp(key, lowerBound) < 0 {
key = lowerBound
}
seekInternalIter := true
// The following noop optimization only applies when i.batch == nil, since
// an iterator over a batch is iterating over mutable data, that may have
// changed since the last seek.
if lastPositioningOp == seekLTLastPositioningOp && i.batch == nil {
cmp := i.cmp(key, i.prefixOrFullSeekKey)
// If this seek is to the same or earlier key, and the iterator is
// already positioned there, this is a noop. This can be helpful for
// sparse key spaces that have many deleted keys, where one can avoid
// the overhead of iterating past them again and again.
if cmp <= 0 {
// NB: when pos != iterPosCurReversePaused, the invariant
// documented earlier implies that iterValidityState !=
// IterAtLimit.
if i.iterValidityState == IterExhausted ||
(i.iterValidityState == IterValid && i.cmp(i.key, key) < 0 &&
(limit == nil || i.cmp(limit, i.key) <= 0)) {
if !invariants.Enabled || !disableSeekOpt(key, uintptr(unsafe.Pointer(i))) {
i.lastPositioningOp = seekLTLastPositioningOp
return i.iterValidityState
}
}
if i.pos == iterPosCurReversePaused && i.cmp(i.iterKey.UserKey, key) < 0 {
// Have some work to do, but don't need to seek, and we can
// start doing findPrevEntry from i.iterKey.
seekInternalIter = false
}
}
}
if seekInternalIter {
i.iterKey, i.iterValue = i.iter.SeekLT(key, base.SeekLTFlagsNone)
i.stats.ReverseSeekCount[InternalIterCall]++
}
i.findPrevEntry(limit)
i.maybeSampleRead()
if i.Error() == nil && i.batch == nil {
// Prepare state for a future noop optimization.
i.prefixOrFullSeekKey = append(i.prefixOrFullSeekKey[:0], key...)
i.lastPositioningOp = seekLTLastPositioningOp
}
return i.iterValidityState
}
// First moves the iterator the the first key/value pair. Returns true if the
// iterator is pointing at a valid entry and false otherwise.
func (i *Iterator) First() bool {
i.err = nil // clear cached iteration error
i.hasPrefix = false
i.lastPositioningOp = unknownLastPositionOp
i.requiresReposition = false
i.stats.ForwardSeekCount[InterfaceCall]++
if lowerBound := i.opts.GetLowerBound(); lowerBound != nil {
i.iterKey, i.iterValue = i.iter.SeekGE(lowerBound, base.SeekGEFlagsNone)
i.stats.ForwardSeekCount[InternalIterCall]++
} else {
i.iterKey, i.iterValue = i.iter.First()
i.stats.ForwardSeekCount[InternalIterCall]++
}
i.findNextEntry(nil)
i.maybeSampleRead()
return i.iterValidityState == IterValid
}
// Last moves the iterator the the last key/value pair. Returns true if the
// iterator is pointing at a valid entry and false otherwise.
func (i *Iterator) Last() bool {
i.err = nil // clear cached iteration error
i.hasPrefix = false
i.lastPositioningOp = unknownLastPositionOp
i.requiresReposition = false
i.stats.ReverseSeekCount[InterfaceCall]++
if upperBound := i.opts.GetUpperBound(); upperBound != nil {
i.iterKey, i.iterValue = i.iter.SeekLT(upperBound, base.SeekLTFlagsNone)
i.stats.ReverseSeekCount[InternalIterCall]++
} else {
i.iterKey, i.iterValue = i.iter.Last()
i.stats.ReverseSeekCount[InternalIterCall]++
}
i.findPrevEntry(nil)
i.maybeSampleRead()
return i.iterValidityState == IterValid
}
// Next moves the iterator to the next key/value pair. Returns true if the
// iterator is pointing at a valid entry and false otherwise.
func (i *Iterator) Next() bool {
return i.NextWithLimit(nil) == IterValid
}
// NextWithLimit moves the iterator to the next key/value pair.
//
// If limit is provided, it serves as a best-effort exclusive limit. If the next
// key is greater than or equal to limit, the Iterator may pause and return
// IterAtLimit. Because limits are best-effort, NextWithLimit may return a key
// beyond limit.
//
// If the Iterator is configured to iterate over range keys, NextWithLimit
// guarantees it will surface any range keys with bounds overlapping the
// keyspace up to limit.
func (i *Iterator) NextWithLimit(limit []byte) IterValidityState {
i.stats.ForwardStepCount[InterfaceCall]++
if limit != nil && i.hasPrefix {
i.err = errors.New("cannot use limit with prefix iteration")
i.iterValidityState = IterExhausted
return i.iterValidityState
}
if i.err != nil {
return i.iterValidityState
}
i.lastPositioningOp = unknownLastPositionOp
i.requiresReposition = false
switch i.pos {
case iterPosCurForward:
i.nextUserKey()
case iterPosCurForwardPaused:
// Already at the right place.
case iterPosCurReverse:
// Switching directions.
// Unless the iterator was exhausted, reverse iteration needs to
// position the iterator at iterPosPrev.
if i.iterKey != nil {
i.err = errors.New("switching from reverse to forward but iter is not at prev")
i.iterValidityState = IterExhausted
return i.iterValidityState
}
// We're positioned before the first key. Need to reposition to point to
// the first key.
if lowerBound := i.opts.GetLowerBound(); lowerBound != nil {
i.iterKey, i.iterValue = i.iter.SeekGE(lowerBound, base.SeekGEFlagsNone)
i.stats.ForwardSeekCount[InternalIterCall]++
} else {
i.iterKey, i.iterValue = i.iter.First()
i.stats.ForwardSeekCount[InternalIterCall]++
}
case iterPosCurReversePaused:
// Switching directions.
// The iterator must not be exhausted since it paused.
if i.iterKey == nil {
i.err = errors.New("switching paused from reverse to forward but iter is exhausted")
i.iterValidityState = IterExhausted
return i.iterValidityState
}
i.nextUserKey()
case iterPosPrev:
// The underlying iterator is pointed to the previous key (this can
// only happen when switching iteration directions). We set
// i.iterValidityState to IterExhausted here to force the calls to
// nextUserKey to save the current key i.iter is pointing at in order
// to determine when the next user-key is reached.
i.iterValidityState = IterExhausted
if i.iterKey == nil {
// We're positioned before the first key. Need to reposition to point to
// the first key.
if lowerBound := i.opts.GetLowerBound(); lowerBound != nil {
i.iterKey, i.iterValue = i.iter.SeekGE(lowerBound, base.SeekGEFlagsNone)
i.stats.ForwardSeekCount[InternalIterCall]++
} else {
i.iterKey, i.iterValue = i.iter.First()
i.stats.ForwardSeekCount[InternalIterCall]++
}
} else {
i.nextUserKey()
}
i.nextUserKey()
case iterPosNext:
// Already at the right place.
}
i.findNextEntry(limit)
i.maybeSampleRead()
return i.iterValidityState
}
// Prev moves the iterator to the previous key/value pair. Returns true if the
// iterator is pointing at a valid entry and false otherwise.
func (i *Iterator) Prev() bool {
return i.PrevWithLimit(nil) == IterValid
}
// PrevWithLimit moves the iterator to the previous key/value pair.
//
// If limit is provided, it serves as a best-effort inclusive limit. If the
// previous key is less than limit, the Iterator may pause and return
// IterAtLimit. Because limits are best-effort, PrevWithLimit may return a key
// beyond limit.
//
// If the Iterator is configured to iterate over range keys, PrevWithLimit
// guarantees it will surface any range keys with bounds overlapping the
// keyspace up to limit.
func (i *Iterator) PrevWithLimit(limit []byte) IterValidityState {
i.stats.ReverseStepCount[InterfaceCall]++
if i.err != nil {
return i.iterValidityState
}
i.lastPositioningOp = unknownLastPositionOp
i.requiresReposition = false
if i.hasPrefix {
i.err = errReversePrefixIteration
i.iterValidityState = IterExhausted
return i.iterValidityState
}
switch i.pos {
case iterPosCurForward:
// Switching directions, and will handle this below.
case iterPosCurForwardPaused:
// Switching directions, and will handle this below.
case iterPosCurReverse:
i.prevUserKey()
case iterPosCurReversePaused:
// Already at the right place.
case iterPosNext:
// The underlying iterator is pointed to the next key (this can only happen
// when switching iteration directions). We will handle this below.
case iterPosPrev:
// Already at the right place.
}
if i.pos == iterPosCurForward || i.pos == iterPosNext || i.pos == iterPosCurForwardPaused {
// Switching direction.
stepAgain := i.pos == iterPosNext
// Synthetic range key markers are a special case. Consider SeekGE(b)
// which finds a range key [a, c). To ensure the user observes the range
// key, the Iterator pauses at Key() = b. The iterator must advance the
// internal iterator to see if there's also a coincident point key at
// 'b', leaving the iterator at iterPosNext if there's not.
//
// This is a problem: Synthetic range key markers are only interleaved
// during the original seek. A subsequent Prev() of i.iter will not move
// back onto the synthetic range key marker. In this case where the
// previous iterator position was a synthetic range key start boundary,
// we must not step a second time.
if i.isEphemeralPosition() {
stepAgain = false
}
// We set i.iterValidityState to IterExhausted here to force the calls
// to prevUserKey to save the current key i.iter is pointing at in
// order to determine when the prev user-key is reached.
i.iterValidityState = IterExhausted
if i.iterKey == nil {
// We're positioned after the last key. Need to reposition to point to
// the last key.
if upperBound := i.opts.GetUpperBound(); upperBound != nil {
i.iterKey, i.iterValue = i.iter.SeekLT(upperBound, base.SeekLTFlagsNone)
i.stats.ReverseSeekCount[InternalIterCall]++
} else {
i.iterKey, i.iterValue = i.iter.Last()
i.stats.ReverseSeekCount[InternalIterCall]++
}
} else {
i.prevUserKey()
}
if stepAgain {
i.prevUserKey()
}
}
i.findPrevEntry(limit)
i.maybeSampleRead()
return i.iterValidityState
}
// RangeKeyData describes a range key's data, set through RangeKeySet. The key
// boundaries of the range key is provided by Iterator.RangeBounds.
type RangeKeyData struct {
Suffix []byte
Value []byte
}
// rangeKeyWithinLimit is called during limited reverse iteration when
// positioned over a key beyond the limit. If there exists a range key that lies
// within the limit, the iterator must not pause in order to ensure the user has
// an opportunity to observe the range key within limit.
//
// It would be valid to ignore the limit whenever there's a range key covering
// the key, but that would introduce nondeterminism. To preserve determinism for
// testing, the iterator ignores the limit only if the covering range key does
// cover the keyspace within the limit.
//
// This awkwardness exists because range keys are interleaved at their inclusive
// start positions. Note that limit is inclusive.
func (i *Iterator) rangeKeyWithinLimit(limit []byte) bool {
if i.rangeKey == nil || !i.opts.rangeKeys() {
return false
}
s := i.rangeKey.iiter.Span()
if s == nil {
// If there are no covering range keys, it is safe to to pause
// immediately.
return false
}
// If the range key ends beyond the limit, then the range key does not cover
// any portion of the keyspace within the limit and it is safe to pause.
if i.cmp(s.End, limit) <= 0 {
return false
}
return true
}
// SpanChanged implements the keyspan.SpanMask interface, used during range key
// iteration.
func (i *iteratorRangeKeyState) SpanChanged(s *keyspan.Span) {
i.activeMaskSuffix = i.activeMaskSuffix[:0]
// Find the smallest suffix of a range key contained within the Span,
// excluding suffixes less than i.opts.RangeKeyMasking.Suffix.
if s == nil || i.opts.RangeKeyMasking.Suffix == nil || s.Empty() {
return
}
for j := range s.Keys {
if s.Keys[j].Suffix == nil {
continue
}
if i.cmp(s.Keys[j].Suffix, i.opts.RangeKeyMasking.Suffix) < 0 {
continue
}
if len(i.activeMaskSuffix) == 0 || i.cmp(i.activeMaskSuffix, s.Keys[j].Suffix) > 0 {
i.activeMaskSuffix = append(i.activeMaskSuffix[:0], s.Keys[j].Suffix...)
}
}
}
// SkipPoint implements the keyspan.SpanMask interface, used during range key
// iteration. Whenever a point key is covered by a non-empty Span, the
// interleaving iterator invokes SkipPoint. This function is responsible for
// performing range key masking.
//
// If a non-nil IterOptions.RangeKeyMasking.Suffix is set, range key masking is
// enabled. Masking hides point keys, transparently skipping over the keys.
// Whether or not a point key is masked is determined by comparing the point
// key's suffix, the overlapping span's keys' suffixes, and the user-configured
// IterOption's RangeKeyMasking.Suffix. When configured with a masking threshold
// _t_, and there exists a span with suffix _r_ covering a point key with suffix
// _p_, and
//
// _t_ ≤ _r_ < _p_
//
// then the point key is elided. Consider the following rendering, where
// suffixes with higher integers sort before suffixes with lower integers:
//
// ^
// @9 | •―――――――――――――――○ [e,m)@9
// s 8 | • l@8
// u 7 |------------------------------------ @7 RangeKeyMasking.Suffix
// f 6 | [h,q)@6 •―――――――――――――――――○ (threshold)
// f 5 | • h@5
// f 4 | • n@4
// i 3 | •―――――――――――○ [f,l)@3
// x 2 | • b@2
// 1 |
// 0 |___________________________________
// a b c d e f g h i j k l m n o p q
//
// An iterator scanning the entire keyspace with the masking threshold set to @7
// will observe point keys b@2 and l@8. The span keys [h,q)@6 and [f,l)@3 serve
// as masks, because cmp(@6,@7) ≥ 0 and cmp(@3,@7) ≥ 0. The span key [e,m)@9
// does not serve as a mask, because cmp(@9,@7) < 0.
//
// Although point l@8 falls within the user key bounds of [e,m)@9, [e,m)@9 is
// non-masking due to its suffix. The point key l@8 also falls within the user
// key bounds of [h,q)@6, but since cmp(@6,@8) ≥ 0, l@8 is unmasked.
//
// Invariant: The userKey is within the user key bounds of the span most
// recently provided to `SpanChanged`.
func (i *iteratorRangeKeyState) SkipPoint(userKey []byte) bool {
if len(i.activeMaskSuffix) == 0 {
// No range key is currently acting as a mask, so don't skip.
return false
}
// Range key masking is enabled and the current span includes a range key
// that is being used as a mask. (NB: SpanChanged already verified that the
// range key's suffix is ≥ RangeKeyMasking.Suffix).
//
// This point key falls within the bounds of the range key (guaranteed by
// the InterleavingIter). Skip the point key if the range key's suffix is
// greater than the point key's suffix.
pointSuffix := userKey[i.split(userKey):]
return len(pointSuffix) > 0 && i.cmp(i.activeMaskSuffix, pointSuffix) < 0
}
// setRangeKey sets the current range key to the underlying iterator's current
// range key state. It does not make copies of any of the key, value or suffix
// buffers, so it must only be used if the underlying iterator's position
// matches the top-level iterator (eg, i.pos = iterPosCur*).
func (i *Iterator) setRangeKey() {
if i.rangeKey == nil || !i.opts.rangeKeys() {
return
}
s := i.rangeKey.iiter.Span()
if s == nil {
i.rangeKey.hasRangeKey = false
if i.rangeKey.start != nil {
// Clear out existing pointers, so that we don't unintentionally retain
// any old range key blocks.
i.rangeKey.start = nil
i.rangeKey.end = nil
for j := 0; j < i.rangeKey.keys.Len(); j++ {
i.rangeKey.keys.data[j].Suffix = nil
i.rangeKey.keys.data[j].Value = nil
}
i.rangeKey.keys.data = i.rangeKey.keys.data[:0]
}
return
}
i.rangeKey.hasRangeKey = true
i.rangeKey.start, i.rangeKey.end = s.Start, s.End
i.rangeKey.keys.data = i.rangeKey.keys.data[:0]
for j := 0; j < len(s.Keys); j++ {
if s.Keys[j].Kind() == base.InternalKeyKindRangeKeySet {
i.rangeKey.keys.data = append(i.rangeKey.keys.data, RangeKeyData{
Suffix: s.Keys[j].Suffix,
Value: s.Keys[j].Value,
})
}
}
sort.Sort(&i.rangeKey.keys)
}
// saveRangeKey sets the current range key to the underlying iterator's current
// range key state, copying all of the key, value and suffixes into
// Iterator-managed buffers. Callers should prefer setRangeKey if under no
// circumstances the underlying iterator will be advanced to the next user key
// before returning to the user.
func (i *Iterator) saveRangeKey() {
if i.rangeKey == nil || !i.opts.rangeKeys() {
return
}
s := i.rangeKey.iiter.Span()
if s == nil {
i.rangeKey.hasRangeKey = false
return
}
i.rangeKey.hasRangeKey = true
// TODO(jackson): Rather than naively copying all the range key state every
// time, we could copy only if it actually changed from the currently saved
// state, with some help from the InterleavingIter.
i.rangeKey.buf = append(i.rangeKey.buf[:0], s.Start...)
i.rangeKey.start = i.rangeKey.buf
i.rangeKey.buf = append(i.rangeKey.buf, s.End...)
i.rangeKey.end = i.rangeKey.buf[len(i.rangeKey.buf)-len(s.End):]
i.rangeKey.keys.data = i.rangeKey.keys.data[:0]
for j := 0; j < len(s.Keys); j++ {
if s.Keys[j].Kind() == base.InternalKeyKindRangeKeySet {
i.rangeKey.buf = append(i.rangeKey.buf, s.Keys[j].Suffix...)
suffix := i.rangeKey.buf[len(i.rangeKey.buf)-len(s.Keys[j].Suffix):]
i.rangeKey.buf = append(i.rangeKey.buf, s.Keys[j].Value...)
value := i.rangeKey.buf[len(i.rangeKey.buf)-len(s.Keys[j].Value):]
i.rangeKey.keys.data = append(i.rangeKey.keys.data, RangeKeyData{
Suffix: suffix,
Value: value,
})
}
}
sort.Sort(&i.rangeKey.keys)
}
// HasPointAndRange indicates whether there exists a point key, a range key or
// both at the current iterator position.
func (i *Iterator) HasPointAndRange() (hasPoint, hasRange bool) {
if i.iterValidityState != IterValid || i.requiresReposition {
return false, false
}
if i.opts.KeyTypes == IterKeyTypePointsOnly {
return true, false
}
return i.rangeKey == nil || !i.rangeKey.rangeKeyOnly, i.rangeKey != nil && i.rangeKey.hasRangeKey
}
// RangeBounds returns the start (inclusive) and end (exclusive) bounds of the
// range key covering the current iterator position. RangeBounds returns nil
// bounds if there is no range key covering the current iterator position, or
// the iterator is not configured to surface range keys.
func (i *Iterator) RangeBounds() (start, end []byte) {
if i.rangeKey == nil || !i.opts.rangeKeys() || !i.rangeKey.hasRangeKey {
return nil, nil
}
return i.rangeKey.start, i.rangeKey.end
}
// Key returns the key of the current key/value pair, or nil if done. The
// caller should not modify the contents of the returned slice, and its
// contents may change on the next call to Next.
func (i *Iterator) Key() []byte {
return i.key
}
// Value returns the value of the current key/value pair, or nil if done. The
// caller should not modify the contents of the returned slice, and its
// contents may change on the next call to Next.
//
// Only valid if HasPointAndRange() returns true for hasPoint.
func (i *Iterator) Value() []byte {
return i.value
}
// RangeKeys returns the range key values and their suffixes covering the
// current iterator position. The range bounds may be retrieved separately
// through Iterator.RangeBounds().
func (i *Iterator) RangeKeys() []RangeKeyData {
if i.rangeKey == nil || !i.opts.rangeKeys() || !i.rangeKey.hasRangeKey {
return nil
}
return i.rangeKey.keys.data
}
// Valid returns true if the iterator is positioned at a valid key/value pair
// and false otherwise.
func (i *Iterator) Valid() bool {
return i.iterValidityState == IterValid && !i.requiresReposition
}
// Error returns any accumulated error.
func (i *Iterator) Error() error {
err := i.err
if i.iter != nil {
err = firstError(i.err, i.iter.Error())
}
return err
}
// Close closes the iterator and returns any accumulated error. Exhausting
// all the key/value pairs in a table is not considered to be an error.
// It is not valid to call any method, including Close, after the iterator
// has been closed.
func (i *Iterator) Close() error {
// Close the child iterator before releasing the readState because when the
// readState is released sstables referenced by the readState may be deleted
// which will fail on Windows if the sstables are still open by the child
// iterator.
if i.iter != nil {
i.err = firstError(i.err, i.iter.Close())
// Closing i.iter did not necessarily close the point and range key
// iterators. Calls to SetOptions may have 'disconnected' either one
// from i.iter if iteration key types were changed. Both point and range
// key iterators are preserved in case the iterator needs to switch key
// types again. We explicitly close both of these iterators here.
//
// NB: If the iterators were still connected to i.iter, they may be
// closed, but calling Close on a closed internal iterator or fragment
// iterator is allowed.
if i.pointIter != nil {
i.err = firstError(i.err, i.pointIter.Close())
}
if i.rangeKey != nil && i.rangeKey.rangeKeyIter != nil {
i.err = firstError(i.err, i.rangeKey.rangeKeyIter.Close())
}
}
err := i.err
if i.readState != nil {
if i.readSampling.pendingCompactions.size > 0 {
// Copy pending read compactions using db.mu.Lock()
i.readState.db.mu.Lock()
i.readState.db.mu.compact.readCompactions.combine(&i.readSampling.pendingCompactions, i.cmp)
reschedule := i.readState.db.mu.compact.rescheduleReadCompaction
i.readState.db.mu.compact.rescheduleReadCompaction = false
concurrentCompactions := i.readState.db.mu.compact.compactingCount
i.readState.db.mu.Unlock()
if reschedule && concurrentCompactions == 0 {
// In a read heavy workload, flushes may not happen frequently enough to
// schedule compactions.
i.readState.db.compactionSchedulers.Add(1)
go i.readState.db.maybeScheduleCompactionAsync()
}
}
i.readState.unref()
i.readState = nil
}
for _, r := range i.externalReaders {
err = firstError(err, r.Close())
}
// Close the closer for the current value if one was open.
if i.valueCloser != nil {
err = firstError(err, i.valueCloser.Close())
i.valueCloser = nil
}
const maxKeyBufCacheSize = 4 << 10 // 4 KB
if i.rangeKey != nil {
// Avoid caching the key buf if it is overly large. The constant is
// fairly arbitrary.
if cap(i.rangeKey.buf) >= maxKeyBufCacheSize {
i.rangeKey.buf = nil
}
*i.rangeKey = iteratorRangeKeyState{buf: i.rangeKey.buf}
iterRangeKeyStateAllocPool.Put(i.rangeKey)
i.rangeKey = nil
}
if alloc := i.alloc; alloc != nil {
// Avoid caching the key buf if it is overly large. The constant is fairly
// arbitrary.
if cap(i.keyBuf) >= maxKeyBufCacheSize {
alloc.keyBuf = nil
} else {
alloc.keyBuf = i.keyBuf
}
if cap(i.prefixOrFullSeekKey) >= maxKeyBufCacheSize {
alloc.prefixOrFullSeekKey = nil
} else {
alloc.prefixOrFullSeekKey = i.prefixOrFullSeekKey
}
for j := range i.boundsBuf {
if cap(i.boundsBuf[j]) >= maxKeyBufCacheSize {
alloc.boundsBuf[j] = nil
} else {
alloc.boundsBuf[j] = i.boundsBuf[j]
}
}
*alloc = iterAlloc{
keyBuf: alloc.keyBuf,
boundsBuf: alloc.boundsBuf,
prefixOrFullSeekKey: alloc.prefixOrFullSeekKey,
}
iterAllocPool.Put(alloc)
} else if alloc := i.getIterAlloc; alloc != nil {
if cap(i.keyBuf) >= maxKeyBufCacheSize {
alloc.keyBuf = nil
} else {
alloc.keyBuf = i.keyBuf
}
*alloc = getIterAlloc{
keyBuf: alloc.keyBuf,
}
getIterAllocPool.Put(alloc)
}
return err
}
// SetBounds sets the lower and upper bounds for the iterator. Once SetBounds
// returns, the caller is free to mutate the provided slices.
//
// The iterator will always be invalidated and must be repositioned with a call
// to SeekGE, SeekPrefixGE, SeekLT, First, or Last.
func (i *Iterator) SetBounds(lower, upper []byte) {
// Ensure that the Iterator appears exhausted, regardless of whether we
// actually have to invalidate the internal iterator. Optimizations that
// avoid exhaustion are an internal implementation detail that shouldn't
// leak through the interface. The caller should still call an absolute
// positioning method to reposition the iterator.
i.requiresReposition = true
if ((i.opts.LowerBound == nil) == (lower == nil)) &&
((i.opts.UpperBound == nil) == (upper == nil)) &&
i.equal(i.opts.LowerBound, lower) &&
i.equal(i.opts.UpperBound, upper) {
// Unchanged, noop.
return
}
// Copy the user-provided bounds into an Iterator-owned buffer, and set them
// on i.opts.{Lower,Upper}Bound.
i.saveBounds(lower, upper)
i.iter.SetBounds(i.opts.LowerBound, i.opts.UpperBound)
// If the iterator has an open point iterator that's not currently being
// used, propagate the new bounds to it.
if i.pointIter != nil && !i.opts.pointKeys() {
i.pointIter.SetBounds(i.opts.LowerBound, i.opts.UpperBound)
}
// Even though this is not a positioning operation, the alteration of the
// bounds means we cannot optimize Seeks by using Next.
i.invalidate()
}
func (i *Iterator) saveBounds(lower, upper []byte) {
// Copy the user-provided bounds into an Iterator-owned buffer. We can't
// overwrite the current bounds, because some internal iterators compare old
// and new bounds for optimizations.
buf := i.boundsBuf[i.boundsBufIdx][:0]
if lower != nil {
buf = append(buf, lower...)
i.opts.LowerBound = buf
} else {
i.opts.LowerBound = nil
}
if upper != nil {
buf = append(buf, upper...)
i.opts.UpperBound = buf[len(buf)-len(upper):]
} else {
i.opts.UpperBound = nil
}
i.boundsBuf[i.boundsBufIdx] = buf
i.boundsBufIdx = 1 - i.boundsBufIdx
}
// SetOptions sets new iterator options for the iterator. Note that the lower
// and upper bounds applied here will supersede any bounds set by previous calls
// to SetBounds.
//
// Note that the slices provided in this SetOptions must not be changed by the
// caller until the iterator is closed, or a subsequent SetBounds or SetOptions
// has returned. This is because comparisons between the existing and new bounds
// are sometimes used to optimize seeking. See the extended commentary on
// SetBounds.
//
// If the iterator was created over an indexed mutable batch, the iterator's
// view of the mutable batch is refreshed.
//
// The iterator will always be invalidated and must be repositioned with a call
// to SeekGE, SeekPrefixGE, SeekLT, First, or Last.
//
// If only lower and upper bounds need to be modified, prefer SetBounds.
func (i *Iterator) SetOptions(o *IterOptions) {
if i.externalReaders != nil {
if err := validateExternalIterOpts(o); err != nil {
panic(err)
}
}
// Ensure that the Iterator appears exhausted, regardless of whether we
// actually have to invalidate the internal iterator. Optimizations that
// avoid exhaustion are an internal implementation detail that shouldn't
// leak through the interface. The caller should still call an absolute
// positioning method to reposition the iterator.
i.requiresReposition = true
// Check if global state requires we close all internal iterators.
//
// If the Iterator is in an error state, invalidate the existing iterators
// so that we reconstruct an iterator state from scratch.
//
// If OnlyReadGuaranteedDurable changed, the iterator stacks are incorrect,
// improperly including or excluding memtables. Invalidate them so that
// finishInitializingIter will reconstruct them.
//
// If either the original options or the new options specify a table filter,
// we need to reconstruct the iterator stacks. If they both supply a table
// filter, we can't be certain that it's the same filter since we have no
// mechanism to compare the filter closures.
closeBoth := i.err != nil ||
o.OnlyReadGuaranteedDurable != i.opts.OnlyReadGuaranteedDurable ||
o.TableFilter != nil || i.opts.TableFilter != nil
// If either options specify block property filters for an iterator stack,
// reconstruct it.
if i.pointIter != nil && (closeBoth || len(o.PointKeyFilters) > 0 || len(i.opts.PointKeyFilters) > 0) {
i.err = firstError(i.err, i.pointIter.Close())
i.pointIter = nil
}
if i.rangeKey != nil && (closeBoth || len(o.RangeKeyFilters) > 0 || len(i.opts.RangeKeyFilters) > 0) {
i.err = firstError(i.err, i.rangeKey.rangeKeyIter.Close())
i.rangeKey = nil
}
// If the iterator is backed by a batch that's been mutated, refresh its
// existing point and range-key iterators, and invalidate the iterator to
// prevent seek-using-next optimizations. If we don't yet have a point-key
// iterator or range-key iterator but we require one, it'll be created in
// the slow path that reconstructs the iterator in finishInitializingIter.
if i.batch != nil {
nextBatchSeqNum := (uint64(len(i.batch.data)) | base.InternalKeySeqNumBatch)
if nextBatchSeqNum != i.batchSeqNum {
i.batchSeqNum = nextBatchSeqNum
if i.pointIter != nil {
if i.batch.countRangeDels == 0 {
// No range deletions exist in the batch. We only need to
// update the batchIter's snapshot.
i.batchPointIter.snapshot = nextBatchSeqNum
i.invalidate()
} else if i.batchRangeDelIter.Count() == 0 {
// When we constructed this iterator, there were no
// rangedels in the batch. Iterator construction will have
// excluded the batch rangedel iterator from the point
// iterator stack. We need to reconstruct the point iterator
// to add i.batchRangeDelIter into the iterator stack.
i.err = firstError(i.err, i.pointIter.Close())
i.pointIter = nil
} else {
// There are range deletions in the batch and we already
// have a batch rangedel iterator. We can update the batch
// rangedel iterator in place.
//
// NB: There may or may not be new range deletions. We can't
// tell based on i.batchRangeDelIter.Count(), which is the
// count of fragmented range deletions, NOT the number of
// range deletions written to the batch
// [i.batch.countRangeDels].
i.batchPointIter.snapshot = nextBatchSeqNum
i.batch.initRangeDelIter(&i.opts, &i.batchRangeDelIter, nextBatchSeqNum)
i.invalidate()
}
}
if i.rangeKey != nil && i.batch.countRangeKeys > 0 {
if i.batchRangeKeyIter.Count() == 0 {
// When we constructed this iterator, there were no range
// keys in the batch. Iterator construction will have
// excluded the batch rangekey iterator from the range key
// iterator stack. We need to reconstruct the range key
// iterator to add i.batchRangeKeyIter into the iterator
// stack.
i.err = firstError(i.err, i.rangeKey.rangeKeyIter.Close())
i.rangeKey = nil
} else {
// There are range keys in the batch and we already
// have a batch rangekey iterator. We can update the batch
// rangekey iterator in place.
//
// NB: There may or may not be new range keys. We can't
// tell based on i.batchRangeKeyIter.Count(), which is the
// count of fragmented range keys, NOT the number of
// range keys written to the batch [i.batch.countRangeKeys].
i.batch.initRangeKeyIter(&i.opts, &i.batchRangeKeyIter, nextBatchSeqNum)
i.invalidate()
}
}
}
}
// Reset combinedIterState.initialized in case the iterator key types
// changed. If there's already a range key iterator stack, the combined
// iterator is already initialized. Additionally, if the iterator is not
// configured to include range keys, mark it as initialized to signal that
// lower level iterators should not trigger a switch to combined iteration.
i.lazyCombinedIter.combinedIterState = combinedIterState{
initialized: i.rangeKey != nil || !i.opts.rangeKeys(),
}
boundsEqual := ((i.opts.LowerBound == nil) == (o.LowerBound == nil)) &&
((i.opts.UpperBound == nil) == (o.UpperBound == nil)) &&
i.equal(i.opts.LowerBound, o.LowerBound) &&
i.equal(i.opts.UpperBound, o.UpperBound)
if boundsEqual && o.KeyTypes == i.opts.KeyTypes &&
(i.pointIter != nil || !i.opts.pointKeys()) &&
(i.rangeKey != nil || !i.opts.rangeKeys() || i.opts.KeyTypes == IterKeyTypePointsAndRanges) &&
i.equal(o.RangeKeyMasking.Suffix, i.opts.RangeKeyMasking.Suffix) &&
o.UseL6Filters == i.opts.UseL6Filters {
// The options are identical, so we can likely use the fast path. In
// addition to all the above constraints, we cannot use the fast path if
// configured to perform lazy combined iteration but an indexed batch
// used by the iterator now contains range keys. Lazy combined iteration
// is not compatible with batch range keys because we always need to
// merge the batch's range keys into iteration.
if i.rangeKey != nil || !i.opts.rangeKeys() || i.batch == nil || i.batch.countRangeKeys == 0 {
// Fast path. This preserves the Seek-using-Next optimizations as
// long as the iterator wasn't already invalidated up above.
return
}
}
// Slow path.
// The options changed. Save the new ones to i.opts.
if boundsEqual {
// Copying the options into i.opts will overwrite LowerBound and
// UpperBound fields with the user-provided slices. We need to hold on
// to the Pebble-owned slices, so save them and re-set them after the
// copy.
lower, upper := i.opts.LowerBound, i.opts.UpperBound
i.opts = *o
i.opts.LowerBound, i.opts.UpperBound = lower, upper
} else {
i.opts = *o
i.saveBounds(o.LowerBound, o.UpperBound)
// Propagate the changed bounds to the existing point iterator.
// NB: We propagate i.opts.{Lower,Upper}Bound, not o.{Lower,Upper}Bound
// because i.opts now point to buffers owned by Pebble.
if i.pointIter != nil {
i.pointIter.SetBounds(i.opts.LowerBound, i.opts.UpperBound)
}
}
// Even though this is not a positioning operation, the invalidation of the
// iterator stack means we cannot optimize Seeks by using Next.
i.invalidate()
// Iterators created through NewExternalIter have a different iterator
// initialization process.
if i.externalReaders != nil {
finishInitializingExternal(i)
return
}
finishInitializingIter(i.alloc)
}
func (i *Iterator) invalidate() {
i.lastPositioningOp = unknownLastPositionOp
i.hasPrefix = false
i.iterKey = nil
i.iterValue = nil
i.err = nil
// This switch statement isn't necessary for correctness since callers
// should call a repositioning method. We could have arbitrarily set i.pos
// to one of the values. But it results in more intuitive behavior in
// tests, which do not always reposition.
switch i.pos {
case iterPosCurForward, iterPosNext, iterPosCurForwardPaused:
i.pos = iterPosCurForward
case iterPosCurReverse, iterPosPrev, iterPosCurReversePaused:
i.pos = iterPosCurReverse
}
i.iterValidityState = IterExhausted
}
// Metrics returns per-iterator metrics.
func (i *Iterator) Metrics() IteratorMetrics {
m := IteratorMetrics{
ReadAmp: 1,
}
if mi, ok := i.iter.(*mergingIter); ok {
m.ReadAmp = len(mi.levels)
}
return m
}
// ResetStats resets the stats to 0.
func (i *Iterator) ResetStats() {
i.stats = IteratorStats{}
i.iter.ResetStats()
}
// Stats returns the current stats.
func (i *Iterator) Stats() IteratorStats {
stats := i.stats
stats.InternalStats = i.iter.Stats()
return stats
}
// CloneOptions configures an iterator constructed through Iterator.Clone.
type CloneOptions struct {
// IterOptions, if non-nil, define the iterator options to configure a
// cloned iterator. If nil, the clone adopts the same IterOptions as the
// iterator being cloned.
IterOptions *IterOptions
// RefreshBatchView may be set to true when cloning an Iterator over an
// indexed batch. When false, the clone adopts the same (possibly stale)
// view of the indexed batch as the cloned Iterator. When true, the clone is
// constructed with a refreshed view of the batch, observing all of the
// batch's mutations at the time of the Clone. If the cloned iterator was
// not constructed to read over an indexed batch, RefreshVatchView has no
// effect.
RefreshBatchView bool
}
// Clone creates a new Iterator over the same underlying data, i.e., over the
// same {batch, memtables, sstables}). The resulting iterator is not positioned.
// It starts with the same IterOptions, unless opts.IterOptions is set.
//
// When called on an Iterator over an indexed batch, the clone's visibility of
// the indexed batch is determined by CloneOptions.RefreshBatchView. If false,
// the clone inherits the iterator's current (possibly stale) view of the batch,
// and callers may call SetOptions to subsequently refresh the clone's view to
// include all batch mutations. If true, the clone is constructed with a
// complete view of the indexed batch's mutations at the time of the Clone.
//
// Callers can use Clone if they need multiple iterators that need to see
// exactly the same underlying state of the DB. This should not be used to
// extend the lifetime of the data backing the original Iterator since that
// will cause an increase in memory and disk usage (use NewSnapshot for that
// purpose).
func (i *Iterator) Clone(opts CloneOptions) (*Iterator, error) {
if opts.IterOptions == nil {
opts.IterOptions = &i.opts
}
readState := i.readState
if readState == nil {
return nil, errors.Errorf("cannot Clone a closed Iterator")
}
// i is already holding a ref, so there is no race with unref here.
readState.ref()
// Bundle various structures under a single umbrella in order to allocate
// them together.
buf := iterAllocPool.Get().(*iterAlloc)
dbi := &buf.dbi
*dbi = Iterator{
opts: *opts.IterOptions,
alloc: buf,
cmp: i.cmp,
equal: i.equal,
merge: i.merge,
split: i.split,
readState: readState,
keyBuf: buf.keyBuf,
prefixOrFullSeekKey: buf.prefixOrFullSeekKey,
boundsBuf: buf.boundsBuf,
batch: i.batch,
batchSeqNum: i.batchSeqNum,
newIters: i.newIters,
newIterRangeKey: i.newIterRangeKey,
seqNum: i.seqNum,
}
dbi.saveBounds(dbi.opts.LowerBound, dbi.opts.UpperBound)
// If the caller requested the clone have a current view of the indexed
// batch, set the clone's batch sequence number appropriately.
if i.batch != nil && opts.RefreshBatchView {
dbi.batchSeqNum = (uint64(len(i.batch.data)) | base.InternalKeySeqNumBatch)
}
return finishInitializingIter(buf), nil
}
func (stats *IteratorStats) String() string {
return redact.StringWithoutMarkers(stats)
}
// SafeFormat implements the redact.SafeFormatter interface.
func (stats *IteratorStats) SafeFormat(s redact.SafePrinter, verb rune) {
for i := range stats.ForwardStepCount {
switch IteratorStatsKind(i) {
case InterfaceCall:
s.SafeString("(interface (dir, seek, step): ")
case InternalIterCall:
s.SafeString(", (internal (dir, seek, step): ")
}
s.Printf("(fwd, %d, %d), (rev, %d, %d))",
redact.Safe(stats.ForwardSeekCount[i]), redact.Safe(stats.ForwardStepCount[i]),
redact.Safe(stats.ReverseSeekCount[i]), redact.Safe(stats.ReverseStepCount[i]))
}
if stats.InternalStats != (InternalIteratorStats{}) {
s.SafeString(",\n(internal-stats: ")
s.Printf("(block-bytes: (total %s, cached %s)), "+
"(points: (count %s, key-bytes %s, value-bytes %s, tombstoned: %s))",
humanize.IEC.Uint64(stats.InternalStats.BlockBytes),
humanize.IEC.Uint64(stats.InternalStats.BlockBytesInCache),
humanize.SI.Uint64(stats.InternalStats.PointCount),
humanize.SI.Uint64(stats.InternalStats.KeyBytes),
humanize.SI.Uint64(stats.InternalStats.ValueBytes),
humanize.SI.Uint64(stats.InternalStats.PointsCoveredByRangeTombstones),
)
}
}