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 }