LCOV - code coverage report
Current view: top level - pebble/metamorphic - key_generator.go (source / functions) Hit Total Coverage
Test: 2024-07-19 08:16Z dbf9ab67 - tests only.lcov Lines: 175 208 84.1 %
Date: 2024-07-19 08:16:35 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2024 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             :         "cmp"
       9             :         "fmt"
      10             :         "slices"
      11             : 
      12             :         "github.com/cockroachdb/pebble"
      13             :         "github.com/cockroachdb/pebble/internal/testkeys"
      14             :         "golang.org/x/exp/rand"
      15             : )
      16             : 
      17             : type keyGenerator struct {
      18             :         keyManager *keyManager
      19             :         rng        *rand.Rand
      20             :         cfg        OpConfig
      21             : }
      22             : 
      23           1 : func newKeyGenerator(km *keyManager, rng *rand.Rand, cfg OpConfig) *keyGenerator {
      24           1 :         return &keyGenerator{
      25           1 :                 keyManager: km,
      26           1 :                 rng:        rng,
      27           1 :                 cfg:        cfg,
      28           1 :         }
      29           1 : }
      30             : 
      31             : // RandKey returns a random key (either a previously known key, or a new key).
      32           1 : func (kg *keyGenerator) RandKey(newKeyProbability float64) []byte {
      33           1 :         return kg.randKey(newKeyProbability, nil /* bounds */)
      34           1 : }
      35             : 
      36             : // RandKeyInRange returns a random key (either a previously known key, or a new
      37             : // key) in the given key range.
      38           1 : func (kg *keyGenerator) RandKeyInRange(newKeyProbability float64, kr pebble.KeyRange) []byte {
      39           1 :         return kg.randKey(newKeyProbability, &kr)
      40           1 : }
      41             : 
      42             : // RandPrefix returns a random prefix key (a key with no suffix).
      43           1 : func (kg *keyGenerator) RandPrefix(newPrefix float64) []byte {
      44           1 :         prefixes := kg.keyManager.prefixes()
      45           1 :         if len(prefixes) > 0 && kg.rng.Float64() > newPrefix {
      46           1 :                 return pickOneUniform(kg.rng, prefixes)
      47           1 :         }
      48             : 
      49             :         // Use a new prefix.
      50           1 :         for {
      51           1 :                 prefix := kg.generateKeyWithSuffix(4, 12, 0)
      52           1 :                 if !kg.keyManager.prefixExists(prefix) {
      53           1 :                         if !kg.keyManager.addNewKey(prefix) {
      54           0 :                                 panic("key must not exist if prefix doesn't exist")
      55             :                         }
      56           1 :                         return prefix
      57             :                 }
      58             :         }
      59             : }
      60             : 
      61             : // UniqueKeys takes a key-generating function and uses it to generate n unique
      62             : // keys, returning them in sorted order.
      63           1 : func (kg *keyGenerator) UniqueKeys(n int, genFn func() []byte) [][]byte {
      64           1 :         keys := make([][]byte, n)
      65           1 :         used := make(map[string]struct{}, n)
      66           1 :         for i := range keys {
      67           1 :                 for attempts := 0; ; attempts++ {
      68           1 :                         keys[i] = genFn()
      69           1 :                         if _, exists := used[string(keys[i])]; !exists {
      70           1 :                                 break
      71             :                         }
      72           1 :                         if attempts > 100000 {
      73           0 :                                 panic("could not generate unique key")
      74             :                         }
      75             :                 }
      76           1 :                 used[string(keys[i])] = struct{}{}
      77             :         }
      78           1 :         slices.SortFunc(keys, kg.cmp)
      79           1 :         return keys
      80             : }
      81             : 
      82             : // SkewedSuffix generates a random suffix according to the configuration's
      83             : // suffix distribution. It takes a probability 0 ≤ p ≤ 1.0 indicating the
      84             : // probability with which the generator should increase the max suffix generated
      85             : // by the generator.
      86             : //
      87             : // May return a nil suffix, with the probability the configuration's suffix
      88             : // distribution assigns to the zero suffix.
      89           1 : func (kg *keyGenerator) SkewedSuffix(incMaxProb float64) []byte {
      90           1 :         if suffix := kg.SkewedSuffixInt(incMaxProb); suffix != 0 {
      91           1 :                 return testkeys.Suffix(suffix)
      92           1 :         }
      93           1 :         return nil
      94             : }
      95             : 
      96             : // SkewedSuffixInt is a variant of SkewedSuffix which returns the unencoded
      97             : // suffix as an integer.
      98           1 : func (kg *keyGenerator) SkewedSuffixInt(incMaxProb float64) int64 {
      99           1 :         if kg.rng.Float64() < incMaxProb {
     100           1 :                 kg.cfg.writeSuffixDist.IncMax(1)
     101           1 :         }
     102           1 :         return int64(kg.cfg.writeSuffixDist.Uint64(kg.rng))
     103             : }
     104             : 
     105             : // IncMaxSuffix increases the max suffix range and returns the new maximum
     106             : // suffix (which is guaranteed to be larger than any previously generated
     107             : // suffix).
     108           1 : func (kg *keyGenerator) IncMaxSuffix() []byte {
     109           1 :         kg.cfg.writeSuffixDist.IncMax(1)
     110           1 :         return testkeys.Suffix(int64(kg.cfg.writeSuffixDist.Max()))
     111           1 : }
     112             : 
     113             : // UniformSuffix returns a suffix in the same range as SkewedSuffix but with a
     114             : // uniform distribution. This is used during reads to better exercise reading a
     115             : // mix of older and newer keys. The suffix can be empty.
     116             : //
     117             : // May return a nil suffix.
     118           1 : func (kg *keyGenerator) UniformSuffix() []byte {
     119           1 :         if suffix := kg.UniformSuffixInt(); suffix != 0 {
     120           1 :                 return testkeys.Suffix(suffix)
     121           1 :         }
     122           1 :         return nil
     123             : }
     124             : 
     125             : // UniformSuffixInt is a variant of UniformSuffix which returns the unencoded
     126             : // suffix as an integer.
     127           1 : func (kg *keyGenerator) UniformSuffixInt() int64 {
     128           1 :         maxVal := kg.cfg.writeSuffixDist.Max()
     129           1 :         return kg.rng.Int63n(int64(maxVal))
     130           1 : }
     131             : 
     132             : // randKey returns a random key (either a previously known key or a new key).
     133             : //
     134             : // If bounds is not nil, the key will be inside the bounds.
     135           1 : func (kg *keyGenerator) randKey(newKeyProbability float64, bounds *pebble.KeyRange) []byte {
     136           1 :         var knownKeys [][]byte
     137           1 :         if bounds == nil {
     138           1 :                 knownKeys = kg.keyManager.knownKeys()
     139           1 :         } else {
     140           1 :                 if kg.cmp(bounds.Start, bounds.End) >= 0 {
     141           0 :                         panic(fmt.Sprintf("invalid bounds [%q, %q)", bounds.Start, bounds.End))
     142             :                 }
     143           1 :                 knownKeys = kg.keyManager.knownKeysInRange(*bounds)
     144             :         }
     145           1 :         switch {
     146           1 :         case len(knownKeys) > 0 && kg.rng.Float64() > newKeyProbability:
     147           1 :                 // Use an existing user key.
     148           1 :                 return pickOneUniform(kg.rng, knownKeys)
     149             : 
     150           1 :         case len(knownKeys) > 0 && kg.rng.Float64() > kg.cfg.newPrefix:
     151           1 :                 // Use an existing prefix but a new suffix, producing a new user key.
     152           1 :                 prefixes := kg.keyManager.prefixes()
     153           1 : 
     154           1 :                 // If we're constrained to a key range, find which existing prefixes
     155           1 :                 // fall within that key range.
     156           1 :                 if bounds != nil {
     157           1 :                         s, _ := slices.BinarySearchFunc(prefixes, bounds.Start, kg.cmp)
     158           1 :                         e, _ := slices.BinarySearchFunc(prefixes, bounds.End, kg.cmp)
     159           1 :                         prefixes = prefixes[s:e]
     160           1 :                 }
     161             : 
     162           1 :                 if len(prefixes) > 0 {
     163           1 :                         for {
     164           1 :                                 // Pick a prefix on each iteration in case most or all suffixes are
     165           1 :                                 // already in use for any individual prefix.
     166           1 :                                 p := kg.rng.Intn(len(prefixes))
     167           1 :                                 suffix := int64(kg.cfg.writeSuffixDist.Uint64(kg.rng))
     168           1 : 
     169           1 :                                 var key []byte
     170           1 :                                 if suffix > 0 {
     171           1 :                                         key = resizeBuffer(key, len(prefixes[p]), testkeys.SuffixLen(suffix))
     172           1 :                                         n := copy(key, prefixes[p])
     173           1 :                                         testkeys.WriteSuffix(key[n:], suffix)
     174           1 :                                 } else {
     175           1 :                                         key = resizeBuffer(key, len(prefixes[p]), 0)
     176           1 :                                         copy(key, prefixes[p])
     177           1 :                                 }
     178             : 
     179           1 :                                 if bounds == nil || (kg.cmp(key, bounds.Start) >= 0 && kg.cmp(key, bounds.End) < 0) {
     180           1 :                                         if kg.keyManager.addNewKey(key) {
     181           1 :                                                 return key
     182           1 :                                         }
     183             :                                 }
     184             : 
     185             :                                 // If the generated key already existed, or the generated key
     186             :                                 // fell outside the provided bounds, increase the suffix
     187             :                                 // distribution and loop.
     188           1 :                                 kg.cfg.writeSuffixDist.IncMax(1)
     189             :                         }
     190             :                 }
     191             :                 // Otherwise fall through to generating a new prefix.
     192             :         }
     193             : 
     194           1 :         if bounds == nil {
     195           1 :                 suffix := kg.SkewedSuffixInt(0.01)
     196           1 :                 for {
     197           1 :                         key := kg.generateKeyWithSuffix(4, 12, suffix)
     198           1 :                         if !kg.keyManager.prefixExists(kg.prefix(key)) {
     199           1 :                                 if !kg.keyManager.addNewKey(key) {
     200           0 :                                         panic("key must not exist if prefix doesn't exist")
     201             :                                 }
     202           1 :                                 return key
     203             :                         }
     204             :                 }
     205             :         }
     206             :         // We need to generate a key between the bounds.
     207           1 :         startPrefix, startSuffix := kg.parseKey(bounds.Start)
     208           1 :         endPrefix, endSuffix := kg.parseKey(bounds.End)
     209           1 :         var prefix []byte
     210           1 :         var suffix int64
     211           1 :         if kg.equal(startPrefix, endPrefix) {
     212           0 :                 prefix = startPrefix
     213           0 :                 // Bounds have the same prefix, generate a suffix in-between.
     214           0 :                 if cmpSuffix(startSuffix, endSuffix) >= 0 {
     215           0 :                         panic(fmt.Sprintf("invalid bounds [%q, %q)", bounds.Start, bounds.End))
     216             :                 }
     217           0 :                 suffix = kg.SkewedSuffixInt(0.01)
     218           0 :                 for i := 0; !(cmpSuffix(startSuffix, suffix) <= 0 && cmpSuffix(suffix, endSuffix) < 0); i++ {
     219           0 :                         if i > 10 {
     220           0 :                                 // This value is always >= startSuffix and < endSuffix.
     221           0 :                                 suffix = (startSuffix + endSuffix) / 2
     222           0 :                                 break
     223             :                         }
     224             :                         // The suffix we want must exist in the current suffix range, we don't
     225             :                         // want to keep increasing it here.
     226           0 :                         suffix = kg.SkewedSuffixInt(0)
     227             :                 }
     228           1 :         } else {
     229           1 :                 prefix = testkeys.RandomPrefixInRange(startPrefix, endPrefix, kg.rng)
     230           1 :                 suffix = kg.SkewedSuffixInt(0.01)
     231           1 :                 if kg.equal(prefix, startPrefix) {
     232           0 :                         // We can't use a suffix which sorts before startSuffix.
     233           0 :                         for i := 0; cmpSuffix(suffix, startSuffix) < 0; i++ {
     234           0 :                                 if i > 10 {
     235           0 :                                         suffix = startSuffix
     236           0 :                                         break
     237             :                                 }
     238           0 :                                 suffix = kg.SkewedSuffixInt(0)
     239             :                         }
     240             :                 }
     241             :         }
     242           1 :         key := slices.Clip(prefix)
     243           1 :         if suffix != 0 {
     244           1 :                 key = append(key, testkeys.Suffix(suffix)...)
     245           1 :         }
     246           1 :         if kg.cmp(key, bounds.Start) < 0 || kg.cmp(key, bounds.End) >= 0 {
     247           0 :                 panic(fmt.Sprintf("invalid randKey %q; bounds: [%q, %q) %v %v", key, bounds.Start, bounds.End, kg.cmp(key, bounds.Start), kg.cmp(key, bounds.End)))
     248             :         }
     249             :         // We might (rarely) produce an existing key here, that's ok.
     250           1 :         kg.keyManager.addNewKey(key)
     251           1 :         return key
     252             : }
     253             : 
     254             : // generateKeyWithSuffix generates a key with a random prefix and the given
     255             : // suffix. If the given suffix is 0, the key will not have a suffix.
     256           1 : func (kg *keyGenerator) generateKeyWithSuffix(minPrefixLen, maxPrefixLen int, suffix int64) []byte {
     257           1 :         prefix := randBytes(kg.rng, minPrefixLen, maxPrefixLen)
     258           1 :         if suffix == 0 {
     259           1 :                 return prefix
     260           1 :         }
     261           1 :         return append(prefix, testkeys.Suffix(suffix)...)
     262             : }
     263             : 
     264             : // cmpSuffix compares two suffixes, where suffix 0 means there is no suffix.
     265           0 : func cmpSuffix(s1, s2 int64) int {
     266           0 :         switch {
     267           0 :         case s1 == s2:
     268           0 :                 return 0
     269           0 :         case s1 == 0:
     270           0 :                 return -1
     271           0 :         case s2 == 0:
     272           0 :                 return +1
     273           0 :         default:
     274           0 :                 return cmp.Compare(s2, s1)
     275             :         }
     276             : }
     277             : 
     278           1 : func (kg *keyGenerator) cmp(a, b []byte) int {
     279           1 :         return kg.keyManager.comparer.Compare(a, b)
     280           1 : }
     281             : 
     282           1 : func (kg *keyGenerator) equal(a, b []byte) bool {
     283           1 :         return kg.keyManager.comparer.Equal(a, b)
     284           1 : }
     285             : 
     286           1 : func (kg *keyGenerator) split(a []byte) int {
     287           1 :         return kg.keyManager.comparer.Split(a)
     288           1 : }
     289             : 
     290           1 : func (kg *keyGenerator) prefix(a []byte) []byte {
     291           1 :         n := kg.split(a)
     292           1 :         return a[:n:n]
     293           1 : }
     294             : 
     295           1 : func (kg *keyGenerator) parseKey(k []byte) (prefix []byte, suffix int64) {
     296           1 :         n := kg.split(k)
     297           1 :         if n == len(k) {
     298           1 :                 return k, 0
     299           1 :         }
     300           1 :         suffix, err := testkeys.ParseSuffix(k[n:])
     301           1 :         if err != nil {
     302           0 :                 panic(fmt.Sprintf("error parsing suffix for key %q", k))
     303             :         }
     304           1 :         return k[:n:n], suffix
     305             : }
     306             : 
     307           1 : func randBytes(rng *rand.Rand, minLen, maxLen int) []byte {
     308           1 :         n := minLen + rng.Intn(maxLen-minLen+1)
     309           1 :         if n == 0 {
     310           1 :                 return nil
     311           1 :         }
     312             :         // NB: The actual random values are not particularly important. We only use
     313             :         // lowercase letters because that makes visual determination of ordering
     314             :         // easier, rather than having to remember the lexicographic ordering of
     315             :         // uppercase vs lowercase, or letters vs numbers vs punctuation.
     316           1 :         const letters = "abcdefghijklmnopqrstuvwxyz"
     317           1 :         const lettersLen = uint64(len(letters))
     318           1 :         const lettersCharsPerRand = 12 // floor(log(math.MaxUint64)/log(lettersLen))
     319           1 : 
     320           1 :         var r uint64
     321           1 :         var q int
     322           1 :         buf := make([]byte, n)
     323           1 :         for i := range buf {
     324           1 :                 if q == 0 {
     325           1 :                         r = rng.Uint64()
     326           1 :                         q = lettersCharsPerRand
     327           1 :                 }
     328           1 :                 buf[i] = letters[r%lettersLen]
     329           1 :                 r = r / lettersLen
     330           1 :                 q--
     331             :         }
     332           1 :         return buf
     333             : }
     334             : 
     335           1 : func pickOneUniform[S ~[]E, E any](rng *rand.Rand, x S) E {
     336           1 :         return x[rng.Intn(len(x))]
     337           1 : }

Generated by: LCOV version 1.14