github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/ts/writes/write_batch_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 writes
    22  
    23  import (
    24  	"bytes"
    25  	"errors"
    26  	"fmt"
    27  	"sync"
    28  	"testing"
    29  
    30  	"github.com/m3db/m3/src/x/checked"
    31  	"github.com/m3db/m3/src/x/ident"
    32  	"github.com/m3db/m3/src/x/pool"
    33  	"github.com/m3db/m3/src/x/serialize"
    34  	xtime "github.com/m3db/m3/src/x/time"
    35  
    36  	"github.com/stretchr/testify/require"
    37  )
    38  
    39  const (
    40  	batchSize = 2
    41  )
    42  
    43  var (
    44  	namespace = ident.StringID("namespace")
    45  	writes    = []testWrite{
    46  		{
    47  			id: ident.StringID("series1"),
    48  			tagIter: ident.NewTagsIterator(ident.NewTags(
    49  				ident.Tag{
    50  					Name:  ident.StringID("name1"),
    51  					Value: ident.StringID("value1"),
    52  				})),
    53  			timestamp:  xtime.Now(),
    54  			value:      0,
    55  			unit:       xtime.Nanosecond,
    56  			annotation: []byte("annotation1"),
    57  		},
    58  		{
    59  			id: ident.StringID("series2"),
    60  			tagIter: ident.NewTagsIterator(ident.NewTags(
    61  				ident.Tag{
    62  					Name:  ident.StringID("name2"),
    63  					Value: ident.StringID("value2"),
    64  				})),
    65  			timestamp:  xtime.Now(),
    66  			value:      1,
    67  			unit:       xtime.Nanosecond,
    68  			annotation: []byte("annotation2s"),
    69  		},
    70  		{
    71  			id: ident.StringID("series3"),
    72  			tagIter: ident.NewTagsIterator(ident.NewTags(
    73  				ident.Tag{
    74  					Name:  ident.StringID("name3"),
    75  					Value: ident.StringID("value3"),
    76  				})),
    77  			timestamp:  xtime.Now(),
    78  			value:      2,
    79  			unit:       xtime.Nanosecond,
    80  			annotation: []byte("annotation3s"),
    81  		},
    82  	}
    83  )
    84  
    85  var (
    86  	testTagEncodingPool = struct {
    87  		once sync.Once
    88  		pool serialize.TagEncoderPool
    89  	}{
    90  		pool: serialize.NewTagEncoderPool(serialize.NewTagEncoderOptions(),
    91  			pool.NewObjectPoolOptions().SetSize(1)),
    92  	}
    93  )
    94  
    95  func getTagEncoder() serialize.TagEncoder {
    96  	testTagEncodingPool.once.Do(func() {
    97  		testTagEncodingPool.pool.Init()
    98  	})
    99  	return testTagEncodingPool.pool.Get()
   100  }
   101  
   102  type testWrite struct {
   103  	id         ident.ID
   104  	tagIter    ident.TagIterator
   105  	timestamp  xtime.UnixNano
   106  	value      float64
   107  	unit       xtime.Unit
   108  	annotation []byte
   109  }
   110  
   111  func (w testWrite) encodedTags(t *testing.T) checked.Bytes {
   112  	encoder := getTagEncoder()
   113  	require.NoError(t, encoder.Encode(w.tagIter.Duplicate()))
   114  	data, ok := encoder.Data()
   115  	require.True(t, ok)
   116  	return data
   117  }
   118  
   119  func TestBatchWriterAddAndIter(t *testing.T) {
   120  	writeBatch := NewWriteBatch(batchSize, namespace, nil)
   121  
   122  	for i, write := range writes {
   123  		writeBatch.Add(
   124  			i,
   125  			write.id,
   126  			write.timestamp,
   127  			write.value,
   128  			write.unit,
   129  			write.annotation)
   130  	}
   131  
   132  	// Make sure all the data is there
   133  	assertDataPresent(t, writes, writeBatch)
   134  }
   135  
   136  func TestBatchWriterAddTaggedAndIter(t *testing.T) {
   137  	writeBatch := NewWriteBatch(batchSize, namespace, nil)
   138  
   139  	for i, write := range writes {
   140  		writeBatch.AddTagged(
   141  			i,
   142  			write.id,
   143  			write.encodedTags(t).Bytes(),
   144  			write.timestamp,
   145  			write.value,
   146  			write.unit,
   147  			write.annotation)
   148  	}
   149  
   150  	// Make sure all the data is there
   151  	assertDataPresent(t, writes, writeBatch)
   152  }
   153  
   154  func TestBatchWriterSetSeries(t *testing.T) {
   155  	writeBatch := NewWriteBatch(batchSize, namespace, nil)
   156  
   157  	for i, write := range writes {
   158  		writeBatch.AddTagged(
   159  			i,
   160  			write.id,
   161  			write.encodedTags(t).Bytes(),
   162  			write.timestamp,
   163  			write.value,
   164  			write.unit,
   165  			write.annotation)
   166  	}
   167  
   168  	// Set the outcome
   169  	iter := writeBatch.Iter()
   170  	for i, curr := range iter {
   171  		if i == 0 {
   172  			// skip the first write.
   173  			writeBatch.SetSkipWrite(i)
   174  			continue
   175  		}
   176  
   177  		var (
   178  			currWrite  = curr.Write
   179  			currSeries = currWrite.Series
   180  			newSeries  = currSeries
   181  		)
   182  		newSeries.ID = ident.StringID(fmt.Sprint(i))
   183  
   184  		if i == len(iter)-1 {
   185  			// Set skip for this to true; it should revert to not skipping after
   186  			// SetOutcome called below.
   187  			err := errors.New("some-error")
   188  			writeBatch.SetError(i, err)
   189  		} else {
   190  			writeBatch.SetSeries(i, newSeries)
   191  		}
   192  	}
   193  
   194  	iter = writeBatch.Iter()
   195  	require.Equal(t, 3, len(iter))
   196  
   197  	require.True(t, iter[0].SkipWrite)
   198  
   199  	for j, curr := range iter[1:] {
   200  		var (
   201  			currWrite  = curr.Write
   202  			currSeries = currWrite.Series
   203  			i          = j + 1
   204  		)
   205  		if i == len(iter)-1 {
   206  			require.Equal(t, errors.New("some-error"), curr.Err)
   207  			require.True(t, curr.SkipWrite)
   208  			continue
   209  		}
   210  
   211  		require.Equal(t, fmt.Sprint(i), string(currSeries.ID.String()))
   212  		require.False(t, curr.SkipWrite)
   213  
   214  		require.NoError(t, curr.Err)
   215  	}
   216  }
   217  
   218  func TestWriteBatchReset(t *testing.T) {
   219  	var (
   220  		numResets  = 10
   221  		writeBatch = NewWriteBatch(batchSize, namespace, nil)
   222  	)
   223  
   224  	for i := 0; i < numResets; i++ {
   225  		writeBatch.Reset(batchSize, namespace)
   226  		for _, write := range writes {
   227  			writeBatch.Add(
   228  				i,
   229  				write.id,
   230  				write.timestamp,
   231  				write.value,
   232  				write.unit,
   233  				write.annotation)
   234  		}
   235  
   236  		// Make sure all the data is there
   237  		assertDataPresent(t, writes, writeBatch)
   238  	}
   239  }
   240  
   241  func assertDataPresent(t *testing.T, writes []testWrite, batchWriter WriteBatch) {
   242  	for _, write := range writes {
   243  		var (
   244  			iter  = batchWriter.Iter()
   245  			found = false
   246  		)
   247  
   248  		for _, currWriteBatch := range iter {
   249  			var (
   250  				currWrite  = currWriteBatch.Write
   251  				currSeries = currWrite.Series
   252  			)
   253  
   254  			if currSeries.ID.Equal(write.id) {
   255  				require.Equal(t, namespace, currWrite.Series.Namespace)
   256  				require.Equal(t, write.timestamp, currWrite.Datapoint.TimestampNanos)
   257  				require.Equal(t, write.value, currWrite.Datapoint.Value)
   258  				require.Equal(t, write.unit, currWrite.Unit)
   259  				require.True(t, bytes.Equal(write.annotation, currWrite.Annotation))
   260  				found = true
   261  				break
   262  			}
   263  		}
   264  
   265  		require.True(t, found, fmt.Sprintf("expected to find series: %s", write.id))
   266  	}
   267  }
   268  
   269  func TestBatchWriterFinalizer(t *testing.T) {
   270  	var (
   271  		numEncodedTagsFinalized = 0
   272  		numAnnotationsFinalized = 0
   273  		numFinalized            = 0
   274  
   275  		finalizeEncodedTagsFn = func(b []byte) {
   276  			numEncodedTagsFinalized++
   277  		}
   278  		finalizeAnnotationFn = func(b []byte) {
   279  			numAnnotationsFinalized++
   280  		}
   281  		finalizeFn = func(b WriteBatch) {
   282  			numFinalized++
   283  		}
   284  	)
   285  
   286  	writeBatch := NewWriteBatch(batchSize, namespace, finalizeFn)
   287  	writeBatch.SetFinalizeEncodedTagsFn(finalizeEncodedTagsFn)
   288  	writeBatch.SetFinalizeAnnotationFn(finalizeAnnotationFn)
   289  
   290  	for i, write := range writes {
   291  		writeBatch.AddTagged(
   292  			i,
   293  			write.id,
   294  			write.encodedTags(t).Bytes(),
   295  			write.timestamp,
   296  			write.value,
   297  			write.unit,
   298  			write.annotation)
   299  	}
   300  
   301  	require.Equal(t, 3, len(writeBatch.Iter()))
   302  	writeBatch.Finalize()
   303  	require.Equal(t, 0, len(writeBatch.Iter()))
   304  	require.Equal(t, 1, numFinalized)
   305  	require.Equal(t, 3, numEncodedTagsFinalized)
   306  	require.Equal(t, 3, numAnnotationsFinalized)
   307  }