Skip to content

Commit

Permalink
internal/keyspan: obey Seek invariants in filteringIter
Browse files Browse the repository at this point in the history
Previously if filteringIter's FilterFunc mutated the passed-in span
to no longer be a valid return value for a SeekLT or SeekGE call,
we would still return that span even though it could be >=  the seek
key (for SeekLT), or less than it (for SeekGE).

This change updates filteringIter to guard for this case before
returning from a seek call.

Found with cockroachdb#3044.

Informs cockroachdb/cockroach#113973, cockroachdb/cockroach#114056.
  • Loading branch information
itsbilal committed Nov 8, 2023
1 parent 81d9a4f commit 1c6b496
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 8 deletions.
25 changes: 21 additions & 4 deletions internal/keyspan/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

package keyspan

import "github.com/cockroachdb/pebble/internal/base"

// FilterFunc defines a transform from the input Span into the output Span. The
// function returns true if the Span should be returned by the iterator, and
// false if the Span should be skipped. The FilterFunc is permitted to mutate
Expand All @@ -21,6 +23,7 @@ type FilterFunc func(in *Span, out *Span) (keep bool)
type filteringIter struct {
iter FragmentIterator
filterFn FilterFunc
cmp base.Compare

// span is a mutable Span passed to the filterFn. The filterFn is free to
// mutate this Span. The slice of Keys in the Span is reused with every call
Expand All @@ -32,18 +35,32 @@ var _ FragmentIterator = (*filteringIter)(nil)

// Filter returns a new filteringIter that will filter the Spans from the
// provided child iterator using the provided FilterFunc.
func Filter(iter FragmentIterator, filter FilterFunc) FragmentIterator {
return &filteringIter{iter: iter, filterFn: filter}
func Filter(iter FragmentIterator, filter FilterFunc, cmp base.Compare) FragmentIterator {
return &filteringIter{iter: iter, filterFn: filter, cmp: cmp}
}

// SeekGE implements FragmentIterator.
func (i *filteringIter) SeekGE(key []byte) *Span {
return i.filter(i.iter.SeekGE(key), +1)
span := i.filter(i.iter.SeekGE(key), +1)
// i.filter could return a span that's less than key, _if_ the filterFunc
// (which has no knowledge of the seek key) mutated the span to end at a key
// less than or equal to `key`. Detect this case and next/invalidate the iter.
if span != nil && i.cmp(span.End, key) <= 0 {
return i.Next()
}
return span
}

// SeekLT implements FragmentIterator.
func (i *filteringIter) SeekLT(key []byte) *Span {
return i.filter(i.iter.SeekLT(key), -1)
span := i.filter(i.iter.SeekLT(key), -1)
// i.filter could return a span that's >= key, _if_ the filterFunc (which has
// no knowledge of the seek key) mutated the span to start at a key greater
// than or equal to `key`. Detect this case and prev/invalidate the iter.
if span != nil && i.cmp(span.Start, key) >= 0 {
return i.Prev()
}
return span
}

// First implements FragmentIterator.
Expand Down
2 changes: 1 addition & 1 deletion internal/keyspan/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func TestFilteringIter(t *testing.T) {
}
}
innerIter := NewIter(cmp, spans)
iter := Filter(innerIter, filter)
iter := Filter(innerIter, filter, cmp)
defer iter.Close()
s := runFragmentIteratorCmd(iter, td.Input, nil)
return s
Expand Down
6 changes: 4 additions & 2 deletions internal/keyspan/truncate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

package keyspan

import "github.com/cockroachdb/pebble/internal/base"
import (
"github.com/cockroachdb/pebble/internal/base"
)

// Truncate creates a new iterator where every span in the supplied iterator is
// truncated to be contained within the range [lower, upper). If start and end
Expand Down Expand Up @@ -69,5 +71,5 @@ func Truncate(
}

return !out.Empty() && cmp(out.Start, out.End) < 0
})
}, cmp)
}
2 changes: 1 addition & 1 deletion table_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,7 @@ func newCombinedDeletionKeyspanIter(
out.Keys = append(out.Keys, k)
}
return len(out.Keys) > 0
})
}, comparer.Compare)
dIter := &keyspan.DefragmentingIter{}
dIter.Init(comparer, iter, equal, reducer, new(keyspan.DefragmentingBuffers))
iter = dIter
Expand Down

0 comments on commit 1c6b496

Please sign in to comment.