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 }