github.com/m3db/m3@v1.5.0/src/dbnode/storage/index/results_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 index
    22  
    23  import (
    24  	"bytes"
    25  	"testing"
    26  
    27  	idxconvert "github.com/m3db/m3/src/dbnode/storage/index/convert"
    28  	"github.com/m3db/m3/src/dbnode/test"
    29  	"github.com/m3db/m3/src/m3ninx/doc"
    30  	"github.com/m3db/m3/src/m3ninx/index/segment/fst/encoding/docs"
    31  	"github.com/m3db/m3/src/x/ident"
    32  	"github.com/m3db/m3/src/x/pool"
    33  	xtest "github.com/m3db/m3/src/x/test"
    34  
    35  	"github.com/stretchr/testify/assert"
    36  	"github.com/stretchr/testify/require"
    37  )
    38  
    39  var (
    40  	// only allocating a single Options to save allocs during tests
    41  	testOpts Options
    42  )
    43  
    44  func init() {
    45  	// Use size of 1 to test edge cases by default
    46  	testOpts = optionsWithDocsArrayPool(NewOptions(), 1, 1)
    47  }
    48  
    49  func optionsWithDocsArrayPool(opts Options, size, capacity int) Options {
    50  	docArrayPool := doc.NewDocumentArrayPool(doc.DocumentArrayPoolOpts{
    51  		Options:     pool.NewObjectPoolOptions().SetSize(size),
    52  		Capacity:    capacity,
    53  		MaxCapacity: capacity,
    54  	})
    55  	docArrayPool.Init()
    56  
    57  	return opts.SetDocumentArrayPool(docArrayPool)
    58  }
    59  
    60  func TestResultsInsertInvalid(t *testing.T) {
    61  	res := NewQueryResults(nil, QueryResultsOptions{}, testOpts)
    62  	assert.True(t, res.EnforceLimits())
    63  
    64  	dInvalid := doc.Metadata{ID: nil}
    65  	size, docsCount, err := res.AddDocuments([]doc.Document{
    66  		doc.NewDocumentFromMetadata(dInvalid),
    67  	})
    68  	require.Error(t, err)
    69  	require.Equal(t, 0, size)
    70  	require.Equal(t, 1, docsCount)
    71  
    72  	require.Equal(t, 0, res.Size())
    73  	require.Equal(t, 1, res.TotalDocsCount())
    74  }
    75  
    76  func TestResultsInsertIdempotency(t *testing.T) {
    77  	res := NewQueryResults(nil, QueryResultsOptions{}, testOpts)
    78  	dValid := doc.Metadata{ID: []byte("abc")}
    79  	size, docsCount, err := res.AddDocuments([]doc.Document{
    80  		doc.NewDocumentFromMetadata(dValid),
    81  	})
    82  	require.NoError(t, err)
    83  	require.Equal(t, 1, size)
    84  	require.Equal(t, 1, docsCount)
    85  
    86  	require.Equal(t, 1, res.Size())
    87  	require.Equal(t, 1, res.TotalDocsCount())
    88  
    89  	size, docsCount, err = res.AddDocuments([]doc.Document{
    90  		doc.NewDocumentFromMetadata(dValid),
    91  	})
    92  	require.NoError(t, err)
    93  	require.Equal(t, 1, size)
    94  	require.Equal(t, 2, docsCount)
    95  
    96  	require.Equal(t, 1, res.Size())
    97  	require.Equal(t, 2, res.TotalDocsCount())
    98  }
    99  
   100  func TestResultsInsertBatchOfTwo(t *testing.T) {
   101  	res := NewQueryResults(nil, QueryResultsOptions{}, testOpts)
   102  	d1 := doc.Metadata{ID: []byte("d1")}
   103  	d2 := doc.Metadata{ID: []byte("d2")}
   104  	size, docsCount, err := res.AddDocuments([]doc.Document{
   105  		doc.NewDocumentFromMetadata(d1),
   106  		doc.NewDocumentFromMetadata(d2),
   107  	})
   108  	require.NoError(t, err)
   109  	require.Equal(t, 2, size)
   110  	require.Equal(t, 2, docsCount)
   111  
   112  	require.Equal(t, 2, res.Size())
   113  	require.Equal(t, 2, res.TotalDocsCount())
   114  }
   115  
   116  func TestResultsFirstInsertWins(t *testing.T) {
   117  	res := NewQueryResults(nil, QueryResultsOptions{}, testOpts)
   118  	d1 := doc.Metadata{ID: []byte("abc")}
   119  	size, docsCount, err := res.AddDocuments([]doc.Document{
   120  		doc.NewDocumentFromMetadata(d1),
   121  	})
   122  	require.NoError(t, err)
   123  	require.Equal(t, 1, size)
   124  	require.Equal(t, 1, docsCount)
   125  
   126  	require.Equal(t, 1, res.Size())
   127  	require.Equal(t, 1, res.TotalDocsCount())
   128  
   129  	d, ok := res.Map().Get(d1.ID)
   130  	require.True(t, ok)
   131  
   132  	tags := test.DocumentToTagIter(t, d)
   133  	require.Equal(t, 0, tags.Remaining())
   134  
   135  	d2 := doc.Metadata{
   136  		ID: []byte("abc"),
   137  		Fields: doc.Fields{
   138  			doc.Field{
   139  				Name:  []byte("foo"),
   140  				Value: []byte("bar"),
   141  			},
   142  		}}
   143  	size, docsCount, err = res.AddDocuments([]doc.Document{
   144  		doc.NewDocumentFromMetadata(d2),
   145  	})
   146  	require.NoError(t, err)
   147  	require.Equal(t, 1, size)
   148  	require.Equal(t, 2, docsCount)
   149  
   150  	require.Equal(t, 1, res.Size())
   151  	require.Equal(t, 2, res.TotalDocsCount())
   152  
   153  	d, ok = res.Map().Get([]byte("abc"))
   154  	require.True(t, ok)
   155  
   156  	tags = test.DocumentToTagIter(t, d)
   157  	require.Equal(t, 0, tags.Remaining())
   158  }
   159  
   160  func TestResultsInsertContains(t *testing.T) {
   161  	res := NewQueryResults(nil, QueryResultsOptions{}, testOpts)
   162  	dValid := doc.Metadata{ID: []byte("abc")}
   163  	size, docsCount, err := res.AddDocuments([]doc.Document{
   164  		doc.NewDocumentFromMetadata(dValid),
   165  	})
   166  	require.NoError(t, err)
   167  	require.Equal(t, 1, size)
   168  	require.Equal(t, 1, docsCount)
   169  
   170  	d, ok := res.Map().Get([]byte("abc"))
   171  	require.True(t, ok)
   172  
   173  	tags := test.DocumentToTagIter(t, d)
   174  	require.Equal(t, 0, tags.Remaining())
   175  }
   176  
   177  func TestResultsInsertDoesNotCopy(t *testing.T) {
   178  	res := NewQueryResults(nil, QueryResultsOptions{}, testOpts)
   179  	dValid := doc.Metadata{ID: []byte("abc"), Fields: []doc.Field{
   180  		{Name: []byte("name"), Value: []byte("value")},
   181  	}}
   182  	size, docsCount, err := res.AddDocuments([]doc.Document{
   183  		doc.NewDocumentFromMetadata(dValid),
   184  	})
   185  	require.NoError(t, err)
   186  	require.Equal(t, 1, size)
   187  	require.Equal(t, 1, docsCount)
   188  
   189  	found := false
   190  
   191  	// Our genny generated maps don't provide access to MapEntry directly,
   192  	// so we iterate over the map to find the added entry. Could avoid this
   193  	// in the future if we expose `func (m *Map) Entry(k Key) Entry {}`.
   194  	reader := docs.NewEncodedDocumentReader()
   195  	for _, entry := range res.Map().Iter() {
   196  		// see if this key has the same value as the added document's ID.
   197  		key := entry.Key()
   198  		if !bytes.Equal(dValid.ID, key) {
   199  			continue
   200  		}
   201  		found = true
   202  
   203  		// Ensure the underlying []byte for ID/Fields is the same.
   204  		require.True(t, xtest.ByteSlicesBackedBySameData(key, dValid.ID))
   205  		d := entry.Value()
   206  		m, err := docs.MetadataFromDocument(d, reader)
   207  		require.NoError(t, err)
   208  
   209  		tags := idxconvert.ToSeriesTags(m, idxconvert.Opts{NoClone: true})
   210  		for _, f := range dValid.Fields {
   211  			fName := f.Name
   212  			fValue := f.Value
   213  
   214  			tagsIter := tags.Duplicate()
   215  			for tagsIter.Next() {
   216  				tag := tagsIter.Current()
   217  				tName := tag.Name.Bytes()
   218  				tValue := tag.Value.Bytes()
   219  				if !bytes.Equal(fName, tName) || !bytes.Equal(fValue, tValue) {
   220  					continue
   221  				}
   222  				require.True(t, xtest.ByteSlicesBackedBySameData(fName, tName))
   223  				require.True(t, xtest.ByteSlicesBackedBySameData(fValue, tValue))
   224  			}
   225  		}
   226  	}
   227  
   228  	require.True(t, found)
   229  }
   230  
   231  func TestResultsReset(t *testing.T) {
   232  	res := NewQueryResults(nil, QueryResultsOptions{}, testOpts)
   233  	d1 := doc.Metadata{ID: []byte("abc")}
   234  	size, docsCount, err := res.AddDocuments([]doc.Document{
   235  		doc.NewDocumentFromMetadata(d1),
   236  	})
   237  	require.NoError(t, err)
   238  	require.Equal(t, 1, size)
   239  	require.Equal(t, 1, docsCount)
   240  
   241  	d, ok := res.Map().Get([]byte("abc"))
   242  	require.True(t, ok)
   243  
   244  	tags := test.DocumentToTagIter(t, d)
   245  	require.Equal(t, 0, tags.Remaining())
   246  
   247  	res.Reset(nil, QueryResultsOptions{})
   248  	_, ok = res.Map().Get([]byte("abc"))
   249  	require.False(t, ok)
   250  	require.Equal(t, 0, tags.Remaining())
   251  	require.Equal(t, 0, res.Size())
   252  	require.Equal(t, 0, res.TotalDocsCount())
   253  }
   254  
   255  func TestResultsResetNamespaceClones(t *testing.T) {
   256  	res := NewQueryResults(nil, QueryResultsOptions{}, testOpts)
   257  	require.Equal(t, nil, res.Namespace())
   258  	nsID := ident.StringID("something")
   259  	res.Reset(nsID, QueryResultsOptions{})
   260  	nsID.Finalize()
   261  	require.Equal(t, "something", res.Namespace().String())
   262  
   263  	// Ensure new NS is cloned
   264  	require.False(t,
   265  		xtest.ByteSlicesBackedBySameData(nsID.Bytes(), res.Namespace().Bytes()))
   266  }
   267  
   268  func TestFinalize(t *testing.T) {
   269  	// Create a Results and insert some data.
   270  	res := NewQueryResults(nil, QueryResultsOptions{}, testOpts)
   271  	d1 := doc.Metadata{ID: []byte("abc")}
   272  	size, docsCount, err := res.AddDocuments([]doc.Document{
   273  		doc.NewDocumentFromMetadata(d1),
   274  	})
   275  	require.NoError(t, err)
   276  	require.Equal(t, 1, size)
   277  	require.Equal(t, 1, docsCount)
   278  
   279  	// Ensure the data is present.
   280  	d, ok := res.Map().Get([]byte("abc"))
   281  	require.True(t, ok)
   282  
   283  	tags := test.DocumentToTagIter(t, d)
   284  	require.Equal(t, 0, tags.Remaining())
   285  
   286  	// Call Finalize() to reset the Results.
   287  	res.Finalize()
   288  
   289  	// Ensure data was removed by call to Finalize().
   290  	_, ok = res.Map().Get([]byte("abc"))
   291  	require.False(t, ok)
   292  	require.Equal(t, 0, res.Size())
   293  	require.Equal(t, 0, res.TotalDocsCount())
   294  }