github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/encoding/series_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 "errors" 25 "testing" 26 "time" 27 28 "github.com/m3db/m3/src/dbnode/ts" 29 "github.com/m3db/m3/src/dbnode/x/xio" 30 "github.com/m3db/m3/src/x/checked" 31 "github.com/m3db/m3/src/x/ident" 32 xtest "github.com/m3db/m3/src/x/test" 33 xtime "github.com/m3db/m3/src/x/time" 34 35 "github.com/stretchr/testify/assert" 36 "github.com/stretchr/testify/require" 37 ) 38 39 type testSeries struct { 40 id string 41 nsID string 42 retainTag bool 43 start xtime.UnixNano 44 end xtime.UnixNano 45 input []inputReplica 46 expected []testValue 47 expectedErr *testSeriesErr 48 49 expectedFirstAnnotation ts.Annotation 50 } 51 52 type inputReplica struct { 53 values []testValue 54 err error 55 } 56 57 type testSeriesErr struct { 58 err error 59 atIdx int 60 } 61 62 func TestMultiReaderMergesReplicas(t *testing.T) { 63 start := xtime.Now().Truncate(time.Minute) 64 end := start.Add(time.Minute) 65 66 values := []inputReplica{ 67 { 68 values: []testValue{ 69 {1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}}, 70 {2.0, start.Add(2 * time.Second), xtime.Second, nil}, 71 {3.0, start.Add(3 * time.Second), xtime.Second, nil}, 72 }, 73 }, 74 { 75 values: []testValue{ 76 {1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}}, 77 {2.0, start.Add(2 * time.Second), xtime.Second, nil}, 78 {3.0, start.Add(3 * time.Second), xtime.Second, nil}, 79 }, 80 }, 81 { 82 values: []testValue{ 83 {3.0, start.Add(3 * time.Second), xtime.Second, nil}, 84 {4.0, start.Add(4 * time.Second), xtime.Second, nil}, 85 {5.0, start.Add(5 * time.Second), xtime.Second, nil}, 86 }, 87 }, 88 } 89 90 expected := []testValue{ 91 {1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}}, 92 {2.0, start.Add(2 * time.Second), xtime.Second, nil}, 93 {3.0, start.Add(3 * time.Second), xtime.Second, nil}, 94 {4.0, start.Add(4 * time.Second), xtime.Second, nil}, 95 {5.0, start.Add(5 * time.Second), xtime.Second, nil}, 96 } 97 98 test := testSeries{ 99 id: "foo", 100 nsID: "bar", 101 start: start, 102 end: end, 103 input: values, 104 expected: expected, 105 expectedFirstAnnotation: []byte{1, 2, 3}, 106 } 107 108 assertTestSeriesIterator(t, test) 109 } 110 111 func TestMultiReaderFiltersToRange(t *testing.T) { 112 start := xtime.Now().Truncate(time.Minute) 113 end := start.Add(time.Minute) 114 115 input := []inputReplica{ 116 { 117 values: []testValue{ 118 {0.0, start.Add(-2 * time.Second), xtime.Second, []byte{1, 2, 3}}, 119 {1.0, start.Add(-1 * time.Second), xtime.Second, nil}, 120 {2.0, start, xtime.Second, nil}, 121 {3.0, start.Add(1 * time.Second), xtime.Second, nil}, 122 {4.0, start.Add(60 * time.Second), xtime.Second, nil}, 123 {5.0, start.Add(61 * time.Second), xtime.Second, nil}, 124 }, 125 }, 126 } 127 128 test := testSeries{ 129 id: "foo", 130 nsID: "bar", 131 start: start, 132 end: end, 133 input: input, 134 expected: input[0].values[2:4], 135 expectedFirstAnnotation: []byte{1, 2, 3}, 136 } 137 138 assertTestSeriesIterator(t, test) 139 } 140 141 func TestSeriesIteratorIgnoresEmptyReplicas(t *testing.T) { 142 start := xtime.Now().Truncate(time.Minute) 143 end := start.Add(time.Minute) 144 145 values := []testValue{ 146 {1.0, start.Add(1 * time.Second), xtime.Second, []byte{3, 2, 1}}, 147 {2.0, start.Add(2 * time.Second), xtime.Second, nil}, 148 {3.0, start.Add(3 * time.Second), xtime.Second, nil}, 149 } 150 151 test := testSeries{ 152 id: "foo", 153 nsID: "bar", 154 start: start, 155 end: end, 156 input: []inputReplica{ 157 {values: values}, 158 {values: []testValue{}}, 159 {values: values}, 160 }, 161 expected: values, 162 expectedFirstAnnotation: []byte{3, 2, 1}, 163 } 164 165 assertTestSeriesIterator(t, test) 166 } 167 168 func TestSeriesIteratorDoesNotIgnoreReplicasWithErrors(t *testing.T) { 169 var ( 170 start = xtime.Now().Truncate(time.Minute) 171 end = start.Add(time.Minute) 172 err = errors.New("some-iteration-error") 173 ) 174 175 test := testSeries{ 176 id: "foo", 177 nsID: "bar", 178 start: start, 179 end: end, 180 input: []inputReplica{ 181 {err: err}, 182 }, 183 expectedErr: &testSeriesErr{err: err}, 184 } 185 186 assertTestSeriesIterator(t, test) 187 } 188 189 func TestSeriesIteratorErrorOnOutOfOrder(t *testing.T) { 190 start := xtime.Now().Truncate(time.Minute) 191 end := start.Add(time.Minute) 192 193 values := []testValue{ 194 {1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}}, 195 {3.0, start.Add(3 * time.Second), xtime.Second, nil}, 196 {2.0, start.Add(2 * time.Second), xtime.Second, nil}, 197 } 198 199 test := testSeries{ 200 id: "foo", 201 nsID: "bar", 202 start: start, 203 end: end, 204 input: []inputReplica{{values: values}}, 205 expected: values[:2], 206 expectedFirstAnnotation: []byte{1, 2, 3}, 207 expectedErr: &testSeriesErr{ 208 err: errOutOfOrderIterator, 209 atIdx: 2, 210 }, 211 } 212 213 assertTestSeriesIterator(t, test) 214 } 215 216 func TestSeriesIteratorSetIterateEqualTimestampStrategy(t *testing.T) { 217 test := testSeries{ 218 id: "foo", 219 nsID: "bar", 220 } 221 222 iter := newTestSeriesIterator(t, test).iter 223 224 // Ensure default value if none set 225 assert.Equal(t, iter.iters.equalTimesStrategy, 226 DefaultIterateEqualTimestampStrategy) 227 228 // Ensure value is propagated during a reset 229 iter.Reset(SeriesIteratorOptions{ 230 ID: ident.StringID("baz"), 231 IterateEqualTimestampStrategy: IterateHighestValue, 232 }) 233 assert.Equal(t, iter.iters.equalTimesStrategy, 234 IterateHighestValue) 235 236 // Ensure falls back to default after a reset without specifying 237 iter.Reset(SeriesIteratorOptions{ 238 ID: ident.StringID("baz"), 239 }) 240 assert.Equal(t, iter.iters.equalTimesStrategy, 241 DefaultIterateEqualTimestampStrategy) 242 } 243 244 type testSeriesConsolidator struct { 245 iters []MultiReaderIterator 246 } 247 248 func (c *testSeriesConsolidator) ConsolidateReplicas( 249 _ []MultiReaderIterator, 250 ) ([]MultiReaderIterator, error) { 251 return c.iters, nil 252 } 253 254 func TestSeriesIteratorSetSeriesIteratorConsolidator(t *testing.T) { 255 ctrl := xtest.NewController(t) 256 defer ctrl.Finish() 257 258 test := testSeries{ 259 id: "foo", 260 nsID: "bar", 261 } 262 263 iter := newTestSeriesIterator(t, test).iter 264 newIter := NewMockMultiReaderIterator(ctrl) 265 newIter.EXPECT().Next().Return(true) 266 newIter.EXPECT().Current().Return(ts.Datapoint{}, xtime.Second, nil).Times(3) 267 268 iter.iters.setFilter(0, 1) 269 consolidator := &testSeriesConsolidator{iters: []MultiReaderIterator{newIter}} 270 oldIter := NewMockMultiReaderIterator(ctrl) 271 oldIters := []MultiReaderIterator{oldIter} 272 iter.multiReaderIters = oldIters 273 assert.Equal(t, oldIter, iter.multiReaderIters[0]) 274 iter.Reset(SeriesIteratorOptions{ 275 Replicas: oldIters, 276 SeriesIteratorConsolidator: consolidator, 277 }) 278 assert.Equal(t, newIter, iter.multiReaderIters[0]) 279 } 280 281 type newTestSeriesIteratorResult struct { 282 iter *seriesIterator 283 multiReaderIterators []MultiReaderIterator 284 } 285 286 func newTestSeriesIterator( 287 t *testing.T, 288 series testSeries, 289 ) newTestSeriesIteratorResult { 290 var iters []MultiReaderIterator 291 for i := range series.input { 292 multiIter := newTestMultiIterator( 293 series.input[i].values, 294 series.input[i].err, 295 ) 296 iters = append(iters, multiIter) 297 } 298 299 iter := NewSeriesIterator(SeriesIteratorOptions{ 300 ID: ident.StringID(series.id), 301 Namespace: ident.StringID(series.nsID), 302 Tags: ident.EmptyTagIterator, 303 StartInclusive: series.start, 304 EndExclusive: series.end, 305 Replicas: iters, 306 }, nil) 307 308 seriesIter, ok := iter.(*seriesIterator) 309 require.True(t, ok) 310 311 return newTestSeriesIteratorResult{ 312 iter: seriesIter, 313 multiReaderIterators: iters, 314 } 315 } 316 317 func assertTestSeriesIterator( 318 t *testing.T, 319 series testSeries, 320 ) { 321 newSeriesIter := newTestSeriesIterator(t, series) 322 iter := newSeriesIter.iter 323 multiReaderIterators := newSeriesIter.multiReaderIterators 324 defer iter.Close() 325 326 assert.Equal(t, series.id, iter.ID().String()) 327 assert.Equal(t, series.nsID, iter.Namespace().String()) 328 assert.Equal(t, series.start, iter.Start()) 329 assert.Equal(t, series.end, iter.End()) 330 for i := 0; i < len(series.expected); i++ { 331 next := iter.Next() 332 if series.expectedErr != nil && i == series.expectedErr.atIdx { 333 assert.False(t, next) 334 break 335 } 336 require.True(t, next) 337 dp, unit, annotation := iter.Current() 338 expected := series.expected[i] 339 assert.Equal(t, expected.value, dp.Value) 340 assert.Equal(t, expected.t, dp.TimestampNanos) 341 assert.Equal(t, expected.unit, unit) 342 assert.Equal(t, expected.annotation, annotation) 343 assert.Equal(t, series.expectedFirstAnnotation, iter.FirstAnnotation()) 344 } 345 // Ensure further calls to next false 346 for i := 0; i < 2; i++ { 347 assert.False(t, iter.Next()) 348 } 349 if series.expectedErr == nil { 350 assert.NoError(t, iter.Err()) 351 } else { 352 assert.Equal(t, series.expectedErr.err, iter.Err()) 353 } 354 for _, iter := range multiReaderIterators { 355 if iter != nil { 356 assert.True(t, iter.(*testMultiIterator).closed) 357 } 358 } 359 } 360 361 func TestSeriesIteratorStats(t *testing.T) { 362 ctrl := xtest.NewController(t) 363 defer ctrl.Finish() 364 365 segReader := func(head, tail int) xio.SegmentReader { 366 var h, t checked.Bytes 367 if head > 0 { 368 h = checked.NewBytes(make([]byte, head), nil) 369 h.IncRef() 370 } 371 if tail > 0 { 372 t = checked.NewBytes(make([]byte, tail), nil) 373 t.IncRef() 374 } 375 return xio.NewSegmentReader(ts.Segment{ 376 Head: h, 377 Tail: t, 378 }) 379 } 380 readerOne := &singleSlicesOfSlicesIterator{ 381 readers: []xio.SegmentReader{segReader(0, 5), segReader(5, 5)}, 382 } 383 readerTwo := &singleSlicesOfSlicesIterator{ 384 readers: []xio.SegmentReader{segReader(2, 2), segReader(5, 0)}, 385 } 386 iter := seriesIterator{ 387 multiReaderIters: []MultiReaderIterator{ 388 &multiReaderIterator{slicesIter: readerOne}, 389 &multiReaderIterator{slicesIter: readerTwo}, 390 }, 391 } 392 stats, err := iter.Stats() 393 require.NoError(t, err) 394 assert.Equal(t, 24, stats.ApproximateSizeInBytes) 395 }