github.com/m3db/m3@v1.5.0/src/dbnode/integration/peers_bootstrap_index_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  	"testing"
    27  	"time"
    28  
    29  	indexpb "github.com/m3db/m3/src/dbnode/generated/proto/index"
    30  	"github.com/m3db/m3/src/dbnode/integration/generate"
    31  	"github.com/m3db/m3/src/dbnode/namespace"
    32  	"github.com/m3db/m3/src/dbnode/persist/fs"
    33  	"github.com/m3db/m3/src/dbnode/retention"
    34  	"github.com/m3db/m3/src/dbnode/storage/index"
    35  	"github.com/m3db/m3/src/m3ninx/generated/proto/fswriter"
    36  	"github.com/m3db/m3/src/m3ninx/idx"
    37  	idxpersist "github.com/m3db/m3/src/m3ninx/persist"
    38  	"github.com/m3db/m3/src/x/ident"
    39  	xtest "github.com/m3db/m3/src/x/test"
    40  	xtime "github.com/m3db/m3/src/x/time"
    41  
    42  	"github.com/stretchr/testify/require"
    43  )
    44  
    45  func TestPeersBootstrapIndexWithIndexingEnabled(t *testing.T) {
    46  	if testing.Short() {
    47  		t.SkipNow() // Just skip if we're doing a short run
    48  	}
    49  
    50  	log := xtest.NewLogger(t)
    51  
    52  	blockSize := 2 * time.Hour
    53  	rOpts := retention.NewOptions().
    54  		SetRetentionPeriod(20 * time.Hour).
    55  		SetBlockSize(blockSize).
    56  		SetBufferPast(10 * time.Minute).
    57  		SetBufferFuture(2 * time.Minute)
    58  
    59  	idxOpts := namespace.NewIndexOptions().
    60  		SetEnabled(true).
    61  		SetBlockSize(blockSize)
    62  	nOpts := namespace.NewOptions().
    63  		SetRetentionOptions(rOpts).
    64  		SetIndexOptions(idxOpts)
    65  	ns1, err := namespace.NewMetadata(testNamespaces[0], nOpts)
    66  	require.NoError(t, err)
    67  	opts := NewTestOptions(t).
    68  		SetNamespaces([]namespace.Metadata{ns1}).
    69  		// Use TChannel clients for writing / reading because we want to target individual nodes at a time
    70  		// and not write/read all nodes in the cluster.
    71  		SetUseTChannelClientForWriting(true).
    72  		SetUseTChannelClientForReading(true)
    73  
    74  	setupOpts := []BootstrappableTestSetupOptions{
    75  		{DisablePeersBootstrapper: true},
    76  		{
    77  			DisableCommitLogBootstrapper: true,
    78  			DisablePeersBootstrapper:     false,
    79  		},
    80  	}
    81  	setups, closeFn := NewDefaultBootstrappableTestSetups(t, opts, setupOpts)
    82  	defer closeFn()
    83  
    84  	// Write test data for first node
    85  	// Write test data
    86  	now := setups[0].NowFn()()
    87  
    88  	fooSeries := generate.Series{
    89  		ID:   ident.StringID("foo"),
    90  		Tags: ident.NewTags(ident.StringTag("city", "new_york"), ident.StringTag("foo", "foo")),
    91  	}
    92  
    93  	barSeries := generate.Series{
    94  		ID:   ident.StringID("bar"),
    95  		Tags: ident.NewTags(ident.StringTag("city", "new_jersey")),
    96  	}
    97  
    98  	bazSeries := generate.Series{
    99  		ID:   ident.StringID("baz"),
   100  		Tags: ident.NewTags(ident.StringTag("city", "seattle")),
   101  	}
   102  
   103  	quxSeries := generate.Series{
   104  		ID:   ident.StringID("qux"),
   105  		Tags: ident.NewTags(ident.StringTag("city", "new_orleans")),
   106  	}
   107  
   108  	seriesMaps := generate.BlocksByStart([]generate.BlockConfig{
   109  		{
   110  			IDs:       []string{quxSeries.ID.String()},
   111  			Tags:      quxSeries.Tags,
   112  			NumPoints: 100,
   113  			Start:     now.Add(-2 * blockSize),
   114  		},
   115  		{
   116  			IDs:       []string{fooSeries.ID.String()},
   117  			Tags:      fooSeries.Tags,
   118  			NumPoints: 100,
   119  			Start:     now.Add(-blockSize),
   120  		},
   121  		{
   122  			IDs:       []string{barSeries.ID.String()},
   123  			Tags:      barSeries.Tags,
   124  			NumPoints: 100,
   125  			Start:     now.Add(-blockSize),
   126  		},
   127  		{
   128  			IDs:       []string{fooSeries.ID.String()},
   129  			Tags:      fooSeries.Tags,
   130  			NumPoints: 50,
   131  			Start:     now,
   132  		},
   133  		{
   134  			IDs:       []string{bazSeries.ID.String()},
   135  			Tags:      bazSeries.Tags,
   136  			NumPoints: 50,
   137  			Start:     now,
   138  		},
   139  	})
   140  	require.NoError(t, writeTestDataToDisk(ns1, setups[0], seriesMaps, 0))
   141  
   142  	for blockStart, series := range seriesMaps {
   143  		docs := generate.ToDocMetadata(series)
   144  		require.NoError(t, writeTestIndexDataToDisk(
   145  			ns1,
   146  			setups[0].StorageOpts(),
   147  			idxpersist.DefaultIndexVolumeType,
   148  			blockStart,
   149  			setups[0].ShardSet().AllIDs(),
   150  			docs,
   151  		))
   152  	}
   153  
   154  	// Start the first server with filesystem bootstrapper
   155  	require.NoError(t, setups[0].StartServer())
   156  
   157  	// Start the last server with peers and filesystem bootstrappers
   158  	require.NoError(t, setups[1].StartServer())
   159  	log.Debug("servers are now up")
   160  
   161  	// Stop the servers
   162  	defer func() {
   163  		setups.parallel(func(s TestSetup) {
   164  			require.NoError(t, s.StopServer())
   165  		})
   166  		log.Debug("servers are now down")
   167  	}()
   168  
   169  	// Verify in-memory data match what we expect
   170  	for _, setup := range setups {
   171  		verifySeriesMaps(t, setup, ns1.ID(), seriesMaps)
   172  	}
   173  
   174  	// Issue some index queries to the second node which bootstrapped the metadata
   175  	session, err := setups[1].M3DBClient().DefaultSession()
   176  	require.NoError(t, err)
   177  
   178  	start := now.Add(-rOpts.RetentionPeriod())
   179  	end := now.Add(blockSize)
   180  	queryOpts := index.QueryOptions{StartInclusive: start, EndExclusive: end}
   181  
   182  	// Match all new_*r*
   183  	regexpQuery, err := idx.NewRegexpQuery([]byte("city"), []byte("new_.*r.*"))
   184  	require.NoError(t, err)
   185  	iter, fetchResponse, err := session.FetchTaggedIDs(ContextWithDefaultTimeout(),
   186  		ns1.ID(), index.Query{Query: regexpQuery}, queryOpts)
   187  	require.NoError(t, err)
   188  	defer iter.Finalize()
   189  
   190  	verifyQueryMetadataResults(t, iter, fetchResponse.Exhaustive, verifyQueryMetadataResultsOptions{
   191  		namespace:  ns1.ID(),
   192  		exhaustive: true,
   193  		expected:   []generate.Series{fooSeries, barSeries, quxSeries},
   194  	})
   195  
   196  	// Match all *e*e*
   197  	regexpQuery, err = idx.NewRegexpQuery([]byte("city"), []byte(".*e.*e.*"))
   198  	require.NoError(t, err)
   199  	iter, fetchResponse, err = session.FetchTaggedIDs(ContextWithDefaultTimeout(),
   200  		ns1.ID(), index.Query{Query: regexpQuery}, queryOpts)
   201  	require.NoError(t, err)
   202  	defer iter.Finalize()
   203  
   204  	verifyQueryMetadataResults(t, iter, fetchResponse.Exhaustive, verifyQueryMetadataResultsOptions{
   205  		namespace:  ns1.ID(),
   206  		exhaustive: true,
   207  		expected:   []generate.Series{barSeries, bazSeries, quxSeries},
   208  	})
   209  
   210  	// Ensure that the index data for qux has been written to disk.
   211  	numDocsPerBlockStart, err := getNumDocsPerBlockStart(
   212  		ns1.ID(),
   213  		setups[1].FilesystemOpts(),
   214  	)
   215  	require.NoError(t, err)
   216  	numDocs, ok := numDocsPerBlockStart[now.Add(-2*blockSize).Truncate(blockSize)]
   217  	require.True(t, ok)
   218  	require.Equal(t, numDocs, 1)
   219  }
   220  
   221  type indexInfo struct {
   222  	Info        indexpb.IndexVolumeInfo
   223  	VolumeIndex int
   224  }
   225  
   226  func getNumDocsPerBlockStart(
   227  	nsID ident.ID,
   228  	fsOpts fs.Options,
   229  ) (map[xtime.UnixNano]int, error) {
   230  	numDocsPerBlockStart := make(map[xtime.UnixNano]int)
   231  	infoFiles := fs.ReadIndexInfoFiles(fs.ReadIndexInfoFilesOptions{
   232  		FilePathPrefix:   fsOpts.FilePathPrefix(),
   233  		Namespace:        nsID,
   234  		ReaderBufferSize: fsOpts.InfoReaderBufferSize(),
   235  	})
   236  	// Grab the latest index info file for each blockstart.
   237  	latestIndexInfoPerBlockStart := make(map[xtime.UnixNano]indexInfo)
   238  	for _, f := range infoFiles {
   239  		info, ok := latestIndexInfoPerBlockStart[xtime.UnixNano(f.Info.BlockStart)]
   240  		if !ok {
   241  			latestIndexInfoPerBlockStart[xtime.UnixNano(f.Info.BlockStart)] = indexInfo{
   242  				Info:        f.Info,
   243  				VolumeIndex: f.ID.VolumeIndex,
   244  			}
   245  			continue
   246  		}
   247  
   248  		if f.ID.VolumeIndex > info.VolumeIndex {
   249  			latestIndexInfoPerBlockStart[xtime.UnixNano(f.Info.BlockStart)] = indexInfo{
   250  				Info:        f.Info,
   251  				VolumeIndex: f.ID.VolumeIndex,
   252  			}
   253  		}
   254  	}
   255  	for blockStart, info := range latestIndexInfoPerBlockStart {
   256  		for _, segment := range info.Info.Segments {
   257  			metadata := fswriter.Metadata{}
   258  			if err := metadata.Unmarshal(segment.Metadata); err != nil {
   259  				return nil, err
   260  			}
   261  			numDocsPerBlockStart[blockStart] += int(metadata.NumDocs)
   262  		}
   263  	}
   264  	return numDocsPerBlockStart, nil
   265  }