github.com/m3db/m3@v1.5.0/src/dbnode/encoding/series_iterator_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 encoding
    22  
    23  import (
    24  	"errors"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/m3db/m3/src/dbnode/ts"
    29  	"github.com/m3db/m3/src/dbnode/x/xio"
    30  	"github.com/m3db/m3/src/x/checked"
    31  	"github.com/m3db/m3/src/x/ident"
    32  	xtest "github.com/m3db/m3/src/x/test"
    33  	xtime "github.com/m3db/m3/src/x/time"
    34  
    35  	"github.com/stretchr/testify/assert"
    36  	"github.com/stretchr/testify/require"
    37  )
    38  
    39  type testSeries struct {
    40  	id          string
    41  	nsID        string
    42  	retainTag   bool
    43  	start       xtime.UnixNano
    44  	end         xtime.UnixNano
    45  	input       []inputReplica
    46  	expected    []testValue
    47  	expectedErr *testSeriesErr
    48  
    49  	expectedFirstAnnotation ts.Annotation
    50  }
    51  
    52  type inputReplica struct {
    53  	values []testValue
    54  	err    error
    55  }
    56  
    57  type testSeriesErr struct {
    58  	err   error
    59  	atIdx int
    60  }
    61  
    62  func TestMultiReaderMergesReplicas(t *testing.T) {
    63  	start := xtime.Now().Truncate(time.Minute)
    64  	end := start.Add(time.Minute)
    65  
    66  	values := []inputReplica{
    67  		{
    68  			values: []testValue{
    69  				{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
    70  				{2.0, start.Add(2 * time.Second), xtime.Second, nil},
    71  				{3.0, start.Add(3 * time.Second), xtime.Second, nil},
    72  			},
    73  		},
    74  		{
    75  			values: []testValue{
    76  				{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
    77  				{2.0, start.Add(2 * time.Second), xtime.Second, nil},
    78  				{3.0, start.Add(3 * time.Second), xtime.Second, nil},
    79  			},
    80  		},
    81  		{
    82  			values: []testValue{
    83  				{3.0, start.Add(3 * time.Second), xtime.Second, nil},
    84  				{4.0, start.Add(4 * time.Second), xtime.Second, nil},
    85  				{5.0, start.Add(5 * time.Second), xtime.Second, nil},
    86  			},
    87  		},
    88  	}
    89  
    90  	expected := []testValue{
    91  		{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
    92  		{2.0, start.Add(2 * time.Second), xtime.Second, nil},
    93  		{3.0, start.Add(3 * time.Second), xtime.Second, nil},
    94  		{4.0, start.Add(4 * time.Second), xtime.Second, nil},
    95  		{5.0, start.Add(5 * time.Second), xtime.Second, nil},
    96  	}
    97  
    98  	test := testSeries{
    99  		id:                      "foo",
   100  		nsID:                    "bar",
   101  		start:                   start,
   102  		end:                     end,
   103  		input:                   values,
   104  		expected:                expected,
   105  		expectedFirstAnnotation: []byte{1, 2, 3},
   106  	}
   107  
   108  	assertTestSeriesIterator(t, test)
   109  }
   110  
   111  func TestMultiReaderFiltersToRange(t *testing.T) {
   112  	start := xtime.Now().Truncate(time.Minute)
   113  	end := start.Add(time.Minute)
   114  
   115  	input := []inputReplica{
   116  		{
   117  			values: []testValue{
   118  				{0.0, start.Add(-2 * time.Second), xtime.Second, []byte{1, 2, 3}},
   119  				{1.0, start.Add(-1 * time.Second), xtime.Second, nil},
   120  				{2.0, start, xtime.Second, nil},
   121  				{3.0, start.Add(1 * time.Second), xtime.Second, nil},
   122  				{4.0, start.Add(60 * time.Second), xtime.Second, nil},
   123  				{5.0, start.Add(61 * time.Second), xtime.Second, nil},
   124  			},
   125  		},
   126  	}
   127  
   128  	test := testSeries{
   129  		id:                      "foo",
   130  		nsID:                    "bar",
   131  		start:                   start,
   132  		end:                     end,
   133  		input:                   input,
   134  		expected:                input[0].values[2:4],
   135  		expectedFirstAnnotation: []byte{1, 2, 3},
   136  	}
   137  
   138  	assertTestSeriesIterator(t, test)
   139  }
   140  
   141  func TestSeriesIteratorIgnoresEmptyReplicas(t *testing.T) {
   142  	start := xtime.Now().Truncate(time.Minute)
   143  	end := start.Add(time.Minute)
   144  
   145  	values := []testValue{
   146  		{1.0, start.Add(1 * time.Second), xtime.Second, []byte{3, 2, 1}},
   147  		{2.0, start.Add(2 * time.Second), xtime.Second, nil},
   148  		{3.0, start.Add(3 * time.Second), xtime.Second, nil},
   149  	}
   150  
   151  	test := testSeries{
   152  		id:    "foo",
   153  		nsID:  "bar",
   154  		start: start,
   155  		end:   end,
   156  		input: []inputReplica{
   157  			{values: values},
   158  			{values: []testValue{}},
   159  			{values: values},
   160  		},
   161  		expected:                values,
   162  		expectedFirstAnnotation: []byte{3, 2, 1},
   163  	}
   164  
   165  	assertTestSeriesIterator(t, test)
   166  }
   167  
   168  func TestSeriesIteratorDoesNotIgnoreReplicasWithErrors(t *testing.T) {
   169  	var (
   170  		start = xtime.Now().Truncate(time.Minute)
   171  		end   = start.Add(time.Minute)
   172  		err   = errors.New("some-iteration-error")
   173  	)
   174  
   175  	test := testSeries{
   176  		id:    "foo",
   177  		nsID:  "bar",
   178  		start: start,
   179  		end:   end,
   180  		input: []inputReplica{
   181  			{err: err},
   182  		},
   183  		expectedErr: &testSeriesErr{err: err},
   184  	}
   185  
   186  	assertTestSeriesIterator(t, test)
   187  }
   188  
   189  func TestSeriesIteratorErrorOnOutOfOrder(t *testing.T) {
   190  	start := xtime.Now().Truncate(time.Minute)
   191  	end := start.Add(time.Minute)
   192  
   193  	values := []testValue{
   194  		{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
   195  		{3.0, start.Add(3 * time.Second), xtime.Second, nil},
   196  		{2.0, start.Add(2 * time.Second), xtime.Second, nil},
   197  	}
   198  
   199  	test := testSeries{
   200  		id:                      "foo",
   201  		nsID:                    "bar",
   202  		start:                   start,
   203  		end:                     end,
   204  		input:                   []inputReplica{{values: values}},
   205  		expected:                values[:2],
   206  		expectedFirstAnnotation: []byte{1, 2, 3},
   207  		expectedErr: &testSeriesErr{
   208  			err:   errOutOfOrderIterator,
   209  			atIdx: 2,
   210  		},
   211  	}
   212  
   213  	assertTestSeriesIterator(t, test)
   214  }
   215  
   216  func TestSeriesIteratorSetIterateEqualTimestampStrategy(t *testing.T) {
   217  	test := testSeries{
   218  		id:   "foo",
   219  		nsID: "bar",
   220  	}
   221  
   222  	iter := newTestSeriesIterator(t, test).iter
   223  
   224  	// Ensure default value if none set
   225  	assert.Equal(t, iter.iters.equalTimesStrategy,
   226  		DefaultIterateEqualTimestampStrategy)
   227  
   228  	// Ensure value is propagated during a reset
   229  	iter.Reset(SeriesIteratorOptions{
   230  		ID:                            ident.StringID("baz"),
   231  		IterateEqualTimestampStrategy: IterateHighestValue,
   232  	})
   233  	assert.Equal(t, iter.iters.equalTimesStrategy,
   234  		IterateHighestValue)
   235  
   236  	// Ensure falls back to default after a reset without specifying
   237  	iter.Reset(SeriesIteratorOptions{
   238  		ID: ident.StringID("baz"),
   239  	})
   240  	assert.Equal(t, iter.iters.equalTimesStrategy,
   241  		DefaultIterateEqualTimestampStrategy)
   242  }
   243  
   244  type testSeriesConsolidator struct {
   245  	iters []MultiReaderIterator
   246  }
   247  
   248  func (c *testSeriesConsolidator) ConsolidateReplicas(
   249  	_ []MultiReaderIterator,
   250  ) ([]MultiReaderIterator, error) {
   251  	return c.iters, nil
   252  }
   253  
   254  func TestSeriesIteratorSetSeriesIteratorConsolidator(t *testing.T) {
   255  	ctrl := xtest.NewController(t)
   256  	defer ctrl.Finish()
   257  
   258  	test := testSeries{
   259  		id:   "foo",
   260  		nsID: "bar",
   261  	}
   262  
   263  	iter := newTestSeriesIterator(t, test).iter
   264  	newIter := NewMockMultiReaderIterator(ctrl)
   265  	newIter.EXPECT().Next().Return(true)
   266  	newIter.EXPECT().Current().Return(ts.Datapoint{}, xtime.Second, nil).Times(3)
   267  
   268  	iter.iters.setFilter(0, 1)
   269  	consolidator := &testSeriesConsolidator{iters: []MultiReaderIterator{newIter}}
   270  	oldIter := NewMockMultiReaderIterator(ctrl)
   271  	oldIters := []MultiReaderIterator{oldIter}
   272  	iter.multiReaderIters = oldIters
   273  	assert.Equal(t, oldIter, iter.multiReaderIters[0])
   274  	iter.Reset(SeriesIteratorOptions{
   275  		Replicas:                   oldIters,
   276  		SeriesIteratorConsolidator: consolidator,
   277  	})
   278  	assert.Equal(t, newIter, iter.multiReaderIters[0])
   279  }
   280  
   281  type newTestSeriesIteratorResult struct {
   282  	iter                 *seriesIterator
   283  	multiReaderIterators []MultiReaderIterator
   284  }
   285  
   286  func newTestSeriesIterator(
   287  	t *testing.T,
   288  	series testSeries,
   289  ) newTestSeriesIteratorResult {
   290  	var iters []MultiReaderIterator
   291  	for i := range series.input {
   292  		multiIter := newTestMultiIterator(
   293  			series.input[i].values,
   294  			series.input[i].err,
   295  		)
   296  		iters = append(iters, multiIter)
   297  	}
   298  
   299  	iter := NewSeriesIterator(SeriesIteratorOptions{
   300  		ID:             ident.StringID(series.id),
   301  		Namespace:      ident.StringID(series.nsID),
   302  		Tags:           ident.EmptyTagIterator,
   303  		StartInclusive: series.start,
   304  		EndExclusive:   series.end,
   305  		Replicas:       iters,
   306  	}, nil)
   307  
   308  	seriesIter, ok := iter.(*seriesIterator)
   309  	require.True(t, ok)
   310  
   311  	return newTestSeriesIteratorResult{
   312  		iter:                 seriesIter,
   313  		multiReaderIterators: iters,
   314  	}
   315  }
   316  
   317  func assertTestSeriesIterator(
   318  	t *testing.T,
   319  	series testSeries,
   320  ) {
   321  	newSeriesIter := newTestSeriesIterator(t, series)
   322  	iter := newSeriesIter.iter
   323  	multiReaderIterators := newSeriesIter.multiReaderIterators
   324  	defer iter.Close()
   325  
   326  	assert.Equal(t, series.id, iter.ID().String())
   327  	assert.Equal(t, series.nsID, iter.Namespace().String())
   328  	assert.Equal(t, series.start, iter.Start())
   329  	assert.Equal(t, series.end, iter.End())
   330  	for i := 0; i < len(series.expected); i++ {
   331  		next := iter.Next()
   332  		if series.expectedErr != nil && i == series.expectedErr.atIdx {
   333  			assert.False(t, next)
   334  			break
   335  		}
   336  		require.True(t, next)
   337  		dp, unit, annotation := iter.Current()
   338  		expected := series.expected[i]
   339  		assert.Equal(t, expected.value, dp.Value)
   340  		assert.Equal(t, expected.t, dp.TimestampNanos)
   341  		assert.Equal(t, expected.unit, unit)
   342  		assert.Equal(t, expected.annotation, annotation)
   343  		assert.Equal(t, series.expectedFirstAnnotation, iter.FirstAnnotation())
   344  	}
   345  	// Ensure further calls to next false
   346  	for i := 0; i < 2; i++ {
   347  		assert.False(t, iter.Next())
   348  	}
   349  	if series.expectedErr == nil {
   350  		assert.NoError(t, iter.Err())
   351  	} else {
   352  		assert.Equal(t, series.expectedErr.err, iter.Err())
   353  	}
   354  	for _, iter := range multiReaderIterators {
   355  		if iter != nil {
   356  			assert.True(t, iter.(*testMultiIterator).closed)
   357  		}
   358  	}
   359  }
   360  
   361  func TestSeriesIteratorStats(t *testing.T) {
   362  	ctrl := xtest.NewController(t)
   363  	defer ctrl.Finish()
   364  
   365  	segReader := func(head, tail int) xio.SegmentReader {
   366  		var h, t checked.Bytes
   367  		if head > 0 {
   368  			h = checked.NewBytes(make([]byte, head), nil)
   369  			h.IncRef()
   370  		}
   371  		if tail > 0 {
   372  			t = checked.NewBytes(make([]byte, tail), nil)
   373  			t.IncRef()
   374  		}
   375  		return xio.NewSegmentReader(ts.Segment{
   376  			Head: h,
   377  			Tail: t,
   378  		})
   379  	}
   380  	readerOne := &singleSlicesOfSlicesIterator{
   381  		readers: []xio.SegmentReader{segReader(0, 5), segReader(5, 5)},
   382  	}
   383  	readerTwo := &singleSlicesOfSlicesIterator{
   384  		readers: []xio.SegmentReader{segReader(2, 2), segReader(5, 0)},
   385  	}
   386  	iter := seriesIterator{
   387  		multiReaderIters: []MultiReaderIterator{
   388  			&multiReaderIterator{slicesIter: readerOne},
   389  			&multiReaderIterator{slicesIter: readerTwo},
   390  		},
   391  	}
   392  	stats, err := iter.Stats()
   393  	require.NoError(t, err)
   394  	assert.Equal(t, 24, stats.ApproximateSizeInBytes)
   395  }