github.com/m3db/m3@v1.5.0/src/dbnode/storage/bootstrap/result/result_data_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 result
    22  
    23  import (
    24  	"fmt"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/m3db/m3/src/dbnode/storage/block"
    29  	"github.com/m3db/m3/src/dbnode/ts"
    30  	"github.com/m3db/m3/src/x/checked"
    31  	"github.com/m3db/m3/src/x/ident"
    32  	xtime "github.com/m3db/m3/src/x/time"
    33  
    34  	"github.com/m3db/m3/src/dbnode/namespace"
    35  	"github.com/stretchr/testify/assert"
    36  	"github.com/stretchr/testify/require"
    37  )
    38  
    39  var testBlockSize = 2 * time.Hour
    40  
    41  func testResultOptions() Options {
    42  	return NewOptions()
    43  }
    44  
    45  func TestDataResultSetUnfulfilledMergeShardResults(t *testing.T) {
    46  	start := xtime.Now().Truncate(testBlockSize)
    47  	rangeOne := shardTimeRanges{
    48  		0: xtime.NewRanges(xtime.Range{
    49  			Start: start,
    50  			End:   start.Add(8 * testBlockSize),
    51  		}),
    52  		1: xtime.NewRanges(xtime.Range{
    53  			Start: start,
    54  			End:   start.Add(testBlockSize),
    55  		}),
    56  	}
    57  
    58  	rangeTwo := shardTimeRanges{
    59  		0: xtime.NewRanges(xtime.Range{
    60  			Start: start.Add(6 * testBlockSize),
    61  			End:   start.Add(10 * testBlockSize),
    62  		}),
    63  		2: xtime.NewRanges(xtime.Range{
    64  			Start: start.Add(testBlockSize),
    65  			End:   start.Add(2 * testBlockSize),
    66  		}),
    67  	}
    68  
    69  	r := NewDataBootstrapResult()
    70  	r.SetUnfulfilled(rangeOne)
    71  	rTwo := NewDataBootstrapResult()
    72  	rTwo.SetUnfulfilled(rangeTwo)
    73  
    74  	rMerged := MergedDataBootstrapResult(nil, nil)
    75  	assert.Nil(t, rMerged)
    76  
    77  	rMerged = MergedDataBootstrapResult(r, nil)
    78  	assert.True(t, rMerged.Unfulfilled().Equal(rangeOne))
    79  
    80  	rMerged = MergedDataBootstrapResult(nil, r)
    81  	assert.True(t, rMerged.Unfulfilled().Equal(rangeOne))
    82  
    83  	rMerged = MergedDataBootstrapResult(r, rTwo)
    84  	expected := shardTimeRanges{
    85  		0: xtime.NewRanges(xtime.Range{
    86  			Start: start,
    87  			End:   start.Add(10 * testBlockSize),
    88  		}),
    89  		1: xtime.NewRanges(xtime.Range{
    90  			Start: start,
    91  			End:   start.Add(testBlockSize),
    92  		}),
    93  		2: xtime.NewRanges(xtime.Range{
    94  			Start: start.Add(testBlockSize),
    95  			End:   start.Add(testBlockSize * 2),
    96  		}),
    97  	}
    98  
    99  	assert.True(t, rMerged.Unfulfilled().Equal(expected))
   100  }
   101  
   102  func TestDataResultSetUnfulfilledOverwitesUnfulfilled(t *testing.T) {
   103  	start := xtime.Now().Truncate(testBlockSize)
   104  	r := NewDataBootstrapResult()
   105  	r.SetUnfulfilled(shardTimeRanges{
   106  		0: xtime.NewRanges(xtime.Range{
   107  			Start: start,
   108  			End:   start.Add(8 * testBlockSize),
   109  		}),
   110  	})
   111  
   112  	expected := shardTimeRanges{0: xtime.NewRanges(xtime.Range{
   113  		Start: start,
   114  		End:   start.Add(8 * testBlockSize),
   115  	})}
   116  
   117  	assert.True(t, r.Unfulfilled().Equal(expected))
   118  	r.SetUnfulfilled(shardTimeRanges{
   119  		0: xtime.NewRanges(xtime.Range{
   120  			Start: start.Add(6 * testBlockSize),
   121  			End:   start.Add(10 * testBlockSize),
   122  		}),
   123  	})
   124  
   125  	expected = shardTimeRanges{0: xtime.NewRanges(xtime.Range{
   126  		Start: start.Add(6 * testBlockSize),
   127  		End:   start.Add(10 * testBlockSize),
   128  	})}
   129  
   130  	assert.True(t, r.Unfulfilled().Equal(expected))
   131  }
   132  
   133  func TestResultSetUnfulfilled(t *testing.T) {
   134  	start := xtime.Now().Truncate(testBlockSize)
   135  
   136  	r := NewDataBootstrapResult()
   137  	r.SetUnfulfilled(shardTimeRanges{
   138  		0: xtime.NewRanges(xtime.Range{
   139  			Start: start,
   140  			End:   start.Add(2 * testBlockSize),
   141  		}),
   142  		1: xtime.NewRanges(xtime.Range{
   143  			Start: start,
   144  			End:   start.Add(2 * testBlockSize),
   145  		}),
   146  	})
   147  	r.SetUnfulfilled(shardTimeRanges{
   148  		1: xtime.NewRanges(xtime.Range{
   149  			Start: start,
   150  			End:   start.Add(2 * testBlockSize),
   151  		}),
   152  	})
   153  
   154  	assert.True(t, r.Unfulfilled().Equal(shardTimeRanges{
   155  		1: xtime.NewRanges(xtime.Range{
   156  			Start: start,
   157  			End:   start.Add(2 * testBlockSize),
   158  		}),
   159  	}))
   160  }
   161  
   162  func TestShardResultIsEmpty(t *testing.T) {
   163  	opts := testResultOptions()
   164  	sr := NewShardResult(opts)
   165  	require.True(t, sr.IsEmpty())
   166  	block := opts.DatabaseBlockOptions().DatabaseBlockPool().Get()
   167  	block.Reset(xtime.Now(), time.Hour, ts.Segment{}, namespace.Context{})
   168  	fooTags := ident.NewTags(ident.StringTag("foo", "foe"))
   169  	sr.AddBlock(ident.StringID("foo"), fooTags, block)
   170  	require.False(t, sr.IsEmpty())
   171  }
   172  
   173  func TestShardResultAddBlock(t *testing.T) {
   174  	opts := testResultOptions()
   175  	sr := NewShardResult(opts)
   176  	start := xtime.Now()
   177  	inputs := []struct {
   178  		id        string
   179  		tags      ident.Tags
   180  		timestamp xtime.UnixNano
   181  	}{
   182  		{"foo", ident.NewTags(ident.StringTag("foo", "foe")), start},
   183  		{"foo", ident.NewTags(ident.StringTag("foo", "foe")), start.Add(2 * time.Hour)},
   184  		{"bar", ident.NewTags(ident.StringTag("bar", "baz")), start},
   185  	}
   186  	for _, input := range inputs {
   187  		block := opts.DatabaseBlockOptions().DatabaseBlockPool().Get()
   188  		block.Reset(input.timestamp, time.Hour, ts.Segment{}, namespace.Context{})
   189  		sr.AddBlock(ident.StringID(input.id), input.tags, block)
   190  	}
   191  	allSeries := sr.AllSeries()
   192  	require.Equal(t, 2, allSeries.Len())
   193  	fooBlocks, ok := allSeries.Get(ident.StringID("foo"))
   194  	require.True(t, ok)
   195  	require.Equal(t, 2, fooBlocks.Blocks.Len())
   196  	barBlocks, ok := allSeries.Get(ident.StringID("bar"))
   197  	require.True(t, ok)
   198  	require.Equal(t, 1, barBlocks.Blocks.Len())
   199  }
   200  
   201  func TestShardResultAddSeries(t *testing.T) {
   202  	opts := testResultOptions()
   203  	sr := NewShardResult(opts)
   204  	start := xtime.Now()
   205  	inputs := []struct {
   206  		id     string
   207  		tags   ident.Tags
   208  		series block.DatabaseSeriesBlocks
   209  	}{
   210  		{"foo", ident.NewTags(ident.StringTag("foo", "foe")), block.NewDatabaseSeriesBlocks(0)},
   211  		{"bar", ident.NewTags(ident.StringTag("bar", "baz")), block.NewDatabaseSeriesBlocks(0)},
   212  	}
   213  	for _, input := range inputs {
   214  		sr.AddSeries(ident.StringID(input.id), input.tags, input.series)
   215  	}
   216  	moreSeries := block.NewDatabaseSeriesBlocks(0)
   217  	block := opts.DatabaseBlockOptions().DatabaseBlockPool().Get()
   218  	block.Reset(start, time.Hour, ts.Segment{}, namespace.Context{})
   219  	moreSeries.AddBlock(block)
   220  	sr.AddSeries(ident.StringID("foo"), ident.NewTags(ident.StringTag("foo", "foe")), moreSeries)
   221  	allSeries := sr.AllSeries()
   222  	require.Equal(t, 2, allSeries.Len())
   223  	fooBlocks, ok := allSeries.Get(ident.StringID("foo"))
   224  	require.True(t, ok)
   225  	require.Equal(t, 1, fooBlocks.Blocks.Len())
   226  	barBlocks, ok := allSeries.Get(ident.StringID("bar"))
   227  	require.True(t, ok)
   228  	require.Equal(t, 0, barBlocks.Blocks.Len())
   229  }
   230  
   231  func TestShardResultAddResult(t *testing.T) {
   232  	opts := testResultOptions()
   233  	sr := NewShardResult(opts)
   234  	sr.AddResult(nil)
   235  	require.True(t, sr.IsEmpty())
   236  	other := NewShardResult(opts)
   237  	other.AddSeries(ident.StringID("foo"), ident.NewTags(ident.StringTag("foo", "foe")), block.NewDatabaseSeriesBlocks(0))
   238  	other.AddSeries(ident.StringID("bar"), ident.NewTags(ident.StringTag("bar", "baz")), block.NewDatabaseSeriesBlocks(0))
   239  	sr.AddResult(other)
   240  	require.Equal(t, 2, sr.AllSeries().Len())
   241  }
   242  
   243  func TestShardResultNumSeries(t *testing.T) {
   244  	opts := testResultOptions()
   245  	sr := NewShardResult(opts)
   246  	sr.AddResult(nil)
   247  	require.True(t, sr.IsEmpty())
   248  	other := NewShardResult(opts)
   249  	other.AddSeries(ident.StringID("foo"), ident.NewTags(ident.StringTag("foo", "foe")), block.NewDatabaseSeriesBlocks(0))
   250  	other.AddSeries(ident.StringID("bar"), ident.NewTags(ident.StringTag("bar", "baz")), block.NewDatabaseSeriesBlocks(0))
   251  	sr.AddResult(other)
   252  	require.Equal(t, int64(2), sr.NumSeries())
   253  }
   254  
   255  func TestShardResultRemoveSeries(t *testing.T) {
   256  	opts := testResultOptions()
   257  	sr := NewShardResult(opts)
   258  	inputs := []struct {
   259  		id     string
   260  		tags   ident.Tags
   261  		series block.DatabaseSeriesBlocks
   262  	}{
   263  		{"foo", ident.NewTags(ident.StringTag("foo", "foe")), block.NewDatabaseSeriesBlocks(0)},
   264  		{"bar", ident.NewTags(ident.StringTag("bar", "baz")), block.NewDatabaseSeriesBlocks(0)},
   265  	}
   266  	for _, input := range inputs {
   267  		sr.AddSeries(ident.StringID(input.id), input.tags, input.series)
   268  	}
   269  	require.Equal(t, 2, sr.AllSeries().Len())
   270  	sr.RemoveSeries(ident.StringID("foo"))
   271  	require.Equal(t, 1, sr.AllSeries().Len())
   272  	sr.RemoveSeries(ident.StringID("nonexistent"))
   273  	require.Equal(t, 1, sr.AllSeries().Len())
   274  }
   275  
   276  func TestShardTimeRangesIsEmpty(t *testing.T) {
   277  	assert.True(t, shardTimeRanges{}.IsEmpty())
   278  	assert.True(t, shardTimeRanges{0: xtime.NewRanges(), 1: xtime.NewRanges()}.IsEmpty())
   279  	assert.True(t, shardTimeRanges{0: xtime.NewRanges(xtime.Range{})}.IsEmpty())
   280  	now := xtime.Now()
   281  	assert.False(t, shardTimeRanges{0: xtime.NewRanges(xtime.Range{
   282  		Start: now,
   283  		End:   now.Add(time.Second),
   284  	})}.IsEmpty())
   285  }
   286  
   287  func TestShardTimeRangesCopy(t *testing.T) {
   288  	now := xtime.Now()
   289  	str := shardTimeRanges{0: xtime.NewRanges(xtime.Range{
   290  		Start: now,
   291  		End:   now.Add(time.Second),
   292  	})}
   293  	copied := str.Copy()
   294  	// Ensure is a copy not same instance
   295  	assert.NotEqual(t, fmt.Sprintf("%p", str), fmt.Sprintf("%p", copied))
   296  	assert.True(t, str.Equal(copied))
   297  }
   298  
   299  func TestShardTimeRangesToUnfulfilledDataResult(t *testing.T) {
   300  	now := xtime.Now()
   301  	str := shardTimeRanges{
   302  		0: xtime.NewRanges(xtime.Range{
   303  			Start: now,
   304  			End:   now.Add(time.Minute),
   305  		}),
   306  		1: xtime.NewRanges(xtime.Range{
   307  			Start: now.Add(3 * time.Minute),
   308  			End:   now.Add(4 * time.Minute),
   309  		}),
   310  	}
   311  	r := str.ToUnfulfilledDataResult()
   312  	assert.True(t, r.Unfulfilled().Equal(str))
   313  }
   314  
   315  func TestShardTimeRangesSubtract(t *testing.T) {
   316  	start := xtime.Now().Truncate(testBlockSize)
   317  	str := shardTimeRanges{
   318  		0: xtime.NewRanges(xtime.Range{
   319  			Start: start,
   320  			End:   start.Add(2 * testBlockSize),
   321  		}),
   322  		1: xtime.NewRanges(xtime.Range{
   323  			Start: start,
   324  			End:   start.Add(2 * testBlockSize),
   325  		}),
   326  	}
   327  	str.Subtract(shardTimeRanges{
   328  		0: xtime.NewRanges(xtime.Range{
   329  			Start: start,
   330  			End:   start.Add(testBlockSize),
   331  		}),
   332  		1: xtime.NewRanges(xtime.Range{
   333  			Start: start.Add(testBlockSize),
   334  			End:   start.Add(2 * testBlockSize),
   335  		}),
   336  	})
   337  
   338  	assert.True(t, str.Equal(shardTimeRanges{
   339  		0: xtime.NewRanges(xtime.Range{
   340  			Start: start.Add(testBlockSize),
   341  			End:   start.Add(2 * testBlockSize),
   342  		}),
   343  		1: xtime.NewRanges(xtime.Range{
   344  			Start: start,
   345  			End:   start.Add(testBlockSize),
   346  		}),
   347  	}))
   348  }
   349  
   350  func TestShardTimeRangesMinMax(t *testing.T) {
   351  	start := xtime.Now().Truncate(testBlockSize)
   352  	str := shardTimeRanges{
   353  		0: xtime.NewRanges(xtime.Range{
   354  			Start: start,
   355  			End:   start.Add(testBlockSize),
   356  		}),
   357  		1: xtime.NewRanges(xtime.Range{
   358  			Start: start.Add(testBlockSize),
   359  			End:   start.Add(2 * testBlockSize),
   360  		}),
   361  	}
   362  
   363  	min, max := str.MinMax()
   364  
   365  	assert.True(t, min.Equal(start))
   366  	assert.True(t, max.Equal(start.Add(2*testBlockSize)))
   367  }
   368  
   369  func TestShardTimeRangesString(t *testing.T) {
   370  	start := xtime.FromSeconds(1472824800)
   371  	ts := [][]xtime.UnixNano{
   372  		{start, start.Add(testBlockSize)},
   373  		{start.Add(2 * testBlockSize), start.Add(4 * testBlockSize)},
   374  		{start, start.Add(2 * testBlockSize)},
   375  	}
   376  
   377  	str := shardTimeRanges{
   378  		0: xtime.NewRanges(
   379  			xtime.Range{Start: ts[0][0], End: ts[0][1]},
   380  			xtime.Range{Start: ts[1][0], End: ts[1][1]}),
   381  		1: xtime.NewRanges(xtime.Range{
   382  			Start: ts[2][0],
   383  			End:   ts[2][1],
   384  		}),
   385  	}
   386  
   387  	expected := "{" +
   388  		fmt.Sprintf("0: [(%v,%v),(%v,%v)], ", ts[0][0], ts[0][1], ts[1][0], ts[1][1]) +
   389  		fmt.Sprintf("1: [(%v,%v)]", ts[2][0], ts[2][1]) +
   390  		"}"
   391  
   392  	assert.Equal(t, expected, str.String())
   393  }
   394  
   395  func TestShardTimeRangesSummaryString(t *testing.T) {
   396  	start := xtime.FromSeconds(1472824800)
   397  	str := shardTimeRanges{
   398  		0: xtime.NewRanges(
   399  			xtime.Range{Start: start, End: start.Add(testBlockSize)},
   400  			xtime.Range{Start: start.Add(2 * testBlockSize), End: start.Add(4 * testBlockSize)}),
   401  		1: xtime.NewRanges(xtime.Range{
   402  			Start: start,
   403  			End:   start.Add(2 * testBlockSize),
   404  		}),
   405  	}
   406  
   407  	expected := "{0: 6h0m0s, 1: 4h0m0s}"
   408  
   409  	assert.Equal(t, expected, str.SummaryString())
   410  }
   411  
   412  func TestEstimateMapBytesSize(t *testing.T) {
   413  	opts := testResultOptions()
   414  	blopts := opts.DatabaseBlockOptions()
   415  
   416  	start := xtime.Now().Truncate(testBlockSize)
   417  
   418  	threeBytes := checked.NewBytes([]byte("123"), nil)
   419  	threeBytes.IncRef()
   420  	blocks := []block.DatabaseBlock{
   421  		block.NewDatabaseBlock(start, testBlockSize, ts.Segment{Head: threeBytes}, blopts, namespace.Context{}),
   422  		block.NewDatabaseBlock(start.Add(1*testBlockSize), testBlockSize, ts.Segment{Tail: threeBytes}, blopts, namespace.Context{}),
   423  	}
   424  
   425  	sr := NewShardResult(opts)
   426  	fooTags := ident.NewTags(ident.StringTag("foo", "foe"))
   427  	barTags := ident.NewTags(ident.StringTag("bar", "baz"))
   428  
   429  	sr.AddBlock(ident.StringID("foo"), fooTags, blocks[0])
   430  	sr.AddBlock(ident.StringID("bar"), barTags, blocks[1])
   431  
   432  	require.Equal(t, int64(24), EstimateMapBytesSize(sr.AllSeries()))
   433  }
   434  
   435  func TestEstimateMapBytesSizeEmpty(t *testing.T) {
   436  	require.Equal(t, int64(0), EstimateMapBytesSize(nil))
   437  }