github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/cmd/pebble/random.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 main 6 7 import ( 8 "crypto/sha1" 9 "encoding/binary" 10 "fmt" 11 "hash" 12 "regexp" 13 "strconv" 14 "strings" 15 "sync/atomic" 16 "time" 17 18 "github.com/petermattis/pebble/internal/randvar" 19 "github.com/petermattis/pebble/internal/rate" 20 "golang.org/x/exp/rand" 21 ) 22 23 var randVarRE = regexp.MustCompile(`^(?:(uniform|zipf):)?(\d+)(?:-(\d+))?$`) 24 25 func newFluctuatingRateLimiter(maxOpsPerSec string) (*rate.Limiter, error) { 26 rateDist, fluctuateDuration, err := parseRateSpec(maxOpsPerSec) 27 if err != nil { 28 return nil, err 29 } 30 limiter := rate.NewLimiter(rate.Limit(rateDist.Uint64()), 1) 31 if fluctuateDuration != 0 { 32 go func(limiter *rate.Limiter) { 33 ticker := time.NewTicker(fluctuateDuration) 34 for i := 0; ; i++ { 35 select { 36 case <-ticker.C: 37 limiter.SetLimit(rate.Limit(rateDist.Uint64())) 38 } 39 } 40 }(limiter) 41 } 42 return limiter, nil 43 } 44 45 func parseRandVarSpec(d string) (randvar.Static, error) { 46 m := randVarRE.FindStringSubmatch(d) 47 if m == nil { 48 return nil, fmt.Errorf("invalid random var spec: %s", d) 49 } 50 51 min, err := strconv.Atoi(m[2]) 52 if err != nil { 53 return nil, err 54 } 55 max := min 56 if m[3] != "" { 57 max, err = strconv.Atoi(m[3]) 58 if err != nil { 59 return nil, err 60 } 61 } 62 63 switch strings.ToLower(m[1]) { 64 case "", "uniform": 65 return randvar.NewUniform(nil, uint64(min), uint64(max)), nil 66 case "zipf": 67 return randvar.NewZipf(nil, uint64(min), uint64(max), 0.99) 68 default: 69 return nil, fmt.Errorf("unknown distribution: %s", m[1]) 70 } 71 } 72 73 func parseRateSpec(v string) (randvar.Static, time.Duration, error) { 74 parts := strings.Split(v, "/") 75 if len(parts) == 0 || len(parts) > 2 { 76 return nil, 0, fmt.Errorf("invalid max-ops-per-sec spec: %s", v) 77 } 78 r, err := parseRandVarSpec(parts[0]) 79 if err != nil { 80 return nil, 0, err 81 } 82 // Don't fluctuate by default. 83 fluctuateDuration := time.Duration(0) 84 if len(parts) == 2 { 85 fluctuateDurationFloat, err := strconv.ParseFloat(parts[1], 64) 86 if err != nil { 87 return nil, 0, err 88 } 89 fluctuateDuration = time.Duration(fluctuateDurationFloat) * time.Second 90 } 91 return r, fluctuateDuration, nil 92 } 93 94 func parseValuesSpec(v string) (randvar.Static, float64, error) { 95 parts := strings.Split(v, "/") 96 if len(parts) == 0 || len(parts) > 2 { 97 return nil, 0, fmt.Errorf("invalid values spec: %s", v) 98 } 99 r, err := parseRandVarSpec(parts[0]) 100 if err != nil { 101 return nil, 0, err 102 } 103 targetCompression := 1.0 104 if len(parts) == 2 { 105 targetCompression, err = strconv.ParseFloat(parts[1], 64) 106 if err != nil { 107 return nil, 0, err 108 } 109 } 110 return r, targetCompression, nil 111 } 112 113 func randomBlock( 114 r *rand.Rand, size int, targetCompressionRatio float64, 115 ) []byte { 116 uniqueSize := int(float64(size) / targetCompressionRatio) 117 if uniqueSize < 1 { 118 uniqueSize = 1 119 } 120 data := make([]byte, size) 121 offset := 0 122 for offset+8 <= uniqueSize { 123 binary.LittleEndian.PutUint64(data[offset:], r.Uint64()) 124 offset += 8 125 } 126 word := r.Uint64() 127 for offset < uniqueSize { 128 data[offset] = byte(word) 129 word >>= 8 130 offset++ 131 } 132 for offset < size { 133 data[offset] = data[offset-uniqueSize] 134 offset++ 135 } 136 return data 137 } 138 139 type sequence struct { 140 val int64 141 cycleLength int64 142 seed int64 143 } 144 145 func (s *sequence) write() int64 { 146 return (atomic.AddInt64(&s.val, 1) - 1) % s.cycleLength 147 } 148 149 // read returns the last key index that has been written. Note that the returned 150 // index might not actually have been written yet, so a read operation cannot 151 // require that the key is present. 152 func (s *sequence) read() int64 { 153 return atomic.LoadInt64(&s.val) % s.cycleLength 154 } 155 156 // keyGenerator generates read and write keys. Read keys may not yet exist and 157 // write keys may already exist. 158 type keyGenerator interface { 159 writeKey() int64 160 readKey() int64 161 rand() *rand.Rand 162 sequence() int64 163 } 164 165 type hashGenerator struct { 166 seq *sequence 167 random *rand.Rand 168 hasher hash.Hash 169 buf [sha1.Size]byte 170 } 171 172 func newHashGenerator(seq *sequence) *hashGenerator { 173 return &hashGenerator{ 174 seq: seq, 175 random: rand.New(rand.NewSource(uint64(time.Now().UnixNano()))), 176 hasher: sha1.New(), 177 } 178 } 179 180 func (g *hashGenerator) hash(v int64) int64 { 181 binary.BigEndian.PutUint64(g.buf[:8], uint64(v)) 182 binary.BigEndian.PutUint64(g.buf[8:16], uint64(g.seq.seed)) 183 g.hasher.Reset() 184 _, _ = g.hasher.Write(g.buf[:16]) 185 g.hasher.Sum(g.buf[:0]) 186 return int64(binary.BigEndian.Uint64(g.buf[:8])) 187 } 188 189 func (g *hashGenerator) writeKey() int64 { 190 return g.hash(g.seq.write()) 191 } 192 193 func (g *hashGenerator) readKey() int64 { 194 v := g.seq.read() 195 if v == 0 { 196 return 0 197 } 198 return g.hash(g.random.Int63n(v)) 199 } 200 201 func (g *hashGenerator) rand() *rand.Rand { 202 return g.random 203 } 204 205 func (g *hashGenerator) sequence() int64 { 206 return atomic.LoadInt64(&g.seq.val) 207 } 208 209 type sequentialGenerator struct { 210 seq *sequence 211 random *rand.Rand 212 } 213 214 func newSequentialGenerator(seq *sequence) *sequentialGenerator { 215 return &sequentialGenerator{ 216 seq: seq, 217 random: rand.New(rand.NewSource(uint64(time.Now().UnixNano()))), 218 } 219 } 220 221 func (g *sequentialGenerator) writeKey() int64 { 222 return g.seq.write() 223 } 224 225 func (g *sequentialGenerator) readKey() int64 { 226 v := g.seq.read() 227 if v == 0 { 228 return 0 229 } 230 return g.random.Int63n(v) 231 } 232 233 func (g *sequentialGenerator) rand() *rand.Rand { 234 return g.random 235 } 236 237 func (g *sequentialGenerator) sequence() int64 { 238 return atomic.LoadInt64(&g.seq.val) 239 }