Repository: cockroachdb/pebble (go)

Issue:
metamorphic: shared cache error on high-CPU machines
The number of secondary cache shards is a function of the number of CPUs, and the metamorphic tests use a fixed secondary cache size when enabled. On high-CPU machines, this can result in shared cache configuration validation erroring the metamorphic test out because the secondary cache size is too small for the configured number of shards.

```
        --- FAIL: TestMeta (0.02s)
            meta.go:471:
                        Error Trace:    meta.go:471
                                                                meta_test.go:66
                        Error:          Received unexpected error:
                                        pebble: could not open remote object cache: cache size 33554432 lower than min 50331648
                                        (1) attached stack trace
                                          -- stack trace:
                                          | github.com/cockroachdb/pebble/objstorage/objstorageprovider.(*provider).remoteInit
                                          |     /home/jackson/go/src/github.com/cockroachdb/pebble/objstorage/objstorageprovider/remote.go:90
                                          | [...repeated from below...]
                                        Wraps: (2) pebble: could not open remote object cache
                                        Wraps: (3) attached stack trace
                                          -- stack trace:
                                          | github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedcache.Open
                                          |     /home/jackson/go/src/github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedcache/shared_cache.go:135
                                          | github.com/cockroachdb/pebble/objstorage/objstorageprovider.(*provider).remoteInit
                                          |     /home/jackson/go/src/github.com/cockroachdb/pebble/objstorage/objstorageprovider/remote.go:87
                                          | github.com/cockroachdb/pebble/objstorage/objstorageprovider.open
                                          |     /home/jackson/go/src/github.com/cockroachdb/pebble/objstorage/objstorageprovider/provider.go:185
                                          | github.com/cockroachdb/pebble/objstorage/objstorageprovider.Open
                                          |     /home/jackson/go/src/github.com/cockroachdb/pebble/objstorage/objstorageprovider/provider.go:149
                                          | github.com/cockroachdb/pebble.Open
                                          |     /home/jackson/go/src/github.com/cockroachdb/pebble/open.go:318
                                          | github.com/cockroachdb/pebble/metamorphic.(*test).init.func12
                                          |     /home/jackson/go/src/github.com/cockroachdb/pebble/metamorphic/test.go:133
                                          | github.com/cockroachdb/pebble/metamorphic.withRetries
                                          |     /home/jackson/go/src/github.com/cockroachdb/pebble/metamorphic/retryable.go:18
                                          | github.com/cockroachdb/pebble/metamorphic.(*test).init
                                          |     /home/jackson/go/src/github.com/cockroachdb/pebble/metamorphic/test.go:132
                                          | github.com/cockroachdb/pebble/metamorphic.RunOnce
                                          |     /home/jackson/go/src/github.com/cockroachdb/pebble/metamorphic/meta.go:471
                                          | github.com/cockroachdb/pebble/internal/metamorphic.TestMeta
                                          |     /home/jackson/go/src/github.com/cockroachdb/pebble/internal/metamorphic/meta_test.go:66
                                          | testing.tRunner
                                          |     /snap/go/10339/src/testing/testing.go:1595
                                          | runtime.goexit
                                          |     /snap/go/10339/src/runtime/asm_amd64.s:1650
                                        Wraps: (4) cache size 33554432 lower than min 50331648
                                        Error types: (1) *withstack.withStack (2) *errutil.withPrefix (3) *withstack.withStack (4) *errutil.leafError
                        Test:           TestMeta
```

cc @RaduBerinde, @joshimhoff 

Pull request:
sharedcache: tolerate small sizes
Instead of erroring out when the cache size is too small given the number of shards, we up the size as needed.

Fixes #2935.

Patch:
diff --git a/iterator.go b/iterator.go
index 8e74c177..df1a0b93 100644
--- a/iterator.go
+++ b/iterator.go
@@ -550,6 +550,21 @@ func (i *Iterator) findNextEntry(limit []byte) {
 			return
 		}
 
