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 }