github.com/m3db/m3@v1.5.0/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 }