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  }