github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/remote/compressed_codecs_test.go (about)

     1  // Copyright (c) 2018 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 remote
    22  
    23  import (
    24  	"sync"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/m3db/m3/src/dbnode/encoding"
    29  	"github.com/m3db/m3/src/query/block"
    30  	rpc "github.com/m3db/m3/src/query/generated/proto/rpcpb"
    31  	"github.com/m3db/m3/src/query/storage/m3/consolidators"
    32  	"github.com/m3db/m3/src/query/test"
    33  	"github.com/m3db/m3/src/x/ident"
    34  	xtime "github.com/m3db/m3/src/x/time"
    35  
    36  	"github.com/golang/mock/gomock"
    37  	"github.com/stretchr/testify/assert"
    38  	"github.com/stretchr/testify/require"
    39  )
    40  
    41  var (
    42  	seriesID        = "series"
    43  	seriesNamespace = test.SeriesNamespace
    44  
    45  	testTags = test.TestTags
    46  
    47  	blockSize = test.BlockSize
    48  
    49  	start       = test.Start
    50  	seriesStart = test.SeriesStart
    51  	middle      = test.Middle
    52  	end         = test.End
    53  
    54  	tagMu sync.RWMutex
    55  )
    56  
    57  func buildTestSeriesIterator(t *testing.T) encoding.SeriesIterator {
    58  	it, err := test.BuildTestSeriesIterator(seriesID)
    59  	assert.NoError(t, err)
    60  	return it
    61  }
    62  
    63  func validateSeriesInternals(t *testing.T, it encoding.SeriesIterator) {
    64  	defer it.Close()
    65  
    66  	replicas, err := it.Replicas()
    67  	require.NoError(t, err)
    68  	expectedReaders := []int{1, 2}
    69  	expectedStarts := []xtime.UnixNano{start.Truncate(blockSize), middle.Truncate(blockSize)}
    70  	for _, replica := range replicas {
    71  		readers := replica.Readers()
    72  		i := 0
    73  		for hasNext := true; hasNext; hasNext = readers.Next() {
    74  			l, s, size := readers.CurrentReaders()
    75  			require.Equal(t, expectedReaders[i], l)
    76  			assert.Equal(t, expectedStarts[i], s)
    77  			assert.Equal(t, blockSize, size)
    78  			for j := 0; j < l; j++ {
    79  				block := readers.CurrentReaderAt(j)
    80  				assert.Equal(t, expectedStarts[i], block.Start)
    81  				assert.Equal(t, blockSize, block.BlockSize)
    82  			}
    83  			i++
    84  		}
    85  	}
    86  }
    87  
    88  func expectedValues() []float64 {
    89  	expectedValues := make([]float64, 60)
    90  	for i := 2; i < 30; i++ {
    91  		expectedValues[i] = float64(i) + 1
    92  	}
    93  	for i := 0; i < 30; i++ {
    94  		expectedValues[i+30] = float64(i) + 101
    95  	}
    96  	return expectedValues[2:]
    97  
    98  }
    99  
   100  func validateSeries(t *testing.T, it encoding.SeriesIterator) {
   101  	defer it.Close()
   102  
   103  	for i, expectedValue := range expectedValues() {
   104  		require.True(t, it.Next())
   105  		dp, unit, annotation := it.Current()
   106  		require.Equal(t, expectedValue, dp.Value)
   107  		require.Equal(t, seriesStart.Add(time.Duration(i)*time.Minute), dp.TimestampNanos)
   108  		uv, err := unit.Value()
   109  		assert.NoError(t, err)
   110  		assert.Equal(t, time.Second, uv)
   111  		assert.Empty(t, annotation)
   112  	}
   113  
   114  	assert.False(t, it.Next())
   115  	assert.Equal(t, seriesID, it.ID().String())
   116  	if it.Namespace() != nil {
   117  		assert.Equal(t, seriesNamespace, it.Namespace().String())
   118  	}
   119  	assert.Equal(t, seriesStart, it.Start())
   120  	assert.Equal(t, end, it.End())
   121  
   122  	tagMu.RLock()
   123  	tagIter := it.Tags()
   124  	tagCount := 0
   125  	expectedCount := 2
   126  	for tagIter.Next() {
   127  		tag := tagIter.Current()
   128  		name := tag.Name.String()
   129  		expectedVal, contains := testTags[name]
   130  		require.True(t, contains)
   131  		assert.Equal(t, expectedVal, tag.Value.String())
   132  		tagCount++
   133  	}
   134  
   135  	assert.Equal(t, expectedCount, tagCount)
   136  	tagMu.RUnlock()
   137  }
   138  
   139  func TestGeneratedSeries(t *testing.T) {
   140  	validateSeries(t, buildTestSeriesIterator(t))
   141  	validateSeriesInternals(t, buildTestSeriesIterator(t))
   142  }
   143  
   144  func verifyCompressedSeries(t *testing.T, s *rpc.Series) {
   145  	meta := s.GetMeta()
   146  	series := s.GetCompressed()
   147  	require.NotNil(t, series)
   148  	assert.Equal(t, []byte(seriesID), meta.GetId())
   149  	assert.Equal(t, int64(seriesStart), meta.GetStartTime())
   150  	assert.Equal(t, int64(end), meta.GetEndTime())
   151  
   152  	replicas := series.GetReplicas()
   153  	require.Len(t, replicas, 2)
   154  
   155  	for _, replica := range replicas {
   156  		segments := replica.GetSegments()
   157  		require.Len(t, segments, 2)
   158  
   159  		seg := segments[0]
   160  		assert.Empty(t, seg.GetUnmerged())
   161  		merged := seg.GetMerged()
   162  		assert.NotNil(t, merged)
   163  		assert.NotEmpty(t, merged.GetHead())
   164  		assert.NotEmpty(t, merged.GetTail())
   165  		assert.Equal(t, int64(start), merged.GetStartTime())
   166  		assert.Equal(t, int64(blockSize), merged.GetBlockSize())
   167  
   168  		seg = segments[1]
   169  		assert.Nil(t, seg.GetMerged())
   170  		unmergedSegments := seg.GetUnmerged()
   171  		assert.Len(t, unmergedSegments, 2)
   172  		for _, unmerged := range unmergedSegments {
   173  			assert.NotEmpty(t, unmerged.GetHead())
   174  			assert.NotEmpty(t, unmerged.GetTail())
   175  			assert.Equal(t, int64(middle.Truncate(blockSize)), unmerged.GetStartTime())
   176  			assert.Equal(t, int64(blockSize), unmerged.GetBlockSize())
   177  		}
   178  	}
   179  }
   180  
   181  func TestConversionToCompressedData(t *testing.T) {
   182  	it := buildTestSeriesIterator(t)
   183  	series, err := CompressedSeriesFromSeriesIterator(it, nil)
   184  	require.Error(t, err)
   185  	require.Nil(t, series)
   186  }
   187  
   188  func TestSeriesConversionFromCompressedData(t *testing.T) {
   189  	it := buildTestSeriesIterator(t)
   190  	series, err := CompressedSeriesFromSeriesIterator(it, nil)
   191  	require.Error(t, err)
   192  	require.Nil(t, series)
   193  }
   194  
   195  func TestSeriesConversionFromCompressedDataWithIteratorPool(t *testing.T) {
   196  	it := buildTestSeriesIterator(t)
   197  	ip := test.MakeMockIteratorPool()
   198  	series, err := CompressedSeriesFromSeriesIterator(it, ip)
   199  
   200  	require.NoError(t, err)
   201  	verifyCompressedSeries(t, series)
   202  	rpcSeries := series.GetCompressed()
   203  	require.NotNil(t, rpcSeries)
   204  	assert.NotEmpty(t, rpcSeries.GetCompressedTags())
   205  
   206  	seriesIterator, err := seriesIteratorFromCompressedSeries(rpcSeries, series.GetMeta(), ip)
   207  	require.NoError(t, err)
   208  	validateSeries(t, seriesIterator)
   209  
   210  	seriesIterator, err = seriesIteratorFromCompressedSeries(rpcSeries, series.GetMeta(), ip)
   211  	require.NoError(t, err)
   212  	validateSeriesInternals(t, seriesIterator)
   213  
   214  	assert.True(t, ip.MriPoolUsed)
   215  	assert.True(t, ip.SiPoolUsed)
   216  	assert.True(t, ip.MriaPoolUsed)
   217  	assert.True(t, ip.CbwPoolUsed)
   218  	assert.True(t, ip.IdentPoolUsed)
   219  	assert.True(t, ip.EncodePoolUsed)
   220  	assert.True(t, ip.DecodePoolUsed)
   221  }
   222  
   223  func TestEncodeToCompressedFetchResult(t *testing.T) {
   224  	iters := encoding.NewSeriesIterators([]encoding.SeriesIterator{
   225  		buildTestSeriesIterator(t),
   226  		buildTestSeriesIterator(t),
   227  	})
   228  	ip := test.MakeMockIteratorPool()
   229  	result, err := consolidators.NewSeriesFetchResult(
   230  		iters,
   231  		nil,
   232  		block.NewResultMetadata(),
   233  	)
   234  
   235  	require.NoError(t, err)
   236  	fetchResult, err := encodeToCompressedSeries(result, ip)
   237  	require.NoError(t, err)
   238  
   239  	require.Len(t, fetchResult, 2)
   240  	for _, series := range fetchResult {
   241  		verifyCompressedSeries(t, series)
   242  	}
   243  
   244  	assert.True(t, ip.EncodePoolUsed)
   245  	assert.False(t, ip.MriPoolUsed)
   246  	assert.False(t, ip.SiPoolUsed)
   247  	assert.False(t, ip.MriaPoolUsed)
   248  	assert.False(t, ip.CbwPoolUsed)
   249  	assert.False(t, ip.IdentPoolUsed)
   250  	assert.False(t, ip.DecodePoolUsed)
   251  }
   252  
   253  func TestDecodeCompressedFetchResult(t *testing.T) {
   254  	iters := encoding.NewSeriesIterators([]encoding.SeriesIterator{
   255  		buildTestSeriesIterator(t),
   256  		buildTestSeriesIterator(t),
   257  	})
   258  	result, err := consolidators.NewSeriesFetchResult(
   259  		iters,
   260  		nil,
   261  		block.NewResultMetadata(),
   262  	)
   263  
   264  	require.NoError(t, err)
   265  	compressed, err := encodeToCompressedSeries(result, nil)
   266  	require.Error(t, err)
   267  	require.Nil(t, compressed)
   268  }
   269  
   270  func TestDecodeCompressedFetchResultWithIteratorPool(t *testing.T) {
   271  	ip := test.MakeMockIteratorPool()
   272  	iters := encoding.NewSeriesIterators([]encoding.SeriesIterator{
   273  		buildTestSeriesIterator(t),
   274  		buildTestSeriesIterator(t),
   275  	})
   276  
   277  	result, err := consolidators.NewSeriesFetchResult(
   278  		iters,
   279  		nil,
   280  		block.NewResultMetadata(),
   281  	)
   282  
   283  	require.NoError(t, err)
   284  	compressed, err := encodeToCompressedSeries(result, ip)
   285  	require.NoError(t, err)
   286  	require.Len(t, compressed, 2)
   287  	for _, series := range compressed {
   288  		verifyCompressedSeries(t, series)
   289  	}
   290  
   291  	fetchResult := &rpc.FetchResponse{
   292  		Series: compressed,
   293  	}
   294  
   295  	revertedIters, err := DecodeCompressedFetchResponse(fetchResult, ip)
   296  	require.NoError(t, err)
   297  	revertedIterList := revertedIters.Iters()
   298  	require.Len(t, revertedIterList, 2)
   299  	for _, seriesIterator := range revertedIterList {
   300  		validateSeries(t, seriesIterator)
   301  	}
   302  
   303  	revertedIters, err = DecodeCompressedFetchResponse(fetchResult, ip)
   304  	require.NoError(t, err)
   305  	revertedIterList = revertedIters.Iters()
   306  	require.Len(t, revertedIterList, 2)
   307  	for _, seriesIterator := range revertedIterList {
   308  		validateSeriesInternals(t, seriesIterator)
   309  	}
   310  
   311  	assert.True(t, ip.MriPoolUsed)
   312  	assert.True(t, ip.SiPoolUsed)
   313  	assert.True(t, ip.MriaPoolUsed)
   314  	assert.True(t, ip.CbwPoolUsed)
   315  	assert.True(t, ip.IdentPoolUsed)
   316  	assert.True(t, ip.EncodePoolUsed)
   317  	assert.True(t, ip.DecodePoolUsed)
   318  }
   319  
   320  // NB: make sure that SeriesIterator is not closed during conversion, or bytes will be empty
   321  func TestConversionDoesNotCloseSeriesIterator(t *testing.T) {
   322  	ctrl := gomock.NewController(t)
   323  	defer ctrl.Finish()
   324  
   325  	mockIter := encoding.NewMockSeriesIterator(ctrl)
   326  	mockIter.EXPECT().Close().Times(0)
   327  	mockIter.EXPECT().Replicas().Return([]encoding.MultiReaderIterator{}, nil).Times(1)
   328  	mockIter.EXPECT().Start().Return(xtime.Now()).Times(1)
   329  	mockIter.EXPECT().End().Return(xtime.Now()).Times(1)
   330  	mockIter.EXPECT().Tags().Return(ident.NewTagsIterator(ident.NewTags())).Times(1)
   331  
   332  	CompressedSeriesFromSeriesIterator(mockIter, nil)
   333  }
   334  
   335  func TestIterablePostCompression(t *testing.T) {
   336  	it := buildTestSeriesIterator(t)
   337  	ip := test.MakeMockIteratorPool()
   338  	series, err := CompressedSeriesFromSeriesIterator(it, ip)
   339  	require.NoError(t, err)
   340  	require.NotNil(t, series)
   341  
   342  	// Should be idempotent.
   343  	series, err = CompressedSeriesFromSeriesIterator(it, ip)
   344  	require.NoError(t, err)
   345  	require.NotNil(t, series)
   346  
   347  	validateSeries(t, it)
   348  }