github.com/m3db/m3@v1.5.0/src/cmd/tools/dtest/util/seed/generator.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package seed
    22  
    23  import (
    24  	"fmt"
    25  	"math/rand"
    26  
    27  	"github.com/m3db/m3/src/cluster/shard"
    28  	"github.com/m3db/m3/src/dbnode/integration/generate"
    29  	ns "github.com/m3db/m3/src/dbnode/namespace"
    30  	"github.com/m3db/m3/src/dbnode/sharding"
    31  	"github.com/m3db/m3/src/x/ident"
    32  	xtime "github.com/m3db/m3/src/x/time"
    33  
    34  	"go.uber.org/zap"
    35  )
    36  
    37  // specific to data generation
    38  const (
    39  	letterBytes   = "`~<=>_-,/.[]{}@$#%+ݿ⬧	0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNŋoOpPqQrRsStTuUvVwWxXyYzZ"
    40  	letterIdxBits = 6                    // 6 bits to represent a letter index
    41  	letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
    42  	letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
    43  )
    44  
    45  type generator struct {
    46  	opts      Options
    47  	logger    *zap.Logger
    48  	r         *rand.Rand
    49  	numPoints unifStats
    50  	idLength  normStats
    51  	ids       []string
    52  }
    53  
    54  // NewGenerator returns a new generator
    55  func NewGenerator(opts Options) Generator {
    56  	g := &generator{
    57  		opts:   opts,
    58  		logger: opts.InstrumentOptions().Logger(),
    59  		r:      rand.New(opts.RandSource()),
    60  		numPoints: unifStats{
    61  			min: opts.MinNumPointsPerID(),
    62  			max: opts.MaxNumPointsPerID(),
    63  		},
    64  		idLength: normStats{
    65  			mean:   opts.IDLengthMean(),
    66  			stddev: opts.IDLengthStddev(),
    67  		},
    68  	}
    69  
    70  	for i := 0; i < opts.NumIDs(); i++ {
    71  		idLen := g.idLength.sample(g.r)
    72  		g.ids = append(g.ids, randStringBytesMaskImprSrc(idLen, opts.RandSource()))
    73  	}
    74  	return g
    75  }
    76  
    77  func (g *generator) Generate(nsCtx ns.Context, shard uint32) error {
    78  	var (
    79  		shardSet     = &fakeShardSet{shard}
    80  		gOpts        = g.opts.GenerateOptions()
    81  		blockSize    = gOpts.BlockSize()
    82  		now          = xtime.ToUnixNano(gOpts.ClockOptions().NowFn()().Truncate(blockSize))
    83  		start        = now.Add(-blockSize)
    84  		earliest     = now.Add(-1 * gOpts.RetentionPeriod())
    85  		blockConfigs []generate.BlockConfig
    86  	)
    87  	for start := start; !start.Before(earliest); start = start.Add(-gOpts.BlockSize()) {
    88  		blockConfigs = append(blockConfigs, g.generateConf(start))
    89  	}
    90  	g.logger.Debug("created block configs")
    91  
    92  	data := generate.BlocksByStart(blockConfigs)
    93  	g.logger.Debug("created fake data")
    94  
    95  	writer := generate.NewWriter(gOpts)
    96  	err := writer.WriteData(nsCtx, shardSet, data, 0)
    97  	if err != nil {
    98  		return fmt.Errorf("unable to write data: %v", err)
    99  	}
   100  
   101  	g.logger.Debug("data written to local fs")
   102  	return nil
   103  }
   104  
   105  func (g *generator) generateConf(start xtime.UnixNano) generate.BlockConfig {
   106  	numPoints := g.numPoints.sample(g.r)
   107  	return generate.BlockConfig{
   108  		Start:     start,
   109  		NumPoints: numPoints,
   110  		IDs:       g.ids,
   111  	}
   112  }
   113  
   114  // Taken from: https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang
   115  func randStringBytesMaskImprSrc(n int, src rand.Source) string {
   116  	b := make([]byte, n)
   117  	// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
   118  	for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
   119  		if remain == 0 {
   120  			cache, remain = src.Int63(), letterIdxMax
   121  		}
   122  		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
   123  			b[i] = letterBytes[idx]
   124  			i--
   125  		}
   126  		cache >>= letterIdxBits
   127  		remain--
   128  	}
   129  
   130  	return string(b)
   131  }
   132  
   133  type unifStats struct {
   134  	min int
   135  	max int
   136  }
   137  
   138  func (s unifStats) sample(r *rand.Rand) int {
   139  	return int(r.Int31n(int32(s.max-s.min))) + s.min
   140  }
   141  
   142  type normStats struct {
   143  	mean   float64
   144  	stddev float64
   145  }
   146  
   147  func (s normStats) sample(r *rand.Rand) int {
   148  	n := int(r.NormFloat64()*s.stddev + s.mean)
   149  	if n < 0 {
   150  		n = n * -1
   151  	}
   152  	return n
   153  }
   154  
   155  type fakeShardSet struct {
   156  	shardID uint32
   157  }
   158  
   159  func (f *fakeShardSet) All() []shard.Shard {
   160  	sh := shard.NewShard(f.shardID)
   161  	return []shard.Shard{sh}
   162  }
   163  
   164  func (f *fakeShardSet) AllIDs() []uint32 {
   165  	return []uint32{f.shardID}
   166  }
   167  
   168  func (f *fakeShardSet) Lookup(id ident.ID) uint32 {
   169  	return f.shardID
   170  }
   171  
   172  func (f *fakeShardSet) LookupStateByID(shardID uint32) (shard.State, error) {
   173  	return shard.Available, nil
   174  }
   175  
   176  func (f *fakeShardSet) Min() uint32 {
   177  	return f.shardID
   178  }
   179  
   180  func (f *fakeShardSet) Max() uint32 {
   181  	return f.shardID
   182  }
   183  
   184  func (f *fakeShardSet) HashFn() sharding.HashFn {
   185  	return nil
   186  }