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

     1  // Copyright (c) 2016 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 bootstrapper
    22  
    23  import (
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/m3db/m3/src/dbnode/namespace"
    28  	"github.com/m3db/m3/src/dbnode/storage/bootstrap"
    29  	"github.com/m3db/m3/src/dbnode/storage/bootstrap/result"
    30  	"github.com/m3db/m3/src/x/context"
    31  	"github.com/m3db/m3/src/x/ident"
    32  	xtime "github.com/m3db/m3/src/x/time"
    33  
    34  	"github.com/golang/mock/gomock"
    35  	"github.com/stretchr/testify/assert"
    36  	"github.com/stretchr/testify/require"
    37  )
    38  
    39  var (
    40  	testNamespaceID    = ident.StringID("testNamespace")
    41  	testTargetStart    = xtime.Now()
    42  	testShard          = uint32(0)
    43  	testDefaultRunOpts = bootstrap.NewRunOptions().
    44  				SetPersistConfig(bootstrap.PersistConfig{Enabled: false})
    45  )
    46  
    47  func testNsMetadata(t *testing.T, withIndex bool) namespace.Metadata {
    48  	opts := namespace.NewOptions()
    49  	opts = opts.SetIndexOptions(opts.IndexOptions().SetEnabled(withIndex))
    50  	md, err := namespace.NewMetadata(testNamespaceID, opts)
    51  	require.NoError(t, err)
    52  	return md
    53  }
    54  
    55  func testBaseBootstrapper(
    56  	t *testing.T,
    57  	ctrl *gomock.Controller,
    58  ) (*bootstrap.MockSource, *bootstrap.MockBootstrapper, baseBootstrapper) {
    59  	source := bootstrap.NewMockSource(ctrl)
    60  	opts := result.NewOptions()
    61  	next := bootstrap.NewMockBootstrapper(ctrl)
    62  	bootstrapper, err := NewBaseBootstrapper("mock", source, opts, next)
    63  	require.NoError(t, err)
    64  	baseBootstrapper := bootstrapper.(baseBootstrapper)
    65  	return source, next, baseBootstrapper
    66  }
    67  
    68  func testTargetRanges() xtime.Ranges {
    69  	return xtime.NewRanges(xtime.Range{Start: testTargetStart, End: testTargetStart.Add(2 * time.Hour)})
    70  }
    71  
    72  func testShardTimeRanges() result.ShardTimeRanges {
    73  	r := result.NewShardTimeRanges()
    74  	r.Set(testShard, testTargetRanges())
    75  	return r
    76  }
    77  
    78  func testResult(
    79  	ns namespace.Metadata,
    80  	withIndex bool,
    81  	shard uint32,
    82  	unfulfilledRange xtime.Ranges,
    83  ) bootstrap.NamespaceResults {
    84  	unfulfilled := result.NewShardTimeRanges()
    85  	unfulfilled.Set(shard, unfulfilledRange)
    86  
    87  	opts := bootstrap.NamespaceResultsMapOptions{}
    88  	results := bootstrap.NewNamespaceResultsMap(opts)
    89  	dataResult := result.NewDataBootstrapResult()
    90  	dataResult.SetUnfulfilled(unfulfilled.Copy())
    91  
    92  	indexResult := result.NewIndexBootstrapResult()
    93  	if withIndex {
    94  		indexResult.SetUnfulfilled(unfulfilled.Copy())
    95  	}
    96  
    97  	results.Set(ns.ID(), bootstrap.NamespaceResult{
    98  		Metadata:    ns,
    99  		Shards:      []uint32{shard},
   100  		DataResult:  dataResult,
   101  		IndexResult: indexResult,
   102  	})
   103  
   104  	return bootstrap.NamespaceResults{Results: results}
   105  }
   106  
   107  func testEmptyResult(
   108  	ns namespace.Metadata,
   109  ) bootstrap.NamespaceResults {
   110  	opts := bootstrap.NamespaceResultsMapOptions{}
   111  	results := bootstrap.NewNamespaceResultsMap(opts)
   112  	results.Set(ns.ID(), bootstrap.NamespaceResult{
   113  		Metadata:    ns,
   114  		DataResult:  result.NewDataBootstrapResult(),
   115  		IndexResult: result.NewIndexBootstrapResult(),
   116  	})
   117  
   118  	return bootstrap.NamespaceResults{Results: results}
   119  }
   120  
   121  func TestBaseBootstrapperEmptyRange(t *testing.T) {
   122  	testBaseBootstrapperEmptyRange(t, false)
   123  }
   124  
   125  func TestBaseBootstrapperEmptyRangeWithIndex(t *testing.T) {
   126  	testBaseBootstrapperEmptyRange(t, true)
   127  }
   128  
   129  func testBaseBootstrapperEmptyRange(t *testing.T, withIndex bool) {
   130  	ctrl := gomock.NewController(t)
   131  	defer ctrl.Finish()
   132  	src, next, base := testBaseBootstrapper(t, ctrl)
   133  	testNs := testNsMetadata(t, withIndex)
   134  
   135  	rngs := result.NewShardTimeRanges()
   136  	unfulfilled := xtime.NewRanges()
   137  	nsResults := testResult(testNs, withIndex, testShard, unfulfilled)
   138  	nextResult := testResult(testNs, withIndex, testShard, xtime.NewRanges())
   139  	shardRangeMatcher := bootstrap.ShardTimeRangesMatcher{Ranges: rngs}
   140  
   141  	tester := bootstrap.BuildNamespacesTester(t, testDefaultRunOpts, rngs, testNs)
   142  	defer tester.Finish()
   143  
   144  	cache := tester.Cache
   145  	src.EXPECT().AvailableData(testNs, shardRangeMatcher, cache, testDefaultRunOpts).
   146  		Return(rngs, nil)
   147  	if withIndex {
   148  		src.EXPECT().AvailableIndex(testNs, shardRangeMatcher, cache, testDefaultRunOpts).
   149  			Return(rngs, nil)
   150  	}
   151  
   152  	matcher := bootstrap.NamespaceMatcher{Namespaces: tester.Namespaces}
   153  	src.EXPECT().
   154  		Read(gomock.Any(), matcher, cache).
   155  		DoAndReturn(func(
   156  			ctx context.Context,
   157  			namespaces bootstrap.Namespaces,
   158  			cache bootstrap.Cache,
   159  		) (bootstrap.NamespaceResults, error) {
   160  			return nsResults, nil
   161  		})
   162  	next.EXPECT().Bootstrap(gomock.Any(), matcher, cache).Return(nextResult, nil)
   163  
   164  	// Test non-nil empty range
   165  	tester.TestBootstrapWith(base)
   166  	tester.TestUnfulfilledForNamespaceIsEmpty(testNs)
   167  	assert.Equal(t, nsResults, tester.Results)
   168  
   169  	tester.EnsureNoLoadedBlocks()
   170  	tester.EnsureNoWrites()
   171  }
   172  
   173  func TestBaseBootstrapperCurrentNoUnfulfilled(t *testing.T) {
   174  	testBaseBootstrapperCurrentNoUnfulfilled(t, false)
   175  }
   176  
   177  func TestBaseBootstrapperCurrentNoUnfulfilledWithIndex(t *testing.T) {
   178  	testBaseBootstrapperCurrentNoUnfulfilled(t, true)
   179  }
   180  
   181  func testBaseBootstrapperCurrentNoUnfulfilled(t *testing.T, withIndex bool) {
   182  	ctrl := gomock.NewController(t)
   183  	defer ctrl.Finish()
   184  	src, next, base := testBaseBootstrapper(t, ctrl)
   185  	testNs := testNsMetadata(t, withIndex)
   186  
   187  	unfulfilled := xtime.NewRanges()
   188  	nsResults := testResult(testNs, withIndex, testShard, unfulfilled)
   189  	nextResult := testResult(testNs, withIndex, testShard, xtime.NewRanges())
   190  
   191  	targetRanges := testShardTimeRanges()
   192  
   193  	tester := bootstrap.BuildNamespacesTester(t, testDefaultRunOpts, targetRanges,
   194  		testNs)
   195  	defer tester.Finish()
   196  
   197  	cache := tester.Cache
   198  	src.EXPECT().AvailableData(testNs, targetRanges, cache, testDefaultRunOpts).
   199  		Return(targetRanges, nil)
   200  	if withIndex {
   201  		src.EXPECT().AvailableIndex(testNs, targetRanges, cache, testDefaultRunOpts).
   202  			Return(targetRanges, nil)
   203  	}
   204  
   205  	matcher := bootstrap.NamespaceMatcher{Namespaces: tester.Namespaces}
   206  	src.EXPECT().
   207  		Read(gomock.Any(), matcher, cache).
   208  		DoAndReturn(func(
   209  			ctx context.Context,
   210  			namespaces bootstrap.Namespaces,
   211  			cache bootstrap.Cache,
   212  		) (bootstrap.NamespaceResults, error) {
   213  			return nsResults, nil
   214  		})
   215  	next.EXPECT().Bootstrap(gomock.Any(), matcher, cache).Return(nextResult, nil)
   216  
   217  	tester.TestBootstrapWith(base)
   218  	assert.Equal(t, nsResults, tester.Results)
   219  	tester.TestUnfulfilledForNamespaceIsEmpty(testNs)
   220  
   221  	tester.EnsureNoLoadedBlocks()
   222  	tester.EnsureNoWrites()
   223  }
   224  
   225  func TestBaseBootstrapperCurrentSomeUnfulfilled(t *testing.T) {
   226  	testBaseBootstrapperCurrentSomeUnfulfilled(t, false)
   227  }
   228  
   229  func TestBaseBootstrapperCurrentSomeUnfulfilledWithIndex(t *testing.T) {
   230  	testBaseBootstrapperCurrentSomeUnfulfilled(t, true)
   231  }
   232  
   233  func testBaseBootstrapperCurrentSomeUnfulfilled(t *testing.T, withIndex bool) {
   234  	ctrl := gomock.NewController(t)
   235  	defer ctrl.Finish()
   236  	src, next, base := testBaseBootstrapper(t, ctrl)
   237  	testNs := testNsMetadata(t, withIndex)
   238  	targetRanges := testShardTimeRanges()
   239  	currUnfulfilled := xtime.NewRanges(xtime.Range{
   240  		Start: testTargetStart.Add(time.Hour),
   241  		End:   testTargetStart.Add(time.Hour * 2),
   242  	})
   243  
   244  	currResult := testResult(testNs, withIndex, testShard, currUnfulfilled)
   245  	nextResult := testResult(testNs, withIndex, testShard, xtime.NewRanges())
   246  	tester := bootstrap.BuildNamespacesTester(t, testDefaultRunOpts, targetRanges,
   247  		testNs)
   248  	defer tester.Finish()
   249  
   250  	cache := tester.Cache
   251  	src.EXPECT().AvailableData(testNs, targetRanges, cache, testDefaultRunOpts).
   252  		Return(targetRanges, nil)
   253  	if withIndex {
   254  		src.EXPECT().AvailableIndex(testNs, targetRanges, cache, testDefaultRunOpts).
   255  			Return(targetRanges, nil)
   256  	}
   257  
   258  	matcher := bootstrap.NamespaceMatcher{Namespaces: tester.Namespaces}
   259  	src.EXPECT().Read(gomock.Any(), matcher, cache).Return(currResult, nil)
   260  	next.EXPECT().Bootstrap(gomock.Any(), matcher, cache).Return(nextResult, nil)
   261  
   262  	tester.TestBootstrapWith(base)
   263  	tester.TestUnfulfilledForNamespaceIsEmpty(testNs)
   264  }
   265  
   266  func testBasebootstrapperNext(
   267  	t *testing.T,
   268  	nextUnfulfilled xtime.Ranges,
   269  	withIndex bool,
   270  ) {
   271  	ctrl := gomock.NewController(t)
   272  	defer ctrl.Finish()
   273  	src, next, base := testBaseBootstrapper(t, ctrl)
   274  	testNs := testNsMetadata(t, withIndex)
   275  	targetRanges := testShardTimeRanges()
   276  
   277  	tester := bootstrap.BuildNamespacesTester(t, testDefaultRunOpts, targetRanges,
   278  		testNs)
   279  	defer tester.Finish()
   280  
   281  	cache := tester.Cache
   282  	src.EXPECT().
   283  		AvailableData(testNs, targetRanges, cache, testDefaultRunOpts).
   284  		Return(result.NewShardTimeRanges(), nil)
   285  	if withIndex {
   286  		src.EXPECT().
   287  			AvailableIndex(testNs, targetRanges, cache, testDefaultRunOpts).
   288  			Return(result.NewShardTimeRanges(), nil)
   289  	}
   290  
   291  	emptyResult := testEmptyResult(testNs)
   292  	nextResult := testResult(testNs, withIndex, testShard, nextUnfulfilled)
   293  	matcher := bootstrap.NamespaceMatcher{Namespaces: tester.Namespaces}
   294  	src.EXPECT().Read(gomock.Any(), matcher, cache).Return(emptyResult, nil)
   295  	next.EXPECT().Bootstrap(gomock.Any(), matcher, cache).Return(nextResult, nil)
   296  
   297  	tester.TestBootstrapWith(base)
   298  
   299  	ex, ok := nextResult.Results.Get(testNs.ID())
   300  	require.True(t, ok)
   301  
   302  	expected := ex.DataResult.Unfulfilled()
   303  	expectedIdx := ex.IndexResult.Unfulfilled()
   304  	if !withIndex {
   305  		expectedIdx = result.NewShardTimeRanges()
   306  	}
   307  
   308  	tester.TestUnfulfilledForNamespace(testNs, expected, expectedIdx)
   309  }
   310  
   311  func TestBaseBootstrapperNextNoUnfulfilled(t *testing.T) {
   312  	nextUnfulfilled := testTargetRanges()
   313  	testBasebootstrapperNext(t, nextUnfulfilled, false)
   314  }
   315  
   316  func TestBaseBootstrapperNextNoUnfulfilledWithIndex(t *testing.T) {
   317  	nextUnfulfilled := testTargetRanges()
   318  	testBasebootstrapperNext(t, nextUnfulfilled, true)
   319  }
   320  
   321  func TestBaseBootstrapperNextSomeUnfulfilled(t *testing.T) {
   322  	nextUnfulfilled := xtime.NewRanges(xtime.Range{
   323  		Start: testTargetStart,
   324  		End:   testTargetStart.Add(time.Hour),
   325  	})
   326  
   327  	testBasebootstrapperNext(t, nextUnfulfilled, false)
   328  }
   329  
   330  func TestBaseBootstrapperNextSomeUnfulfilledWithIndex(t *testing.T) {
   331  	nextUnfulfilled := xtime.NewRanges(xtime.Range{
   332  		Start: testTargetStart,
   333  		End:   testTargetStart.Add(time.Hour),
   334  	})
   335  
   336  	testBasebootstrapperNext(t, nextUnfulfilled, true)
   337  }