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  }