LCOV - code coverage report
Current view: top level - pebble/metamorphic - config.go (source / functions) Hit Total Coverage
Test: 2025-01-03 08:17Z 465b9db0 - meta test only.lcov Lines: 91 203 44.8 %
Date: 2025-01-03 08:18:42 Functions: 0 0 -

          Line data    Source code
       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             :         "math/rand/v2"
       9             : 
      10             :         "github.com/cockroachdb/pebble"
      11             :         "github.com/cockroachdb/pebble/internal/base"
      12             :         "github.com/cockroachdb/pebble/internal/randvar"
      13             :         "github.com/cockroachdb/pebble/sstable"
      14             : )
      15             : 
      16             : // OpType is an enum of possible operation types.
      17             : type OpType int
      18             : 
      19             : // These constants define the set of possible operation types performed by the
      20             : // metamorphic test.
      21             : const (
      22             :         OpBatchAbort OpType = iota
      23             :         OpBatchCommit
      24             :         OpDBCheckpoint
      25             :         OpDBClose
      26             :         OpDBCompact
      27             :         OpDBDownload
      28             :         OpDBFlush
      29             :         OpDBRatchetFormatMajorVersion
      30             :         OpDBRestart
      31             :         OpIterClose
      32             :         OpIterFirst
      33             :         OpIterLast
      34             :         OpIterNext
      35             :         OpIterNextWithLimit
      36             :         OpIterNextPrefix
      37             :         OpIterCanSingleDelete
      38             :         OpIterPrev
      39             :         OpIterPrevWithLimit
      40             :         OpIterSeekGE
      41             :         OpIterSeekGEWithLimit
      42             :         OpIterSeekLT
      43             :         OpIterSeekLTWithLimit
      44             :         OpIterSeekPrefixGE
      45             :         OpIterSetBounds
      46             :         OpIterSetOptions
      47             :         OpNewBatch
      48             :         OpNewIndexedBatch
      49             :         OpNewIter
      50             :         OpNewIterUsingClone
      51             :         OpNewSnapshot
      52             :         OpNewExternalObj
      53             :         OpReaderGet
      54             :         OpReplicate
      55             :         OpSnapshotClose
      56             :         OpWriterApply
      57             :         OpWriterDelete
      58             :         OpWriterDeleteRange
      59             :         OpWriterIngest
      60             :         OpWriterIngestAndExcise
      61             :         OpWriterIngestExternalFiles
      62             :         OpWriterLogData
      63             :         OpWriterMerge
      64             :         OpWriterRangeKeyDelete
      65             :         OpWriterRangeKeySet
      66             :         OpWriterRangeKeyUnset
      67             :         OpWriterSet
      68             :         OpWriterSingleDelete
      69             :         NumOpTypes
      70             : )
      71             : 
      72           0 : func (o OpType) isDelete() bool {
      73           0 :         return o == OpWriterDelete || o == OpWriterDeleteRange || o == OpWriterSingleDelete
      74           0 : }
      75             : 
      76             : // OpConfig describes the distribution of operations and their attributes.
      77             : type OpConfig struct {
      78             :         // Weights for the operation mix to generate. ops[i] corresponds to the
      79             :         // weight for opType(i).
      80             :         ops [NumOpTypes]int
      81             : 
      82             :         // newPrefix configures the probability that when generating a new user key,
      83             :         // the generated key uses a new key prefix rather than an existing prefix
      84             :         // with a suffix.
      85             :         newPrefix float64
      86             :         // writeSuffixDist defines the distribution of key suffixes during writing.
      87             :         // It's a dynamic randvar to roughly emulate workloads with MVCC timestamps,
      88             :         // skewing towards most recent timestamps.
      89             :         writeSuffixDist randvar.Dynamic
      90             : 
      91             :         // numInstances defines the number of pebble instances created for this
      92             :         // metamorphic test run.
      93             :         numInstances int
      94             : 
      95             :         // TODO(peter): unimplemented
      96             :         // keyDist        randvar.Dynamic
      97             :         // keySizeDist    randvar.Static
      98             :         // valueSizeDist  randvar.Static
      99             :         // updateFrac     float64
     100             :         // lowerBoundFrac float64
     101             :         // upperBoundFrac float64
     102             : }
     103             : 
     104             : // WithNewPrefixProbability returns a modified op configuration with the
     105             : // probability of generating a new key prefix set to the provided value in
     106             : // [0,1.0].
     107           1 : func (c OpConfig) WithNewPrefixProbability(p float64) OpConfig {
     108           1 :         c.newPrefix = p
     109           1 :         return c
     110           1 : }
     111             : 
     112             : // WithOpWeight returns a modified op configuration with the weight of the
     113             : // provided operation type overidden.
     114           1 : func (c OpConfig) WithOpWeight(op OpType, weight int) OpConfig {
     115           1 :         c.ops[op] = weight
     116           1 :         return c
     117           1 : }
     118             : 
     119             : var presetConfigs = []OpConfig{
     120             :         DefaultOpConfig(),
     121             :         // Generate a configuration that helps exercise code paths dependent on many
     122             :         // versions of keys with the same prefixes. The default configuration does
     123             :         // not tend to generate many versions of the same key. Additionally, its
     124             :         // relatively high weight for deletion write operations makes it less likely
     125             :         // that we'll accumulate enough versions to exercise some code paths (eg,
     126             :         // see #2921 which requires >16 SETs for versions of the same prefix to
     127             :         // reside in a single block to exercise the code path).
     128             :         //
     129             :         // To encourage generation of many versions of the same keys, generate a new
     130             :         // prefix only 4% of the time when generating a new key. The remaining 96%
     131             :         // of new key generations will use an existing prefix. To keep the size of
     132             :         // the database growing, we also reduce the probability of delete write
     133             :         // operations significantly.
     134             :         DefaultOpConfig().
     135             :                 WithNewPrefixProbability(0.04).
     136             :                 WithOpWeight(OpWriterDeleteRange, 1).
     137             :                 WithOpWeight(OpWriterDelete, 5).
     138             :                 WithOpWeight(OpWriterSingleDelete, 5).
     139             :                 WithOpWeight(OpWriterMerge, 0),
     140             : }
     141             : 
     142             : var multiInstancePresetConfig = multiInstanceConfig()
     143             : 
     144             : // DefaultOpConfig returns the default distribution of operations.
     145           1 : func DefaultOpConfig() OpConfig {
     146           1 :         return OpConfig{
     147           1 :                 // dbClose is not in this list since it is deterministically generated once, at the end of the test.
     148           1 :                 ops: [NumOpTypes]int{
     149           1 :                         OpBatchAbort:                  5,
     150           1 :                         OpBatchCommit:                 5,
     151           1 :                         OpDBCheckpoint:                1,
     152           1 :                         OpDBCompact:                   1,
     153           1 :                         OpDBDownload:                  1,
     154           1 :                         OpDBFlush:                     2,
     155           1 :                         OpDBRatchetFormatMajorVersion: 1,
     156           1 :                         OpDBRestart:                   2,
     157           1 :                         OpIterClose:                   5,
     158           1 :                         OpIterFirst:                   100,
     159           1 :                         OpIterLast:                    100,
     160           1 :                         OpIterNext:                    100,
     161           1 :                         OpIterNextWithLimit:           20,
     162           1 :                         OpIterNextPrefix:              20,
     163           1 :                         OpIterCanSingleDelete:         20,
     164           1 :                         OpIterPrev:                    100,
     165           1 :                         OpIterPrevWithLimit:           20,
     166           1 :                         OpIterSeekGE:                  100,
     167           1 :                         OpIterSeekGEWithLimit:         20,
     168           1 :                         OpIterSeekLT:                  100,
     169           1 :                         OpIterSeekLTWithLimit:         20,
     170           1 :                         OpIterSeekPrefixGE:            100,
     171           1 :                         OpIterSetBounds:               100,
     172           1 :                         OpIterSetOptions:              10,
     173           1 :                         OpNewBatch:                    5,
     174           1 :                         OpNewIndexedBatch:             5,
     175           1 :                         OpNewIter:                     10,
     176           1 :                         OpNewIterUsingClone:           5,
     177           1 :                         OpNewSnapshot:                 10,
     178           1 :                         OpReaderGet:                   100,
     179           1 :                         OpReplicate:                   0,
     180           1 :                         OpSnapshotClose:               10,
     181           1 :                         OpWriterApply:                 10,
     182           1 :                         OpWriterDelete:                100,
     183           1 :                         OpWriterDeleteRange:           50,
     184           1 :                         OpWriterIngest:                100,
     185           1 :                         OpWriterIngestAndExcise:       50,
     186           1 :                         OpWriterLogData:               10,
     187           1 :                         OpWriterMerge:                 100,
     188           1 :                         OpWriterRangeKeySet:           10,
     189           1 :                         OpWriterRangeKeyUnset:         10,
     190           1 :                         OpWriterRangeKeyDelete:        5,
     191           1 :                         OpWriterSet:                   100,
     192           1 :                         OpWriterSingleDelete:          50,
     193           1 :                         OpNewExternalObj:              5,
     194           1 :                         OpWriterIngestExternalFiles:   100,
     195           1 :                 },
     196           1 :                 // Use a new prefix 75% of the time (and 25% of the time use an existing
     197           1 :                 // prefix with an alternative suffix).
     198           1 :                 newPrefix: 0.75,
     199           1 :                 // Use a skewed distribution of suffixes to mimic MVCC timestamps. The
     200           1 :                 // range will be widened whenever a suffix is found to already be in use
     201           1 :                 // for a particular prefix.
     202           1 :                 writeSuffixDist: mustDynamic(randvar.NewSkewedLatest(0, 1, 0.99)),
     203           1 :         }
     204           1 : }
     205             : 
     206             : // ReadOpConfig builds an OpConfig that performs only read operations.
     207           0 : func ReadOpConfig() OpConfig {
     208           0 :         return OpConfig{
     209           0 :                 // dbClose is not in this list since it is deterministically generated once, at the end of the test.
     210           0 :                 ops: [NumOpTypes]int{
     211           0 :                         OpBatchAbort:                  0,
     212           0 :                         OpBatchCommit:                 0,
     213           0 :                         OpDBCheckpoint:                0,
     214           0 :                         OpDBCompact:                   0,
     215           0 :                         OpDBFlush:                     0,
     216           0 :                         OpDBRatchetFormatMajorVersion: 0,
     217           0 :                         OpDBRestart:                   0,
     218           0 :                         OpIterClose:                   5,
     219           0 :                         OpIterFirst:                   100,
     220           0 :                         OpIterLast:                    100,
     221           0 :                         OpIterNext:                    100,
     222           0 :                         OpIterNextWithLimit:           20,
     223           0 :                         OpIterNextPrefix:              20,
     224           0 :                         OpIterPrev:                    100,
     225           0 :                         OpIterPrevWithLimit:           20,
     226           0 :                         OpIterSeekGE:                  100,
     227           0 :                         OpIterSeekGEWithLimit:         20,
     228           0 :                         OpIterSeekLT:                  100,
     229           0 :                         OpIterSeekLTWithLimit:         20,
     230           0 :                         OpIterSeekPrefixGE:            100,
     231           0 :                         OpIterSetBounds:               100,
     232           0 :                         OpIterSetOptions:              10,
     233           0 :                         OpNewBatch:                    0,
     234           0 :                         OpNewIndexedBatch:             0,
     235           0 :                         OpNewIter:                     10,
     236           0 :                         OpNewIterUsingClone:           5,
     237           0 :                         OpNewSnapshot:                 10,
     238           0 :                         OpReaderGet:                   100,
     239           0 :                         OpSnapshotClose:               10,
     240           0 :                         OpWriterApply:                 0,
     241           0 :                         OpWriterDelete:                0,
     242           0 :                         OpWriterDeleteRange:           0,
     243           0 :                         OpWriterIngest:                0,
     244           0 :                         OpWriterLogData:               0,
     245           0 :                         OpWriterMerge:                 0,
     246           0 :                         OpWriterRangeKeySet:           0,
     247           0 :                         OpWriterRangeKeyUnset:         0,
     248           0 :                         OpWriterRangeKeyDelete:        0,
     249           0 :                         OpWriterSet:                   0,
     250           0 :                         OpWriterSingleDelete:          0,
     251           0 :                 },
     252           0 :                 // Use a new prefix 75% of the time (and 25% of the time use an existing
     253           0 :                 // prefix with an alternative suffix).
     254           0 :                 newPrefix: 0.75,
     255           0 :                 // Use a skewed distribution of suffixes to mimic MVCC timestamps. The
     256           0 :                 // range will be widened whenever a suffix is found to already be in use
     257           0 :                 // for a particular prefix.
     258           0 :                 writeSuffixDist: mustDynamic(randvar.NewSkewedLatest(0, 1, 0.99)),
     259           0 :         }
     260           0 : }
     261             : 
     262             : // WriteOpConfig builds an OpConfig suitable for generating a random test
     263             : // database. It generates Writer operations and some meta database operations
     264             : // like flushes and manual compactions, but it does not generate any reads.
     265           0 : func WriteOpConfig() OpConfig {
     266           0 :         return OpConfig{
     267           0 :                 // dbClose is not in this list since it is deterministically generated once, at the end of the test.
     268           0 :                 ops: [NumOpTypes]int{
     269           0 :                         OpBatchAbort:                  0,
     270           0 :                         OpBatchCommit:                 5,
     271           0 :                         OpDBCheckpoint:                0,
     272           0 :                         OpDBCompact:                   1,
     273           0 :                         OpDBFlush:                     2,
     274           0 :                         OpDBRatchetFormatMajorVersion: 1,
     275           0 :                         OpDBRestart:                   2,
     276           0 :                         OpIterClose:                   0,
     277           0 :                         OpIterFirst:                   0,
     278           0 :                         OpIterLast:                    0,
     279           0 :                         OpIterNext:                    0,
     280           0 :                         OpIterNextWithLimit:           0,
     281           0 :                         OpIterNextPrefix:              0,
     282           0 :                         OpIterPrev:                    0,
     283           0 :                         OpIterPrevWithLimit:           0,
     284           0 :                         OpIterSeekGE:                  0,
     285           0 :                         OpIterSeekGEWithLimit:         0,
     286           0 :                         OpIterSeekLT:                  0,
     287           0 :                         OpIterSeekLTWithLimit:         0,
     288           0 :                         OpIterSeekPrefixGE:            0,
     289           0 :                         OpIterSetBounds:               0,
     290           0 :                         OpIterSetOptions:              0,
     291           0 :                         OpNewBatch:                    10,
     292           0 :                         OpNewIndexedBatch:             0,
     293           0 :                         OpNewIter:                     0,
     294           0 :                         OpNewIterUsingClone:           0,
     295           0 :                         OpNewSnapshot:                 10,
     296           0 :                         OpReaderGet:                   0,
     297           0 :                         OpSnapshotClose:               10,
     298           0 :                         OpWriterApply:                 10,
     299           0 :                         OpWriterDelete:                100,
     300           0 :                         OpWriterDeleteRange:           20,
     301           0 :                         OpWriterIngest:                100,
     302           0 :                         OpWriterLogData:               10,
     303           0 :                         OpWriterMerge:                 100,
     304           0 :                         OpWriterRangeKeySet:           10,
     305           0 :                         OpWriterRangeKeyUnset:         10,
     306           0 :                         OpWriterRangeKeyDelete:        5,
     307           0 :                         OpWriterSet:                   100,
     308           0 :                         OpWriterSingleDelete:          50,
     309           0 :                 },
     310           0 :                 // Use a new prefix 75% of the time (and 25% of the time use an existing
     311           0 :                 // prefix with an alternative suffix).
     312           0 :                 newPrefix: 0.75,
     313           0 :                 // Use a skewed distribution of suffixes to mimic MVCC timestamps. The
     314           0 :                 // range will be widened whenever a suffix is found to already be in use
     315           0 :                 // for a particular prefix.
     316           0 :                 writeSuffixDist: mustDynamic(randvar.NewSkewedLatest(0, 1, 0.99)),
     317           0 :         }
     318           0 : }
     319             : 
     320           1 : func multiInstanceConfig() OpConfig {
     321           1 :         cfg := DefaultOpConfig()
     322           1 :         cfg.ops[OpReplicate] = 5
     323           1 :         // Single deletes and merges are disabled in multi-instance mode, as
     324           1 :         // replicateOp doesn't support them.
     325           1 :         cfg.ops[OpWriterSingleDelete] = 0
     326           1 :         cfg.ops[OpWriterMerge] = 0
     327           1 : 
     328           1 :         // TODO(radu): external file ingest doesn't yet work with OpReplicate ("cannot
     329           1 :         // use skip-shared iteration due to non-shareable files in lower levels").
     330           1 :         cfg.ops[OpNewExternalObj] = 0
     331           1 :         cfg.ops[OpWriterIngestExternalFiles] = 0
     332           1 :         return cfg
     333           1 : }
     334             : 
     335           1 : func mustDynamic(dyn randvar.Dynamic, err error) randvar.Dynamic {
     336           1 :         if err != nil {
     337           0 :                 panic(err)
     338             :         }
     339           1 :         return dyn
     340             : }
     341             : 
     342             : var knownKeyFormats = []KeyFormat{
     343             :         TestkeysKeyFormat,
     344             : }
     345             : 
     346           1 : var keyFormatsByName = func() map[string]KeyFormat {
     347           1 :         m := make(map[string]KeyFormat)
     348           1 :         for _, kf := range knownKeyFormats {
     349           1 :                 m[kf.Name] = kf
     350           1 :         }
     351           1 :         return m
     352             : }()
     353             : 
     354             : // A KeyFormat dictates the format of key-value pairs uses by the metamorphic
     355             : // test.
     356             : type KeyFormat struct {
     357             :         Name                         string
     358             :         Comparer                     *base.Comparer
     359             :         KeySchema                    *pebble.KeySchema
     360             :         FormatKey                    func(UserKey) string
     361             :         FormatKeySuffix              func(UserKeySuffix) string
     362             :         ParseFormattedKey            func(string) (UserKey, error)
     363             :         ParseFormattedKeySuffix      func(string) (UserKeySuffix, error)
     364             :         NewGenerator                 func(*keyManager, *rand.Rand, OpConfig) KeyGenerator
     365             :         NewSuffixFilterMask          func() pebble.BlockPropertyFilterMask
     366             :         NewSuffixBlockPropertyFilter func(min []byte, max []byte) sstable.BlockPropertyFilter
     367             : }
     368             : 
     369             : // KeyGenerator is an interface for generating keys, prefixes and suffixes.
     370             : type KeyGenerator interface {
     371             :         // IncMaxSuffix increases the max suffix range and returns the new maximum
     372             :         // suffix (which is guaranteed to be larger than any previously generated
     373             :         // suffix).
     374             :         IncMaxSuffix() []byte
     375             :         // RandKey returns a random key (either a previously known key, or a new
     376             :         // key). The provided probability determines the likelihood of generating a
     377             :         // new key.
     378             :         RandKey(newKeyProbability float64) []byte
     379             :         // RandKeyInRange returns a random key (either a previously known key, or a
     380             :         // new key) in the given key range.
     381             :         RandKeyInRange(newKeyProbability float64, kr pebble.KeyRange) []byte
     382             :         // RandPrefix returns a random prefix key (a key with no suffix). The
     383             :         // provided probability determines the likelihood of generating a new
     384             :         // prefix.
     385             :         RandPrefix(newPrefix float64) []byte
     386             :         // SkewedSuffix generates a random suffix according to the configuration's
     387             :         // suffix distribution. It takes a probability 0 ≤ p ≤ 1.0 indicating the
     388             :         // probability with which the generator should increase the max suffix
     389             :         // generated by the generator.
     390             :         //
     391             :         // May return a nil suffix, with the probability the configuration's suffix
     392             :         // distribution assigns to the zero suffix.
     393             :         SkewedSuffix(incMaxProb float64) []byte
     394             :         // SuffixRange generates a new uniformly random range of suffixes [low,
     395             :         // high) such that high is guaranteed to be strictly greater than low.
     396             :         SuffixRange() (low, high []byte)
     397             :         // UniformSuffix returns a suffix in the same range as SkewedSuffix but with
     398             :         // a uniform distribution. This is used during reads to better exercise
     399             :         // reading a mix of older and newer keys. The suffix can be empty.
     400             :         //
     401             :         // May return a nil suffix.
     402             :         UniformSuffix() []byte
     403             :         // UniqueKeys takes a key-generating function and uses it to generate n
     404             :         // unique keys, returning them in sorted order.
     405             :         UniqueKeys(n int, genFn func() []byte) [][]byte
     406             : }

Generated by: LCOV version 1.14