github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/encoding/multi_reader_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  	"fmt"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/m3db/m3/src/dbnode/namespace"
    29  	"github.com/m3db/m3/src/dbnode/x/xio"
    30  	xtime "github.com/m3db/m3/src/x/time"
    31  
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  type testMultiReader struct {
    37  	input       [][]testMultiReaderEntries
    38  	expected    []testValue
    39  	expectedErr *testMultiReaderError
    40  }
    41  
    42  type testMultiReaderEntries struct {
    43  	values []testValue
    44  	err    *testMultiReaderError
    45  }
    46  
    47  type testMultiReaderError struct {
    48  	err   error
    49  	atIdx int
    50  }
    51  
    52  func TestMultiReaderIteratorMergesMulti(t *testing.T) {
    53  	start := xtime.Now().Truncate(time.Minute)
    54  
    55  	values := [][]testValue{
    56  		{
    57  			{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
    58  			{2.0, start.Add(2 * time.Second), xtime.Second, nil},
    59  			{3.0, start.Add(3 * time.Second), xtime.Second, nil},
    60  		},
    61  		{
    62  			{4.0, start.Add(4 * time.Second), xtime.Second, []byte{4, 5, 6}},
    63  			{5.0, start.Add(5 * time.Second), xtime.Second, nil},
    64  			{6.0, start.Add(6 * time.Second), xtime.Second, nil},
    65  		},
    66  	}
    67  
    68  	test := testMultiReader{
    69  		input: [][]testMultiReaderEntries{
    70  			{{values: values[0]}, {values: values[1]}},
    71  		},
    72  		expected: append(values[0], values[1]...),
    73  	}
    74  
    75  	assertTestMultiReaderIterator(t, test)
    76  }
    77  
    78  func TestMultiReaderIteratorMergesEmpty(t *testing.T) {
    79  	values := [][]testValue{
    80  		{},
    81  		{},
    82  	}
    83  
    84  	test := testMultiReader{
    85  		input: [][]testMultiReaderEntries{
    86  			{{values: values[0]}},
    87  			{{values: values[1]}},
    88  		},
    89  		expected: values[0],
    90  	}
    91  
    92  	assertTestMultiReaderIterator(t, test)
    93  
    94  	test = testMultiReader{
    95  		input: [][]testMultiReaderEntries{
    96  			{{values: values[0]}, {values: values[1]}},
    97  		},
    98  		expected: values[0],
    99  	}
   100  
   101  	assertTestMultiReaderIterator(t, test)
   102  }
   103  
   104  func TestMultiReaderIteratorReadsSlicesInOrder(t *testing.T) {
   105  	start := xtime.Now().Truncate(time.Minute)
   106  
   107  	values := [][]testValue{
   108  		{
   109  			{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
   110  			{2.0, start.Add(2 * time.Second), xtime.Second, nil},
   111  			{3.0, start.Add(3 * time.Second), xtime.Second, nil},
   112  		},
   113  		{
   114  			{4.0, start.Add(4 * time.Second), xtime.Second, []byte{4, 5, 6}},
   115  			{5.0, start.Add(5 * time.Second), xtime.Second, nil},
   116  			{6.0, start.Add(6 * time.Second), xtime.Second, nil},
   117  		},
   118  	}
   119  
   120  	test := testMultiReader{
   121  		input: [][]testMultiReaderEntries{
   122  			{{values: values[0]}},
   123  			{{values: values[1]}},
   124  		},
   125  		expected: append(values[0], values[1]...),
   126  	}
   127  
   128  	assertTestMultiReaderIterator(t, test)
   129  }
   130  
   131  func TestMultiReaderIteratorReadsSlicesWithNoEntries(t *testing.T) {
   132  	start := xtime.Now().Truncate(time.Minute)
   133  
   134  	values := [][]testValue{
   135  		{
   136  			{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
   137  			{2.0, start.Add(2 * time.Second), xtime.Second, nil},
   138  			{3.0, start.Add(3 * time.Second), xtime.Second, nil},
   139  		},
   140  		{
   141  			{4.0, start.Add(4 * time.Second), xtime.Second, []byte{4, 5, 6}},
   142  			{5.0, start.Add(5 * time.Second), xtime.Second, nil},
   143  			{6.0, start.Add(6 * time.Second), xtime.Second, nil},
   144  		},
   145  	}
   146  
   147  	test := testMultiReader{
   148  		input: [][]testMultiReaderEntries{
   149  			{{values: values[0]}},
   150  			{},
   151  			{{values: values[1]}},
   152  		},
   153  		expected: append(values[0], values[1]...),
   154  	}
   155  
   156  	assertTestMultiReaderIterator(t, test)
   157  }
   158  
   159  func TestMultiReaderIteratorReadsSlicesWithEmptyEntries(t *testing.T) {
   160  	start := xtime.Now().Truncate(time.Minute)
   161  
   162  	values := [][]testValue{
   163  		{
   164  			{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
   165  			{2.0, start.Add(2 * time.Second), xtime.Second, nil},
   166  			{3.0, start.Add(3 * time.Second), xtime.Second, nil},
   167  		},
   168  		{},
   169  		{
   170  			{4.0, start.Add(4 * time.Second), xtime.Second, []byte{4, 5, 6}},
   171  			{5.0, start.Add(5 * time.Second), xtime.Second, nil},
   172  			{6.0, start.Add(6 * time.Second), xtime.Second, nil},
   173  		},
   174  	}
   175  
   176  	test := testMultiReader{
   177  		input: [][]testMultiReaderEntries{
   178  			{{values: values[0]}},
   179  			{{values: values[1]}},
   180  			{{values: values[2]}},
   181  		},
   182  		expected: append(values[0], values[2]...),
   183  	}
   184  
   185  	assertTestMultiReaderIterator(t, test)
   186  }
   187  
   188  func TestMultiReaderIteratorDeduplicatesSingle(t *testing.T) {
   189  	start := xtime.Now().Truncate(time.Minute)
   190  
   191  	values := []testValue{
   192  		{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
   193  		{2.0, start.Add(2 * time.Second), xtime.Second, nil},
   194  		{2.0, start.Add(2 * time.Second), xtime.Second, nil},
   195  	}
   196  
   197  	test := testMultiReader{
   198  		input: [][]testMultiReaderEntries{
   199  			{{values: values}},
   200  		},
   201  		expected: values[:2],
   202  	}
   203  
   204  	assertTestMultiReaderIterator(t, test)
   205  }
   206  
   207  func TestMultiReaderIteratorDeduplicatesMulti(t *testing.T) {
   208  	start := xtime.Now().Truncate(time.Minute)
   209  
   210  	values := []testValue{
   211  		{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
   212  		{2.0, start.Add(2 * time.Second), xtime.Second, nil},
   213  		{3.0, start.Add(3 * time.Second), xtime.Second, nil},
   214  	}
   215  
   216  	test := testMultiReader{
   217  		input: [][]testMultiReaderEntries{
   218  			{
   219  				{values: values},
   220  				{values: values},
   221  				{values: values},
   222  			},
   223  		},
   224  		expected: values,
   225  	}
   226  
   227  	assertTestMultiReaderIterator(t, test)
   228  }
   229  
   230  func TestMultiReaderIteratorErrorOnOutOfOrder(t *testing.T) {
   231  	start := xtime.Now().Truncate(time.Minute)
   232  
   233  	values := []testValue{
   234  		{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
   235  		{3.0, start.Add(3 * time.Second), xtime.Second, nil},
   236  		{2.0, start.Add(2 * time.Second), xtime.Second, nil},
   237  	}
   238  
   239  	test := testMultiReader{
   240  		input: [][]testMultiReaderEntries{
   241  			{{values: values}},
   242  		},
   243  		expected: values,
   244  		expectedErr: &testMultiReaderError{
   245  			err:   errOutOfOrderIterator,
   246  			atIdx: 2,
   247  		},
   248  	}
   249  
   250  	assertTestMultiReaderIterator(t, test)
   251  }
   252  
   253  func TestMultiReaderIteratorErrorOnInnerIteratorError(t *testing.T) {
   254  	start := xtime.Now().Truncate(time.Minute)
   255  
   256  	values := []testValue{
   257  		{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
   258  		{2.0, start.Add(2 * time.Second), xtime.Second, nil},
   259  		{3.0, start.Add(3 * time.Second), xtime.Second, nil},
   260  	}
   261  
   262  	err := fmt.Errorf("an error")
   263  
   264  	test := testMultiReader{
   265  		input: [][]testMultiReaderEntries{
   266  			{{
   267  				values: values,
   268  				err: &testMultiReaderError{
   269  					err:   err,
   270  					atIdx: 2,
   271  				},
   272  			}},
   273  		},
   274  		expected: values,
   275  		expectedErr: &testMultiReaderError{
   276  			err:   err,
   277  			atIdx: 2,
   278  		},
   279  	}
   280  
   281  	assertTestMultiReaderIterator(t, test)
   282  }
   283  
   284  func assertTestMultiReaderIterator(
   285  	t *testing.T,
   286  	test testMultiReader,
   287  ) {
   288  	type readerEntries struct {
   289  		reader  xio.Reader64
   290  		entries *testMultiReaderEntries
   291  	}
   292  
   293  	var (
   294  		blocks          [][]xio.BlockReader
   295  		entriesByReader []readerEntries
   296  	)
   297  
   298  	for i := range test.input {
   299  		var blocksArray []xio.BlockReader
   300  		for j := range test.input[i] {
   301  			reader := &testNoopReader{}
   302  			entries := &test.input[i][j]
   303  			block := xio.BlockReader{
   304  				SegmentReader: reader,
   305  			}
   306  			entriesByReader = append(entriesByReader, readerEntries{
   307  				reader:  block,
   308  				entries: entries,
   309  			})
   310  			blocksArray = append(blocksArray, block)
   311  		}
   312  
   313  		blocks = append(blocks, blocksArray)
   314  	}
   315  
   316  	var testIterators []*testIterator
   317  	var iteratorAlloc func(xio.Reader64, namespace.SchemaDescr) ReaderIterator
   318  	iteratorAlloc = func(reader xio.Reader64, _ namespace.SchemaDescr) ReaderIterator {
   319  		for i := range entriesByReader {
   320  			if reader != entriesByReader[i].reader {
   321  				continue
   322  			}
   323  			entries := entriesByReader[i].entries
   324  			it := newTestIterator(entries.values).(*testIterator)
   325  			// Install error if set
   326  			if entries.err != nil {
   327  				it.onNext = func(oldIdx, newIdx int) {
   328  					if newIdx == entries.err.atIdx {
   329  						it.err = entries.err.err
   330  					}
   331  				}
   332  			}
   333  			it.onReset = func(r xio.Reader64, descr namespace.SchemaDescr) {
   334  				newIt := iteratorAlloc(r, descr).(*testIterator)
   335  				*it = *newIt
   336  				// We close this here as we never actually use this iterator
   337  				// and we test at the end of the test to ensure all iterators were closed
   338  				// by the multi reader iterator
   339  				newIt.closed = true
   340  			}
   341  			testIterators = append(testIterators, it)
   342  			return it
   343  		}
   344  		assert.Fail(t, "iterator allocate called for unknown reader")
   345  		return nil
   346  	}
   347  
   348  	iter := NewMultiReaderIterator(iteratorAlloc, nil)
   349  	slicesIter := newTestReaderSliceOfSlicesIterator(blocks)
   350  	iter.ResetSliceOfSlices(slicesIter, nil)
   351  
   352  	for i := 0; i < len(test.expected); i++ {
   353  		next := iter.Next()
   354  		if test.expectedErr != nil && i == test.expectedErr.atIdx {
   355  			assert.Equal(t, false, next)
   356  			break
   357  		}
   358  		require.Equal(t, true, next, "expected next for idx %d", i)
   359  		dp, unit, annotation := iter.Current()
   360  		expected := test.expected[i]
   361  		require.Equal(t, expected.value, dp.Value, fmt.Sprintf("mismatch for idx %d", i))
   362  		require.Equal(t, expected.t, dp.TimestampNanos, fmt.Sprintf("mismatch for idx %d", i))
   363  		require.Equal(t, expected.unit, unit, fmt.Sprintf("mismatch for idx %d", i))
   364  		require.Equal(t, expected.annotation, annotation, fmt.Sprintf("mismatch for idx %d", i))
   365  	}
   366  
   367  	// Ensure further calls to next false
   368  	for i := 0; i < 2; i++ {
   369  		assert.Equal(t, false, iter.Next())
   370  	}
   371  
   372  	if test.expectedErr == nil {
   373  		assert.NoError(t, iter.Err())
   374  	} else {
   375  		assert.Equal(t, test.expectedErr.err, iter.Err())
   376  	}
   377  
   378  	iter.Close()
   379  
   380  	// Ensure all closed
   381  	for _, iter := range testIterators {
   382  		assert.Equal(t, true, iter.closed)
   383  	}
   384  	assert.Equal(t, true, slicesIter.(*testReaderSliceOfSlicesIterator).closed)
   385  }