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

     1  // Copyright (c) 2020 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 commitlog
    22  
    23  import (
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/m3db/m3/src/dbnode/namespace"
    28  	"github.com/m3db/m3/src/dbnode/persist/fs"
    29  	"github.com/m3db/m3/src/dbnode/persist/fs/commitlog"
    30  	"github.com/m3db/m3/src/dbnode/storage/bootstrap"
    31  	"github.com/m3db/m3/src/dbnode/storage/bootstrap/result"
    32  	"github.com/m3db/m3/src/dbnode/ts"
    33  	"github.com/m3db/m3/src/x/ident"
    34  	"github.com/m3db/m3/src/x/pool"
    35  	"github.com/m3db/m3/src/x/serialize"
    36  	xtime "github.com/m3db/m3/src/x/time"
    37  
    38  	"github.com/stretchr/testify/require"
    39  )
    40  
    41  var namespaceOptions = namespace.NewOptions()
    42  
    43  func toEncodedBytes(
    44  	t *testing.T,
    45  	pool serialize.TagEncoderPool,
    46  	tags ...ident.Tag,
    47  ) []byte {
    48  	encoder := pool.Get()
    49  	seriesTags := ident.NewTags(tags...)
    50  	err := encoder.Encode(ident.NewTagsIterator(seriesTags))
    51  	require.NoError(t, err)
    52  	data, ok := encoder.Data()
    53  	require.True(t, ok)
    54  	return data.Bytes()
    55  }
    56  
    57  func TestBootstrapIndex(t *testing.T) {
    58  	var (
    59  		opts             = testDefaultOpts
    60  		src              = newCommitLogSource(opts, fs.Inspection{}).(*commitLogSource)
    61  		dataBlockSize    = 2 * time.Hour
    62  		indexBlockSize   = 4 * time.Hour
    63  		namespaceOptions = namespaceOptions.
    64  					SetRetentionOptions(
    65  				namespaceOptions.
    66  					RetentionOptions().
    67  					SetBlockSize(dataBlockSize),
    68  			).
    69  			SetIndexOptions(
    70  				namespaceOptions.
    71  					IndexOptions().
    72  					SetBlockSize(indexBlockSize).
    73  					SetEnabled(true),
    74  			)
    75  	)
    76  	md1, err := namespace.NewMetadata(testNamespaceID, namespaceOptions)
    77  	require.NoError(t, err)
    78  
    79  	testNamespaceID2 := ident.StringID("someOtherNamespace")
    80  	md2, err := namespace.NewMetadata(testNamespaceID2, namespaceOptions)
    81  	require.NoError(t, err)
    82  
    83  	testNamespaceNoData := ident.StringID("noData")
    84  	md3, err := namespace.NewMetadata(testNamespaceNoData, namespaceOptions)
    85  	require.NoError(t, err)
    86  
    87  	now := xtime.Now()
    88  	start := now.Truncate(indexBlockSize)
    89  
    90  	testTagEncodingPool := serialize.
    91  		NewTagEncoderPool(serialize.NewTagEncoderOptions(),
    92  			pool.NewObjectPoolOptions().SetSize(1))
    93  	testTagEncodingPool.Init()
    94  
    95  	fooTags := toEncodedBytes(t, testTagEncodingPool,
    96  		ident.StringTag("city", "ny"),
    97  		ident.StringTag("conference", "monitoroma"),
    98  	)
    99  
   100  	barTags := toEncodedBytes(t, testTagEncodingPool,
   101  		ident.StringTag("city", "sf"))
   102  	bazTags := toEncodedBytes(t, testTagEncodingPool,
   103  		ident.StringTag("city", "oakland"))
   104  
   105  	shardn := func(n int) uint32 { return uint32(n) }
   106  	foo := ts.Series{UniqueIndex: 0, Namespace: testNamespaceID, Shard: shardn(0),
   107  		ID: ident.StringID("foo"), EncodedTags: fooTags}
   108  	bar := ts.Series{UniqueIndex: 1, Namespace: testNamespaceID, Shard: shardn(0),
   109  		ID: ident.StringID("bar"), EncodedTags: barTags}
   110  	baz := ts.Series{UniqueIndex: 2, Namespace: testNamespaceID, Shard: shardn(5),
   111  		ID: ident.StringID("baz"), EncodedTags: bazTags}
   112  	// Make sure we can handle series that don't have tags.
   113  	untagged := ts.Series{UniqueIndex: 3, Namespace: testNamespaceID,
   114  		Shard: shardn(5), ID: ident.StringID("untagged")}
   115  	// Make sure we skip series that are not within the bootstrap range.
   116  	outOfRange := ts.Series{UniqueIndex: 4, Namespace: testNamespaceID,
   117  		Shard: shardn(3), ID: ident.StringID("outOfRange")}
   118  	// Make sure we skip and dont panic on writes for shards that are higher than the maximum we're trying to bootstrap.
   119  	shardTooHigh := ts.Series{UniqueIndex: 5, Namespace: testNamespaceID,
   120  		Shard: shardn(100), ID: ident.StringID("shardTooHigh")}
   121  	// Make sure we skip series for shards that have no requested bootstrap ranges. The shard for this write needs
   122  	// to be less than the highest shard we actually plan to bootstrap.
   123  	noShardBootstrapRange := ts.Series{UniqueIndex: 6, Namespace: testNamespaceID,
   124  		Shard: shardn(4), ID: ident.StringID("noShardBootstrapRange")}
   125  	// Make sure it handles multiple namespaces
   126  	someOtherNamespace := ts.Series{UniqueIndex: 7, Namespace: testNamespaceID2,
   127  		Shard: shardn(0), ID: ident.StringID("series_OtherNamespace")}
   128  
   129  	valuesNs := testValues{
   130  		{foo, start, 1.0, xtime.Second, nil},
   131  		{foo, start, 2.0, xtime.Second, nil},
   132  		{foo, start.Add(dataBlockSize), 3.0, xtime.Second, nil},
   133  		{bar, start.Add(dataBlockSize), 1.0, xtime.Second, nil},
   134  		{bar, start.Add(dataBlockSize), 2.0, xtime.Second, nil},
   135  		{baz, start.Add(2 * dataBlockSize), 1.0, xtime.Second, nil},
   136  		{baz, start.Add(2 * dataBlockSize), 2.0, xtime.Second, nil},
   137  		{untagged, start.Add(2 * dataBlockSize), 1.0, xtime.Second, nil},
   138  	}
   139  
   140  	invalidNs := testValues{
   141  		{outOfRange, start.Add(-dataBlockSize), 1.0, xtime.Second, nil},
   142  		{shardTooHigh, start.Add(dataBlockSize), 1.0, xtime.Second, nil},
   143  		{noShardBootstrapRange, start.Add(dataBlockSize), 1.0, xtime.Second, nil},
   144  	}
   145  
   146  	valuesOtherNs := testValues{
   147  		{someOtherNamespace, start.Add(dataBlockSize), 1.0, xtime.Second, nil},
   148  	}
   149  
   150  	values := append(valuesNs, invalidNs...)
   151  	values = append(values, valuesOtherNs...)
   152  	src.newIteratorFn = func(
   153  		_ commitlog.IteratorOpts,
   154  	) (commitlog.Iterator, []commitlog.ErrorWithPath, error) {
   155  		return newTestCommitLogIterator(values, nil), nil, nil
   156  	}
   157  
   158  	ranges := xtime.NewRanges(
   159  		xtime.Range{Start: start, End: start.Add(dataBlockSize)},
   160  		xtime.Range{Start: start.Add(dataBlockSize), End: start.Add(2 * dataBlockSize)},
   161  		xtime.Range{Start: start.Add(2 * dataBlockSize), End: start.Add(3 * dataBlockSize)})
   162  
   163  	// Don't include ranges for shard 4 as thats how we're testing the noShardBootstrapRange series.
   164  	targetRanges := result.NewShardTimeRanges().Set(
   165  		shardn(0),
   166  		ranges,
   167  	).Set(
   168  		shardn(1),
   169  		ranges,
   170  	).Set(
   171  		shardn(2),
   172  		ranges,
   173  	).Set(
   174  		shardn(5),
   175  		ranges,
   176  	)
   177  
   178  	tester := bootstrap.BuildNamespacesTester(t, testDefaultRunOpts, targetRanges, md1, md2, md3)
   179  	defer tester.Finish()
   180  
   181  	tester.TestReadWith(src)
   182  	tester.TestUnfulfilledForNamespaceIsEmpty(md1)
   183  	tester.TestUnfulfilledForNamespaceIsEmpty(md2)
   184  	tester.TestUnfulfilledForNamespaceIsEmpty(md3)
   185  
   186  	nsWrites := tester.EnsureDumpWritesForNamespace(md1)
   187  	enforceValuesAreCorrect(t, valuesNs, nsWrites)
   188  
   189  	otherNamespaceWrites := tester.EnsureDumpWritesForNamespace(md2)
   190  	enforceValuesAreCorrect(t, valuesOtherNs, otherNamespaceWrites)
   191  
   192  	noWrites := tester.EnsureDumpWritesForNamespace(md3)
   193  	require.Equal(t, 0, len(noWrites))
   194  	tester.EnsureNoLoadedBlocks()
   195  }
   196  
   197  func TestBootstrapIndexEmptyShardTimeRanges(t *testing.T) {
   198  	var (
   199  		opts             = testDefaultOpts
   200  		src              = newCommitLogSource(opts, fs.Inspection{}).(*commitLogSource)
   201  		dataBlockSize    = 2 * time.Hour
   202  		indexBlockSize   = 4 * time.Hour
   203  		namespaceOptions = namespace.NewOptions().
   204  					SetRetentionOptions(
   205  				namespace.NewOptions().
   206  					RetentionOptions().
   207  					SetBlockSize(dataBlockSize),
   208  			).
   209  			SetIndexOptions(
   210  				namespace.NewOptions().
   211  					IndexOptions().
   212  					SetBlockSize(indexBlockSize).
   213  					SetEnabled(true),
   214  			)
   215  	)
   216  	md, err := namespace.NewMetadata(testNamespaceID, namespaceOptions)
   217  	require.NoError(t, err)
   218  
   219  	values := testValues{}
   220  	src.newIteratorFn = func(_ commitlog.IteratorOpts) (commitlog.Iterator, []commitlog.ErrorWithPath, error) {
   221  		return newTestCommitLogIterator(values, nil), nil, nil
   222  	}
   223  
   224  	target := result.NewShardTimeRanges()
   225  	tester := bootstrap.BuildNamespacesTester(t, testDefaultRunOpts, target, md)
   226  	defer tester.Finish()
   227  
   228  	tester.TestReadWith(src)
   229  	tester.TestUnfulfilledForNamespaceIsEmpty(md)
   230  	tester.EnsureNoLoadedBlocks()
   231  	tester.EnsureNoWrites()
   232  }