github.com/m3db/m3@v1.5.0/src/dbnode/integration/commitlog_bootstrap_index_perf_speed_test.go (about) 1 // +build integration 2 3 // Copyright (c) 2018 Uber Technologies, Inc. 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 23 package integration 24 25 import ( 26 "encoding/binary" 27 "encoding/hex" 28 "fmt" 29 "io/ioutil" 30 "math/rand" 31 "os" 32 "strconv" 33 "testing" 34 "time" 35 36 "github.com/m3db/m3/src/dbnode/namespace" 37 "github.com/m3db/m3/src/dbnode/persist/fs" 38 "github.com/m3db/m3/src/dbnode/persist/fs/commitlog" 39 "github.com/m3db/m3/src/dbnode/retention" 40 "github.com/m3db/m3/src/dbnode/ts" 41 "github.com/m3db/m3/src/x/checked" 42 "github.com/m3db/m3/src/x/clock" 43 "github.com/m3db/m3/src/x/context" 44 "github.com/m3db/m3/src/x/ident" 45 xtime "github.com/m3db/m3/src/x/time" 46 47 "github.com/c2h5oh/datasize" 48 "github.com/stretchr/testify/require" 49 "go.uber.org/zap" 50 ) 51 52 // TestCommitLogIndexPerfSpeedBootstrap tests the performance of the commit log 53 // bootstrapper when a large amount of data is present that needs to be read. 54 // Note: Also useful for doing adhoc testing, using the environment variables 55 // to turn up and down the number of IDs and points. 56 func TestCommitLogIndexPerfSpeedBootstrap(t *testing.T) { 57 if testing.Short() { 58 t.SkipNow() // Just skip if we're doing a short run 59 } 60 61 // Test setup 62 var ( 63 rOpts = retention.NewOptions().SetRetentionPeriod(12 * time.Hour) 64 blockSize = rOpts.BlockSize() 65 ) 66 67 nsOpts := namespace.NewOptions(). 68 SetRetentionOptions(rOpts). 69 SetIndexOptions(namespace.NewIndexOptions(). 70 SetEnabled(true). 71 SetBlockSize(2 * blockSize)) 72 ns, err := namespace.NewMetadata(testNamespaces[0], nsOpts) 73 require.NoError(t, err) 74 opts := NewTestOptions(t). 75 SetNamespaces([]namespace.Metadata{ns}). 76 // Allow for wall clock timing 77 SetNowFn(time.Now) 78 79 setup, err := NewTestSetup(t, opts, nil) 80 require.NoError(t, err) 81 defer setup.Close() 82 83 commitLogOpts := setup.StorageOpts().CommitLogOptions(). 84 SetFlushInterval(defaultIntegrationTestFlushInterval) 85 setup.SetStorageOpts(setup.StorageOpts().SetCommitLogOptions(commitLogOpts)) 86 87 log := setup.StorageOpts().InstrumentOptions().Logger() 88 log.Info("commit log bootstrap test") 89 90 // Write test data 91 log.Info("generating data") 92 93 // NB(r): Use TEST_NUM_SERIES=50000 for a representative large data set to 94 // test loading locally 95 numSeries := 1024 96 if str := os.Getenv("TEST_NUM_SERIES"); str != "" { 97 numSeries, err = strconv.Atoi(str) 98 require.NoError(t, err) 99 } 100 101 step := time.Second 102 numPoints := 128 103 if str := os.Getenv("TEST_NUM_POINTS"); str != "" { 104 numPoints, err = strconv.Atoi(str) 105 require.NoError(t, err) 106 } 107 108 require.True(t, (time.Duration(numPoints)*step) < blockSize, 109 fmt.Sprintf("num points %d multiplied by step %s is greater than block size %s", 110 numPoints, step.String(), blockSize.String())) 111 112 numTags := 8 113 if str := os.Getenv("TEST_NUM_TAGS"); str != "" { 114 numTags, err = strconv.Atoi(str) 115 require.NoError(t, err) 116 } 117 118 numTagSets := 128 119 if str := os.Getenv("TEST_NUM_TAG_SETS"); str != "" { 120 numTagSets, err = strconv.Atoi(str) 121 require.NoError(t, err) 122 } 123 124 // Pre-generate tag sets, but not too many to reduce heap size. 125 tagSets := make([]ident.Tags, 0, numTagSets) 126 for i := 0; i < numTagSets; i++ { 127 tags := ident.NewTags() 128 for j := 0; j < numTags; j++ { 129 tag := ident.Tag{ 130 Name: ident.StringID(fmt.Sprintf("series.%d.tag.%d", i, j)), 131 Value: ident.StringID(fmt.Sprintf("series.%d.tag-value.%d", i, j)), 132 } 133 tags.Append(tag) 134 } 135 tagSets = append(tagSets, tags) 136 } 137 138 log.Info("writing data") 139 140 now := setup.NowFn()() 141 blockStart := now.Add(-3 * blockSize) 142 143 // create new commit log 144 commitLog, err := commitlog.NewCommitLog(commitLogOpts) 145 require.NoError(t, err) 146 require.NoError(t, commitLog.Open()) 147 148 ctx := context.NewBackground() 149 defer ctx.Close() 150 151 shardSet := setup.ShardSet() 152 idPrefix := "test.id.test.id.test.id.test.id.test.id.test.id.test.id.test.id" 153 idPrefixBytes := []byte(idPrefix) 154 numBytes := make([]byte, 8) 155 numHexBytes := make([]byte, hex.EncodedLen(len(numBytes))) 156 tagEncoderPool := commitLogOpts.FilesystemOptions().TagEncoderPool() 157 tagSliceIter := ident.NewTagsIterator(ident.Tags{}) 158 for i := 0; i < numPoints; i++ { 159 for j := 0; j < numSeries; j++ { 160 checkedBytes := checked.NewBytes(nil, nil) 161 seriesID := ident.BinaryID(checkedBytes) 162 163 // Write the ID prefix 164 checkedBytes.Resize(0) 165 checkedBytes.AppendAll(idPrefixBytes) 166 167 // Write out the binary representation then hex encode the 168 // that into the ID to give it a unique ID for this series number 169 binary.LittleEndian.PutUint64(numBytes, uint64(j)) 170 hex.Encode(numHexBytes, numBytes) 171 checkedBytes.AppendAll(numHexBytes) 172 173 // Use the tag sets appropriate for this series number 174 seriesTags := tagSets[j%len(tagSets)] 175 176 tagSliceIter.Reset(seriesTags) 177 tagEncoder := tagEncoderPool.Get() 178 err := tagEncoder.Encode(tagSliceIter) 179 require.NoError(t, err) 180 181 encodedTagsChecked, ok := tagEncoder.Data() 182 require.True(t, ok) 183 184 series := ts.Series{ 185 Namespace: ns.ID(), 186 Shard: shardSet.Lookup(seriesID), 187 ID: seriesID, 188 EncodedTags: ts.EncodedTags(encodedTagsChecked.Bytes()), 189 UniqueIndex: uint64(j), 190 } 191 dp := ts.Datapoint{ 192 TimestampNanos: blockStart.Add(time.Duration(i) * step), 193 Value: rand.Float64(), //nolint: gosec 194 } 195 require.NoError(t, commitLog.Write(ctx, series, dp, xtime.Second, nil)) 196 } 197 } 198 199 // ensure writes finished 200 require.NoError(t, commitLog.Close()) 201 202 log.Info("finished writing data") 203 204 // emit how big commit logs are 205 commitLogsDirPath := fs.CommitLogsDirPath(commitLogOpts.FilesystemOptions().FilePathPrefix()) 206 files, err := ioutil.ReadDir(commitLogsDirPath) 207 require.NoError(t, err) 208 209 log.Info("test wrote commit logs", zap.Int("numFiles", len(files))) 210 for _, file := range files { 211 log.Info("test wrote commit logs", 212 zap.String("file", file.Name()), 213 zap.String("size", datasize.ByteSize(file.Size()).HR())) 214 } 215 216 // Setup bootstrapper after writing data so filesystem inspection can find it. 217 setupCommitLogBootstrapperWithFSInspection(t, setup, commitLogOpts) 218 219 // restore now time so measurements take effect 220 setup.SetStorageOpts(setup.StorageOpts().SetClockOptions(clock.NewOptions())) 221 222 // Start the server with filesystem bootstrapper 223 require.NoError(t, setup.StartServer()) 224 log.Debug("server is now up") 225 226 // Stop the server 227 defer func() { 228 require.NoError(t, setup.StopServer()) 229 log.Debug("server is now down") 230 }() 231 }