github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/storage/bootstrap/result/result_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 result
    22  
    23  import (
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/m3db/m3/src/dbnode/namespace"
    28  	"github.com/m3db/m3/src/m3ninx/index/segment"
    29  	idxpersist "github.com/m3db/m3/src/m3ninx/persist"
    30  	xtime "github.com/m3db/m3/src/x/time"
    31  
    32  	"github.com/golang/mock/gomock"
    33  	"github.com/stretchr/testify/assert"
    34  	"github.com/stretchr/testify/require"
    35  )
    36  
    37  func TestIndexResultMergeMergesExistingSegments(t *testing.T) {
    38  	ctrl := gomock.NewController(t)
    39  	defer ctrl.Finish()
    40  
    41  	start := xtime.Now().Truncate(testBlockSize)
    42  
    43  	segments := []Segment{
    44  		NewSegment(segment.NewMockSegment(ctrl), false),
    45  		NewSegment(segment.NewMockSegment(ctrl), false),
    46  		NewSegment(segment.NewMockSegment(ctrl), false),
    47  		NewSegment(segment.NewMockSegment(ctrl), false),
    48  		NewSegment(segment.NewMockSegment(ctrl), false),
    49  		NewSegment(segment.NewMockSegment(ctrl), false),
    50  	}
    51  
    52  	times := []xtime.UnixNano{start, start.Add(testBlockSize), start.Add(2 * testBlockSize)}
    53  	tr0 := NewShardTimeRangesFromRange(times[0], times[1], 1, 2, 3)
    54  	tr1 := NewShardTimeRangesFromRange(times[1], times[2], 1, 2, 3)
    55  
    56  	first := NewIndexBootstrapResult()
    57  	blk1 := NewIndexBlockByVolumeType(times[0])
    58  	blk1.SetBlock(idxpersist.DefaultIndexVolumeType, NewIndexBlock([]Segment{segments[0]}, tr0))
    59  	first.Add(blk1, nil)
    60  	blk2 := NewIndexBlockByVolumeType(times[0])
    61  	blk2.SetBlock(idxpersist.DefaultIndexVolumeType, NewIndexBlock([]Segment{segments[1]}, tr0))
    62  	first.Add(blk2, nil)
    63  	blk3 := NewIndexBlockByVolumeType(times[1])
    64  	blk3.SetBlock(idxpersist.DefaultIndexVolumeType, NewIndexBlock([]Segment{segments[2], segments[3]}, tr1))
    65  	first.Add(blk3, nil)
    66  
    67  	second := NewIndexBootstrapResult()
    68  	blk4 := NewIndexBlockByVolumeType(times[0])
    69  	blk4.SetBlock(idxpersist.DefaultIndexVolumeType, NewIndexBlock([]Segment{segments[4]}, tr0))
    70  	second.Add(blk4, nil)
    71  	blk5 := NewIndexBlockByVolumeType(times[1])
    72  	blk5.SetBlock(idxpersist.DefaultIndexVolumeType, NewIndexBlock([]Segment{segments[5]}, tr1))
    73  	second.Add(blk5, nil)
    74  
    75  	merged := MergedIndexBootstrapResult(first, second)
    76  
    77  	expected := NewIndexBootstrapResult()
    78  	blk6 := NewIndexBlockByVolumeType(times[0])
    79  	blk6.SetBlock(idxpersist.DefaultIndexVolumeType, NewIndexBlock([]Segment{segments[0], segments[1], segments[4]}, tr0))
    80  	expected.Add(blk6, nil)
    81  	blk7 := NewIndexBlockByVolumeType(times[1])
    82  	blk7.SetBlock(idxpersist.DefaultIndexVolumeType, NewIndexBlock([]Segment{segments[2], segments[3], segments[5]}, tr1))
    83  	expected.Add(blk7, nil)
    84  
    85  	assert.True(t, segmentsInResultsSame(expected.IndexResults(), merged.IndexResults()))
    86  }
    87  
    88  func TestIndexResultSetUnfulfilled(t *testing.T) {
    89  	ctrl := gomock.NewController(t)
    90  	defer ctrl.Finish()
    91  
    92  	t0 := xtime.Now().Truncate(time.Hour)
    93  	tn := func(i int) xtime.UnixNano {
    94  		return t0.Add(time.Duration(i) * time.Hour)
    95  	}
    96  	results := NewIndexBootstrapResult()
    97  	testRanges := NewShardTimeRangesFromRange(tn(0), tn(1), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    98  	results.SetUnfulfilled(testRanges)
    99  	require.Equal(t, testRanges, results.Unfulfilled())
   100  }
   101  
   102  func TestIndexResultAdd(t *testing.T) {
   103  	ctrl := gomock.NewController(t)
   104  	defer ctrl.Finish()
   105  
   106  	t0 := xtime.Now().Truncate(time.Hour)
   107  	tn := func(i int) xtime.UnixNano {
   108  		return t0.Add(time.Duration(i) * time.Hour)
   109  	}
   110  	results := NewIndexBootstrapResult()
   111  	testRanges := NewShardTimeRangesFromRange(tn(0), tn(1), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
   112  	results.Add(NewIndexBlockByVolumeType(0), testRanges)
   113  	require.Equal(t, testRanges, results.Unfulfilled())
   114  }
   115  
   116  func TestShardTimeRangesToUnfulfilledIndexResult(t *testing.T) {
   117  	now := xtime.Now()
   118  	str := shardTimeRanges{
   119  		0: xtime.NewRanges(xtime.Range{
   120  			Start: now,
   121  			End:   now.Add(time.Minute),
   122  		}),
   123  		1: xtime.NewRanges(xtime.Range{
   124  			Start: now.Add(3 * time.Minute),
   125  			End:   now.Add(4 * time.Minute),
   126  		}),
   127  	}
   128  	r := str.ToUnfulfilledIndexResult()
   129  	assert.Equal(t, 0, len(r.IndexResults()))
   130  	assert.True(t, r.Unfulfilled().Equal(str))
   131  }
   132  
   133  func TestIndexResultsMarkFulfilled(t *testing.T) {
   134  	ctrl := gomock.NewController(t)
   135  	defer ctrl.Finish()
   136  
   137  	iopts := namespace.NewIndexOptions().SetBlockSize(time.Hour * 2)
   138  	t0 := xtime.Now().Truncate(2 * time.Hour)
   139  	tn := func(i int) xtime.UnixNano {
   140  		return t0.Add(time.Duration(i) * time.Hour)
   141  	}
   142  	results := make(IndexResults)
   143  
   144  	// range checks
   145  	require.Error(t, results.MarkFulfilled(tn(0),
   146  		NewShardTimeRangesFromRange(tn(4), tn(6), 1), idxpersist.DefaultIndexVolumeType, iopts))
   147  	require.Error(t, results.MarkFulfilled(tn(0),
   148  		NewShardTimeRangesFromRange(tn(-1), tn(1), 1), idxpersist.DefaultIndexVolumeType, iopts))
   149  
   150  	// valid add
   151  	fulfilledRange := NewShardTimeRangesFromRange(tn(0), tn(1), 1)
   152  	require.NoError(t, results.MarkFulfilled(tn(0), fulfilledRange, idxpersist.DefaultIndexVolumeType, iopts))
   153  	require.Equal(t, 1, len(results))
   154  	blkByVolumeType, ok := results[tn(0)]
   155  	require.True(t, ok)
   156  	require.True(t, tn(0).Equal(blkByVolumeType.blockStart))
   157  	blk, ok := blkByVolumeType.GetBlock(idxpersist.DefaultIndexVolumeType)
   158  	require.True(t, ok)
   159  	require.Equal(t, fulfilledRange, blk.fulfilled)
   160  
   161  	// additional add for same block
   162  	nextFulfilledRange := NewShardTimeRangesFromRange(tn(1), tn(2), 2)
   163  	require.NoError(t, results.MarkFulfilled(tn(1), nextFulfilledRange, idxpersist.DefaultIndexVolumeType, iopts))
   164  	require.Equal(t, 1, len(results))
   165  	blkByVolumeType, ok = results[tn(0)]
   166  	require.True(t, ok)
   167  	require.True(t, tn(0).Equal(blkByVolumeType.blockStart))
   168  	fulfilledRange.AddRanges(nextFulfilledRange)
   169  	blk, ok = blkByVolumeType.GetBlock(idxpersist.DefaultIndexVolumeType)
   170  	require.True(t, ok)
   171  	require.Equal(t, fulfilledRange, blk.fulfilled)
   172  
   173  	// additional add for next block
   174  	nextFulfilledRange = NewShardTimeRangesFromRange(tn(2), tn(4), 1, 2, 3)
   175  	require.NoError(t, results.MarkFulfilled(tn(2), nextFulfilledRange, idxpersist.DefaultIndexVolumeType, iopts))
   176  	require.Equal(t, 2, len(results))
   177  	blkByVolumeType, ok = results[tn(2)]
   178  	require.True(t, ok)
   179  	require.True(t, tn(2).Equal(blkByVolumeType.blockStart))
   180  	blk, ok = blkByVolumeType.GetBlock(idxpersist.DefaultIndexVolumeType)
   181  	require.True(t, ok)
   182  	require.Equal(t, nextFulfilledRange, blk.fulfilled)
   183  }
   184  
   185  func segmentsInResultsSame(a, b IndexResults) bool {
   186  	if len(a) != len(b) {
   187  		return false
   188  	}
   189  	for t, blockByVolumeType := range a {
   190  		otherBlockByVolumeType, ok := b[t]
   191  		if !ok {
   192  			return false
   193  		}
   194  		block, ok := blockByVolumeType.GetBlock(idxpersist.DefaultIndexVolumeType)
   195  		if !ok {
   196  			return false
   197  		}
   198  		otherBlock, ok := otherBlockByVolumeType.GetBlock(idxpersist.DefaultIndexVolumeType)
   199  		if !ok {
   200  			return false
   201  		}
   202  		if len(block.Segments()) != len(otherBlock.Segments()) {
   203  			return false
   204  		}
   205  		for i, s := range block.Segments() {
   206  			if s != otherBlock.Segments()[i] {
   207  				return false
   208  			}
   209  		}
   210  	}
   211  	return true
   212  }