github.com/m3db/m3@v1.5.0/src/dbnode/storage/bootstrap/bootstrapper/fs/source_index_bench_test.go (about)

     1  // Copyright (c) 2019 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 fs
    22  
    23  import (
    24  	"io/ioutil"
    25  	"math"
    26  	"net/http"
    27  	"net/http/httptest"
    28  	_ "net/http/pprof" // pprof: for debug listen server if configured
    29  	"os"
    30  	"strconv"
    31  	"strings"
    32  	"testing"
    33  	"time"
    34  
    35  	"github.com/m3db/m3/src/dbnode/namespace"
    36  	"github.com/m3db/m3/src/dbnode/persist"
    37  	"github.com/m3db/m3/src/dbnode/persist/fs"
    38  	"github.com/m3db/m3/src/dbnode/persist/fs/msgpack"
    39  	"github.com/m3db/m3/src/dbnode/storage/bootstrap"
    40  	"github.com/m3db/m3/src/dbnode/storage/bootstrap/result"
    41  	"github.com/m3db/m3/src/dbnode/storage/series"
    42  	xtime "github.com/m3db/m3/src/x/time"
    43  
    44  	"github.com/davecgh/go-spew/spew"
    45  	"github.com/pkg/profile"
    46  	"github.com/stretchr/testify/require"
    47  )
    48  
    49  // BenchmarkBootstrapIndex allows for testing indexing bootstrap time with the
    50  // FS bootstrapper, this tests the speed and performance of index segment
    51  // building from reading a set of files that sit on disk taken from a real
    52  // DB node.
    53  // To use test data and capture CPU profile run with:
    54  // TEST_TSDB_DIR=/tmp/m3db_data PROFILE_CPU=true go test -v -run none -bench Index
    55  func BenchmarkBootstrapIndex(b *testing.B) {
    56  	dir, err := ioutil.TempDir("", "var_lib_m3db_fake")
    57  	require.NoError(b, err)
    58  	defer os.RemoveAll(dir)
    59  
    60  	srv := httptest.NewServer(http.DefaultServeMux)
    61  	spew.Printf("test server with pprof: %v\n", srv.URL)
    62  
    63  	timesOpts := testTimesOptions{
    64  		numBlocks: 2,
    65  	}
    66  	times := newTestBootstrapIndexTimes(timesOpts)
    67  
    68  	testNamespace := testNs1ID
    69  	testNamespaceMetadata := testNsMetadata(b)
    70  	if testDir := os.Getenv("TEST_TSDB_DIR"); testDir != "" {
    71  		spew.Printf("using test dir: %s\n", testDir)
    72  
    73  		// Allow for test directory overrides, must name the namespace
    74  		// "test_namespace" in the override directory.
    75  		dir = testDir
    76  
    77  		namespaceDataDirPath := fs.NamespaceDataDirPath(dir, testNamespace)
    78  		handle, err := os.Open(namespaceDataDirPath)
    79  		require.NoError(b, err)
    80  
    81  		results, err := handle.Readdir(0)
    82  		require.NoError(b, err)
    83  
    84  		require.NoError(b, handle.Close())
    85  
    86  		var shards []uint32
    87  		for _, result := range results {
    88  			if !result.IsDir() {
    89  				// Looking for shard directories.
    90  				spew.Printf("shard discover: entry not directory, %v\n", result.Name())
    91  				continue
    92  			}
    93  
    94  			v, err := strconv.Atoi(result.Name())
    95  			if err != nil {
    96  				// Not a shard directory.
    97  				spew.Printf("shard discover: not number, %v, %v\n", result.Name(), err)
    98  				continue
    99  			}
   100  
   101  			shards = append(shards, uint32(v))
   102  		}
   103  
   104  		spew.Printf("discovered shards: dir=%v, shards=%v\n",
   105  			namespaceDataDirPath, shards)
   106  
   107  		// Clear the shard time ranges and add new ones.
   108  		times.shardTimeRanges = result.NewShardTimeRanges()
   109  		times.start = xtime.UnixNano(math.MaxInt64)
   110  		times.end = xtime.UnixNano(0)
   111  		for _, shard := range shards {
   112  			var (
   113  				min     = xtime.UnixNano(math.MaxInt64)
   114  				max     = xtime.UnixNano(0)
   115  				ranges  = xtime.NewRanges()
   116  				entries = fs.ReadInfoFiles(dir, testNamespace, shard,
   117  					0, msgpack.NewDecodingOptions(), persist.FileSetFlushType)
   118  			)
   119  			for _, entry := range entries {
   120  				if entry.Err != nil {
   121  					require.NoError(b, entry.Err.Error())
   122  				}
   123  
   124  				start := xtime.UnixNano(entry.Info.BlockStart)
   125  				if start.Before(min) {
   126  					min = start
   127  				}
   128  
   129  				blockSize := time.Duration(entry.Info.BlockSize)
   130  				end := start.Add(blockSize)
   131  				if end.After(max) {
   132  					max = end
   133  				}
   134  
   135  				ranges.AddRange(xtime.Range{Start: start, End: end})
   136  
   137  				// Override the block size if different.
   138  				namespaceOpts := testNamespaceMetadata.Options()
   139  				retentionOpts := namespaceOpts.RetentionOptions()
   140  				currBlockSize := retentionOpts.BlockSize()
   141  				if blockSize > currBlockSize {
   142  					newRetentionOpts := retentionOpts.
   143  						SetBlockSize(blockSize).
   144  						// 42yrs of retention to make sure blocks are in retention.
   145  						// Why 42? Because it's the answer to life, the universe and everything.
   146  						SetRetentionPeriod(42 * 365 * 24 * time.Hour)
   147  					newIndexOpts := namespaceOpts.IndexOptions().SetBlockSize(blockSize)
   148  					newNamespaceOpts := namespaceOpts.
   149  						SetRetentionOptions(newRetentionOpts).
   150  						SetIndexOptions(newIndexOpts)
   151  					testNamespaceMetadata, err = namespace.NewMetadata(testNamespace, newNamespaceOpts)
   152  					require.NoError(b, err)
   153  				}
   154  			}
   155  
   156  			if ranges.IsEmpty() {
   157  				continue // Nothing to bootstrap for shard.
   158  			}
   159  
   160  			times.shardTimeRanges.Set(shard, ranges)
   161  
   162  			if min.Before(times.start) {
   163  				times.start = min
   164  			}
   165  			if max.After(times.end) {
   166  				times.end = max
   167  			}
   168  		}
   169  	} else {
   170  		writeTSDBGoodTaggedSeriesDataFiles(b, dir, testNamespace, times.start)
   171  	}
   172  
   173  	testOpts := newTestOptionsWithPersistManager(b, dir).
   174  		SetResultOptions(testDefaultResultOpts.SetSeriesCachePolicy(series.CacheLRU))
   175  
   176  	src, err := newFileSystemSource(testOpts)
   177  	require.NoError(b, err)
   178  
   179  	runOpts := testDefaultRunOpts.
   180  		SetPersistConfig(bootstrap.PersistConfig{
   181  			Enabled:     true,
   182  			FileSetType: persist.FileSetFlushType,
   183  		})
   184  
   185  	tester := bootstrap.BuildNamespacesTester(b, runOpts,
   186  		times.shardTimeRanges, testNamespaceMetadata)
   187  	defer tester.Finish()
   188  
   189  	spew.Printf("running test with times: %v\n", times)
   190  
   191  	if strings.ToLower(os.Getenv("PROFILE_CPU")) == "true" {
   192  		p := profile.Start(profile.CPUProfile)
   193  		defer p.Stop()
   194  	}
   195  
   196  	b.ResetTimer()
   197  	b.StartTimer()
   198  	tester.TestReadWith(src)
   199  	b.StopTimer()
   200  }