github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/internal/metamorphic/retryable.go (about)

     1  // Copyright 2020 The LevelDB-Go and Pebble Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  package metamorphic
     6  
     7  import (
     8  	"bytes"
     9  
    10  	"github.com/cockroachdb/errors"
    11  	"github.com/zuoyebang/bitalostable"
    12  	"github.com/zuoyebang/bitalostable/internal/errorfs"
    13  	"github.com/zuoyebang/bitalostable/internal/testkeys"
    14  )
    15  
    16  // withRetries executes fn, retrying it whenever an errorfs.ErrInjected error
    17  // is returned.  It returns the first nil or non-errorfs.ErrInjected error
    18  // returned by fn.
    19  func withRetries(fn func() error) error {
    20  	for {
    21  		if err := fn(); !errors.Is(err, errorfs.ErrInjected) {
    22  			return err
    23  		}
    24  	}
    25  }
    26  
    27  // retryableIter holds an iterator and the state necessary to reset it to its
    28  // state after the last successful operation. This allows us to retry failed
    29  // iterator operations by running them again on a non-error iterator with the
    30  // same pre-operation state.
    31  type retryableIter struct {
    32  	iter    *bitalostable.Iterator
    33  	lastKey []byte
    34  
    35  	// When filterMax is >0, this iterator filters out keys with suffixes
    36  	// outside of the range [filterMin, filterMax). Keys without suffixes are
    37  	// surfaced. This is used to ensure determinism regardless of whether
    38  	// block-property filters filter keys or not.
    39  	filterMin, filterMax uint64
    40  
    41  	// rangeKeyChangeGuess is only used if the iterator has a filter set. A single
    42  	// operation on the retryableIter may result in many operations on
    43  	// retryableIter.iter if we need to skip filtered keys. Thus, the value of
    44  	// retryableIter.iter.RangeKeyChanged() will not necessarily indicate if the
    45  	// range key actually changed.
    46  	//
    47  	// Since one call to a positioning operation may lead to multiple
    48  	// positioning operations, we set rangeKeyChangeGuess to false, iff every single
    49  	// positioning operation returned iter.RangeKeyChanged() == false.
    50  	//
    51  	// rangeKeyChangeGuess == true implies that at least one of the many iterator
    52  	// operations returned RangeKeyChanged to true, but we may have false
    53  	// positives. We can't assume that the range key actually changed if
    54  	// iter.RangeKeyChanged() returns true after one of the positioning
    55  	// operations. Consider a db with two range keys, which are also in the same
    56  	// block, a-f, g-h where the iterator's filter excludes g-h. If the iterator
    57  	// is positioned on a-f, then a call to SeekLT(z), will position the iterator
    58  	// over g-h, but the retryableIter will call Prev and the iterator will be
    59  	// positioned back over a-f. In this case the range key hasn't changed, but
    60  	// one of the positioning operations will return iter.RangeKeyChanged() ==
    61  	// true.
    62  	rangeKeyChangeGuess bool
    63  
    64  	// rangeKeyChanged is the true accurate value of whether the range key has
    65  	// changed from the perspective of a client of the retryableIter. It is used
    66  	// to determine if rangeKeyChangeGuess is a false positive. It is computed
    67  	// by comparing the range key at the current position with the range key
    68  	// at the previous position.
    69  	rangeKeyChanged bool
    70  
    71  	// When a filter is set on the iterator, one positioning op from the
    72  	// perspective of a client of the retryableIter, may result in multiple
    73  	// intermediary positioning ops. This bool is set if the current positioning
    74  	// op is intermediate.
    75  	intermediatePosition bool
    76  
    77  	rkeyBuff []byte
    78  }
    79  
    80  func (i *retryableIter) shouldFilter() bool {
    81  	if i.filterMax == 0 {
    82  		return false
    83  	}
    84  	k := i.iter.Key()
    85  	n := testkeys.Comparer.Split(k)
    86  	if n == len(k) {
    87  		// No suffix, don't filter it.
    88  		return false
    89  	}
    90  	v, err := testkeys.ParseSuffix(k[n:])
    91  	if err != nil {
    92  		panic(err)
    93  	}
    94  	ts := uint64(v)
    95  	return ts < i.filterMin || ts >= i.filterMax
    96  }
    97  
    98  func (i *retryableIter) needRetry() bool {
    99  	return errors.Is(i.iter.Error(), errorfs.ErrInjected)
   100  }
   101  
   102  func (i *retryableIter) withRetry(fn func()) {
   103  	for {
   104  		fn()
   105  		if !i.needRetry() {
   106  			break
   107  		}
   108  		for i.needRetry() {
   109  			i.iter.SeekGE(i.lastKey)
   110  		}
   111  	}
   112  
   113  	i.lastKey = i.lastKey[:0]
   114  	if i.iter.Valid() {
   115  		i.lastKey = append(i.lastKey, i.iter.Key()...)
   116  	}
   117  }
   118  
   119  func (i *retryableIter) Close() error {
   120  	return i.iter.Close()
   121  }
   122  
   123  func (i *retryableIter) Error() error {
   124  	return i.iter.Error()
   125  }
   126  
   127  // A call to an iterator positioning function may result in sub calls to other
   128  // iterator positioning functions. We need to run some code in the top level
   129  // call, so we use withPosition to reduce code duplication in the positioning
   130  // functions.
   131  func (i *retryableIter) withPosition(fn func()) {
   132  	// For the top level op, i.intermediatePosition must always be false.
   133  	intermediate := i.intermediatePosition
   134  	// Any subcalls to positioning ops will be intermediate.
   135  	i.intermediatePosition = true
   136  	defer func() {
   137  		i.intermediatePosition = intermediate
   138  	}()
   139  
   140  	if !intermediate {
   141  		// Clear out the previous value stored in the buff.
   142  		i.rkeyBuff = i.rkeyBuff[:0]
   143  		if _, hasRange := i.iter.HasPointAndRange(); hasRange {
   144  			// This is a top level positioning op. We should determine if the iter
   145  			// is positioned over a range key to later determine if the range key
   146  			// changed.
   147  			startTmp, _ := i.iter.RangeBounds()
   148  			i.rkeyBuff = append(i.rkeyBuff, startTmp...)
   149  
   150  		}
   151  		// Set this to false. Any positioning op can set this to true.
   152  		i.rangeKeyChangeGuess = false
   153  	}
   154  
   155  	fn()
   156  
   157  	if !intermediate {
   158  		// Check if the range key changed.
   159  		var newStartKey []byte
   160  		if _, hasRange := i.iter.HasPointAndRange(); hasRange {
   161  			newStartKey, _ = i.iter.RangeBounds()
   162  		}
   163  
   164  		i.rangeKeyChanged = !bytes.Equal(newStartKey, i.rkeyBuff)
   165  	}
   166  }
   167  
   168  func (i *retryableIter) updateRangeKeyChangedGuess() {
   169  	i.rangeKeyChangeGuess = i.rangeKeyChangeGuess || i.iter.RangeKeyChanged()
   170  }
   171  
   172  func (i *retryableIter) First() bool {
   173  	var valid bool
   174  	i.withPosition(func() {
   175  		i.withRetry(func() {
   176  			valid = i.iter.First()
   177  		})
   178  		i.updateRangeKeyChangedGuess()
   179  		if valid && i.shouldFilter() {
   180  			valid = i.Next()
   181  		}
   182  	})
   183  	return valid
   184  }
   185  
   186  func (i *retryableIter) Key() []byte {
   187  	return i.iter.Key()
   188  }
   189  
   190  func (i *retryableIter) RangeKeyChanged() bool {
   191  	if i.filterMax == 0 {
   192  		return i.iter.RangeKeyChanged()
   193  	}
   194  
   195  	if !i.rangeKeyChangeGuess {
   196  		// false negatives shouldn't be possible so just return.
   197  		return false
   198  	}
   199  
   200  	// i.rangeKeyChangeGuess is true. This may be a false positive, so just
   201  	// return i.rangeKeyChanged which will always be correct.
   202  	return i.rangeKeyChanged
   203  }
   204  
   205  func (i *retryableIter) HasPointAndRange() (bool, bool) {
   206  	return i.iter.HasPointAndRange()
   207  }
   208  
   209  func (i *retryableIter) RangeBounds() ([]byte, []byte) {
   210  	return i.iter.RangeBounds()
   211  }
   212  
   213  func (i *retryableIter) RangeKeys() []bitalostable.RangeKeyData {
   214  	return i.iter.RangeKeys()
   215  }
   216  
   217  func (i *retryableIter) Last() bool {
   218  	var valid bool
   219  	i.withPosition(func() {
   220  		i.withRetry(func() { valid = i.iter.Last() })
   221  		i.updateRangeKeyChangedGuess()
   222  		if valid && i.shouldFilter() {
   223  			valid = i.Prev()
   224  		}
   225  	})
   226  	return valid
   227  }
   228  
   229  func (i *retryableIter) Next() bool {
   230  	var valid bool
   231  	i.withPosition(func() {
   232  		i.withRetry(func() {
   233  			valid = i.iter.Next()
   234  			i.updateRangeKeyChangedGuess()
   235  			for valid && i.shouldFilter() {
   236  				valid = i.iter.Next()
   237  				i.updateRangeKeyChangedGuess()
   238  			}
   239  		})
   240  	})
   241  	return valid
   242  }
   243  
   244  func (i *retryableIter) NextWithLimit(limit []byte) bitalostable.IterValidityState {
   245  	var validity bitalostable.IterValidityState
   246  	i.withPosition(func() {
   247  		i.withRetry(func() {
   248  			validity = i.iter.NextWithLimit(limit)
   249  			i.updateRangeKeyChangedGuess()
   250  			for validity == bitalostable.IterValid && i.shouldFilter() {
   251  				validity = i.iter.NextWithLimit(limit)
   252  				i.updateRangeKeyChangedGuess()
   253  			}
   254  		})
   255  	})
   256  	return validity
   257  
   258  }
   259  
   260  func (i *retryableIter) Prev() bool {
   261  	var valid bool
   262  	i.withPosition(func() {
   263  		i.withRetry(func() {
   264  			valid = i.iter.Prev()
   265  			i.updateRangeKeyChangedGuess()
   266  			for valid && i.shouldFilter() {
   267  				valid = i.iter.Prev()
   268  				i.updateRangeKeyChangedGuess()
   269  			}
   270  		})
   271  	})
   272  	return valid
   273  }
   274  
   275  func (i *retryableIter) PrevWithLimit(limit []byte) bitalostable.IterValidityState {
   276  	var validity bitalostable.IterValidityState
   277  	i.withPosition(func() {
   278  		i.withRetry(func() {
   279  			validity = i.iter.PrevWithLimit(limit)
   280  			i.updateRangeKeyChangedGuess()
   281  			for validity == bitalostable.IterValid && i.shouldFilter() {
   282  				validity = i.iter.PrevWithLimit(limit)
   283  				i.updateRangeKeyChangedGuess()
   284  			}
   285  		})
   286  	})
   287  	return validity
   288  }
   289  
   290  func (i *retryableIter) SeekGE(key []byte) bool {
   291  	var valid bool
   292  	i.withPosition(func() {
   293  		i.withRetry(func() { valid = i.iter.SeekGE(key) })
   294  		i.updateRangeKeyChangedGuess()
   295  		if valid && i.shouldFilter() {
   296  			valid = i.Next()
   297  		}
   298  	})
   299  	return valid
   300  }
   301  
   302  func (i *retryableIter) SeekGEWithLimit(key []byte, limit []byte) bitalostable.IterValidityState {
   303  	var validity bitalostable.IterValidityState
   304  	i.withPosition(func() {
   305  		i.withRetry(func() { validity = i.iter.SeekGEWithLimit(key, limit) })
   306  		i.updateRangeKeyChangedGuess()
   307  		if validity == bitalostable.IterValid && i.shouldFilter() {
   308  			validity = i.NextWithLimit(limit)
   309  		}
   310  	})
   311  	return validity
   312  }
   313  
   314  func (i *retryableIter) SeekLT(key []byte) bool {
   315  	var valid bool
   316  	i.withPosition(func() {
   317  		i.withRetry(func() { valid = i.iter.SeekLT(key) })
   318  		i.updateRangeKeyChangedGuess()
   319  		if valid && i.shouldFilter() {
   320  			valid = i.Prev()
   321  		}
   322  	})
   323  	return valid
   324  }
   325  
   326  func (i *retryableIter) SeekLTWithLimit(key []byte, limit []byte) bitalostable.IterValidityState {
   327  	var validity bitalostable.IterValidityState
   328  	i.withPosition(func() {
   329  		i.withRetry(func() { validity = i.iter.SeekLTWithLimit(key, limit) })
   330  		i.updateRangeKeyChangedGuess()
   331  		if validity == bitalostable.IterValid && i.shouldFilter() {
   332  			validity = i.PrevWithLimit(limit)
   333  		}
   334  	})
   335  	return validity
   336  }
   337  
   338  func (i *retryableIter) SeekPrefixGE(key []byte) bool {
   339  	var valid bool
   340  	i.withPosition(func() {
   341  		i.withRetry(func() { valid = i.iter.SeekPrefixGE(key) })
   342  		i.updateRangeKeyChangedGuess()
   343  		if valid && i.shouldFilter() {
   344  			valid = i.Next()
   345  		}
   346  	})
   347  	return valid
   348  }
   349  
   350  func (i *retryableIter) SetBounds(lower, upper []byte) {
   351  	i.iter.SetBounds(lower, upper)
   352  }
   353  
   354  func (i *retryableIter) SetOptions(opts *bitalostable.IterOptions) {
   355  	i.iter.SetOptions(opts)
   356  }
   357  
   358  func (i *retryableIter) Valid() bool {
   359  	return i.iter.Valid()
   360  }
   361  
   362  func (i *retryableIter) Value() []byte {
   363  	return i.iter.Value()
   364  }