+		// If the user has configured a SkipPoint function, invoke it to see
+		// whether we should skip over the current user key.
+		if i.opts.SkipPoint != nil && key.Kind() != InternalKeyKindRangeKeySet && i.opts.SkipPoint(i.iterKey.UserKey) {
+			// NB: We could call nextUserKey, but in some cases the SkipPoint
+			// predicate function might be cheaper than nextUserKey's key copy
+			// and key comparison. This should be the case for MVCC suffix
+			// comparisons, for example. In the future, we could expand the
+			// SkipPoint interface to give the implementor more control over
+			// whether we skip over just the internal key, the user key, or even
+			// the key prefix.
+			i.stats.ForwardStepCount[InternalIterCall]++
+			i.iterKey, i.iterValue = i.iter.Next()
+			continue
+		}
+
 		switch key.Kind() {
 		case InternalKeyKindRangeKeySet:
 			// Save the current key.
@@ -911,6 +926,26 @@ func (i *Iterator) findPrevEntry(limit []byte) {
 			}
 		}
 
+		// If the user has configured a SkipPoint function, invoke it to see
+		// whether we should skip over the current user key.
+		if i.opts.SkipPoint != nil && key.Kind() != InternalKeyKindRangeKeySet && i.opts.SkipPoint(key.UserKey) {
+			// NB: We could call prevUserKey, but in some cases the SkipPoint
+			// predicate function might be cheaper than prevUserKey's key copy
+			// and key comparison. This should be the case for MVCC suffix
+			// comparisons, for example. In the future, we could expand the
+			// SkipPoint interface to give the implementor more control over
+			// whether we skip over just the internal key, the user key, or even
+			// the key prefix.
+			i.stats.ReverseStepCount[InternalIterCall]++
+			i.iterKey, i.iterValue = i.iter.Prev()
+			if limit != nil && i.iterKey != nil && i.cmp(limit, i.iterKey.UserKey) > 0 && !i.rangeKeyWithinLimit(limit) {
+				i.iterValidityState = IterAtLimit
+				i.pos = iterPosCurReversePaused
+				return
+			}
+			continue
+		}
+
 		switch key.Kind() {
 		case InternalKeyKindRangeKeySet:
 			// Range key start boundary markers are interleaved with the maximum
@@ -948,12 +983,12 @@ func (i *Iterator) findPrevEntry(limit []byte) {
 			// 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
+			// 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.
+			// other than the firstLoopIter and SkipPoint cases 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
@@ -2428,7 +2463,8 @@ func (i *Iterator) SetOptions(o *IterOptions) {
 	// 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 ||
-		o.RangeKeyMasking.Filter != nil || i.opts.RangeKeyMasking.Filter != nil) {
+		o.RangeKeyMasking.Filter != nil || i.opts.RangeKeyMasking.Filter != nil || o.SkipPoint != nil ||
+		i.opts.SkipPoint != nil) {
 		i.err = firstError(i.err, i.pointIter.Close())
 		i.pointIter = nil
 	}
diff --git a/metamorphic/ops.go b/metamorphic/ops.go
index eb79c852..1ab5216c 100644
--- a/metamorphic/ops.go
+++ b/metamorphic/ops.go
@@ -18,6 +18,7 @@ import (
 	"github.com/cockroachdb/pebble/internal/base"
 	"github.com/cockroachdb/pebble/internal/keyspan"
 	"github.com/cockroachdb/pebble/internal/private"
+	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/cockroachdb/pebble/objstorage/objstorageprovider"
 	"github.com/cockroachdb/pebble/sstable"
 	"github.com/cockroachdb/pebble/vfs"
@@ -711,7 +712,7 @@ func (o *newIterOp) run(t *test, h historyRecorder) {
 		// close this iter and retry NewIter
 		_ = i.Close()
 	}
-	t.setIter(o.iterID, i, o.filterMin, o.filterMax)
+	t.setIter(o.iterID, i)
 
 	// Trash the bounds to ensure that Pebble doesn't rely on the stability of
 	// the user-provided bounds.
@@ -763,13 +764,7 @@ func (o *newIterUsingCloneOp) run(t *test, h historyRecorder) {
 	if err != nil {
 		panic(err)
 	}
-	filterMin, filterMax := o.filterMin, o.filterMax
-	if cloneOpts.IterOptions == nil {
-		// We're adopting the same block property filters as iter, so we need to
-		// adopt the same run-time filters to ensure determinism.
-		filterMin, filterMax = iter.filterMin, iter.filterMax
-	}
-	t.setIter(o.iterID, i, filterMin, filterMax)
+	t.setIter(o.iterID, i)
 	h.Recordf("%s // %v", o, i.Error())
 }
 
@@ -853,9 +848,6 @@ func (o *iterSetOptionsOp) run(t *test, h historyRecorder) {
 	rand.Read(opts.LowerBound[:])
 	rand.Read(opts.UpperBound[:])
 
-	// Adjust the iterator's filters.
-	i.filterMin, i.filterMax = o.filterMin, o.filterMax
-
 	h.Recordf("%s // %v", o, i.Error())
 }
 
@@ -893,6 +885,22 @@ func iterOptions(o iterOpts) *pebble.IterOptions {
 		opts.PointKeyFilters = []pebble.BlockPropertyFilter{
 			sstable.NewTestKeysBlockPropertyFilter(o.filterMin, o.filterMax),
 		}
+		// Enforce the timestamp bounds in SkipPoint, so that the iterator never
+		// returns a key outside the filterMin, filterMax bounds. This provides
+		// deterministic iteration.
+		opts.SkipPoint = func(k []byte) (skip bool) {
+			n := testkeys.Comparer.Split(k)
+			if n == len(k) {
+				// No suffix, don't skip it.
+				return false
+			}
+			v, err := testkeys.ParseSuffix(k[n:])
+			if err != nil {
+				panic(err)
+			}
+			ts := uint64(v)
+			return ts < o.filterMin || ts >= o.filterMax
+		}
 	}
 	return opts
 }
diff --git a/metamorphic/retryable.go b/metamorphic/retryable.go
index a71f0c43..2e244366 100644
--- a/metamorphic/retryable.go
+++ b/metamorphic/retryable.go
@@ -5,11 +5,8 @@
 package metamorphic
 
 import (
-	"bytes"
-
 	"github.com/cockroachdb/errors"
 	"github.com/cockroachdb/pebble"
-	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/cockroachdb/pebble/vfs/errorfs"
 )
 
@@ -31,68 +28,6 @@ func withRetries(fn func() error) error {
 type retryableIter struct {
 	iter    *pebble.Iterator
 	lastKey []byte
-
-	// When filterMax is >0, this iterator filters out keys with suffixes
-	// outside of the range [filterMin, filterMax). Keys without suffixes are
-	// surfaced. This is used to ensure determinism regardless of whether
-	// block-property filters filter keys or not.
-	filterMin, filterMax uint64
-
-	// rangeKeyChangeGuess is only used if the iterator has a filter set. A single
-	// operation on the retryableIter may result in many operations on
-	// retryableIter.iter if we need to skip filtered keys. Thus, the value of
-	// retryableIter.iter.RangeKeyChanged() will not necessarily indicate if the
-	// range key actually changed.
-	//
-	// Since one call to a positioning operation may lead to multiple
-	// positioning operations, we set rangeKeyChangeGuess to false, iff every single
-	// positioning operation returned iter.RangeKeyChanged() == false.
-	//
-	// rangeKeyChangeGuess == true implies that at least one of the many iterator
-	// operations returned RangeKeyChanged to true, but we may have false
-	// positives. We can't assume that the range key actually changed if
-	// iter.RangeKeyChanged() returns true after one of the positioning
-	// operations. Consider a db with two range keys, which are also in the same
-	// block, a-f, g-h where the iterator's filter excludes g-h. If the iterator
-	// is positioned on a-f, then a call to SeekLT(z), will position the iterator
-	// over g-h, but the retryableIter will call Prev and the iterator will be
-	// positioned back over a-f. In this case the range key hasn't changed, but
-	// one of the positioning operations will return iter.RangeKeyChanged() ==
-	// true.
-	rangeKeyChangeGuess bool
-
-	// rangeKeyChanged is the true accurate value of whether the range key has
-	// changed from the perspective of a client of the retryableIter. It is used
-	// to determine if rangeKeyChangeGuess is a false positive. It is computed
-	// by comparing the range key at the current position with the range key
-	// at the previous position.
-	rangeKeyChanged bool
-
-	// When a filter is set on the iterator, one positioning op from the
-	// perspective of a client of the retryableIter, may result in multiple
-	// intermediary positioning ops. This bool is set if the current positioning
-	// op is intermediate.
-	intermediatePosition bool
-
-	rkeyBuff []byte
-}
-
-func (i *retryableIter) shouldFilter() bool {
-	if i.filterMax == 0 {
-		return false
-	}
-	k := i.iter.Key()
-	n := testkeys.Comparer.Split(k)
-	if n == len(k) {
-		// No suffix, don't filter it.
-		return false
-	}
-	v, err := testkeys.ParseSuffix(k[n:])
-	if err != nil {
-		panic(err)
-	}
-	ts := uint64(v)
-	return ts < i.filterMin || ts >= i.filterMax
 }
 
 func (i *retryableIter) needRetry() bool {
@@ -124,61 +59,10 @@ func (i *retryableIter) Error() error {
 	return i.iter.Error()
 }
 
-// A call to an iterator positioning function may result in sub calls to other
-// iterator positioning functions. We need to run some code in the top level
-// call, so we use withPosition to reduce code duplication in the positioning
-// functions.
-func (i *retryableIter) withPosition(fn func()) {
-	// For the top level op, i.intermediatePosition must always be false.
-	intermediate := i.intermediatePosition
-	// Any subcalls to positioning ops will be intermediate.
-	i.intermediatePosition = true
-	defer func() {
-		i.intermediatePosition = intermediate
-	}()
-
-	if !intermediate {
-		// Clear out the previous value stored in the buff.
-		i.rkeyBuff = i.rkeyBuff[:0]
-		if _, hasRange := i.iter.HasPointAndRange(); hasRange {
-			// This is a top level positioning op. We should determine if the iter
-			// is positioned over a range key to later determine if the range key
-			// changed.
-			startTmp, _ := i.iter.RangeBounds()
-			i.rkeyBuff = append(i.rkeyBuff, startTmp...)
-
-		}
-		// Set this to false. Any positioning op can set this to true.
-		i.rangeKeyChangeGuess = false
-	}
-
-	fn()
-
-	if !intermediate {
-		// Check if the range key changed.
-		var newStartKey []byte
-		if _, hasRange := i.iter.HasPointAndRange(); hasRange {
-			newStartKey, _ = i.iter.RangeBounds()
-		}
-
-		i.rangeKeyChanged = !bytes.Equal(newStartKey, i.rkeyBuff)
-	}
-}
-
-func (i *retryableIter) updateRangeKeyChangedGuess() {
-	i.rangeKeyChangeGuess = i.rangeKeyChangeGuess || i.iter.RangeKeyChanged()
-}
-
 func (i *retryableIter) First() bool {
 	var valid bool
-	i.withPosition(func() {
-		i.withRetry(func() {
-			valid = i.iter.First()
-		})
-		i.updateRangeKeyChangedGuess()
-		if valid && i.shouldFilter() {
-			valid = i.Next()
-		}
+	i.withRetry(func() {
+		valid = i.iter.First()
 	})
 	return valid
 }
@@ -188,18 +72,7 @@ func (i *retryableIter) Key() []byte {
 }
 
 func (i *retryableIter) RangeKeyChanged() bool {
-	if i.filterMax == 0 {
-		return i.iter.RangeKeyChanged()
-	}
-
-	if !i.rangeKeyChangeGuess {
-		// false negatives shouldn't be possible so just return.
-		return false
-	}
-
-	// i.rangeKeyChangeGuess is true. This may be a false positive, so just
-	// return i.rangeKeyChanged which will always be correct.
-	return i.rangeKeyChanged
+	return i.iter.RangeKeyChanged()
 }
 
 func (i *retryableIter) HasPointAndRange() (bool, bool) {
@@ -216,42 +89,22 @@ func (i *retryableIter) RangeKeys() []pebble.RangeKeyData {
 
 func (i *retryableIter) Last() bool {
 	var valid bool
-	i.withPosition(func() {
-		i.withRetry(func() { valid = i.iter.Last() })
-		i.updateRangeKeyChangedGuess()
-		if valid && i.shouldFilter() {
-			valid = i.Prev()
-		}
-	})
+	i.withRetry(func() { valid = i.iter.Last() })
 	return valid
 }
 
 func (i *retryableIter) Next() bool {
 	var valid bool
-	i.withPosition(func() {
-		i.withRetry(func() {
-			valid = i.iter.Next()
-			i.updateRangeKeyChangedGuess()
-			for valid && i.shouldFilter() {
-				valid = i.iter.Next()
-				i.updateRangeKeyChangedGuess()
-			}
-		})
+	i.withRetry(func() {
+		valid = i.iter.Next()
 	})
 	return valid
 }
 
 func (i *retryableIter) NextWithLimit(limit []byte) pebble.IterValidityState {
 	var validity pebble.IterValidityState
-	i.withPosition(func() {
-		i.withRetry(func() {
-			validity = i.iter.NextWithLimit(limit)
-			i.updateRangeKeyChangedGuess()
-			for validity == pebble.IterValid && i.shouldFilter() {
-				validity = i.iter.NextWithLimit(limit)
-				i.updateRangeKeyChangedGuess()
-			}
-		})
+	i.withRetry(func() {
+		validity = i.iter.NextWithLimit(limit)
 	})
 	return validity
 
@@ -259,106 +112,55 @@ func (i *retryableIter) NextWithLimit(limit []byte) pebble.IterValidityState {
 
 func (i *retryableIter) NextPrefix() bool {
 	var valid bool
-	i.withPosition(func() {
-		i.withRetry(func() {
-			valid = i.iter.NextPrefix()
-			i.updateRangeKeyChangedGuess()
-			for valid && i.shouldFilter() {
-				valid = i.iter.Next()
-				i.updateRangeKeyChangedGuess()
-			}
-		})
+	i.withRetry(func() {
+		valid = i.iter.NextPrefix()
 	})
 	return valid
 }
 
 func (i *retryableIter) Prev() bool {
 	var valid bool
-	i.withPosition(func() {
-		i.withRetry(func() {
-			valid = i.iter.Prev()
-			i.updateRangeKeyChangedGuess()
-			for valid && i.shouldFilter() {
-				valid = i.iter.Prev()
-				i.updateRangeKeyChangedGuess()
-			}
-		})
+	i.withRetry(func() {
+		valid = i.iter.Prev()
 	})
 	return valid
 }
 
 func (i *retryableIter) PrevWithLimit(limit []byte) pebble.IterValidityState {
 	var validity pebble.IterValidityState
-	i.withPosition(func() {
-		i.withRetry(func() {
-			validity = i.iter.PrevWithLimit(limit)
-			i.updateRangeKeyChangedGuess()
-			for validity == pebble.IterValid && i.shouldFilter() {
-				validity = i.iter.PrevWithLimit(limit)
-				i.updateRangeKeyChangedGuess()
-			}
-		})
+	i.withRetry(func() {
+		validity = i.iter.PrevWithLimit(limit)
 	})
 	return validity
 }
 
 func (i *retryableIter) SeekGE(key []byte) bool {
 	var valid bool
-	i.withPosition(func() {
-		i.withRetry(func() { valid = i.iter.SeekGE(key) })
-		i.updateRangeKeyChangedGuess()
-		if valid && i.shouldFilter() {
-			valid = i.Next()
-		}
-	})
+	i.withRetry(func() { valid = i.iter.SeekGE(key) })
 	return valid
 }
 
 func (i *retryableIter) SeekGEWithLimit(key []byte, limit []byte) pebble.IterValidityState {
 	var validity pebble.IterValidityState
-	i.withPosition(func() {
-		i.withRetry(func() { validity = i.iter.SeekGEWithLimit(key, limit) })
-		i.updateRangeKeyChangedGuess()
-		if validity == pebble.IterValid && i.shouldFilter() {
-			validity = i.NextWithLimit(limit)
-		}
-	})
+	i.withRetry(func() { validity = i.iter.SeekGEWithLimit(key, limit) })
 	return validity
 }
 
 func (i *retryableIter) SeekLT(key []byte) bool {
 	var valid bool
-	i.withPosition(func() {
-		i.withRetry(func() { valid = i.iter.SeekLT(key) })
-		i.updateRangeKeyChangedGuess()
-		if valid && i.shouldFilter() {
-			valid = i.Prev()
-		}
-	})
+	i.withRetry(func() { valid = i.iter.SeekLT(key) })
 	return valid
 }
 
 func (i *retryableIter) SeekLTWithLimit(key []byte, limit []byte) pebble.IterValidityState {
 	var validity pebble.IterValidityState
-	i.withPosition(func() {
-		i.withRetry(func() { validity = i.iter.SeekLTWithLimit(key, limit) })
-		i.updateRangeKeyChangedGuess()
-		if validity == pebble.IterValid && i.shouldFilter() {
-			validity = i.PrevWithLimit(limit)
-		}
-	})
+	i.withRetry(func() { validity = i.iter.SeekLTWithLimit(key, limit) })
 	return validity
 }
 
 func (i *retryableIter) SeekPrefixGE(key []byte) bool {
 	var valid bool
-	i.withPosition(func() {
-		i.withRetry(func() { valid = i.iter.SeekPrefixGE(key) })
-		i.updateRangeKeyChangedGuess()
-		if valid && i.shouldFilter() {
-			valid = i.Next()
-		}
-	})
+	i.withRetry(func() { valid = i.iter.SeekPrefixGE(key) })
 	return valid
 }
 
diff --git a/objstorage/objstorageprovider/sharedcache/shared_cache.go b/objstorage/objstorageprovider/sharedcache/shared_cache.go
index 84f79350..6d6409e9 100644
--- a/objstorage/objstorageprovider/sharedcache/shared_cache.go
+++ b/objstorage/objstorageprovider/sharedcache/shared_cache.go
@@ -130,9 +130,10 @@ func Open(
 	sizeBytes int64,
 	numShards int,
 ) (*Cache, error) {
-	min := shardingBlockSize * int64(numShards)
-	if sizeBytes < min {
-		return nil, errors.Errorf("cache size %d lower than min %d", sizeBytes, min)
+	if minSize := shardingBlockSize * int64(numShards); sizeBytes < minSize {
+		// Up the size so that we have one block per shard. In practice, this should
+		// only happen in tests.
+		sizeBytes = minSize
 	}
 
 	c := &Cache{
diff --git a/options.go b/options.go
index 7f1da092..75281ce0 100644
--- a/options.go
+++ b/options.go
@@ -119,6 +119,16 @@ type IterOptions struct {
 	// false to skip scanning. This function must be thread-safe since the same
 	// function can be used by multiple iterators, if the iterator is cloned.
 	TableFilter func(userProps map[string]string) bool
+	// SkipPoint may be used to skip over point keys that don't match an
+	// arbitrary predicate during iteration. If set, the Iterator invokes
+	// SkipPoint for keys encountered. If SkipPoint returns true, the iterator
+	// will skip the key without yielding it to the iterator operation in
+	// progress.
+	//
+	// SkipPoint must be a pure function and always return the same result when
+	// provided the same arguments. The iterator may call SkipPoint multiple
+	// times for the same user key.
+	SkipPoint func(userKey []byte) bool
 	// PointKeyFilters can be used to avoid scanning tables and blocks in tables
 	// when iterating over point keys. This slice represents an intersection
 	// across all filters, i.e., all filters must indicate that the block is


Test patch:
diff --git a/metamorphic/test.go b/metamorphic/test.go
index f1ec3794..9a167cc1 100644
--- a/metamorphic/test.go
+++ b/metamorphic/test.go
@@ -260,15 +260,13 @@ func (t *test) setBatch(id objID, b *pebble.Batch) {
 	t.batches[id.slot()] = b
 }
 
-func (t *test) setIter(id objID, i *pebble.Iterator, filterMin, filterMax uint64) {
+func (t *test) setIter(id objID, i *pebble.Iterator) {
 	if id.tag() != iterTag {
 		panic(fmt.Sprintf("invalid iter ID: %s", id))
 	}
 	t.iters[id.slot()] = &retryableIter{
-		iter:      i,
-		lastKey:   nil,
-		filterMin: filterMin,
-		filterMax: filterMax,
+		iter:    i,
+		lastKey: nil,
 	}
 }