github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/internal/metamorphic/generator.go (about) 1 // Copyright 2019 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 "fmt" 10 11 "github.com/zuoyebang/bitalostable" 12 "github.com/zuoyebang/bitalostable/internal/randvar" 13 "github.com/zuoyebang/bitalostable/internal/testkeys" 14 "golang.org/x/exp/rand" 15 ) 16 17 type iterOpts struct { 18 lower []byte 19 upper []byte 20 keyTypes uint32 // bitalostable.IterKeyType 21 // maskSuffix may be set if keyTypes is IterKeyTypePointsAndRanges to 22 // configure IterOptions.RangeKeyMasking.Suffix. 23 maskSuffix []byte 24 25 // If filterMax is >0, this iterator will filter out any keys that have 26 // suffixes that don't fall within the range [filterMin,filterMax). 27 // Additionally, the iterator will be constructed with a block-property 28 // filter that filters out blocks accordingly. Not all OPTIONS hook up the 29 // corresponding block property collector, so block-filtering may still be 30 // effectively disabled in some runs. The iterator operations themselves 31 // however will always skip past any points that should be filtered to 32 // ensure determinism. 33 filterMin uint64 34 filterMax uint64 35 36 // NB: If adding or removing fields, ensure IsZero is in sync. 37 } 38 39 func (o iterOpts) IsZero() bool { 40 return o.lower == nil && o.upper == nil && o.keyTypes == 0 && 41 o.maskSuffix == nil && o.filterMin == 0 && o.filterMax == 0 42 } 43 44 type generator struct { 45 cfg config 46 rng *rand.Rand 47 48 init *initOp 49 ops []op 50 51 // keyManager tracks the state of keys a operation generation time. 52 keyManager *keyManager 53 // Unordered sets of object IDs for live objects. Used to randomly select on 54 // object when generating an operation. There are 4 concrete objects: the DB 55 // (of which there is exactly 1), batches, iterators, and snapshots. 56 // 57 // liveBatches contains the live indexed and write-only batches. 58 liveBatches objIDSlice 59 // liveIters contains the live iterators. 60 liveIters objIDSlice 61 itersLastOpts map[objID]iterOpts 62 // liveReaders contains the DB, and any live indexed batches and snapshots. The DB is always 63 // at index 0. 64 liveReaders objIDSlice 65 // liveSnapshots contains the live snapshots. 66 liveSnapshots objIDSlice 67 // liveWriters contains the DB, and any live batches. The DB is always at index 0. 68 liveWriters objIDSlice 69 70 // Maps used to find associated objects during generation. These maps are not 71 // needed during test execution. 72 // 73 // batchID -> batch iters: used to keep track of the open iterators on an 74 // indexed batch. The iter set value will also be indexed by the readers map. 75 batches map[objID]objIDSet 76 // iterID -> reader iters: used to keep track of all of the open 77 // iterators. The iter set value will also be indexed by either the batches 78 // or snapshots maps. 79 iters map[objID]objIDSet 80 // readerID -> reader iters: used to keep track of the open iterators on a 81 // reader. The iter set value will also be indexed by either the batches or 82 // snapshots maps. This map is the union of batches and snapshots maps. 83 readers map[objID]objIDSet 84 // snapshotID -> snapshot iters: used to keep track of the open iterators on 85 // a snapshot. The iter set value will also be indexed by the readers map. 86 snapshots map[objID]objIDSet 87 // iterSequenceNumber is the metaTimestamp at which the iter was created. 88 iterCreationTimestamp map[objID]int 89 // iterReaderID is a map from an iterID to a readerID. 90 iterReaderID map[objID]objID 91 } 92 93 func newGenerator(rng *rand.Rand, cfg config, km *keyManager) *generator { 94 g := &generator{ 95 cfg: cfg, 96 rng: rng, 97 init: &initOp{}, 98 keyManager: km, 99 liveReaders: objIDSlice{makeObjID(dbTag, 0)}, 100 liveWriters: objIDSlice{makeObjID(dbTag, 0)}, 101 batches: make(map[objID]objIDSet), 102 iters: make(map[objID]objIDSet), 103 readers: make(map[objID]objIDSet), 104 snapshots: make(map[objID]objIDSet), 105 itersLastOpts: make(map[objID]iterOpts), 106 iterCreationTimestamp: make(map[objID]int), 107 iterReaderID: make(map[objID]objID), 108 } 109 // Note that the initOp fields are populated during generation. 110 g.ops = append(g.ops, g.init) 111 return g 112 } 113 114 func generate(rng *rand.Rand, count uint64, cfg config, km *keyManager) []op { 115 g := newGenerator(rng, cfg, km) 116 117 generators := []func(){ 118 batchAbort: g.batchAbort, 119 batchCommit: g.batchCommit, 120 dbCheckpoint: g.dbCheckpoint, 121 dbCompact: g.dbCompact, 122 dbFlush: g.dbFlush, 123 dbRestart: g.dbRestart, 124 iterClose: g.randIter(g.iterClose), 125 iterFirst: g.randIter(g.iterFirst), 126 iterLast: g.randIter(g.iterLast), 127 iterNext: g.randIter(g.iterNext), 128 iterNextWithLimit: g.randIter(g.iterNextWithLimit), 129 iterPrev: g.randIter(g.iterPrev), 130 iterPrevWithLimit: g.randIter(g.iterPrevWithLimit), 131 iterSeekGE: g.randIter(g.iterSeekGE), 132 iterSeekGEWithLimit: g.randIter(g.iterSeekGEWithLimit), 133 iterSeekLT: g.randIter(g.iterSeekLT), 134 iterSeekLTWithLimit: g.randIter(g.iterSeekLTWithLimit), 135 iterSeekPrefixGE: g.randIter(g.iterSeekPrefixGE), 136 iterSetBounds: g.randIter(g.iterSetBounds), 137 iterSetOptions: g.randIter(g.iterSetOptions), 138 newBatch: g.newBatch, 139 newIndexedBatch: g.newIndexedBatch, 140 newIter: g.newIter, 141 newIterUsingClone: g.newIterUsingClone, 142 newSnapshot: g.newSnapshot, 143 readerGet: g.readerGet, 144 snapshotClose: g.snapshotClose, 145 writerApply: g.writerApply, 146 writerDelete: g.writerDelete, 147 writerDeleteRange: g.writerDeleteRange, 148 writerIngest: g.writerIngest, 149 writerMerge: g.writerMerge, 150 writerRangeKeyDelete: g.writerRangeKeyDelete, 151 writerRangeKeySet: g.writerRangeKeySet, 152 writerRangeKeyUnset: g.writerRangeKeyUnset, 153 writerSet: g.writerSet, 154 writerSingleDelete: g.writerSingleDelete, 155 } 156 157 // TPCC-style deck of cards randomization. Every time the end of the deck is 158 // reached, we shuffle the deck. 159 deck := randvar.NewDeck(g.rng, cfg.ops...) 160 for i := uint64(0); i < count; i++ { 161 generators[deck.Int()]() 162 } 163 164 g.dbClose() 165 return g.ops 166 } 167 168 func (g *generator) add(op op) { 169 g.ops = append(g.ops, op) 170 g.keyManager.update(op) 171 } 172 173 // randKeyToWrite returns a key for any write other than SingleDelete. 174 // 175 // TODO(peter): make the size and distribution of keys configurable. See 176 // keyDist and keySizeDist in config.go. 177 func (g *generator) randKeyToWrite(newKey float64) []byte { 178 return g.randKeyHelper(g.keyManager.eligibleWriteKeys(), newKey) 179 } 180 181 // prefixKeyRange generates a [start, end) pair consisting of two prefix keys. 182 func (g *generator) prefixKeyRange() ([]byte, []byte) { 183 start := g.randPrefixToWrite(0.001) 184 end := g.randPrefixToWrite(0.001) 185 for g.cmp(start, end) == 0 { 186 end = g.randPrefixToWrite(0.05) 187 } 188 if g.cmp(start, end) > 0 { 189 start, end = end, start 190 } 191 return start, end 192 } 193 194 // randPrefixToWrite returns a prefix key (a key with no suffix) for a range key 195 // write operation. 196 func (g *generator) randPrefixToWrite(newPrefix float64) []byte { 197 prefixes := g.keyManager.prefixes() 198 if len(prefixes) > 0 && g.rng.Float64() > newPrefix { 199 // Use an existing prefix. 200 p := g.rng.Intn(len(prefixes)) 201 return prefixes[p] 202 } 203 204 // Use a new prefix. 205 var prefix []byte 206 for { 207 prefix = g.randKeyHelperSuffix(nil, 4, 12, 0) 208 if !g.keyManager.prefixExists(prefix) { 209 if !g.keyManager.addNewKey(prefix) { 210 panic("key must not exist if prefix doesn't exist") 211 } 212 return prefix 213 } 214 } 215 } 216 217 // randSuffixToWrite generates a random suffix according to the configuration's suffix 218 // distribution. It takes a probability 0 ≤ p ≤ 1.0 indicating the probability 219 // with which the generator should increase the max suffix generated by the 220 // generator. 221 // 222 // randSuffixToWrite may return a nil suffix, with the probability the 223 // configuration's suffix distribution assigns to the zero suffix. 224 func (g *generator) randSuffixToWrite(incMaxProb float64) []byte { 225 if g.rng.Float64() < incMaxProb { 226 g.cfg.writeSuffixDist.IncMax(1) 227 } 228 return suffixFromInt(int(g.cfg.writeSuffixDist.Uint64(g.rng))) 229 } 230 231 // randSuffixToRead generates a random suffix used during reads. The suffixes 232 // generated by this function are within the same range as suffixes generated by 233 // randSuffixToWrite, however randSuffixToRead pulls from a uniform 234 // distribution. 235 func (g *generator) randSuffixToRead() []byte { 236 // When reading, don't apply the recency skewing in order to better exercise 237 // a reading a mix of older and newer keys. 238 max := g.cfg.writeSuffixDist.Max() 239 return suffixFromInt(int(g.rng.Uint64n(max))) 240 } 241 242 func suffixFromInt(suffix int) []byte { 243 // Treat the zero as no suffix to match the behavior during point key 244 // generation in randKeyHelper. 245 if suffix == 0 { 246 return nil 247 } 248 return testkeys.Suffix(suffix) 249 } 250 251 func (g *generator) randKeyToSingleDelete(id objID) []byte { 252 keys := g.keyManager.eligibleSingleDeleteKeys(id) 253 length := len(keys) 254 if length == 0 { 255 return nil 256 } 257 return keys[g.rng.Intn(length)] 258 } 259 260 // randKeyToRead returns a key for read operations. 261 func (g *generator) randKeyToRead(newKey float64) []byte { 262 return g.randKeyHelper(g.keyManager.eligibleReadKeys(), newKey) 263 } 264 265 func (g *generator) randKeyHelper(keys [][]byte, newKey float64) []byte { 266 switch { 267 case len(keys) > 0 && g.rng.Float64() > newKey: 268 // Use an existing user key. 269 return keys[g.rng.Intn(len(keys))] 270 271 case len(keys) > 0 && g.rng.Float64() > g.cfg.newPrefix: 272 // Use an existing prefix but a new suffix, producing a new user key. 273 prefixes := g.keyManager.prefixes() 274 var key []byte 275 for { 276 // Pick a prefix on each iteration in case most or all suffixes are 277 // already in use for any individual prefix. 278 p := g.rng.Intn(len(prefixes)) 279 suffix := int(g.cfg.writeSuffixDist.Uint64(g.rng)) 280 281 if suffix > 0 { 282 key = resizeBuffer(key, len(prefixes[p]), testkeys.SuffixLen(suffix)) 283 n := copy(key, prefixes[p]) 284 testkeys.WriteSuffix(key[n:], suffix) 285 } else { 286 key = resizeBuffer(key, len(prefixes[p]), 0) 287 copy(key, prefixes[p]) 288 } 289 if g.keyManager.addNewKey(key) { 290 return key 291 } 292 293 // If the generated key already existed, increase the suffix 294 // distribution to make a generating a new user key with an existing 295 // prefix more likely. 296 g.cfg.writeSuffixDist.IncMax(1) 297 } 298 299 default: 300 // Use a new prefix, producing a new user key. 301 suffix := int(g.cfg.writeSuffixDist.Uint64(g.rng)) 302 var key []byte 303 for { 304 key = g.randKeyHelperSuffix(nil, 4, 12, suffix) 305 if !g.keyManager.prefixExists(key[:testkeys.Comparer.Split(key)]) { 306 if !g.keyManager.addNewKey(key) { 307 panic("key must not exist if prefix doesn't exist") 308 } 309 return key 310 } 311 } 312 } 313 } 314 315 // randKeyHelperSuffix is a helper function for randKeyHelper, and should not be 316 // invoked directly. 317 func (g *generator) randKeyHelperSuffix(dst []byte, minPrefixLen, maxPrefixLen, suffix int) []byte { 318 n := minPrefixLen 319 if maxPrefixLen > minPrefixLen { 320 n += g.rng.Intn(maxPrefixLen - minPrefixLen) 321 } 322 // In order to test a mix of suffixed and unsuffixed keys, omit the zero 323 // suffix. 324 if suffix == 0 { 325 dst = resizeBuffer(dst, n, 0) 326 g.fillRand(dst) 327 return dst 328 } 329 suffixLen := testkeys.SuffixLen(suffix) 330 dst = resizeBuffer(dst, n, suffixLen) 331 g.fillRand(dst[:n]) 332 testkeys.WriteSuffix(dst[n:], suffix) 333 return dst 334 } 335 336 func resizeBuffer(buf []byte, prefixLen, suffixLen int) []byte { 337 if cap(buf) >= prefixLen+suffixLen { 338 return buf[:prefixLen+suffixLen] 339 } 340 return make([]byte, prefixLen+suffixLen) 341 } 342 343 // TODO(peter): make the value size configurable. See valueSizeDist in 344 // config.go. 345 func (g *generator) randValue(min, max int) []byte { 346 n := min 347 if max > min { 348 n += g.rng.Intn(max - min) 349 } 350 buf := make([]byte, n) 351 g.fillRand(buf) 352 return buf 353 } 354 355 func (g *generator) fillRand(buf []byte) { 356 // NB: The actual random values are not particularly important. We only use 357 // lowercase letters because that makes visual determination of ordering 358 // easier, rather than having to remember the lexicographic ordering of 359 // uppercase vs lowercase, or letters vs numbers vs punctuation. 360 const letters = "abcdefghijklmnopqrstuvwxyz" 361 const lettersLen = uint64(len(letters)) 362 const lettersCharsPerRand = 12 // floor(log(math.MaxUint64)/log(lettersLen)) 363 364 var r uint64 365 var q int 366 for i := 0; i < len(buf); i++ { 367 if q == 0 { 368 r = g.rng.Uint64() 369 q = lettersCharsPerRand 370 } 371 buf[i] = letters[r%lettersLen] 372 r = r / lettersLen 373 q-- 374 } 375 } 376 377 func (g *generator) newBatch() { 378 batchID := makeObjID(batchTag, g.init.batchSlots) 379 g.init.batchSlots++ 380 g.liveBatches = append(g.liveBatches, batchID) 381 g.liveWriters = append(g.liveWriters, batchID) 382 383 g.add(&newBatchOp{ 384 batchID: batchID, 385 }) 386 } 387 388 func (g *generator) newIndexedBatch() { 389 batchID := makeObjID(batchTag, g.init.batchSlots) 390 g.init.batchSlots++ 391 g.liveBatches = append(g.liveBatches, batchID) 392 g.liveReaders = append(g.liveReaders, batchID) 393 g.liveWriters = append(g.liveWriters, batchID) 394 395 iters := make(objIDSet) 396 g.batches[batchID] = iters 397 g.readers[batchID] = iters 398 399 g.add(&newIndexedBatchOp{ 400 batchID: batchID, 401 }) 402 } 403 404 func (g *generator) batchClose(batchID objID) { 405 g.liveBatches.remove(batchID) 406 iters := g.batches[batchID] 407 delete(g.batches, batchID) 408 409 if iters != nil { 410 g.liveReaders.remove(batchID) 411 delete(g.readers, batchID) 412 } 413 g.liveWriters.remove(batchID) 414 for _, id := range iters.sorted() { 415 g.liveIters.remove(id) 416 delete(g.iters, id) 417 g.add(&closeOp{objID: id}) 418 } 419 } 420 421 func (g *generator) batchAbort() { 422 if len(g.liveBatches) == 0 { 423 return 424 } 425 426 batchID := g.liveBatches.rand(g.rng) 427 g.batchClose(batchID) 428 429 g.add(&closeOp{objID: batchID}) 430 } 431 432 func (g *generator) batchCommit() { 433 if len(g.liveBatches) == 0 { 434 return 435 } 436 437 batchID := g.liveBatches.rand(g.rng) 438 g.batchClose(batchID) 439 440 g.add(&batchCommitOp{ 441 batchID: batchID, 442 }) 443 } 444 445 func (g *generator) dbClose() { 446 // Close any live iterators and snapshots, so that we can close the DB 447 // cleanly. 448 for len(g.liveIters) > 0 { 449 g.randIter(g.iterClose)() 450 } 451 for len(g.liveSnapshots) > 0 { 452 g.snapshotClose() 453 } 454 g.add(&closeOp{objID: makeObjID(dbTag, 0)}) 455 } 456 457 func (g *generator) dbCheckpoint() { 458 g.add(&checkpointOp{}) 459 } 460 461 func (g *generator) dbCompact() { 462 // Generate new key(s) with a 1% probability. 463 start := g.randKeyToRead(0.01) 464 end := g.randKeyToRead(0.01) 465 if g.cmp(start, end) > 0 { 466 start, end = end, start 467 } 468 g.add(&compactOp{ 469 start: start, 470 end: end, 471 parallelize: g.rng.Float64() < 0.5, 472 }) 473 } 474 475 func (g *generator) dbFlush() { 476 g.add(&flushOp{}) 477 } 478 479 func (g *generator) dbRestart() { 480 // Close any live iterators and snapshots, so that we can close the DB 481 // cleanly. 482 for len(g.liveIters) > 0 { 483 g.randIter(g.iterClose)() 484 } 485 for len(g.liveSnapshots) > 0 { 486 g.snapshotClose() 487 } 488 // Close the batches. 489 for len(g.liveBatches) > 0 { 490 g.batchClose(g.liveBatches[0]) 491 } 492 if len(g.liveReaders) != 1 || len(g.liveWriters) != 1 { 493 panic(fmt.Sprintf("unexpected counts: liveReaders %d, liveWriters: %d", 494 len(g.liveReaders), len(g.liveWriters))) 495 } 496 g.add(&dbRestartOp{}) 497 } 498 499 func (g *generator) newIter() { 500 iterID := makeObjID(iterTag, g.init.iterSlots) 501 g.init.iterSlots++ 502 g.liveIters = append(g.liveIters, iterID) 503 504 readerID := g.liveReaders.rand(g.rng) 505 if iters := g.readers[readerID]; iters != nil { 506 iters[iterID] = struct{}{} 507 g.iters[iterID] = iters 508 //lint:ignore SA9003 - readability 509 } else { 510 // NB: the DB object does not track its open iterators because it never 511 // closes. 512 } 513 514 var opts iterOpts 515 // Generate lower/upper bounds with a 10% probability. 516 if g.rng.Float64() <= 0.1 { 517 // Generate a new key with a .1% probability. 518 opts.lower = g.randKeyToRead(0.001) 519 } 520 if g.rng.Float64() <= 0.1 { 521 // Generate a new key with a .1% probability. 522 opts.upper = g.randKeyToRead(0.001) 523 } 524 if g.cmp(opts.lower, opts.upper) > 0 { 525 opts.lower, opts.upper = opts.upper, opts.lower 526 } 527 opts.keyTypes, opts.maskSuffix = g.randKeyTypesAndMask() 528 529 // With a low probability, enable automatic filtering of keys with suffixes 530 // not in the provided range. This filtering occurs both through 531 // block-property filtering and explicitly within the iterator operations to 532 // ensure determinism. 533 if g.rng.Intn(10) == 1 { 534 max := g.cfg.writeSuffixDist.Max() 535 opts.filterMin, opts.filterMax = g.rng.Uint64n(max)+1, g.rng.Uint64n(max)+1 536 if opts.filterMin > opts.filterMax { 537 opts.filterMin, opts.filterMax = opts.filterMax, opts.filterMin 538 } else if opts.filterMin == opts.filterMax { 539 opts.filterMax = opts.filterMin + 1 540 } 541 } 542 543 g.itersLastOpts[iterID] = opts 544 g.iterCreationTimestamp[iterID] = g.keyManager.nextMetaTimestamp() 545 g.iterReaderID[iterID] = readerID 546 g.add(&newIterOp{ 547 readerID: readerID, 548 iterID: iterID, 549 iterOpts: opts, 550 }) 551 } 552 553 func (g *generator) randKeyTypesAndMask() (keyTypes uint32, maskSuffix []byte) { 554 // Iterate over different key types. 555 p := g.rng.Float64() 556 switch { 557 case p < 0.2: // 20% probability 558 keyTypes = uint32(bitalostable.IterKeyTypePointsOnly) 559 case p < 0.8: // 60% probability 560 keyTypes = uint32(bitalostable.IterKeyTypePointsAndRanges) 561 // With 50% probability, enable masking. 562 if g.rng.Intn(2) == 1 { 563 maskSuffix = g.randSuffixToRead() 564 } 565 default: // 20% probability 566 keyTypes = uint32(bitalostable.IterKeyTypeRangesOnly) 567 } 568 return keyTypes, maskSuffix 569 } 570 571 func (g *generator) newIterUsingClone() { 572 if len(g.liveIters) == 0 { 573 return 574 } 575 existingIterID := g.liveIters.rand(g.rng) 576 iterID := makeObjID(iterTag, g.init.iterSlots) 577 g.init.iterSlots++ 578 g.liveIters = append(g.liveIters, iterID) 579 if iters := g.iters[existingIterID]; iters != nil { 580 iters[iterID] = struct{}{} 581 g.iters[iterID] = iters 582 //lint:ignore SA9003 - readability 583 } else { 584 // NB: the DB object does not track its open iterators because it never 585 // closes. 586 } 587 588 var opts iterOpts 589 if g.rng.Intn(2) == 1 { 590 g.maybeMutateOptions(&opts) 591 g.itersLastOpts[iterID] = opts 592 } else { 593 g.itersLastOpts[iterID] = g.itersLastOpts[existingIterID] 594 } 595 596 g.iterCreationTimestamp[iterID] = g.keyManager.nextMetaTimestamp() 597 g.iterReaderID[iterID] = g.iterReaderID[existingIterID] 598 g.add(&newIterUsingCloneOp{ 599 existingIterID: existingIterID, 600 iterID: iterID, 601 refreshBatch: g.rng.Intn(2) == 1, 602 iterOpts: opts, 603 }) 604 } 605 606 func (g *generator) iterClose(iterID objID) { 607 g.liveIters.remove(iterID) 608 if readerIters, ok := g.iters[iterID]; ok { 609 delete(g.iters, iterID) 610 delete(readerIters, iterID) 611 //lint:ignore SA9003 - readability 612 } else { 613 // NB: the DB object does not track its open iterators because it never 614 // closes. 615 } 616 617 g.add(&closeOp{objID: iterID}) 618 } 619 620 func (g *generator) iterSetBounds(iterID objID) { 621 iterLastOpts := g.itersLastOpts[iterID] 622 var lower, upper []byte 623 genLower := g.rng.Float64() <= 0.9 624 genUpper := g.rng.Float64() <= 0.9 625 // When one of ensureLowerGE, ensureUpperLE is true, the new bounds 626 // don't overlap with the previous bounds. 627 var ensureLowerGE, ensureUpperLE bool 628 if genLower && iterLastOpts.upper != nil && g.rng.Float64() <= 0.9 { 629 ensureLowerGE = true 630 } 631 if (!ensureLowerGE || g.rng.Float64() < 0.5) && genUpper && iterLastOpts.lower != nil { 632 ensureUpperLE = true 633 ensureLowerGE = false 634 } 635 attempts := 0 636 for { 637 attempts++ 638 if genLower { 639 // Generate a new key with a .1% probability. 640 lower = g.randKeyToRead(0.001) 641 } 642 if genUpper { 643 // Generate a new key with a .1% probability. 644 upper = g.randKeyToRead(0.001) 645 } 646 if g.cmp(lower, upper) > 0 { 647 lower, upper = upper, lower 648 } 649 if ensureLowerGE && g.cmp(iterLastOpts.upper, lower) > 0 { 650 if attempts < 25 { 651 continue 652 } 653 lower = iterLastOpts.upper 654 upper = lower 655 break 656 } 657 if ensureUpperLE && g.cmp(upper, iterLastOpts.lower) > 0 { 658 if attempts < 25 { 659 continue 660 } 661 upper = iterLastOpts.lower 662 lower = upper 663 break 664 } 665 break 666 } 667 newOpts := iterLastOpts 668 newOpts.lower = lower 669 newOpts.upper = upper 670 g.itersLastOpts[iterID] = newOpts 671 g.add(&iterSetBoundsOp{ 672 iterID: iterID, 673 lower: lower, 674 upper: upper, 675 }) 676 // Additionally seek the iterator in a manner consistent with the bounds, 677 // and do some steps (Next/Prev). The seeking exercises typical 678 // CockroachDB behavior when using iterators and the steps are trying to 679 // stress the region near the bounds. Ideally, we should not do this as 680 // part of generating a single op, but this is easier than trying to 681 // control future op generation via generator state. 682 doSeekLT := upper != nil && g.rng.Float64() < 0.5 683 doSeekGE := lower != nil && g.rng.Float64() < 0.5 684 if doSeekLT && doSeekGE { 685 // Pick the seek. 686 if g.rng.Float64() < 0.5 { 687 doSeekGE = false 688 } else { 689 doSeekLT = false 690 } 691 } 692 if doSeekLT { 693 g.add(&iterSeekLTOp{ 694 iterID: iterID, 695 key: upper, 696 }) 697 if g.rng.Float64() < 0.5 { 698 g.iterNext(iterID) 699 } 700 if g.rng.Float64() < 0.5 { 701 g.iterNext(iterID) 702 } 703 if g.rng.Float64() < 0.5 { 704 g.iterPrev(iterID) 705 } 706 } else if doSeekGE { 707 g.add(&iterSeekGEOp{ 708 iterID: iterID, 709 key: lower, 710 }) 711 if g.rng.Float64() < 0.5 { 712 g.iterPrev(iterID) 713 } 714 if g.rng.Float64() < 0.5 { 715 g.iterPrev(iterID) 716 } 717 if g.rng.Float64() < 0.5 { 718 g.iterNext(iterID) 719 } 720 } 721 } 722 723 func (g *generator) iterSetOptions(iterID objID) { 724 opts := g.itersLastOpts[iterID] 725 g.maybeMutateOptions(&opts) 726 g.itersLastOpts[iterID] = opts 727 g.add(&iterSetOptionsOp{ 728 iterID: iterID, 729 iterOpts: opts, 730 }) 731 732 // Additionally, perform a random absolute positioning operation. The 733 // SetOptions contract requires one before the next relative positioning 734 // operation. Ideally, we should not do this as part of generating a single 735 // op, but this is easier than trying to control future op generation via 736 // generator state. 737 g.pickOneUniform( 738 g.iterFirst, 739 g.iterLast, 740 g.iterSeekGE, 741 g.iterSeekGEWithLimit, 742 g.iterSeekPrefixGE, 743 g.iterSeekLT, 744 g.iterSeekLTWithLimit, 745 )(iterID) 746 } 747 748 func (g *generator) iterSeekGE(iterID objID) { 749 g.add(&iterSeekGEOp{ 750 iterID: iterID, 751 key: g.randKeyToRead(0.001), // 0.1% new keys 752 }) 753 } 754 755 func (g *generator) iterSeekGEWithLimit(iterID objID) { 756 // 0.1% new keys 757 key, limit := g.randKeyToRead(0.001), g.randKeyToRead(0.001) 758 if g.cmp(key, limit) > 0 { 759 key, limit = limit, key 760 } 761 g.add(&iterSeekGEOp{ 762 iterID: iterID, 763 key: key, 764 limit: limit, 765 }) 766 } 767 768 func (g *generator) randKeyToReadWithinBounds(lower, upper []byte, readerID objID) []*keyMeta { 769 var inRangeKeys []*keyMeta 770 for _, keyMeta := range g.keyManager.byObj[readerID] { 771 posKey := keyMeta.key 772 if g.cmp(posKey, lower) < 0 || g.cmp(posKey, upper) >= 0 { 773 continue 774 } 775 inRangeKeys = append(inRangeKeys, keyMeta) 776 } 777 return inRangeKeys 778 } 779 780 func (g *generator) iterSeekPrefixGE(iterID objID) { 781 lower := g.itersLastOpts[iterID].lower 782 upper := g.itersLastOpts[iterID].upper 783 iterCreationTimestamp := g.iterCreationTimestamp[iterID] 784 var key []byte 785 786 // We try to make sure that the SeekPrefixGE key is within the iter bounds, 787 // and that the iter can read the key. If the key was created on a batch 788 // which deleted the key, then the key will still be considered visible 789 // by the current logic. We're also not accounting for keys written to 790 // batches which haven't been presisted to the DB. But we're only picking 791 // keys in a best effort manner, and the logic is better than picking a 792 // random key. 793 if g.rng.Intn(10) >= 1 { 794 possibleKeys := make([][]byte, 0, 100) 795 inRangeKeys := g.randKeyToReadWithinBounds(lower, upper, dbObjID) 796 for _, keyMeta := range inRangeKeys { 797 posKey := keyMeta.key 798 var foundWriteWithoutDelete bool 799 for _, update := range keyMeta.updateOps { 800 if update.metaTimestamp > iterCreationTimestamp { 801 break 802 } 803 804 if update.deleted { 805 foundWriteWithoutDelete = false 806 } else { 807 foundWriteWithoutDelete = true 808 } 809 } 810 if foundWriteWithoutDelete { 811 possibleKeys = append(possibleKeys, posKey) 812 } 813 } 814 815 if len(possibleKeys) > 0 { 816 key = []byte(possibleKeys[g.rng.Int31n(int32(len(possibleKeys)))]) 817 } 818 } 819 820 if key == nil { 821 // TODO(bananabrick): We should try and use keys within the bounds, 822 // even if we couldn't find any keys visible to the iterator. However, 823 // doing this in experiments didn't really increase the valid 824 // SeekPrefixGE calls by much. 825 key = g.randKeyToRead(0) // 0% new keys 826 } 827 828 g.add(&iterSeekPrefixGEOp{ 829 iterID: iterID, 830 key: key, 831 }) 832 } 833 834 func (g *generator) iterSeekLT(iterID objID) { 835 g.add(&iterSeekLTOp{ 836 iterID: iterID, 837 key: g.randKeyToRead(0.001), // 0.1% new keys 838 }) 839 } 840 841 func (g *generator) iterSeekLTWithLimit(iterID objID) { 842 // 0.1% new keys 843 key, limit := g.randKeyToRead(0.001), g.randKeyToRead(0.001) 844 if g.cmp(limit, key) > 0 { 845 key, limit = limit, key 846 } 847 g.add(&iterSeekLTOp{ 848 iterID: iterID, 849 key: key, 850 limit: limit, 851 }) 852 } 853 854 // randIter performs partial func application ("currying"), returning a new 855 // function that supplies the given func with a random iterator. 856 func (g *generator) randIter(gen func(objID)) func() { 857 return func() { 858 if len(g.liveIters) == 0 { 859 return 860 } 861 gen(g.liveIters.rand(g.rng)) 862 } 863 } 864 865 func (g *generator) iterFirst(iterID objID) { g.add(&iterFirstOp{iterID: iterID}) } 866 func (g *generator) iterLast(iterID objID) { g.add(&iterLastOp{iterID: iterID}) } 867 func (g *generator) iterNext(iterID objID) { g.add(&iterNextOp{iterID: iterID}) } 868 func (g *generator) iterPrev(iterID objID) { g.add(&iterPrevOp{iterID: iterID}) } 869 870 func (g *generator) iterNextWithLimit(iterID objID) { 871 g.add(&iterNextOp{ 872 iterID: iterID, 873 limit: g.randKeyToRead(0.001), // 0.1% new keys 874 }) 875 } 876 877 func (g *generator) iterPrevWithLimit(iterID objID) { 878 g.add(&iterPrevOp{ 879 iterID: iterID, 880 limit: g.randKeyToRead(0.001), // 0.1% new keys 881 }) 882 } 883 884 func (g *generator) readerGet() { 885 if len(g.liveReaders) == 0 { 886 return 887 } 888 889 g.add(&getOp{ 890 readerID: g.liveReaders.rand(g.rng), 891 key: g.randKeyToRead(0.001), // 0.1% new keys 892 }) 893 } 894 895 func (g *generator) newSnapshot() { 896 snapID := makeObjID(snapTag, g.init.snapshotSlots) 897 g.init.snapshotSlots++ 898 g.liveSnapshots = append(g.liveSnapshots, snapID) 899 g.liveReaders = append(g.liveReaders, snapID) 900 901 iters := make(objIDSet) 902 g.snapshots[snapID] = iters 903 g.readers[snapID] = iters 904 905 g.add(&newSnapshotOp{ 906 snapID: snapID, 907 }) 908 } 909 910 func (g *generator) snapshotClose() { 911 if len(g.liveSnapshots) == 0 { 912 return 913 } 914 915 snapID := g.liveSnapshots.rand(g.rng) 916 g.liveSnapshots.remove(snapID) 917 iters := g.snapshots[snapID] 918 delete(g.snapshots, snapID) 919 g.liveReaders.remove(snapID) 920 delete(g.readers, snapID) 921 922 for _, id := range iters.sorted() { 923 g.liveIters.remove(id) 924 delete(g.iters, id) 925 g.add(&closeOp{objID: id}) 926 } 927 928 g.add(&closeOp{objID: snapID}) 929 } 930 931 func (g *generator) writerApply() { 932 if len(g.liveBatches) == 0 { 933 return 934 } 935 if len(g.liveWriters) < 2 { 936 panic(fmt.Sprintf("insufficient liveWriters (%d) to apply batch", len(g.liveWriters))) 937 } 938 939 batchID := g.liveBatches.rand(g.rng) 940 941 var writerID objID 942 for { 943 writerID = g.liveWriters.rand(g.rng) 944 if writerID != batchID { 945 break 946 } 947 } 948 949 g.batchClose(batchID) 950 951 g.add(&applyOp{ 952 writerID: writerID, 953 batchID: batchID, 954 }) 955 } 956 957 func (g *generator) writerDelete() { 958 if len(g.liveWriters) == 0 { 959 return 960 } 961 962 writerID := g.liveWriters.rand(g.rng) 963 g.add(&deleteOp{ 964 writerID: writerID, 965 key: g.randKeyToWrite(0.001), // 0.1% new keys 966 }) 967 } 968 969 func (g *generator) writerDeleteRange() { 970 if len(g.liveWriters) == 0 { 971 return 972 } 973 974 start := g.randKeyToWrite(0.001) 975 end := g.randKeyToWrite(0.001) 976 if g.cmp(start, end) > 0 { 977 start, end = end, start 978 } 979 980 writerID := g.liveWriters.rand(g.rng) 981 g.add(&deleteRangeOp{ 982 writerID: writerID, 983 start: start, 984 end: end, 985 }) 986 } 987 988 func (g *generator) writerRangeKeyDelete() { 989 if len(g.liveWriters) == 0 { 990 return 991 } 992 start, end := g.prefixKeyRange() 993 994 writerID := g.liveWriters.rand(g.rng) 995 g.add(&rangeKeyDeleteOp{ 996 writerID: writerID, 997 start: start, 998 end: end, 999 }) 1000 } 1001 1002 func (g *generator) writerRangeKeySet() { 1003 if len(g.liveWriters) == 0 { 1004 return 1005 } 1006 start, end := g.prefixKeyRange() 1007 1008 // 90% of the time, set a suffix. 1009 var suffix []byte 1010 if g.rng.Float64() < 0.90 { 1011 // Increase the max suffix 5% of the time. 1012 suffix = g.randSuffixToWrite(0.05) 1013 } 1014 1015 writerID := g.liveWriters.rand(g.rng) 1016 g.add(&rangeKeySetOp{ 1017 writerID: writerID, 1018 start: start, 1019 end: end, 1020 suffix: suffix, 1021 value: g.randValue(0, 20), 1022 }) 1023 } 1024 1025 func (g *generator) writerRangeKeyUnset() { 1026 if len(g.liveWriters) == 0 { 1027 return 1028 } 1029 start, end := g.prefixKeyRange() 1030 1031 // 90% of the time, set a suffix. 1032 var suffix []byte 1033 if g.rng.Float64() < 0.90 { 1034 // Increase the max suffix 5% of the time. 1035 suffix = g.randSuffixToWrite(0.05) 1036 } 1037 1038 // TODO(jackson): Increase probability of effective unsets? Purely random 1039 // unsets are unlikely to remove an active range key. 1040 1041 writerID := g.liveWriters.rand(g.rng) 1042 g.add(&rangeKeyUnsetOp{ 1043 writerID: writerID, 1044 start: start, 1045 end: end, 1046 suffix: suffix, 1047 }) 1048 } 1049 1050 func (g *generator) writerIngest() { 1051 if len(g.liveBatches) == 0 { 1052 return 1053 } 1054 1055 // TODO(nicktrav): this is resulting in too many single batch ingests. 1056 // Consider alternatives. One possibility would be to pass through whether 1057 // we can tolerate failure or not, and if the ingestOp encounters a 1058 // failure, it would retry after splitting into single batch ingests. 1059 1060 // Ingest between 1 and 3 batches. 1061 batchIDs := make([]objID, 0, 1+g.rng.Intn(3)) 1062 canFail := cap(batchIDs) > 1 1063 for i := 0; i < cap(batchIDs); i++ { 1064 batchID := g.liveBatches.rand(g.rng) 1065 if canFail && !g.keyManager.canTolerateApplyFailure(batchID) { 1066 continue 1067 } 1068 // After the ingest runs, it either succeeds and the keys are in the 1069 // DB, or it fails and these keys never make it to the DB. 1070 g.batchClose(batchID) 1071 batchIDs = append(batchIDs, batchID) 1072 if len(g.liveBatches) == 0 { 1073 break 1074 } 1075 } 1076 if len(batchIDs) == 0 && len(g.liveBatches) > 0 { 1077 // Unable to find multiple batches because of the 1078 // canTolerateApplyFailure call above, so just pick one batch. 1079 batchID := g.liveBatches.rand(g.rng) 1080 g.batchClose(batchID) 1081 batchIDs = append(batchIDs, batchID) 1082 } 1083 g.add(&ingestOp{ 1084 batchIDs: batchIDs, 1085 }) 1086 } 1087 1088 func (g *generator) writerMerge() { 1089 if len(g.liveWriters) == 0 { 1090 return 1091 } 1092 1093 writerID := g.liveWriters.rand(g.rng) 1094 g.add(&mergeOp{ 1095 writerID: writerID, 1096 // 20% new keys. 1097 key: g.randKeyToWrite(0.2), 1098 value: g.randValue(0, 20), 1099 }) 1100 } 1101 1102 func (g *generator) writerSet() { 1103 if len(g.liveWriters) == 0 { 1104 return 1105 } 1106 1107 writerID := g.liveWriters.rand(g.rng) 1108 g.add(&setOp{ 1109 writerID: writerID, 1110 // 50% new keys. 1111 key: g.randKeyToWrite(0.5), 1112 value: g.randValue(0, 20), 1113 }) 1114 } 1115 1116 func (g *generator) writerSingleDelete() { 1117 if len(g.liveWriters) == 0 { 1118 return 1119 } 1120 1121 writerID := g.liveWriters.rand(g.rng) 1122 key := g.randKeyToSingleDelete(writerID) 1123 if key == nil { 1124 return 1125 } 1126 g.add(&singleDeleteOp{ 1127 writerID: writerID, 1128 key: key, 1129 // Keys eligible for single deletes can be removed with a regular 1130 // delete. Mutate a percentage of SINGLEDEL ops into DELETEs. Note that 1131 // here we are only determining whether the replacement *could* happen. 1132 // At test runtime, the `replaceSingleDelete` test option must also be 1133 // set to true for the single delete to be replaced. 1134 maybeReplaceDelete: g.rng.Float64() < 0.25, 1135 }) 1136 } 1137 1138 func (g *generator) maybeMutateOptions(opts *iterOpts) { 1139 // With 95% probability, allow changes to any options at all. This ensures 1140 // that in 5% of cases there are no changes, and SetOptions hits its fast 1141 // path. 1142 if g.rng.Intn(100) >= 5 { 1143 // With 1/3 probability, clear existing bounds. 1144 if opts.lower != nil && g.rng.Intn(3) == 0 { 1145 opts.lower = nil 1146 } 1147 if opts.upper != nil && g.rng.Intn(3) == 0 { 1148 opts.upper = nil 1149 } 1150 // With 1/3 probability, update the bounds. 1151 if g.rng.Intn(3) == 0 { 1152 // Generate a new key with a .1% probability. 1153 opts.lower = g.randKeyToRead(0.001) 1154 } 1155 if g.rng.Intn(3) == 0 { 1156 // Generate a new key with a .1% probability. 1157 opts.upper = g.randKeyToRead(0.001) 1158 } 1159 if g.cmp(opts.lower, opts.upper) > 0 { 1160 opts.lower, opts.upper = opts.upper, opts.lower 1161 } 1162 1163 // With 1/3 probability, update the key-types/mask. 1164 if g.rng.Intn(3) == 0 { 1165 opts.keyTypes, opts.maskSuffix = g.randKeyTypesAndMask() 1166 } 1167 1168 // With 1/3 probability, clear existing filter. 1169 if opts.filterMax > 0 && g.rng.Intn(3) == 0 { 1170 opts.filterMax, opts.filterMin = 0, 0 1171 } 1172 // With 10% probability, set a filter range. 1173 if g.rng.Intn(10) == 1 { 1174 max := g.cfg.writeSuffixDist.Max() 1175 opts.filterMin, opts.filterMax = g.rng.Uint64n(max)+1, g.rng.Uint64n(max)+1 1176 if opts.filterMin > opts.filterMax { 1177 opts.filterMin, opts.filterMax = opts.filterMax, opts.filterMin 1178 } else if opts.filterMin == opts.filterMax { 1179 opts.filterMax = opts.filterMin + 1 1180 } 1181 } 1182 } 1183 } 1184 1185 func (g *generator) pickOneUniform(options ...func(objID)) func(objID) { 1186 i := g.rng.Intn(len(options)) 1187 return options[i] 1188 } 1189 1190 func (g *generator) cmp(a, b []byte) int { 1191 return g.keyManager.comparer.Compare(a, b) 1192 } 1193 1194 func (g *generator) String() string { 1195 var buf bytes.Buffer 1196 for _, op := range g.ops { 1197 fmt.Fprintf(&buf, "%s\n", op) 1198 } 1199 return buf.String() 1200 }