github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/internal/metamorphic/ops.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 "io" 11 "math/rand" 12 "path/filepath" 13 "strings" 14 15 "github.com/cockroachdb/errors" 16 "github.com/zuoyebang/bitalostable" 17 "github.com/zuoyebang/bitalostable/internal/base" 18 "github.com/zuoyebang/bitalostable/internal/errorfs" 19 "github.com/zuoyebang/bitalostable/internal/keyspan" 20 "github.com/zuoyebang/bitalostable/internal/private" 21 "github.com/zuoyebang/bitalostable/internal/testkeys/blockprop" 22 "github.com/zuoyebang/bitalostable/sstable" 23 "github.com/zuoyebang/bitalostable/vfs" 24 ) 25 26 // op defines the interface for a single operation, such as creating a batch, 27 // or advancing an iterator. 28 type op interface { 29 run(t *test, h *history) 30 String() string 31 } 32 33 // initOp performs test initialization 34 type initOp struct { 35 batchSlots uint32 36 iterSlots uint32 37 snapshotSlots uint32 38 } 39 40 func (o *initOp) run(t *test, h *history) { 41 t.batches = make([]*bitalostable.Batch, o.batchSlots) 42 t.iters = make([]*retryableIter, o.iterSlots) 43 t.snapshots = make([]*bitalostable.Snapshot, o.snapshotSlots) 44 h.Recordf("%s", o) 45 } 46 47 func (o *initOp) String() string { 48 return fmt.Sprintf("Init(%d /* batches */, %d /* iters */, %d /* snapshots */)", 49 o.batchSlots, o.iterSlots, o.snapshotSlots) 50 } 51 52 // applyOp models a Writer.Apply operation. 53 type applyOp struct { 54 writerID objID 55 batchID objID 56 } 57 58 func (o *applyOp) run(t *test, h *history) { 59 b := t.getBatch(o.batchID) 60 w := t.getWriter(o.writerID) 61 err := w.Apply(b, t.writeOpts) 62 h.Recordf("%s // %v", o, err) 63 _ = b.Close() 64 t.clearObj(o.batchID) 65 } 66 67 func (o *applyOp) String() string { 68 return fmt.Sprintf("%s.Apply(%s)", o.writerID, o.batchID) 69 } 70 71 // checkpointOp models a DB.Checkpoint operation. 72 type checkpointOp struct{} 73 74 func (o *checkpointOp) run(t *test, h *history) { 75 err := withRetries(func() error { 76 return t.db.Checkpoint(o.dir(t.dir, t.idx)) 77 }) 78 h.Recordf("%s // %v", o, err) 79 } 80 81 func (o *checkpointOp) dir(dataDir string, idx int) string { 82 return filepath.Join(dataDir, "checkpoints", fmt.Sprintf("op-%06d", idx)) 83 } 84 85 func (o *checkpointOp) String() string { 86 return "db.Checkpoint()" 87 } 88 89 // closeOp models a {Batch,Iterator,Snapshot}.Close operation. 90 type closeOp struct { 91 objID objID 92 } 93 94 func (o *closeOp) run(t *test, h *history) { 95 c := t.getCloser(o.objID) 96 t.clearObj(o.objID) 97 err := c.Close() 98 h.Recordf("%s // %v", o, err) 99 } 100 101 func (o *closeOp) String() string { 102 return fmt.Sprintf("%s.Close()", o.objID) 103 } 104 105 // compactOp models a DB.Compact operation. 106 type compactOp struct { 107 start []byte 108 end []byte 109 parallelize bool 110 } 111 112 func (o *compactOp) run(t *test, h *history) { 113 err := withRetries(func() error { 114 return t.db.Compact(o.start, o.end, o.parallelize) 115 }) 116 h.Recordf("%s // %v", o, err) 117 } 118 119 func (o *compactOp) String() string { 120 return fmt.Sprintf("db.Compact(%q, %q, %t /* parallelize */)", o.start, o.end, o.parallelize) 121 } 122 123 // deleteOp models a Write.Delete operation. 124 type deleteOp struct { 125 writerID objID 126 key []byte 127 } 128 129 func (o *deleteOp) run(t *test, h *history) { 130 w := t.getWriter(o.writerID) 131 err := w.Delete(o.key, t.writeOpts) 132 h.Recordf("%s // %v", o, err) 133 } 134 135 func (o *deleteOp) String() string { 136 return fmt.Sprintf("%s.Delete(%q)", o.writerID, o.key) 137 } 138 139 // singleDeleteOp models a Write.SingleDelete operation. 140 type singleDeleteOp struct { 141 writerID objID 142 key []byte 143 maybeReplaceDelete bool 144 } 145 146 func (o *singleDeleteOp) run(t *test, h *history) { 147 w := t.getWriter(o.writerID) 148 var err error 149 if t.testOpts.replaceSingleDelete && o.maybeReplaceDelete { 150 err = w.Delete(o.key, t.writeOpts) 151 } else { 152 err = w.SingleDelete(o.key, t.writeOpts) 153 } 154 // NOTE: even if the SINGLEDEL was replaced with a DELETE, we must still 155 // write the former to the history log. The log line will indicate whether 156 // or not the delete *could* have been replaced. The OPTIONS file should 157 // also be consulted to determine what happened at runtime (i.e. by taking 158 // the logical AND). 159 h.Recordf("%s // %v", o, err) 160 } 161 162 func (o *singleDeleteOp) String() string { 163 return fmt.Sprintf("%s.SingleDelete(%q, %v /* maybeReplaceDelete */)", o.writerID, o.key, o.maybeReplaceDelete) 164 } 165 166 // deleteRangeOp models a Write.DeleteRange operation. 167 type deleteRangeOp struct { 168 writerID objID 169 start []byte 170 end []byte 171 } 172 173 func (o *deleteRangeOp) run(t *test, h *history) { 174 w := t.getWriter(o.writerID) 175 err := w.DeleteRange(o.start, o.end, t.writeOpts) 176 h.Recordf("%s // %v", o, err) 177 } 178 179 func (o *deleteRangeOp) String() string { 180 return fmt.Sprintf("%s.DeleteRange(%q, %q)", o.writerID, o.start, o.end) 181 } 182 183 // flushOp models a DB.Flush operation. 184 type flushOp struct { 185 } 186 187 func (o *flushOp) run(t *test, h *history) { 188 err := t.db.Flush() 189 h.Recordf("%s // %v", o, err) 190 } 191 192 func (o *flushOp) String() string { 193 return "db.Flush()" 194 } 195 196 // mergeOp models a Write.Merge operation. 197 type mergeOp struct { 198 writerID objID 199 key []byte 200 value []byte 201 } 202 203 func (o *mergeOp) run(t *test, h *history) { 204 w := t.getWriter(o.writerID) 205 err := w.Merge(o.key, o.value, t.writeOpts) 206 h.Recordf("%s // %v", o, err) 207 } 208 209 func (o *mergeOp) String() string { 210 return fmt.Sprintf("%s.Merge(%q, %q)", o.writerID, o.key, o.value) 211 } 212 213 // setOp models a Write.Set operation. 214 type setOp struct { 215 writerID objID 216 key []byte 217 value []byte 218 } 219 220 func (o *setOp) run(t *test, h *history) { 221 w := t.getWriter(o.writerID) 222 err := w.Set(o.key, o.value, t.writeOpts) 223 h.Recordf("%s // %v", o, err) 224 } 225 226 func (o *setOp) String() string { 227 return fmt.Sprintf("%s.Set(%q, %q)", o.writerID, o.key, o.value) 228 } 229 230 // rangeKeyDeleteOp models a Write.RangeKeyDelete operation. 231 type rangeKeyDeleteOp struct { 232 writerID objID 233 start []byte 234 end []byte 235 } 236 237 func (o *rangeKeyDeleteOp) run(t *test, h *history) { 238 w := t.getWriter(o.writerID) 239 err := w.RangeKeyDelete(o.start, o.end, t.writeOpts) 240 h.Recordf("%s // %v", o, err) 241 } 242 243 func (o *rangeKeyDeleteOp) String() string { 244 return fmt.Sprintf("%s.RangeKeyDelete(%q, %q)", o.writerID, o.start, o.end) 245 } 246 247 // rangeKeySetOp models a Write.RangeKeySet operation. 248 type rangeKeySetOp struct { 249 writerID objID 250 start []byte 251 end []byte 252 suffix []byte 253 value []byte 254 } 255 256 func (o *rangeKeySetOp) run(t *test, h *history) { 257 w := t.getWriter(o.writerID) 258 err := w.RangeKeySet(o.start, o.end, o.suffix, o.value, t.writeOpts) 259 h.Recordf("%s // %v", o, err) 260 } 261 262 func (o *rangeKeySetOp) String() string { 263 return fmt.Sprintf("%s.RangeKeySet(%q, %q, %q, %q)", 264 o.writerID, o.start, o.end, o.suffix, o.value) 265 } 266 267 // rangeKeyUnsetOp models a Write.RangeKeyUnset operation. 268 type rangeKeyUnsetOp struct { 269 writerID objID 270 start []byte 271 end []byte 272 suffix []byte 273 } 274 275 func (o *rangeKeyUnsetOp) run(t *test, h *history) { 276 w := t.getWriter(o.writerID) 277 err := w.RangeKeyUnset(o.start, o.end, o.suffix, t.writeOpts) 278 h.Recordf("%s // %v", o, err) 279 } 280 281 func (o *rangeKeyUnsetOp) String() string { 282 return fmt.Sprintf("%s.RangeKeyUnset(%q, %q, %q)", 283 o.writerID, o.start, o.end, o.suffix) 284 } 285 286 // newBatchOp models a Write.NewBatch operation. 287 type newBatchOp struct { 288 batchID objID 289 } 290 291 func (o *newBatchOp) run(t *test, h *history) { 292 b := t.db.NewBatch() 293 t.setBatch(o.batchID, b) 294 h.Recordf("%s", o) 295 } 296 297 func (o *newBatchOp) String() string { 298 return fmt.Sprintf("%s = db.NewBatch()", o.batchID) 299 } 300 301 // newIndexedBatchOp models a Write.NewIndexedBatch operation. 302 type newIndexedBatchOp struct { 303 batchID objID 304 } 305 306 func (o *newIndexedBatchOp) run(t *test, h *history) { 307 b := t.db.NewIndexedBatch() 308 t.setBatch(o.batchID, b) 309 h.Recordf("%s", o) 310 } 311 312 func (o *newIndexedBatchOp) String() string { 313 return fmt.Sprintf("%s = db.NewIndexedBatch()", o.batchID) 314 } 315 316 // batchCommitOp models a Batch.Commit operation. 317 type batchCommitOp struct { 318 batchID objID 319 } 320 321 func (o *batchCommitOp) run(t *test, h *history) { 322 b := t.getBatch(o.batchID) 323 t.clearObj(o.batchID) 324 err := b.Commit(t.writeOpts) 325 h.Recordf("%s // %v", o, err) 326 } 327 328 func (o *batchCommitOp) String() string { 329 return fmt.Sprintf("%s.Commit()", o.batchID) 330 } 331 332 // ingestOp models a DB.Ingest operation. 333 type ingestOp struct { 334 batchIDs []objID 335 } 336 337 func (o *ingestOp) run(t *test, h *history) { 338 // We can only use apply as an alternative for ingestion if we are ingesting 339 // a single batch. If we are ingesting multiple batches, the batches may 340 // overlap which would cause ingestion to fail but apply would succeed. 341 if t.testOpts.ingestUsingApply && len(o.batchIDs) == 1 { 342 id := o.batchIDs[0] 343 b := t.getBatch(id) 344 iter, rangeDelIter, rangeKeyIter := private.BatchSort(b) 345 c, err := o.collapseBatch(t, iter, rangeDelIter, rangeKeyIter) 346 if err == nil { 347 w := t.getWriter(makeObjID(dbTag, 0)) 348 err = w.Apply(c, t.writeOpts) 349 } 350 _ = b.Close() 351 _ = c.Close() 352 t.clearObj(id) 353 h.Recordf("%s // %v", o, err) 354 return 355 } 356 357 var paths []string 358 var err error 359 for i, id := range o.batchIDs { 360 b := t.getBatch(id) 361 t.clearObj(id) 362 path, err2 := o.build(t, h, b, i) 363 if err2 != nil { 364 h.Recordf("Build(%s) // %v", id, err2) 365 } 366 err = firstError(err, err2) 367 if err2 == nil { 368 paths = append(paths, path) 369 } 370 err = firstError(err, b.Close()) 371 } 372 373 err = firstError(err, withRetries(func() error { 374 return t.db.Ingest(paths) 375 })) 376 377 h.Recordf("%s // %v", o, err) 378 } 379 380 func (o *ingestOp) build(t *test, h *history, b *bitalostable.Batch, i int) (string, error) { 381 rootFS := vfs.Root(t.opts.FS) 382 path := rootFS.PathJoin(t.tmpDir, fmt.Sprintf("ext%d", i)) 383 f, err := rootFS.Create(path) 384 if err != nil { 385 return "", err 386 } 387 388 iter, rangeDelIter, rangeKeyIter := private.BatchSort(b) 389 defer closeIters(iter, rangeDelIter, rangeKeyIter) 390 391 equal := t.opts.Comparer.Equal 392 tableFormat := t.db.FormatMajorVersion().MaxTableFormat() 393 w := sstable.NewWriter(f, t.opts.MakeWriterOptions(0, tableFormat)) 394 395 var lastUserKey []byte 396 for key, value := iter.First(); key != nil; key, value = iter.Next() { 397 // Ignore duplicate keys. 398 if equal(lastUserKey, key.UserKey) { 399 continue 400 } 401 // NB: We don't have to copy the key or value since we're reading from a 402 // batch which doesn't do prefix compression. 403 lastUserKey = key.UserKey 404 405 key.SetSeqNum(0) 406 if err := w.Add(*key, value); err != nil { 407 return "", err 408 } 409 } 410 if err := iter.Close(); err != nil { 411 return "", err 412 } 413 iter = nil 414 415 if rangeDelIter != nil { 416 // NB: The range tombstones have already been fragmented by the Batch. 417 for t := rangeDelIter.First(); t != nil; t = rangeDelIter.Next() { 418 // NB: We don't have to copy the key or value since we're reading from a 419 // batch which doesn't do prefix compression. 420 if err := w.DeleteRange(t.Start, t.End); err != nil { 421 return "", err 422 } 423 } 424 if err := rangeDelIter.Close(); err != nil { 425 return "", err 426 } 427 rangeDelIter = nil 428 } 429 430 if err := w.Close(); err != nil { 431 return "", err 432 } 433 return path, nil 434 } 435 436 func closeIters( 437 pointIter base.InternalIterator, 438 rangeDelIter keyspan.FragmentIterator, 439 rangeKeyIter keyspan.FragmentIterator, 440 ) { 441 if pointIter != nil { 442 pointIter.Close() 443 } 444 if rangeDelIter != nil { 445 rangeDelIter.Close() 446 } 447 if rangeKeyIter != nil { 448 rangeKeyIter.Close() 449 } 450 } 451 452 // collapseBatch collapses the mutations in a batch to be equivalent to an 453 // sstable ingesting those mutations. Duplicate updates to a key are collapsed 454 // so that only the latest update is performed. All range deletions are 455 // performed first in the batch to match the semantics of ingestion where a 456 // range deletion does not delete a point record contained in the sstable. 457 func (o *ingestOp) collapseBatch( 458 t *test, pointIter base.InternalIterator, rangeDelIter, rangeKeyIter keyspan.FragmentIterator, 459 ) (*bitalostable.Batch, error) { 460 defer closeIters(pointIter, rangeDelIter, rangeKeyIter) 461 equal := t.opts.Comparer.Equal 462 collapsed := t.db.NewBatch() 463 464 if rangeDelIter != nil { 465 // NB: The range tombstones have already been fragmented by the Batch. 466 for t := rangeDelIter.First(); t != nil; t = rangeDelIter.Next() { 467 // NB: We don't have to copy the key or value since we're reading from a 468 // batch which doesn't do prefix compression. 469 if err := collapsed.DeleteRange(t.Start, t.End, nil); err != nil { 470 return nil, err 471 } 472 } 473 if err := rangeDelIter.Close(); err != nil { 474 return nil, err 475 } 476 rangeDelIter = nil 477 } 478 479 if pointIter != nil { 480 var lastUserKey []byte 481 for key, value := pointIter.First(); key != nil; key, value = pointIter.Next() { 482 // Ignore duplicate keys. 483 if equal(lastUserKey, key.UserKey) { 484 continue 485 } 486 // NB: We don't have to copy the key or value since we're reading from a 487 // batch which doesn't do prefix compression. 488 lastUserKey = key.UserKey 489 490 var err error 491 switch key.Kind() { 492 case bitalostable.InternalKeyKindDelete: 493 err = collapsed.Delete(key.UserKey, nil) 494 case bitalostable.InternalKeyKindSingleDelete: 495 err = collapsed.SingleDelete(key.UserKey, nil) 496 case bitalostable.InternalKeyKindSet: 497 err = collapsed.Set(key.UserKey, value, nil) 498 case bitalostable.InternalKeyKindMerge: 499 err = collapsed.Merge(key.UserKey, value, nil) 500 case bitalostable.InternalKeyKindLogData: 501 err = collapsed.LogData(key.UserKey, nil) 502 default: 503 err = errors.Errorf("unknown batch record kind: %d", key.Kind()) 504 } 505 if err != nil { 506 return nil, err 507 } 508 } 509 if err := pointIter.Close(); err != nil { 510 return nil, err 511 } 512 pointIter = nil 513 } 514 515 return collapsed, nil 516 } 517 518 func (o *ingestOp) String() string { 519 var buf strings.Builder 520 buf.WriteString("db.Ingest(") 521 for i, id := range o.batchIDs { 522 if i > 0 { 523 buf.WriteString(", ") 524 } 525 buf.WriteString(id.String()) 526 } 527 buf.WriteString(")") 528 return buf.String() 529 } 530 531 // getOp models a Reader.Get operation. 532 type getOp struct { 533 readerID objID 534 key []byte 535 } 536 537 func (o *getOp) run(t *test, h *history) { 538 r := t.getReader(o.readerID) 539 var val []byte 540 var closer io.Closer 541 err := withRetries(func() (err error) { 542 val, closer, err = r.Get(o.key) 543 return err 544 }) 545 h.Recordf("%s // [%q] %v", o, val, err) 546 if closer != nil { 547 closer.Close() 548 } 549 } 550 551 func (o *getOp) String() string { 552 return fmt.Sprintf("%s.Get(%q)", o.readerID, o.key) 553 } 554 555 // newIterOp models a Reader.NewIter operation. 556 type newIterOp struct { 557 readerID objID 558 iterID objID 559 iterOpts 560 } 561 562 func (o *newIterOp) run(t *test, h *history) { 563 r := t.getReader(o.readerID) 564 opts := iterOptions(o.iterOpts) 565 566 var i *bitalostable.Iterator 567 for { 568 i = r.NewIter(opts) 569 if err := i.Error(); !errors.Is(err, errorfs.ErrInjected) { 570 break 571 } 572 // close this iter and retry NewIter 573 _ = i.Close() 574 } 575 t.setIter(o.iterID, i, o.filterMin, o.filterMax) 576 577 // Trash the bounds to ensure that Pebble doesn't rely on the stability of 578 // the user-provided bounds. 579 if opts != nil { 580 rand.Read(opts.LowerBound[:]) 581 rand.Read(opts.UpperBound[:]) 582 } 583 h.Recordf("%s // %v", o, i.Error()) 584 } 585 586 func (o *newIterOp) String() string { 587 return fmt.Sprintf("%s = %s.NewIter(%q, %q, %d /* key types */, %d, %d, %q /* masking suffix */)", 588 o.iterID, o.readerID, o.lower, o.upper, o.keyTypes, o.filterMin, o.filterMax, o.maskSuffix) 589 } 590 591 // newIterUsingCloneOp models a Iterator.Clone operation. 592 type newIterUsingCloneOp struct { 593 existingIterID objID 594 iterID objID 595 refreshBatch bool 596 iterOpts 597 } 598 599 func (o *newIterUsingCloneOp) run(t *test, h *history) { 600 iter := t.getIter(o.existingIterID) 601 cloneOpts := bitalostable.CloneOptions{ 602 IterOptions: iterOptions(o.iterOpts), 603 RefreshBatchView: o.refreshBatch, 604 } 605 i, err := iter.iter.Clone(cloneOpts) 606 if err != nil { 607 panic(err) 608 } 609 filterMin, filterMax := o.filterMin, o.filterMax 610 if cloneOpts.IterOptions == nil { 611 // We're adopting the same block property filters as iter, so we need to 612 // adopt the same run-time filters to ensure determinism. 613 filterMin, filterMax = iter.filterMin, iter.filterMax 614 } 615 t.setIter(o.iterID, i, filterMin, filterMax) 616 h.Recordf("%s // %v", o, i.Error()) 617 } 618 619 func (o *newIterUsingCloneOp) String() string { 620 return fmt.Sprintf("%s = %s.Clone(%t, %q, %q, %d /* key types */, %d, %d, %q /* masking suffix */)", 621 o.iterID, o.existingIterID, o.refreshBatch, o.lower, o.upper, 622 o.keyTypes, o.filterMin, o.filterMax, o.maskSuffix) 623 } 624 625 // iterSetBoundsOp models an Iterator.SetBounds operation. 626 type iterSetBoundsOp struct { 627 iterID objID 628 lower []byte 629 upper []byte 630 } 631 632 func (o *iterSetBoundsOp) run(t *test, h *history) { 633 i := t.getIter(o.iterID) 634 var lower, upper []byte 635 if o.lower != nil { 636 lower = append(lower, o.lower...) 637 } 638 if o.upper != nil { 639 upper = append(upper, o.upper...) 640 } 641 i.SetBounds(lower, upper) 642 643 // Trash the bounds to ensure that Pebble doesn't rely on the stability of 644 // the user-provided bounds. 645 rand.Read(lower[:]) 646 rand.Read(upper[:]) 647 648 h.Recordf("%s // %v", o, i.Error()) 649 } 650 651 func (o *iterSetBoundsOp) String() string { 652 return fmt.Sprintf("%s.SetBounds(%q, %q)", o.iterID, o.lower, o.upper) 653 } 654 655 // iterSetOptionsOp models an Iterator.SetOptions operation. 656 type iterSetOptionsOp struct { 657 iterID objID 658 iterOpts 659 } 660 661 func (o *iterSetOptionsOp) run(t *test, h *history) { 662 i := t.getIter(o.iterID) 663 664 opts := iterOptions(o.iterOpts) 665 if opts == nil { 666 opts = &bitalostable.IterOptions{} 667 } 668 i.SetOptions(opts) 669 670 // Trash the bounds to ensure that Pebble doesn't rely on the stability of 671 // the user-provided bounds. 672 rand.Read(opts.LowerBound[:]) 673 rand.Read(opts.UpperBound[:]) 674 675 // Adjust the iterator's filters. 676 i.filterMin, i.filterMax = o.filterMin, o.filterMax 677 678 h.Recordf("%s // %v", o, i.Error()) 679 } 680 681 func (o *iterSetOptionsOp) String() string { 682 return fmt.Sprintf("%s.SetOptions(%q, %q, %d /* key types */, %d, %d, %q /* masking suffix */)", 683 o.iterID, o.lower, o.upper, o.keyTypes, o.filterMin, o.filterMax, o.maskSuffix) 684 } 685 686 func iterOptions(o iterOpts) *bitalostable.IterOptions { 687 if o.IsZero() { 688 return nil 689 } 690 var lower, upper []byte 691 if o.lower != nil { 692 lower = append(lower, o.lower...) 693 } 694 if o.upper != nil { 695 upper = append(upper, o.upper...) 696 } 697 opts := &bitalostable.IterOptions{ 698 LowerBound: lower, 699 UpperBound: upper, 700 KeyTypes: bitalostable.IterKeyType(o.keyTypes), 701 RangeKeyMasking: bitalostable.RangeKeyMasking{ 702 Suffix: o.maskSuffix, 703 }, 704 } 705 if opts.RangeKeyMasking.Suffix != nil { 706 opts.RangeKeyMasking.Filter = func() bitalostable.BlockPropertyFilterMask { 707 return blockprop.NewMaskingFilter() 708 } 709 } 710 if o.filterMax > 0 { 711 opts.PointKeyFilters = []bitalostable.BlockPropertyFilter{ 712 blockprop.NewBlockPropertyFilter(o.filterMin, o.filterMax), 713 } 714 } 715 return opts 716 } 717 718 // iterSeekGEOp models an Iterator.SeekGE[WithLimit] operation. 719 type iterSeekGEOp struct { 720 iterID objID 721 key []byte 722 limit []byte 723 } 724 725 func iteratorPos(i *retryableIter) string { 726 var buf bytes.Buffer 727 fmt.Fprintf(&buf, "%q", i.Key()) 728 hasPoint, hasRange := i.HasPointAndRange() 729 if hasPoint { 730 fmt.Fprintf(&buf, ",%q", i.Value()) 731 } else { 732 fmt.Fprint(&buf, ",<no point>") 733 } 734 if hasRange { 735 start, end := i.RangeBounds() 736 fmt.Fprintf(&buf, ",[%q,%q)=>{", start, end) 737 for i, rk := range i.RangeKeys() { 738 if i > 0 { 739 fmt.Fprint(&buf, ",") 740 } 741 fmt.Fprintf(&buf, "%q=%q", rk.Suffix, rk.Value) 742 } 743 fmt.Fprint(&buf, "}") 744 } else { 745 fmt.Fprint(&buf, ",<no range>") 746 } 747 if i.RangeKeyChanged() { 748 fmt.Fprint(&buf, "*") 749 } 750 return buf.String() 751 } 752 753 func validBoolToStr(valid bool) string { 754 return fmt.Sprintf("%t", valid) 755 } 756 757 func validityStateToStr(validity bitalostable.IterValidityState) (bool, string) { 758 // We can't distinguish between IterExhausted and IterAtLimit in a 759 // deterministic manner. 760 switch validity { 761 case bitalostable.IterExhausted, bitalostable.IterAtLimit: 762 return false, "invalid" 763 case bitalostable.IterValid: 764 return true, "valid" 765 default: 766 panic("unknown validity") 767 } 768 } 769 770 func (o *iterSeekGEOp) run(t *test, h *history) { 771 i := t.getIter(o.iterID) 772 var valid bool 773 var validStr string 774 if o.limit == nil { 775 valid = i.SeekGE(o.key) 776 validStr = validBoolToStr(valid) 777 } else { 778 valid, validStr = validityStateToStr(i.SeekGEWithLimit(o.key, o.limit)) 779 } 780 if valid { 781 h.Recordf("%s // [%s,%s] %v", o, validStr, iteratorPos(i), i.Error()) 782 } else { 783 h.Recordf("%s // [%s] %v", o, validStr, i.Error()) 784 } 785 } 786 787 func (o *iterSeekGEOp) String() string { 788 return fmt.Sprintf("%s.SeekGE(%q, %q)", o.iterID, o.key, o.limit) 789 } 790 791 // iterSeekPrefixGEOp models an Iterator.SeekPrefixGE operation. 792 type iterSeekPrefixGEOp struct { 793 iterID objID 794 key []byte 795 } 796 797 func (o *iterSeekPrefixGEOp) run(t *test, h *history) { 798 i := t.getIter(o.iterID) 799 valid := i.SeekPrefixGE(o.key) 800 if valid { 801 h.Recordf("%s // [%t,%s] %v", o, valid, iteratorPos(i), i.Error()) 802 } else { 803 h.Recordf("%s // [%t] %v", o, valid, i.Error()) 804 } 805 } 806 807 func (o *iterSeekPrefixGEOp) String() string { 808 return fmt.Sprintf("%s.SeekPrefixGE(%q)", o.iterID, o.key) 809 } 810 811 // iterSeekLTOp models an Iterator.SeekLT[WithLimit] operation. 812 type iterSeekLTOp struct { 813 iterID objID 814 key []byte 815 limit []byte 816 } 817 818 func (o *iterSeekLTOp) run(t *test, h *history) { 819 i := t.getIter(o.iterID) 820 var valid bool 821 var validStr string 822 if o.limit == nil { 823 valid = i.SeekLT(o.key) 824 validStr = validBoolToStr(valid) 825 } else { 826 valid, validStr = validityStateToStr(i.SeekLTWithLimit(o.key, o.limit)) 827 } 828 if valid { 829 h.Recordf("%s // [%s,%s] %v", o, validStr, iteratorPos(i), i.Error()) 830 } else { 831 h.Recordf("%s // [%s] %v", o, validStr, i.Error()) 832 } 833 } 834 835 func (o *iterSeekLTOp) String() string { 836 return fmt.Sprintf("%s.SeekLT(%q, %q)", o.iterID, o.key, o.limit) 837 } 838 839 // iterFirstOp models an Iterator.First operation. 840 type iterFirstOp struct { 841 iterID objID 842 } 843 844 func (o *iterFirstOp) run(t *test, h *history) { 845 i := t.getIter(o.iterID) 846 valid := i.First() 847 if valid { 848 h.Recordf("%s // [%t,%s] %v", o, valid, iteratorPos(i), i.Error()) 849 } else { 850 h.Recordf("%s // [%t] %v", o, valid, i.Error()) 851 } 852 } 853 854 func (o *iterFirstOp) String() string { 855 return fmt.Sprintf("%s.First()", o.iterID) 856 } 857 858 // iterLastOp models an Iterator.Last operation. 859 type iterLastOp struct { 860 iterID objID 861 } 862 863 func (o *iterLastOp) run(t *test, h *history) { 864 i := t.getIter(o.iterID) 865 valid := i.Last() 866 if valid { 867 h.Recordf("%s // [%t,%s] %v", o, valid, iteratorPos(i), i.Error()) 868 } else { 869 h.Recordf("%s // [%t] %v", o, valid, i.Error()) 870 } 871 } 872 873 func (o *iterLastOp) String() string { 874 return fmt.Sprintf("%s.Last()", o.iterID) 875 } 876 877 // iterNextOp models an Iterator.Next[WithLimit] operation. 878 type iterNextOp struct { 879 iterID objID 880 limit []byte 881 } 882 883 func (o *iterNextOp) run(t *test, h *history) { 884 i := t.getIter(o.iterID) 885 var valid bool 886 var validStr string 887 if o.limit == nil { 888 valid = i.Next() 889 validStr = validBoolToStr(valid) 890 } else { 891 valid, validStr = validityStateToStr(i.NextWithLimit(o.limit)) 892 } 893 if valid { 894 h.Recordf("%s // [%s,%s] %v", o, validStr, iteratorPos(i), i.Error()) 895 } else { 896 h.Recordf("%s // [%s] %v", o, validStr, i.Error()) 897 } 898 } 899 900 func (o *iterNextOp) String() string { 901 return fmt.Sprintf("%s.Next(%q)", o.iterID, o.limit) 902 } 903 904 // iterPrevOp models an Iterator.Prev[WithLimit] operation. 905 type iterPrevOp struct { 906 iterID objID 907 limit []byte 908 } 909 910 func (o *iterPrevOp) run(t *test, h *history) { 911 i := t.getIter(o.iterID) 912 var valid bool 913 var validStr string 914 if o.limit == nil { 915 valid = i.Prev() 916 validStr = validBoolToStr(valid) 917 } else { 918 valid, validStr = validityStateToStr(i.PrevWithLimit(o.limit)) 919 } 920 if valid { 921 h.Recordf("%s // [%s,%s] %v", o, validStr, iteratorPos(i), i.Error()) 922 } else { 923 h.Recordf("%s // [%s] %v", o, validStr, i.Error()) 924 } 925 } 926 927 func (o *iterPrevOp) String() string { 928 return fmt.Sprintf("%s.Prev(%q)", o.iterID, o.limit) 929 } 930 931 // newSnapshotOp models a DB.NewSnapshot operation. 932 type newSnapshotOp struct { 933 snapID objID 934 } 935 936 func (o *newSnapshotOp) run(t *test, h *history) { 937 s := t.db.NewSnapshot() 938 t.setSnapshot(o.snapID, s) 939 h.Recordf("%s", o) 940 } 941 942 func (o *newSnapshotOp) String() string { 943 return fmt.Sprintf("%s = db.NewSnapshot()", o.snapID) 944 } 945 946 type dbRestartOp struct { 947 } 948 949 func (o *dbRestartOp) run(t *test, h *history) { 950 if err := t.restartDB(); err != nil { 951 h.Recordf("%s // %v", o, err) 952 h.err.Store(errors.Wrap(err, "dbRestartOp")) 953 } else { 954 h.Recordf("%s", o) 955 } 956 } 957 958 func (o *dbRestartOp) String() string { 959 return "db.Restart()" 960 } 961 962 func formatOps(ops []op) string { 963 var buf strings.Builder 964 for _, op := range ops { 965 fmt.Fprintf(&buf, "%s\n", op) 966 } 967 return buf.String() 968 }