github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/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  }