github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/integration/fs_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  	"github.com/m3db/m3/src/dbnode/integration/generate"
    30  	"github.com/m3db/m3/src/dbnode/namespace"
    31  	"github.com/m3db/m3/src/dbnode/retention"
    32  	"github.com/m3db/m3/src/dbnode/storage/index"
    33  	"github.com/m3db/m3/src/m3ninx/doc"
    34  	"github.com/m3db/m3/src/m3ninx/idx"
    35  	idxpersist "github.com/m3db/m3/src/m3ninx/persist"
    36  	"github.com/m3db/m3/src/x/context"
    37  	"github.com/m3db/m3/src/x/ident"
    38  	xtime "github.com/m3db/m3/src/x/time"
    39  
    40  	"github.com/stretchr/testify/require"
    41  	"go.uber.org/zap"
    42  )
    43  
    44  func TestFilesystemBootstrapIndexWithIndexingEnabled(t *testing.T) {
    45  	testFilesystemBootstrapIndexWithIndexingEnabled(t,
    46  		testFilesystemBootstrapIndexWithIndexingEnabledOptions{})
    47  }
    48  
    49  // TestFilesystemBootstrapIndexWithIndexingEnabledAndCheckTickFreeMmap makes
    50  // sure that bootstrapped segments free mmap calls occur.
    51  func TestFilesystemBootstrapIndexWithIndexingEnabledAndCheckTickFreeMmap(t *testing.T) {
    52  	testFilesystemBootstrapIndexWithIndexingEnabled(t,
    53  		testFilesystemBootstrapIndexWithIndexingEnabledOptions{
    54  			test: func(t *testing.T, setup TestSetup) {
    55  				var (
    56  					cancellable             = context.NewCancellable()
    57  					numSegmentsBootstrapped int64
    58  					freeMmap                int64
    59  				)
    60  				for _, ns := range setup.DB().Namespaces() {
    61  					idx, err := ns.Index()
    62  					require.NoError(t, err)
    63  
    64  					result, err := idx.Tick(cancellable, xtime.Now())
    65  					require.NoError(t, err)
    66  
    67  					numSegmentsBootstrapped += result.NumSegmentsBootstrapped
    68  					freeMmap += result.FreeMmap
    69  				}
    70  
    71  				log := setup.StorageOpts().InstrumentOptions().Logger()
    72  				log.Info("ticked namespaces",
    73  					zap.Int64("numSegmentsBootstrapped", numSegmentsBootstrapped),
    74  					zap.Int64("freeMmap", freeMmap))
    75  				require.True(t, numSegmentsBootstrapped > 0)
    76  				require.True(t, freeMmap > 0)
    77  			},
    78  		})
    79  }
    80  
    81  type testFilesystemBootstrapIndexWithIndexingEnabledOptions struct {
    82  	// test is an extended test to run at the end of the core bootstrap test.
    83  	test func(t *testing.T, setup TestSetup)
    84  }
    85  
    86  func testFilesystemBootstrapIndexWithIndexingEnabled(
    87  	t *testing.T,
    88  	testOpts testFilesystemBootstrapIndexWithIndexingEnabledOptions,
    89  ) {
    90  	if testing.Short() {
    91  		t.SkipNow() // Just skip if we're doing a short run
    92  	}
    93  
    94  	var (
    95  		blockSize = 2 * time.Hour
    96  		rOpts     = retention.NewOptions().SetRetentionPeriod(6 * blockSize).SetBlockSize(blockSize)
    97  		idxOpts   = namespace.NewIndexOptions().SetEnabled(true).SetBlockSize(2 * blockSize)
    98  		nOpts     = namespace.NewOptions().SetRetentionOptions(rOpts).SetIndexOptions(idxOpts)
    99  	)
   100  	ns1, err := namespace.NewMetadata(testNamespaces[0], nOpts)
   101  	require.NoError(t, err)
   102  	ns2, err := namespace.NewMetadata(testNamespaces[1], nOpts)
   103  	require.NoError(t, err)
   104  
   105  	opts := NewTestOptions(t).
   106  		SetNamespaces([]namespace.Metadata{ns1, ns2})
   107  
   108  	// Test setup
   109  	setup, err := NewTestSetup(t, opts, nil)
   110  	require.NoError(t, err)
   111  	defer setup.Close()
   112  
   113  	require.NoError(t, setup.InitializeBootstrappers(InitializeBootstrappersOptions{
   114  		WithFileSystem: true,
   115  	}))
   116  
   117  	// Write test data
   118  	now := setup.NowFn()()
   119  
   120  	fooSeries := generate.Series{
   121  		ID:   ident.StringID("foo"),
   122  		Tags: ident.NewTags(ident.StringTag("city", "new_york"), ident.StringTag("foo", "foo")),
   123  	}
   124  	fooDoc := doc.Metadata{
   125  		ID: fooSeries.ID.Bytes(),
   126  		Fields: []doc.Field{
   127  			{Name: []byte("city"), Value: []byte("new_york")},
   128  			{Name: []byte("foo"), Value: []byte("foo")},
   129  		},
   130  	}
   131  
   132  	barSeries := generate.Series{
   133  		ID:   ident.StringID("bar"),
   134  		Tags: ident.NewTags(ident.StringTag("city", "new_jersey")),
   135  	}
   136  	barDoc := doc.Metadata{
   137  		ID: barSeries.ID.Bytes(),
   138  		Fields: []doc.Field{
   139  			{Name: []byte("city"), Value: []byte("new_jersey")},
   140  		},
   141  	}
   142  
   143  	bazSeries := generate.Series{
   144  		ID:   ident.StringID("baz"),
   145  		Tags: ident.NewTags(ident.StringTag("city", "seattle")),
   146  	}
   147  	bazDoc := doc.Metadata{
   148  		ID: bazSeries.ID.Bytes(),
   149  		Fields: []doc.Field{
   150  			{Name: []byte("city"), Value: []byte("seattle")},
   151  		},
   152  	}
   153  
   154  	seriesMaps := generate.BlocksByStart([]generate.BlockConfig{
   155  		{
   156  			IDs:       []string{fooSeries.ID.String()},
   157  			Tags:      fooSeries.Tags,
   158  			NumPoints: 100,
   159  			Start:     now.Add(-3 * blockSize),
   160  		},
   161  		{
   162  			IDs:       []string{barSeries.ID.String()},
   163  			Tags:      barSeries.Tags,
   164  			NumPoints: 100,
   165  			Start:     now.Add(-3 * blockSize),
   166  		},
   167  		{
   168  			IDs:       []string{fooSeries.ID.String()},
   169  			Tags:      fooSeries.Tags,
   170  			NumPoints: 50,
   171  			Start:     now,
   172  		},
   173  		{
   174  			IDs:       []string{bazSeries.ID.String()},
   175  			Tags:      bazSeries.Tags,
   176  			NumPoints: 50,
   177  			Start:     now,
   178  		},
   179  	})
   180  
   181  	defaultIndexDocs := []doc.Metadata{
   182  		fooDoc,
   183  		barDoc,
   184  		bazDoc,
   185  	}
   186  
   187  	require.NoError(t, writeTestDataToDisk(ns1, setup, seriesMaps, 0))
   188  	require.NoError(t, writeTestDataToDisk(ns2, setup, nil, 0))
   189  	require.NoError(t, writeTestIndexDataToDisk(
   190  		ns1,
   191  		setup.StorageOpts(),
   192  		idxpersist.DefaultIndexVolumeType,
   193  		now.Add(-blockSize),
   194  		setup.ShardSet().AllIDs(),
   195  		defaultIndexDocs,
   196  	))
   197  
   198  	// Start the server with filesystem bootstrapper
   199  	log := setup.StorageOpts().InstrumentOptions().Logger()
   200  	log.Debug("filesystem bootstrap test")
   201  	require.NoError(t, setup.StartServer())
   202  	log.Debug("server is now up")
   203  
   204  	// Stop the server
   205  	defer func() {
   206  		require.NoError(t, setup.StopServerAndVerifyOpenFilesAreClosed())
   207  		setup.Close()
   208  		log.Debug("server is now down")
   209  	}()
   210  
   211  	// Verify data matches what we expect
   212  	verifySeriesMaps(t, setup, testNamespaces[0], seriesMaps)
   213  	verifySeriesMaps(t, setup, testNamespaces[1], nil)
   214  
   215  	// Issue some index queries
   216  	session, err := setup.M3DBClient().DefaultSession()
   217  	require.NoError(t, err)
   218  
   219  	start := now.Add(-rOpts.RetentionPeriod())
   220  	end := now.Add(blockSize)
   221  	queryOpts := index.QueryOptions{StartInclusive: start, EndExclusive: end}
   222  
   223  	// Match all new_*r*
   224  	regexpQuery, err := idx.NewRegexpQuery([]byte("city"), []byte("new_.*r.*"))
   225  	require.NoError(t, err)
   226  	iter, fetchResponse, err := session.FetchTaggedIDs(ContextWithDefaultTimeout(),
   227  		ns1.ID(), index.Query{Query: regexpQuery}, queryOpts)
   228  	require.NoError(t, err)
   229  	defer iter.Finalize()
   230  
   231  	verifyQueryMetadataResults(t, iter, fetchResponse.Exhaustive, verifyQueryMetadataResultsOptions{
   232  		namespace:  ns1.ID(),
   233  		exhaustive: true,
   234  		expected:   []generate.Series{fooSeries, barSeries},
   235  	})
   236  
   237  	// Match all *e*e*
   238  	regexpQuery, err = idx.NewRegexpQuery([]byte("city"), []byte(".*e.*e.*"))
   239  	require.NoError(t, err)
   240  	iter, fetchResponse, err = session.FetchTaggedIDs(ContextWithDefaultTimeout(),
   241  		ns1.ID(), index.Query{Query: regexpQuery}, queryOpts)
   242  	require.NoError(t, err)
   243  	defer iter.Finalize()
   244  
   245  	verifyQueryMetadataResults(t, iter, fetchResponse.Exhaustive, verifyQueryMetadataResultsOptions{
   246  		namespace:  ns1.ID(),
   247  		exhaustive: true,
   248  		expected:   []generate.Series{barSeries, bazSeries},
   249  	})
   250  
   251  	if testOpts.test != nil {
   252  		testOpts.test(t, setup)
   253  	}
   254  }