github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/storage/index/block_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 stdlibctx "context" 25 "fmt" 26 "sort" 27 "strings" 28 "testing" 29 "time" 30 31 "github.com/m3db/m3/src/dbnode/namespace" 32 "github.com/m3db/m3/src/dbnode/retention" 33 "github.com/m3db/m3/src/dbnode/storage/bootstrap/result" 34 "github.com/m3db/m3/src/dbnode/storage/index/compaction" 35 "github.com/m3db/m3/src/dbnode/storage/limits" 36 "github.com/m3db/m3/src/dbnode/test" 37 "github.com/m3db/m3/src/dbnode/tracepoint" 38 "github.com/m3db/m3/src/m3ninx/doc" 39 "github.com/m3db/m3/src/m3ninx/idx" 40 "github.com/m3db/m3/src/m3ninx/index/segment" 41 "github.com/m3db/m3/src/m3ninx/index/segment/mem" 42 idxpersist "github.com/m3db/m3/src/m3ninx/persist" 43 "github.com/m3db/m3/src/m3ninx/search" 44 "github.com/m3db/m3/src/x/context" 45 "github.com/m3db/m3/src/x/ident" 46 "github.com/m3db/m3/src/x/instrument" 47 "github.com/m3db/m3/src/x/pool" 48 "github.com/m3db/m3/src/x/resource" 49 "github.com/m3db/m3/src/x/tallytest" 50 xtime "github.com/m3db/m3/src/x/time" 51 52 "github.com/golang/mock/gomock" 53 "github.com/opentracing/opentracing-go" 54 opentracinglog "github.com/opentracing/opentracing-go/log" 55 "github.com/opentracing/opentracing-go/mocktracer" 56 "github.com/stretchr/testify/assert" 57 "github.com/stretchr/testify/require" 58 "github.com/uber-go/tally" 59 "go.uber.org/zap" 60 ) 61 62 var ( 63 defaultQuery = Query{ 64 Query: idx.NewTermQuery([]byte("foo"), []byte("bar")), 65 } 66 67 emptyLogFields = []opentracinglog.Field{} 68 ) 69 70 func newTestNSMetadata(t require.TestingT) namespace.Metadata { 71 ropts := retention.NewOptions(). 72 SetBlockSize(time.Hour). 73 SetRetentionPeriod(24 * time.Hour) 74 iopts := namespace.NewIndexOptions(). 75 SetEnabled(true). 76 SetBlockSize(time.Hour) 77 md, err := namespace.NewMetadata(ident.StringID("testNs"), 78 namespace.NewOptions().SetRetentionOptions(ropts).SetIndexOptions(iopts)) 79 require.NoError(t, err) 80 return md 81 } 82 83 func TestBlockCtor(t *testing.T) { 84 md := newTestNSMetadata(t) 85 start := xtime.Now().Truncate(time.Hour) 86 b, err := NewBlock(start, md, BlockOptions{}, 87 namespace.NewRuntimeOptionsManager("foo"), testOpts) 88 require.NoError(t, err) 89 90 require.Equal(t, start, b.StartTime()) 91 require.Equal(t, start.Add(time.Hour), b.EndTime()) 92 require.NoError(t, b.Close()) 93 require.Error(t, b.Close()) 94 } 95 96 // nolint: unparam 97 func testWriteBatchOptionsWithBlockSize(d time.Duration) WriteBatchOptions { 98 return WriteBatchOptions{ 99 IndexBlockSize: d, 100 } 101 } 102 103 func TestBlockWriteAfterClose(t *testing.T) { 104 ctrl := gomock.NewController(t) 105 defer ctrl.Finish() 106 107 testMD := newTestNSMetadata(t) 108 blockSize := time.Hour 109 110 now := xtime.Now() 111 blockStart := now.Truncate(blockSize) 112 113 nowNotBlockStartAligned := now. 114 Truncate(blockSize). 115 Add(time.Minute) 116 117 b, err := NewBlock(blockStart, testMD, BlockOptions{}, 118 namespace.NewRuntimeOptionsManager("foo"), testOpts) 119 require.NoError(t, err) 120 require.NoError(t, b.Close()) 121 122 lifecycle := doc.NewMockOnIndexSeries(ctrl) 123 lifecycle.EXPECT().OnIndexFinalize(blockStart) 124 125 batch := NewWriteBatch(testWriteBatchOptionsWithBlockSize(blockSize)) 126 batch.Append(WriteBatchEntry{ 127 Timestamp: nowNotBlockStartAligned, 128 OnIndexSeries: lifecycle, 129 }, doc.Metadata{}) 130 131 res, err := b.WriteBatch(batch) 132 require.Error(t, err) 133 require.Equal(t, int64(0), res.NumSuccess) 134 require.Equal(t, int64(1), res.NumError) 135 136 verified := 0 137 batch.ForEach(func( 138 idx int, 139 entry WriteBatchEntry, 140 doc doc.Metadata, 141 result WriteBatchEntryResult, 142 ) { 143 verified++ 144 require.Error(t, result.Err) 145 require.Equal(t, errUnableToWriteBlockClosed, result.Err) 146 }) 147 require.Equal(t, 1, verified) 148 } 149 150 func TestBlockWriteAfterSeal(t *testing.T) { 151 ctrl := gomock.NewController(t) 152 defer ctrl.Finish() 153 154 blockSize := time.Hour 155 testMD := newTestNSMetadata(t) 156 157 now := xtime.Now() 158 blockStart := now.Truncate(blockSize) 159 160 nowNotBlockStartAligned := now. 161 Truncate(blockSize). 162 Add(time.Minute) 163 164 b, err := NewBlock(blockStart, testMD, BlockOptions{}, 165 namespace.NewRuntimeOptionsManager("foo"), testOpts) 166 require.NoError(t, err) 167 require.NoError(t, b.Seal()) 168 169 lifecycle := doc.NewMockOnIndexSeries(ctrl) 170 lifecycle.EXPECT().OnIndexFinalize(blockStart) 171 172 batch := NewWriteBatch(testWriteBatchOptionsWithBlockSize(blockSize)) 173 batch.Append(WriteBatchEntry{ 174 Timestamp: nowNotBlockStartAligned, 175 OnIndexSeries: lifecycle, 176 }, doc.Metadata{}) 177 178 res, err := b.WriteBatch(batch) 179 require.Error(t, err) 180 require.Equal(t, int64(0), res.NumSuccess) 181 require.Equal(t, int64(1), res.NumError) 182 183 verified := 0 184 batch.ForEach(func( 185 idx int, 186 entry WriteBatchEntry, 187 doc doc.Metadata, 188 result WriteBatchEntryResult, 189 ) { 190 verified++ 191 require.Error(t, result.Err) 192 require.Equal(t, errUnableToWriteBlockSealed, result.Err) 193 }) 194 require.Equal(t, 1, verified) 195 } 196 197 func TestBlockWrite(t *testing.T) { 198 ctrl := gomock.NewController(t) 199 defer ctrl.Finish() 200 201 testMD := newTestNSMetadata(t) 202 blockSize := time.Hour 203 204 now := xtime.Now() 205 blockStart := now.Truncate(blockSize) 206 207 nowNotBlockStartAligned := now. 208 Truncate(blockSize). 209 Add(time.Minute) 210 211 blk, err := NewBlock(blockStart, testMD, BlockOptions{}, 212 namespace.NewRuntimeOptionsManager("foo"), testOpts) 213 require.NoError(t, err) 214 defer func() { 215 require.NoError(t, blk.Close()) 216 }() 217 218 b, ok := blk.(*block) 219 require.True(t, ok) 220 221 h1 := doc.NewMockOnIndexSeries(ctrl) 222 h1.EXPECT().OnIndexFinalize(blockStart) 223 h1.EXPECT().OnIndexSuccess(blockStart) 224 225 h2 := doc.NewMockOnIndexSeries(ctrl) 226 h2.EXPECT().OnIndexFinalize(blockStart) 227 h2.EXPECT().OnIndexSuccess(blockStart) 228 229 batch := NewWriteBatch(testWriteBatchOptionsWithBlockSize(blockSize)) 230 batch.Append(WriteBatchEntry{ 231 Timestamp: nowNotBlockStartAligned, 232 OnIndexSeries: h1, 233 }, testDoc1()) 234 batch.Append(WriteBatchEntry{ 235 Timestamp: nowNotBlockStartAligned, 236 OnIndexSeries: h2, 237 }, testDoc2()) 238 239 res, err := b.WriteBatch(batch) 240 require.NoError(t, err) 241 require.Equal(t, int64(2), res.NumSuccess) 242 require.Equal(t, int64(0), res.NumError) 243 } 244 245 func TestBlockWriteActualSegmentPartialFailure(t *testing.T) { 246 ctrl := gomock.NewController(t) 247 defer ctrl.Finish() 248 249 md := newTestNSMetadata(t) 250 blockSize := time.Hour 251 252 now := xtime.Now() 253 blockStart := now.Truncate(blockSize) 254 255 nowNotBlockStartAligned := now. 256 Truncate(blockSize). 257 Add(time.Minute) 258 259 blk, err := NewBlock(blockStart, md, BlockOptions{}, 260 namespace.NewRuntimeOptionsManager("foo"), testOpts) 261 require.NoError(t, err) 262 b, ok := blk.(*block) 263 require.True(t, ok) 264 265 h1 := doc.NewMockOnIndexSeries(ctrl) 266 h1.EXPECT().OnIndexFinalize(blockStart) 267 h1.EXPECT().OnIndexSuccess(blockStart) 268 269 h2 := doc.NewMockOnIndexSeries(ctrl) 270 h2.EXPECT().OnIndexFinalize(blockStart) 271 272 batch := NewWriteBatch(testWriteBatchOptionsWithBlockSize(blockSize)) 273 batch.Append(WriteBatchEntry{ 274 Timestamp: nowNotBlockStartAligned, 275 OnIndexSeries: h1, 276 }, testDoc1()) 277 batch.Append(WriteBatchEntry{ 278 Timestamp: nowNotBlockStartAligned, 279 OnIndexSeries: h2, 280 }, doc.Metadata{}) 281 res, err := b.WriteBatch(batch) 282 require.Error(t, err) 283 require.Equal(t, int64(1), res.NumSuccess) 284 require.Equal(t, int64(1), res.NumError) 285 286 verified := 0 287 batch.ForEach(func( 288 idx int, 289 entry WriteBatchEntry, 290 _ doc.Metadata, 291 result WriteBatchEntryResult, 292 ) { 293 verified++ 294 if idx == 0 { 295 require.NoError(t, result.Err) 296 } else { 297 require.Error(t, result.Err) 298 require.Equal(t, doc.ErrEmptyDocument, result.Err) 299 } 300 }) 301 require.Equal(t, 2, verified) 302 } 303 304 func TestBlockWritePartialFailure(t *testing.T) { 305 ctrl := gomock.NewController(t) 306 defer ctrl.Finish() 307 308 md := newTestNSMetadata(t) 309 blockSize := time.Hour 310 311 now := xtime.Now() 312 blockStart := now.Truncate(blockSize) 313 314 nowNotBlockStartAligned := now. 315 Truncate(blockSize). 316 Add(time.Minute) 317 318 blk, err := NewBlock(blockStart, md, BlockOptions{}, 319 namespace.NewRuntimeOptionsManager("foo"), testOpts) 320 require.NoError(t, err) 321 b, ok := blk.(*block) 322 require.True(t, ok) 323 324 h1 := doc.NewMockOnIndexSeries(ctrl) 325 h1.EXPECT().OnIndexFinalize(blockStart) 326 h1.EXPECT().OnIndexSuccess(blockStart) 327 328 h2 := doc.NewMockOnIndexSeries(ctrl) 329 h2.EXPECT().OnIndexFinalize(blockStart) 330 331 batch := NewWriteBatch(testWriteBatchOptionsWithBlockSize(blockSize)) 332 batch.Append(WriteBatchEntry{ 333 Timestamp: nowNotBlockStartAligned, 334 OnIndexSeries: h1, 335 }, testDoc1()) 336 batch.Append(WriteBatchEntry{ 337 Timestamp: nowNotBlockStartAligned, 338 OnIndexSeries: h2, 339 }, doc.Metadata{}) 340 341 res, err := b.WriteBatch(batch) 342 require.Error(t, err) 343 require.Equal(t, int64(1), res.NumSuccess) 344 require.Equal(t, int64(1), res.NumError) 345 346 verified := 0 347 batch.ForEach(func( 348 idx int, 349 entry WriteBatchEntry, 350 doc doc.Metadata, 351 result WriteBatchEntryResult, 352 ) { 353 verified++ 354 if idx == 0 { 355 require.NoError(t, result.Err) 356 } else { 357 require.Error(t, result.Err) 358 } 359 }) 360 require.Equal(t, 2, verified) 361 } 362 363 func TestBlockQueryAfterClose(t *testing.T) { 364 testMD := newTestNSMetadata(t) 365 start := xtime.Now().Truncate(time.Hour) 366 b, err := NewBlock(start, testMD, BlockOptions{}, 367 namespace.NewRuntimeOptionsManager("foo"), testOpts) 368 require.NoError(t, err) 369 370 require.Equal(t, start, b.StartTime()) 371 require.Equal(t, start.Add(time.Hour), b.EndTime()) 372 require.NoError(t, b.Close()) 373 374 _, err = b.QueryIter(context.NewBackground(), defaultQuery) 375 require.Error(t, err) 376 } 377 378 func TestBlockQueryWithCancelledQuery(t *testing.T) { 379 ctrl := gomock.NewController(t) 380 defer ctrl.Finish() 381 382 testMD := newTestNSMetadata(t) 383 start := xtime.Now().Truncate(time.Hour) 384 blk, err := NewBlock(start, testMD, BlockOptions{}, 385 namespace.NewRuntimeOptionsManager("foo"), testOpts) 386 require.NoError(t, err) 387 388 b, ok := blk.(*block) 389 require.True(t, ok) 390 391 exec := search.NewMockExecutor(ctrl) 392 b.newExecutorWithRLockFn = func() (search.Executor, error) { 393 return exec, nil 394 } 395 396 dIter := doc.NewMockQueryDocIterator(ctrl) 397 gomock.InOrder( 398 exec.EXPECT().Execute(gomock.Any(), gomock.Any()).Return(dIter, nil), 399 dIter.EXPECT().Next().Return(true), 400 dIter.EXPECT().Done().Return(false), 401 exec.EXPECT().Close().Return(nil), 402 ) 403 404 require.Equal(t, start, b.StartTime()) 405 require.Equal(t, start.Add(time.Hour), b.EndTime()) 406 407 // Precancel query. 408 stdCtx, cancel := stdlibctx.WithCancel(stdlibctx.Background()) 409 cancel() 410 ctx := context.NewWithGoContext(stdCtx) 411 defer ctx.BlockingClose() 412 413 results := NewQueryResults(nil, QueryResultsOptions{}, testOpts) 414 415 queryIter, err := b.QueryIter(ctx, defaultQuery) 416 require.NoError(t, err) 417 err = b.QueryWithIter(ctx, QueryOptions{}, queryIter, results, time.Now().Add(time.Minute), emptyLogFields) 418 require.Error(t, err) 419 require.Equal(t, stdlibctx.Canceled, err) 420 } 421 422 func TestBlockQueryExecutorError(t *testing.T) { 423 testMD := newTestNSMetadata(t) 424 start := xtime.Now().Truncate(time.Hour) 425 blk, err := NewBlock(start, testMD, BlockOptions{}, 426 namespace.NewRuntimeOptionsManager("foo"), testOpts) 427 require.NoError(t, err) 428 429 b, ok := blk.(*block) 430 require.True(t, ok) 431 432 b.newExecutorWithRLockFn = func() (search.Executor, error) { 433 return nil, fmt.Errorf("random-err") 434 } 435 436 _, err = b.QueryIter(context.NewBackground(), defaultQuery) 437 require.Error(t, err) 438 } 439 440 func TestBlockQuerySegmentReaderError(t *testing.T) { 441 ctrl := gomock.NewController(t) 442 defer ctrl.Finish() 443 444 testMD := newTestNSMetadata(t) 445 start := xtime.Now().Truncate(time.Hour) 446 blk, err := NewBlock(start, testMD, BlockOptions{}, 447 namespace.NewRuntimeOptionsManager("foo"), testOpts) 448 require.NoError(t, err) 449 450 b, ok := blk.(*block) 451 require.True(t, ok) 452 453 seg := segment.NewMockSegment(ctrl) 454 b.mutableSegments.foregroundSegments = []*readableSeg{newReadableSeg(seg, testOpts)} 455 randErr := fmt.Errorf("random-err") 456 seg.EXPECT().Reader().Return(nil, randErr) 457 458 _, err = b.QueryIter(context.NewBackground(), defaultQuery) 459 require.Equal(t, randErr, err) 460 } 461 462 func TestBlockQueryAddResultsSegmentsError(t *testing.T) { 463 ctrl := gomock.NewController(t) 464 defer ctrl.Finish() 465 466 testMD := newTestNSMetadata(t) 467 start := xtime.Now().Truncate(time.Hour) 468 blk, err := NewBlock(start, testMD, BlockOptions{}, 469 namespace.NewRuntimeOptionsManager("foo"), testOpts) 470 require.NoError(t, err) 471 472 b, ok := blk.(*block) 473 require.True(t, ok) 474 475 seg1 := segment.NewMockMutableSegment(ctrl) 476 seg2 := segment.NewMockMutableSegment(ctrl) 477 seg3 := segment.NewMockMutableSegment(ctrl) 478 479 b.mutableSegments.foregroundSegments = []*readableSeg{newReadableSeg(seg1, testOpts)} 480 b.shardRangesSegmentsByVolumeType = map[idxpersist.IndexVolumeType][]blockShardRangesSegments{ 481 idxpersist.DefaultIndexVolumeType: { 482 {segments: []segment.Segment{seg2, seg3}}, 483 }, 484 } 485 486 r1 := segment.NewMockReader(ctrl) 487 seg1.EXPECT().Reader().Return(r1, nil) 488 r1.EXPECT().Close().Return(nil) 489 490 r2 := segment.NewMockReader(ctrl) 491 seg2.EXPECT().Reader().Return(r2, nil) 492 r2.EXPECT().Close().Return(nil) 493 494 randErr := fmt.Errorf("random-err") 495 seg3.EXPECT().Reader().Return(nil, randErr) 496 497 _, err = b.QueryIter(context.NewBackground(), defaultQuery) 498 require.Equal(t, randErr, err) 499 } 500 501 func TestBlockMockQueryExecutorExecError(t *testing.T) { 502 ctrl := gomock.NewController(t) 503 defer ctrl.Finish() 504 505 testMD := newTestNSMetadata(t) 506 start := xtime.Now().Truncate(time.Hour) 507 blk, err := NewBlock(start, testMD, BlockOptions{}, 508 namespace.NewRuntimeOptionsManager("foo"), testOpts) 509 require.NoError(t, err) 510 511 b, ok := blk.(*block) 512 require.True(t, ok) 513 514 // dIter:= doc.NewMockIterator(ctrl) 515 exec := search.NewMockExecutor(ctrl) 516 b.newExecutorWithRLockFn = func() (search.Executor, error) { 517 return exec, nil 518 } 519 gomock.InOrder( 520 exec.EXPECT().Execute(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("randomerr")), 521 exec.EXPECT().Close(), 522 ) 523 524 _, err = b.QueryIter(context.NewBackground(), defaultQuery) 525 require.Error(t, err) 526 } 527 528 func TestBlockMockQueryExecutorExecIterErr(t *testing.T) { 529 ctrl := gomock.NewController(t) 530 defer ctrl.Finish() 531 532 testMD := newTestNSMetadata(t) 533 start := xtime.Now().Truncate(time.Hour) 534 blk, err := NewBlock(start, testMD, BlockOptions{}, 535 namespace.NewRuntimeOptionsManager("foo"), testOpts) 536 require.NoError(t, err) 537 538 b, ok := blk.(*block) 539 require.True(t, ok) 540 541 exec := search.NewMockExecutor(ctrl) 542 b.newExecutorWithRLockFn = func() (search.Executor, error) { 543 return exec, nil 544 } 545 546 dIter := doc.NewMockQueryDocIterator(ctrl) 547 gomock.InOrder( 548 exec.EXPECT().Execute(gomock.Any(), gomock.Any()).Return(dIter, nil), 549 dIter.EXPECT().Next().Return(true), 550 dIter.EXPECT().Current().Return(doc.NewDocumentFromMetadata(testDoc1())), 551 dIter.EXPECT().Next().Return(false), 552 dIter.EXPECT().Err().Return(fmt.Errorf("randomerr")), 553 dIter.EXPECT().Done().Return(true), 554 exec.EXPECT().Close(), 555 ) 556 557 ctx := context.NewBackground() 558 559 queryIter, err := b.QueryIter(ctx, defaultQuery) 560 require.NoError(t, err) 561 562 err = b.QueryWithIter(ctx, QueryOptions{}, queryIter, 563 NewQueryResults(nil, QueryResultsOptions{}, testOpts), time.Now().Add(time.Minute), emptyLogFields) 564 require.Error(t, err) 565 566 // NB(r): Make sure to call finalizers blockingly (to finish 567 // the expected close calls) 568 ctx.BlockingClose() 569 } 570 571 func TestBlockMockQueryExecutorExecLimit(t *testing.T) { 572 ctrl := gomock.NewController(t) 573 defer ctrl.Finish() 574 575 testMD := newTestNSMetadata(t) 576 start := xtime.Now().Truncate(time.Hour) 577 blk, err := NewBlock(start, testMD, BlockOptions{}, 578 namespace.NewRuntimeOptionsManager("foo"), testOpts) 579 require.NoError(t, err) 580 581 b, ok := blk.(*block) 582 require.True(t, ok) 583 584 exec := search.NewMockExecutor(ctrl) 585 b.newExecutorWithRLockFn = func() (search.Executor, error) { 586 return exec, nil 587 } 588 589 dIter := doc.NewMockQueryDocIterator(ctrl) 590 gomock.InOrder( 591 exec.EXPECT().Execute(gomock.Any(), gomock.Any()).Return(dIter, nil), 592 dIter.EXPECT().Next().Return(true), 593 dIter.EXPECT().Current().Return(doc.NewDocumentFromMetadata(testDoc1())), 594 dIter.EXPECT().Next().Return(true), 595 dIter.EXPECT().Err().Return(nil), 596 dIter.EXPECT().Done().Return(true), 597 exec.EXPECT().Close().Return(nil), 598 ) 599 limit := 1 600 results := NewQueryResults(nil, 601 QueryResultsOptions{SizeLimit: limit}, testOpts) 602 603 ctx := context.NewBackground() 604 605 queryIter, err := b.QueryIter(ctx, defaultQuery) 606 require.NoError(t, err) 607 err = b.QueryWithIter(ctx, QueryOptions{SeriesLimit: limit}, queryIter, results, time.Now().Add(time.Minute), 608 emptyLogFields) 609 require.NoError(t, err) 610 611 require.Equal(t, 1, results.Map().Len()) 612 d, ok := results.Map().Get(testDoc1().ID) 613 require.True(t, ok) 614 615 t1 := test.DocumentToTagIter(t, d) 616 require.True(t, ident.NewTagIterMatcher( 617 ident.MustNewTagStringsIterator("bar", "baz")).Matches( 618 t1)) 619 620 // NB(r): Make sure to call finalizers blockingly (to finish 621 // the expected close calls) 622 ctx.BlockingClose() 623 } 624 625 func TestBlockMockQuerySeriesLimitNonExhaustive(t *testing.T) { 626 ctrl := gomock.NewController(t) 627 defer ctrl.Finish() 628 629 testMD := newTestNSMetadata(t) 630 start := xtime.Now().Truncate(time.Hour) 631 blk, err := NewBlock(start, testMD, BlockOptions{}, 632 namespace.NewRuntimeOptionsManager("foo"), testOpts) 633 require.NoError(t, err) 634 635 b, ok := blk.(*block) 636 require.True(t, ok) 637 638 exec := search.NewMockExecutor(ctrl) 639 b.newExecutorWithRLockFn = func() (search.Executor, error) { 640 return exec, nil 641 } 642 643 dIter := doc.NewMockQueryDocIterator(ctrl) 644 gomock.InOrder( 645 exec.EXPECT().Execute(gomock.Any(), gomock.Any()).Return(dIter, nil), 646 dIter.EXPECT().Next().Return(true), 647 dIter.EXPECT().Current().Return(doc.NewDocumentFromMetadata(testDoc1())), 648 dIter.EXPECT().Next().Return(true), 649 dIter.EXPECT().Err().Return(nil), 650 dIter.EXPECT().Done().Return(true), 651 exec.EXPECT().Close().Return(nil), 652 ) 653 limit := 1 654 results := NewQueryResults(nil, QueryResultsOptions{SizeLimit: 1}, testOpts) 655 656 ctx := context.NewBackground() 657 658 queryIter, err := b.QueryIter(ctx, defaultQuery) 659 require.NoError(t, err) 660 err = b.QueryWithIter(ctx, QueryOptions{SeriesLimit: limit}, queryIter, results, time.Now().Add(time.Minute), 661 emptyLogFields) 662 require.NoError(t, err) 663 664 require.Equal(t, 1, results.Map().Len()) 665 666 d, ok := results.Map().Get(testDoc1().ID) 667 require.True(t, ok) 668 669 t1 := test.DocumentToTagIter(t, d) 670 require.True(t, ident.NewTagIterMatcher( 671 ident.MustNewTagStringsIterator("bar", "baz")).Matches( 672 t1)) 673 674 // NB(r): Make sure to call finalizers blockingly (to finish 675 // the expected close calls) 676 ctx.BlockingClose() 677 } 678 679 func TestBlockMockQuerySeriesLimitExhaustive(t *testing.T) { 680 ctrl := gomock.NewController(t) 681 defer ctrl.Finish() 682 683 testMD := newTestNSMetadata(t) 684 start := xtime.Now().Truncate(time.Hour) 685 blk, err := NewBlock(start, testMD, BlockOptions{}, 686 namespace.NewRuntimeOptionsManager("foo"), testOpts) 687 require.NoError(t, err) 688 689 b, ok := blk.(*block) 690 require.True(t, ok) 691 692 exec := search.NewMockExecutor(ctrl) 693 b.newExecutorWithRLockFn = func() (search.Executor, error) { 694 return exec, nil 695 } 696 697 dIter := doc.NewMockQueryDocIterator(ctrl) 698 gomock.InOrder( 699 exec.EXPECT().Execute(gomock.Any(), gomock.Any()).Return(dIter, nil), 700 dIter.EXPECT().Next().Return(true), 701 dIter.EXPECT().Current().Return(doc.NewDocumentFromMetadata(testDoc1())), 702 dIter.EXPECT().Next().Return(false), 703 dIter.EXPECT().Err().Return(nil), 704 dIter.EXPECT().Done().Return(true), 705 exec.EXPECT().Close().Return(nil), 706 ) 707 limit := 2 708 results := NewQueryResults(nil, 709 QueryResultsOptions{SizeLimit: limit}, testOpts) 710 711 ctx := context.NewBackground() 712 713 queryIter, err := b.QueryIter(ctx, defaultQuery) 714 require.NoError(t, err) 715 err = b.QueryWithIter(ctx, QueryOptions{SeriesLimit: limit}, queryIter, results, time.Now().Add(time.Minute), 716 emptyLogFields) 717 require.NoError(t, err) 718 719 rMap := results.Map() 720 require.Equal(t, 1, rMap.Len()) 721 d, ok := rMap.Get(testDoc1().ID) 722 require.True(t, ok) 723 t1 := test.DocumentToTagIter(t, d) 724 require.True(t, ident.NewTagIterMatcher( 725 ident.MustNewTagStringsIterator("bar", "baz")).Matches( 726 t1)) 727 728 // NB(r): Make sure to call finalizers blockingly (to finish 729 // the expected close calls) 730 ctx.BlockingClose() 731 } 732 733 func TestBlockMockQueryDocsLimitNonExhaustive(t *testing.T) { 734 ctrl := gomock.NewController(t) 735 defer ctrl.Finish() 736 737 testMD := newTestNSMetadata(t) 738 start := xtime.Now().Truncate(time.Hour) 739 blk, err := NewBlock(start, testMD, BlockOptions{}, 740 namespace.NewRuntimeOptionsManager("foo"), testOpts) 741 require.NoError(t, err) 742 743 b, ok := blk.(*block) 744 require.True(t, ok) 745 746 exec := search.NewMockExecutor(ctrl) 747 b.newExecutorWithRLockFn = func() (search.Executor, error) { 748 return exec, nil 749 } 750 751 dIter := doc.NewMockQueryDocIterator(ctrl) 752 gomock.InOrder( 753 exec.EXPECT().Execute(gomock.Any(), gomock.Any()).Return(dIter, nil), 754 dIter.EXPECT().Next().Return(true), 755 dIter.EXPECT().Current().Return(doc.NewDocumentFromMetadata(testDoc1())), 756 dIter.EXPECT().Next().Return(true), 757 dIter.EXPECT().Err().Return(nil), 758 dIter.EXPECT().Done().Return(true), 759 exec.EXPECT().Close().Return(nil), 760 ) 761 docsLimit := 1 762 results := NewQueryResults(nil, QueryResultsOptions{}, testOpts) 763 764 ctx := context.NewBackground() 765 766 queryIter, err := b.QueryIter(ctx, defaultQuery) 767 require.NoError(t, err) 768 err = b.QueryWithIter(ctx, QueryOptions{DocsLimit: docsLimit}, queryIter, results, time.Now().Add(time.Minute), 769 emptyLogFields) 770 require.NoError(t, err) 771 772 require.Equal(t, 1, results.Map().Len()) 773 d, ok := results.Map().Get(testDoc1().ID) 774 require.True(t, ok) 775 t1 := test.DocumentToTagIter(t, d) 776 require.True(t, ident.NewTagIterMatcher( 777 ident.MustNewTagStringsIterator("bar", "baz")).Matches( 778 t1)) 779 780 // NB(r): Make sure to call finalizers blockingly (to finish 781 // the expected close calls) 782 ctx.BlockingClose() 783 } 784 785 func TestBlockMockQueryDocsLimitExhaustive(t *testing.T) { 786 ctrl := gomock.NewController(t) 787 defer ctrl.Finish() 788 789 testMD := newTestNSMetadata(t) 790 start := xtime.Now().Truncate(time.Hour) 791 blk, err := NewBlock(start, testMD, BlockOptions{}, 792 namespace.NewRuntimeOptionsManager("foo"), testOpts) 793 require.NoError(t, err) 794 795 b, ok := blk.(*block) 796 require.True(t, ok) 797 798 exec := search.NewMockExecutor(ctrl) 799 b.newExecutorWithRLockFn = func() (search.Executor, error) { 800 return exec, nil 801 } 802 803 dIter := doc.NewMockQueryDocIterator(ctrl) 804 gomock.InOrder( 805 exec.EXPECT().Execute(gomock.Any(), gomock.Any()).Return(dIter, nil), 806 dIter.EXPECT().Next().Return(true), 807 dIter.EXPECT().Current().Return(doc.NewDocumentFromMetadata(testDoc1())), 808 dIter.EXPECT().Next().Return(false), 809 dIter.EXPECT().Err().Return(nil), 810 dIter.EXPECT().Done().Return(false), 811 exec.EXPECT().Close().Return(nil), 812 ) 813 docsLimit := 2 814 results := NewQueryResults(nil, 815 QueryResultsOptions{}, testOpts) 816 817 ctx := context.NewBackground() 818 819 queryIter, err := b.QueryIter(ctx, defaultQuery) 820 require.NoError(t, err) 821 err = b.QueryWithIter(ctx, QueryOptions{DocsLimit: docsLimit}, queryIter, results, time.Now().Add(time.Minute), 822 emptyLogFields) 823 require.NoError(t, err) 824 825 rMap := results.Map() 826 require.Equal(t, 1, rMap.Len()) 827 d, ok := rMap.Get(testDoc1().ID) 828 require.True(t, ok) 829 t1 := test.DocumentToTagIter(t, d) 830 require.True(t, ident.NewTagIterMatcher( 831 ident.MustNewTagStringsIterator("bar", "baz")).Matches( 832 t1)) 833 834 // NB(r): Make sure to call finalizers blockingly (to finish 835 // the expected close calls) 836 ctx.BlockingClose() 837 } 838 839 func TestBlockMockQueryMergeResultsMapLimit(t *testing.T) { 840 ctrl := gomock.NewController(t) 841 defer ctrl.Finish() 842 843 testMD := newTestNSMetadata(t) 844 start := xtime.Now().Truncate(time.Hour) 845 blk, err := NewBlock(start, testMD, BlockOptions{}, 846 namespace.NewRuntimeOptionsManager("foo"), testOpts) 847 require.NoError(t, err) 848 849 b, ok := blk.(*block) 850 require.True(t, ok) 851 require.NoError(t, b.Seal()) 852 853 exec := search.NewMockExecutor(ctrl) 854 b.newExecutorWithRLockFn = func() (search.Executor, error) { 855 return exec, nil 856 } 857 858 limit := 1 859 results := NewQueryResults(nil, 860 QueryResultsOptions{SizeLimit: limit}, testOpts) 861 _, _, err = results.AddDocuments([]doc.Document{ 862 doc.NewDocumentFromMetadata(testDoc1()), 863 }) 864 require.NoError(t, err) 865 866 dIter := doc.NewMockQueryDocIterator(ctrl) 867 gomock.InOrder( 868 exec.EXPECT().Execute(gomock.Any(), gomock.Any()).Return(dIter, nil), 869 dIter.EXPECT().Next().Return(true), 870 dIter.EXPECT().Err().Return(nil), 871 dIter.EXPECT().Done().Return(true), 872 exec.EXPECT().Close().Return(nil), 873 ) 874 875 ctx := context.NewBackground() 876 877 queryIter, err := b.QueryIter(ctx, defaultQuery) 878 require.NoError(t, err) 879 err = b.QueryWithIter(ctx, QueryOptions{SeriesLimit: limit}, queryIter, results, time.Now().Add(time.Minute), 880 emptyLogFields) 881 require.NoError(t, err) 882 883 rMap := results.Map() 884 require.Equal(t, 1, rMap.Len()) 885 d, ok := rMap.Get(testDoc1().ID) 886 require.True(t, ok) 887 t1 := test.DocumentToTagIter(t, d) 888 require.True(t, ident.NewTagIterMatcher( 889 ident.MustNewTagStringsIterator("bar", "baz")).Matches( 890 t1)) 891 892 // NB(r): Make sure to call finalizers blockingly (to finish 893 // the expected close calls) 894 ctx.BlockingClose() 895 } 896 897 func TestBlockQueryWithIterMetrics(t *testing.T) { 898 block, err := NewBlock(xtime.Now().Truncate(time.Hour), 899 newTestNSMetadata(t), BlockOptions{}, 900 namespace.NewRuntimeOptionsManager("foo"), 901 testOpts) 902 require.NoError(t, err) 903 904 ctrl := gomock.NewController(t) 905 defer ctrl.Finish() 906 907 ctx := context.NewBackground() 908 909 results := NewQueryResults(nil, QueryResultsOptions{}, testOpts) 910 _, _, err = results.AddDocuments([]doc.Document{ 911 doc.NewDocumentFromMetadata(testDoc1()), 912 doc.NewDocumentFromMetadata(testDoc2()), 913 }) 914 require.NoError(t, err) 915 916 queryIter := NewMockQueryIterator(ctrl) 917 notImportantInt := 0 918 gomock.InOrder( 919 queryIter.EXPECT().Next(ctx).Return(true), 920 queryIter.EXPECT().Current().Return(doc.NewDocumentFromMetadata(testDoc3())), 921 queryIter.EXPECT().Next(ctx).Return(false), 922 queryIter.EXPECT().Err().Return(nil), 923 924 // Iterator returns one document, so expect numOfSeries that have been added to the results object to be 1 925 // and number of documents to be 1 926 queryIter.EXPECT().AddSeries(gomock.Eq(1)), 927 queryIter.EXPECT().AddDocs(gomock.Eq(1)), 928 929 queryIter.EXPECT().Done().Return(true), 930 queryIter.EXPECT().Counts().Return(notImportantInt, notImportantInt), 931 ) 932 933 err = block.QueryWithIter(ctx, QueryOptions{}, queryIter, results, time.Now().Add(time.Minute), 934 emptyLogFields) 935 require.NoError(t, err) 936 } 937 938 func TestBlockMockQueryMergeResultsDupeID(t *testing.T) { 939 ctrl := gomock.NewController(t) 940 defer ctrl.Finish() 941 942 testMD := newTestNSMetadata(t) 943 start := xtime.Now().Truncate(time.Hour) 944 blk, err := NewBlock(start, testMD, BlockOptions{}, 945 namespace.NewRuntimeOptionsManager("foo"), testOpts) 946 require.NoError(t, err) 947 948 b, ok := blk.(*block) 949 require.True(t, ok) 950 951 exec := search.NewMockExecutor(ctrl) 952 b.newExecutorWithRLockFn = func() (search.Executor, error) { 953 return exec, nil 954 } 955 956 results := NewQueryResults(nil, QueryResultsOptions{}, testOpts) 957 _, _, err = results.AddDocuments([]doc.Document{ 958 doc.NewDocumentFromMetadata(testDoc1()), 959 }) 960 require.NoError(t, err) 961 962 dIter := doc.NewMockQueryDocIterator(ctrl) 963 gomock.InOrder( 964 exec.EXPECT().Execute(gomock.Any(), gomock.Any()).Return(dIter, nil), 965 dIter.EXPECT().Next().Return(true), 966 dIter.EXPECT().Current().Return(doc.NewDocumentFromMetadata(testDoc1DupeID())), 967 dIter.EXPECT().Next().Return(true), 968 dIter.EXPECT().Current().Return(doc.NewDocumentFromMetadata(testDoc2())), 969 dIter.EXPECT().Next().Return(false), 970 dIter.EXPECT().Err().Return(nil), 971 dIter.EXPECT().Done().Return(true), 972 exec.EXPECT().Close().Return(nil), 973 ) 974 975 ctx := context.NewBackground() 976 977 queryIter, err := b.QueryIter(ctx, defaultQuery) 978 require.NoError(t, err) 979 err = b.QueryWithIter(ctx, QueryOptions{}, queryIter, results, time.Now().Add(time.Minute), 980 emptyLogFields) 981 require.NoError(t, err) 982 983 rMap := results.Map() 984 require.Equal(t, 2, rMap.Len()) 985 d, ok := rMap.Get(testDoc1().ID) 986 require.True(t, ok) 987 t1 := test.DocumentToTagIter(t, d) 988 require.True(t, ident.NewTagIterMatcher( 989 ident.MustNewTagStringsIterator("bar", "baz")).Matches( 990 t1)) 991 992 d, ok = rMap.Get(testDoc2().ID) 993 require.True(t, ok) 994 t2 := test.DocumentToTagIter(t, d) 995 require.True(t, ident.NewTagIterMatcher( 996 ident.MustNewTagStringsIterator("bar", "baz", "some", "more")).Matches( 997 t2)) 998 999 // NB(r): Make sure to call finalizers blockingly (to finish 1000 // the expected close calls) 1001 ctx.BlockingClose() 1002 } 1003 1004 func TestBlockAddResultsAddsSegment(t *testing.T) { 1005 ctrl := gomock.NewController(t) 1006 defer ctrl.Finish() 1007 1008 testMD := newTestNSMetadata(t) 1009 start := xtime.Now().Truncate(time.Hour) 1010 blk, err := NewBlock(start, testMD, BlockOptions{}, 1011 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1012 require.NoError(t, err) 1013 1014 b, ok := blk.(*block) 1015 require.True(t, ok) 1016 1017 seg1 := segment.NewMockMutableSegment(ctrl) 1018 results := result.NewIndexBlockByVolumeType(start) 1019 results.SetBlock(idxpersist.DefaultIndexVolumeType, 1020 result.NewIndexBlock([]result.Segment{result.NewSegment(seg1, true)}, 1021 result.NewShardTimeRangesFromRange(start, start.Add(time.Hour), 1, 2, 3))) 1022 require.NoError(t, b.AddResults(results)) 1023 shardRangesSegments := b.shardRangesSegmentsByVolumeType[idxpersist.DefaultIndexVolumeType] 1024 require.Equal(t, 1, len(shardRangesSegments)) 1025 1026 require.Equal(t, seg1, shardRangesSegments[0].segments[0]) 1027 } 1028 1029 func TestBlockAddResultsAfterCloseFails(t *testing.T) { 1030 ctrl := gomock.NewController(t) 1031 defer ctrl.Finish() 1032 1033 testMD := newTestNSMetadata(t) 1034 start := xtime.Now().Truncate(time.Hour) 1035 blk, err := NewBlock(start, testMD, BlockOptions{}, 1036 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1037 require.NoError(t, err) 1038 require.NoError(t, blk.Close()) 1039 1040 seg1 := segment.NewMockMutableSegment(ctrl) 1041 results := result.NewIndexBlockByVolumeType(start) 1042 results.SetBlock(idxpersist.DefaultIndexVolumeType, 1043 result.NewIndexBlock([]result.Segment{result.NewSegment(seg1, true)}, 1044 result.NewShardTimeRangesFromRange(start, start.Add(time.Hour), 1, 2, 3))) 1045 require.Error(t, blk.AddResults(results)) 1046 } 1047 1048 func TestBlockAddResultsAfterSealWorks(t *testing.T) { 1049 ctrl := gomock.NewController(t) 1050 defer ctrl.Finish() 1051 1052 testMD := newTestNSMetadata(t) 1053 start := xtime.Now().Truncate(time.Hour) 1054 blk, err := NewBlock(start, testMD, BlockOptions{}, 1055 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1056 require.NoError(t, err) 1057 require.NoError(t, blk.Seal()) 1058 1059 b, ok := blk.(*block) 1060 require.True(t, ok) 1061 1062 seg1 := segment.NewMockMutableSegment(ctrl) 1063 results := result.NewIndexBlockByVolumeType(start) 1064 results.SetBlock(idxpersist.DefaultIndexVolumeType, 1065 result.NewIndexBlock([]result.Segment{result.NewSegment(seg1, true)}, 1066 result.NewShardTimeRangesFromRange(start, start.Add(time.Hour), 1, 2, 3))) 1067 require.NoError(t, b.AddResults(results)) 1068 shardRangesSegments := b.shardRangesSegmentsByVolumeType[idxpersist.DefaultIndexVolumeType] 1069 require.Equal(t, 1, len(shardRangesSegments)) 1070 1071 require.Equal(t, seg1, shardRangesSegments[0].segments[0]) 1072 } 1073 1074 func TestBlockTickSingleSegment(t *testing.T) { 1075 ctrl := gomock.NewController(t) 1076 defer ctrl.Finish() 1077 1078 testMD := newTestNSMetadata(t) 1079 start := xtime.Now().Truncate(time.Hour) 1080 blk, err := NewBlock(start, testMD, BlockOptions{}, 1081 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1082 require.NoError(t, err) 1083 1084 b, ok := blk.(*block) 1085 require.True(t, ok) 1086 1087 seg1 := segment.NewMockSegment(ctrl) 1088 b.mutableSegments.foregroundSegments = []*readableSeg{newReadableSeg(seg1, testOpts)} 1089 seg1.EXPECT().Size().Return(int64(10)) 1090 1091 result, err := blk.Tick(nil) 1092 require.NoError(t, err) 1093 require.Equal(t, int64(1), result.NumSegments) 1094 require.Equal(t, int64(10), result.NumDocs) 1095 } 1096 1097 func TestBlockTickMultipleSegment(t *testing.T) { 1098 ctrl := gomock.NewController(t) 1099 defer ctrl.Finish() 1100 1101 testMD := newTestNSMetadata(t) 1102 start := xtime.Now().Truncate(time.Hour) 1103 blk, err := NewBlock(start, testMD, BlockOptions{}, 1104 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1105 require.NoError(t, err) 1106 1107 b, ok := blk.(*block) 1108 require.True(t, ok) 1109 1110 seg1 := segment.NewMockSegment(ctrl) 1111 b.mutableSegments.foregroundSegments = []*readableSeg{newReadableSeg(seg1, testOpts)} 1112 seg1.EXPECT().Size().Return(int64(10)) 1113 1114 seg2 := segment.NewMockMutableSegment(ctrl) 1115 seg2.EXPECT().Size().Return(int64(20)) 1116 results := result.NewIndexBlockByVolumeType(start) 1117 results.SetBlock(idxpersist.DefaultIndexVolumeType, 1118 result.NewIndexBlock([]result.Segment{result.NewSegment(seg2, true)}, 1119 result.NewShardTimeRangesFromRange(start, start.Add(time.Hour), 1, 2, 3))) 1120 require.NoError(t, b.AddResults(results)) 1121 1122 result, err := blk.Tick(nil) 1123 require.NoError(t, err) 1124 require.Equal(t, int64(2), result.NumSegments) 1125 require.Equal(t, int64(30), result.NumDocs) 1126 } 1127 1128 func TestBlockTickAfterSeal(t *testing.T) { 1129 ctrl := gomock.NewController(t) 1130 defer ctrl.Finish() 1131 1132 testMD := newTestNSMetadata(t) 1133 start := xtime.Now().Truncate(time.Hour) 1134 blk, err := NewBlock(start, testMD, BlockOptions{}, 1135 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1136 require.NoError(t, err) 1137 require.NoError(t, blk.Seal()) 1138 1139 b, ok := blk.(*block) 1140 require.True(t, ok) 1141 1142 seg1 := segment.NewMockSegment(ctrl) 1143 b.mutableSegments.foregroundSegments = []*readableSeg{newReadableSeg(seg1, testOpts)} 1144 seg1.EXPECT().Size().Return(int64(10)) 1145 1146 result, err := blk.Tick(nil) 1147 require.NoError(t, err) 1148 require.Equal(t, int64(1), result.NumSegments) 1149 require.Equal(t, int64(10), result.NumDocs) 1150 } 1151 1152 func TestBlockTickAfterClose(t *testing.T) { 1153 ctrl := gomock.NewController(t) 1154 defer ctrl.Finish() 1155 1156 testMD := newTestNSMetadata(t) 1157 start := xtime.Now().Truncate(time.Hour) 1158 blk, err := NewBlock(start, testMD, BlockOptions{}, 1159 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1160 require.NoError(t, err) 1161 require.NoError(t, blk.Close()) 1162 1163 _, err = blk.Tick(nil) 1164 require.Error(t, err) 1165 } 1166 1167 func TestBlockAddResultsRangeCheck(t *testing.T) { 1168 ctrl := gomock.NewController(t) 1169 defer ctrl.Finish() 1170 1171 testMD := newTestNSMetadata(t) 1172 start := xtime.Now().Truncate(time.Hour) 1173 blk, err := NewBlock(start, testMD, BlockOptions{}, 1174 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1175 require.NoError(t, err) 1176 1177 b, ok := blk.(*block) 1178 require.True(t, ok) 1179 1180 seg1 := segment.NewMockMutableSegment(ctrl) 1181 results := result.NewIndexBlockByVolumeType(start) 1182 results.SetBlock(idxpersist.DefaultIndexVolumeType, 1183 result.NewIndexBlock([]result.Segment{result.NewSegment(seg1, true)}, 1184 result.NewShardTimeRangesFromRange(start.Add(-1*time.Minute), start, 1, 2, 3))) 1185 require.Error(t, b.AddResults(results)) 1186 1187 results = result.NewIndexBlockByVolumeType(start) 1188 results.SetBlock(idxpersist.DefaultIndexVolumeType, 1189 result.NewIndexBlock([]result.Segment{result.NewSegment(seg1, true)}, 1190 result.NewShardTimeRangesFromRange(start, start.Add(2*time.Hour), 1, 2, 3))) 1191 require.Error(t, b.AddResults(results)) 1192 } 1193 1194 func TestBlockAddResultsCoversCurrentData(t *testing.T) { 1195 ctrl := gomock.NewController(t) 1196 defer ctrl.Finish() 1197 1198 testMD := newTestNSMetadata(t) 1199 start := xtime.Now().Truncate(time.Hour) 1200 blk, err := NewBlock(start, testMD, BlockOptions{}, 1201 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1202 require.NoError(t, err) 1203 1204 b, ok := blk.(*block) 1205 require.True(t, ok) 1206 1207 seg1 := segment.NewMockMutableSegment(ctrl) 1208 results := result.NewIndexBlockByVolumeType(start) 1209 results.SetBlock(idxpersist.DefaultIndexVolumeType, 1210 result.NewIndexBlock([]result.Segment{result.NewSegment(seg1, true)}, 1211 result.NewShardTimeRangesFromRange(start, start.Add(time.Hour), 1, 2, 3))) 1212 require.NoError(t, b.AddResults(results)) 1213 1214 seg2 := segment.NewMockMutableSegment(ctrl) 1215 seg1.EXPECT().Close().Return(nil) 1216 results = result.NewIndexBlockByVolumeType(start) 1217 results.SetBlock(idxpersist.DefaultIndexVolumeType, 1218 result.NewIndexBlock([]result.Segment{result.NewSegment(seg2, true)}, 1219 result.NewShardTimeRangesFromRange(start, start.Add(time.Hour), 1, 2, 3, 4))) 1220 require.NoError(t, b.AddResults(results)) 1221 1222 require.NoError(t, b.Seal()) 1223 seg2.EXPECT().Close().Return(nil) 1224 require.NoError(t, b.Close()) 1225 } 1226 1227 func TestBlockAddResultsDoesNotCoverCurrentData(t *testing.T) { 1228 ctrl := gomock.NewController(t) 1229 defer ctrl.Finish() 1230 1231 testMD := newTestNSMetadata(t) 1232 start := xtime.Now().Truncate(time.Hour) 1233 blk, err := NewBlock(start, testMD, BlockOptions{}, 1234 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1235 require.NoError(t, err) 1236 1237 b, ok := blk.(*block) 1238 require.True(t, ok) 1239 1240 seg1 := segment.NewMockMutableSegment(ctrl) 1241 results := result.NewIndexBlockByVolumeType(start) 1242 results.SetBlock(idxpersist.DefaultIndexVolumeType, 1243 result.NewIndexBlock([]result.Segment{result.NewSegment(seg1, true)}, 1244 result.NewShardTimeRangesFromRange(start, start.Add(time.Hour), 1, 2, 3))) 1245 require.NoError(t, b.AddResults(results)) 1246 1247 seg2 := segment.NewMockMutableSegment(ctrl) 1248 results = result.NewIndexBlockByVolumeType(start) 1249 results.SetBlock(idxpersist.DefaultIndexVolumeType, 1250 result.NewIndexBlock([]result.Segment{result.NewSegment(seg2, true)}, 1251 result.NewShardTimeRangesFromRange(start, start.Add(time.Hour), 1, 2, 5))) 1252 require.NoError(t, b.AddResults(results)) 1253 1254 require.NoError(t, b.Seal()) 1255 1256 seg1.EXPECT().Close().Return(nil) 1257 seg2.EXPECT().Close().Return(nil) 1258 require.NoError(t, b.Close()) 1259 } 1260 1261 func TestBlockNeedsMutableSegmentsEvicted(t *testing.T) { 1262 ctrl := gomock.NewController(t) 1263 defer ctrl.Finish() 1264 1265 testMD := newTestNSMetadata(t) 1266 start := xtime.Now().Truncate(time.Hour) 1267 blk, err := NewBlock(start, testMD, BlockOptions{}, 1268 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1269 require.NoError(t, err) 1270 1271 b, ok := blk.(*block) 1272 require.True(t, ok) 1273 1274 // empty to start, so shouldn't need eviction 1275 require.False(t, b.NeedsMutableSegmentsEvicted()) 1276 1277 // perform write and ensure it says it needs eviction 1278 h1 := doc.NewMockOnIndexSeries(ctrl) 1279 h1.EXPECT().OnIndexFinalize(start) 1280 h1.EXPECT().OnIndexSuccess(start) 1281 batch := NewWriteBatch(testWriteBatchOptionsWithBlockSize(time.Hour)) 1282 batch.Append(WriteBatchEntry{ 1283 Timestamp: start.Add(time.Minute), 1284 OnIndexSeries: h1, 1285 }, testDoc1()) 1286 res, err := b.WriteBatch(batch) 1287 require.NoError(t, err) 1288 require.Equal(t, int64(1), res.NumSuccess) 1289 require.Equal(t, int64(0), res.NumError) 1290 1291 require.True(t, b.NeedsMutableSegmentsEvicted()) 1292 } 1293 1294 func TestBlockNeedsMutableSegmentsEvictedMutableSegments(t *testing.T) { 1295 ctrl := gomock.NewController(t) 1296 defer ctrl.Finish() 1297 1298 testMD := newTestNSMetadata(t) 1299 start := xtime.Now().Truncate(time.Hour) 1300 blk, err := NewBlock(start, testMD, BlockOptions{}, 1301 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1302 require.NoError(t, err) 1303 1304 b, ok := blk.(*block) 1305 require.True(t, ok) 1306 1307 // empty to start, so shouldn't need eviction 1308 require.False(t, b.NeedsMutableSegmentsEvicted()) 1309 seg1 := segment.NewMockMutableSegment(ctrl) 1310 seg1.EXPECT().Size().Return(int64(0)).AnyTimes() 1311 results := result.NewIndexBlockByVolumeType(start) 1312 results.SetBlock(idxpersist.DefaultIndexVolumeType, 1313 result.NewIndexBlock([]result.Segment{result.NewSegment(seg1, true)}, 1314 result.NewShardTimeRangesFromRange(start, start.Add(time.Hour), 1, 2, 3))) 1315 require.NoError(t, b.AddResults(results)) 1316 require.False(t, b.NeedsMutableSegmentsEvicted()) 1317 1318 seg2 := segment.NewMockMutableSegment(ctrl) 1319 seg2.EXPECT().Size().Return(int64(1)).AnyTimes() 1320 seg3 := segment.NewMockSegment(ctrl) 1321 results = result.NewIndexBlockByVolumeType(start) 1322 results.SetBlock(idxpersist.DefaultIndexVolumeType, 1323 result.NewIndexBlock([]result.Segment{result.NewSegment(seg2, true), result.NewSegment(seg3, true)}, 1324 result.NewShardTimeRangesFromRange(start, start.Add(time.Hour), 1, 2, 4))) 1325 require.NoError(t, b.AddResults(results)) 1326 require.True(t, b.NeedsMutableSegmentsEvicted()) 1327 } 1328 1329 func TestBlockEvictMutableSegmentsSimple(t *testing.T) { 1330 ctrl := gomock.NewController(t) 1331 defer ctrl.Finish() 1332 1333 testMD := newTestNSMetadata(t) 1334 start := xtime.Now().Truncate(time.Hour) 1335 blk, err := NewBlock(start, testMD, BlockOptions{}, 1336 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1337 require.NoError(t, err) 1338 err = blk.EvictMutableSegments() 1339 require.Error(t, err) 1340 1341 require.NoError(t, blk.Seal()) 1342 err = blk.EvictMutableSegments() 1343 require.NoError(t, err) 1344 } 1345 1346 func TestBlockEvictMutableSegmentsAddResults(t *testing.T) { 1347 ctrl := gomock.NewController(t) 1348 defer ctrl.Finish() 1349 1350 testMD := newTestNSMetadata(t) 1351 start := xtime.Now().Truncate(time.Hour) 1352 blk, err := NewBlock(start, testMD, BlockOptions{}, 1353 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1354 require.NoError(t, err) 1355 1356 b, ok := blk.(*block) 1357 require.True(t, ok) 1358 require.NoError(t, b.Seal()) 1359 1360 seg1 := segment.NewMockMutableSegment(ctrl) 1361 results := result.NewIndexBlockByVolumeType(start) 1362 results.SetBlock(idxpersist.DefaultIndexVolumeType, 1363 result.NewIndexBlock([]result.Segment{result.NewSegment(seg1, true)}, 1364 result.NewShardTimeRangesFromRange(start, start.Add(time.Hour), 1, 2, 3))) 1365 require.NoError(t, b.AddResults(results)) 1366 seg1.EXPECT().Close().Return(nil) 1367 err = b.EvictMutableSegments() 1368 require.NoError(t, err) 1369 1370 seg2 := segment.NewMockMutableSegment(ctrl) 1371 seg3 := segment.NewMockSegment(ctrl) 1372 results = result.NewIndexBlockByVolumeType(start) 1373 results.SetBlock(idxpersist.DefaultIndexVolumeType, 1374 result.NewIndexBlock([]result.Segment{result.NewSegment(seg2, true), result.NewSegment(seg3, true)}, 1375 result.NewShardTimeRangesFromRange(start, start.Add(time.Hour), 1, 2, 4))) 1376 require.NoError(t, b.AddResults(results)) 1377 seg2.EXPECT().Close().Return(nil) 1378 err = b.EvictMutableSegments() 1379 require.NoError(t, err) 1380 } 1381 1382 func TestBlockE2EInsertQuery(t *testing.T) { 1383 ctrl := gomock.NewController(t) 1384 defer ctrl.Finish() 1385 1386 blockSize := time.Hour 1387 1388 testMD := newTestNSMetadata(t) 1389 now := xtime.Now() 1390 blockStart := now.Truncate(blockSize) 1391 1392 nowNotBlockStartAligned := now. 1393 Truncate(blockSize). 1394 Add(time.Minute) 1395 1396 // Use a larger batch size to simulate large number in a batch 1397 // coming back (to ensure code path for reusing buffers for iterator 1398 // is covered). 1399 testOpts := optionsWithDocsArrayPool(testOpts, 16, 256) 1400 1401 blk, err := NewBlock(blockStart, testMD, 1402 BlockOptions{ 1403 ForegroundCompactorMmapDocsData: true, 1404 BackgroundCompactorMmapDocsData: true, 1405 }, 1406 namespace.NewRuntimeOptionsManager("foo"), 1407 testOpts) 1408 require.NoError(t, err) 1409 b, ok := blk.(*block) 1410 require.True(t, ok) 1411 1412 h1 := doc.NewMockOnIndexSeries(ctrl) 1413 h1.EXPECT().OnIndexFinalize(blockStart) 1414 h1.EXPECT().OnIndexSuccess(blockStart) 1415 1416 h2 := doc.NewMockOnIndexSeries(ctrl) 1417 h2.EXPECT().OnIndexFinalize(blockStart) 1418 h2.EXPECT().OnIndexSuccess(blockStart) 1419 1420 batch := NewWriteBatch(testWriteBatchOptionsWithBlockSize(blockSize)) 1421 batch.Append(WriteBatchEntry{ 1422 Timestamp: nowNotBlockStartAligned, 1423 OnIndexSeries: h1, 1424 }, testDoc1()) 1425 batch.Append(WriteBatchEntry{ 1426 Timestamp: nowNotBlockStartAligned, 1427 OnIndexSeries: h2, 1428 }, testDoc2()) 1429 1430 res, err := b.WriteBatch(batch) 1431 require.NoError(t, err) 1432 require.Equal(t, int64(2), res.NumSuccess) 1433 require.Equal(t, int64(0), res.NumError) 1434 1435 q, err := idx.NewRegexpQuery([]byte("bar"), []byte("b.*")) 1436 require.NoError(t, err) 1437 1438 ctx := context.NewBackground() 1439 // create initial span from a mock tracer and get ctx 1440 mtr := mocktracer.New() 1441 sp := mtr.StartSpan("root") 1442 ctx.SetGoContext(opentracing.ContextWithSpan(stdlibctx.Background(), sp)) 1443 1444 results := NewQueryResults(nil, QueryResultsOptions{}, testOpts) 1445 queryIter, err := b.QueryIter(ctx, Query{q}) 1446 require.NoError(t, err) 1447 err = b.QueryWithIter(ctx, QueryOptions{}, queryIter, results, time.Now().Add(time.Minute), emptyLogFields) 1448 require.NoError(t, err) 1449 require.Equal(t, 2, results.Size()) 1450 1451 rMap := results.Map() 1452 d, ok := rMap.Get(testDoc1().ID) 1453 require.True(t, ok) 1454 t1 := test.DocumentToTagIter(t, d) 1455 require.True(t, ident.NewTagIterMatcher( 1456 ident.MustNewTagStringsIterator("bar", "baz")).Matches( 1457 t1)) 1458 1459 d, ok = rMap.Get(testDoc2().ID) 1460 require.True(t, ok) 1461 t2 := test.DocumentToTagIter(t, d) 1462 require.True(t, ident.NewTagIterMatcher( 1463 ident.MustNewTagStringsIterator("bar", "baz", "some", "more")).Matches( 1464 t2)) 1465 1466 sp.Finish() 1467 spans := mtr.FinishedSpans() 1468 require.Len(t, spans, 4) 1469 require.Equal(t, tracepoint.SearchExecutorIndexSearch, spans[0].OperationName) 1470 require.Equal(t, tracepoint.NSIdxBlockQueryAddDocuments, spans[1].OperationName) 1471 require.Equal(t, tracepoint.BlockQuery, spans[2].OperationName) 1472 } 1473 1474 func TestBlockE2EInsertQueryLimit(t *testing.T) { 1475 ctrl := gomock.NewController(t) 1476 defer ctrl.Finish() 1477 1478 testMD := newTestNSMetadata(t) 1479 blockSize := time.Hour 1480 1481 now := xtime.Now() 1482 blockStart := now.Truncate(blockSize) 1483 1484 nowNotBlockStartAligned := now. 1485 Truncate(blockSize). 1486 Add(time.Minute) 1487 1488 blk, err := NewBlock(blockStart, testMD, BlockOptions{}, 1489 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1490 require.NoError(t, err) 1491 b, ok := blk.(*block) 1492 require.True(t, ok) 1493 1494 h1 := doc.NewMockOnIndexSeries(ctrl) 1495 h1.EXPECT().ReconciledOnIndexSeries().Return(h1, &resource.NoopCloser{}, false) 1496 h1.EXPECT().OnIndexFinalize(blockStart) 1497 h1.EXPECT().OnIndexSuccess(blockStart) 1498 h1.EXPECT().IndexedRange().Return(blockStart, blockStart) 1499 h1.EXPECT().IndexedForBlockStart(blockStart).Return(true) 1500 1501 h2 := doc.NewMockOnIndexSeries(ctrl) 1502 h2.EXPECT().OnIndexFinalize(blockStart) 1503 h2.EXPECT().OnIndexSuccess(blockStart) 1504 1505 batch := NewWriteBatch(testWriteBatchOptionsWithBlockSize(blockSize)) 1506 batch.Append(WriteBatchEntry{ 1507 Timestamp: nowNotBlockStartAligned, 1508 OnIndexSeries: h1, 1509 }, testDoc1()) 1510 batch.Append(WriteBatchEntry{ 1511 Timestamp: nowNotBlockStartAligned, 1512 OnIndexSeries: h2, 1513 }, testDoc2()) 1514 1515 res, err := b.WriteBatch(batch) 1516 require.NoError(t, err) 1517 require.Equal(t, int64(2), res.NumSuccess) 1518 require.Equal(t, int64(0), res.NumError) 1519 1520 q, err := idx.NewRegexpQuery([]byte("bar"), []byte("b.*")) 1521 require.NoError(t, err) 1522 1523 limit := 1 1524 results := NewQueryResults(nil, 1525 QueryResultsOptions{SizeLimit: limit}, testOpts) 1526 ctx := context.NewBackground() 1527 queryIter, err := b.QueryIter(ctx, Query{q}) 1528 require.NoError(t, err) 1529 err = b.QueryWithIter(ctx, QueryOptions{ 1530 SeriesLimit: limit, 1531 StartInclusive: blockStart, 1532 EndExclusive: blockStart.Add(time.Second), 1533 }, queryIter, results, time.Now().Add(time.Minute), 1534 emptyLogFields) 1535 require.NoError(t, err) 1536 require.Equal(t, 1, results.Size()) 1537 1538 rMap := results.Map() 1539 numFound := 0 1540 d, ok := rMap.Get(testDoc1().ID) 1541 if ok { 1542 numFound++ 1543 t1 := test.DocumentToTagIter(t, d) 1544 require.True(t, ident.NewTagIterMatcher( 1545 ident.MustNewTagStringsIterator("bar", "baz")).Matches( 1546 t1)) 1547 } 1548 1549 d, ok = rMap.Get(testDoc2().ID) 1550 if ok { 1551 numFound++ 1552 t2 := test.DocumentToTagIter(t, d) 1553 require.True(t, ident.NewTagIterMatcher( 1554 ident.MustNewTagStringsIterator("bar", "baz", "some", "more")).Matches( 1555 t2)) 1556 } 1557 1558 require.Equal(t, 1, numFound) 1559 } 1560 1561 func TestBlockE2EInsertAddResultsQuery(t *testing.T) { 1562 ctrl := gomock.NewController(t) 1563 defer ctrl.Finish() 1564 1565 testMD := newTestNSMetadata(t) 1566 blockSize := time.Hour 1567 1568 now := xtime.Now() 1569 blockStart := now.Truncate(blockSize) 1570 1571 nowNotBlockStartAligned := now. 1572 Truncate(blockSize). 1573 Add(time.Minute) 1574 1575 blk, err := NewBlock(blockStart, testMD, BlockOptions{}, 1576 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1577 require.NoError(t, err) 1578 b, ok := blk.(*block) 1579 require.True(t, ok) 1580 1581 closer := &resource.NoopCloser{} 1582 h1 := doc.NewMockOnIndexSeries(ctrl) 1583 h1.EXPECT().ReconciledOnIndexSeries().Return(h1, closer, false) 1584 h1.EXPECT().OnIndexFinalize(blockStart) 1585 h1.EXPECT().OnIndexSuccess(blockStart) 1586 h1.EXPECT().IndexedRange().Return(blockStart, blockStart) 1587 h1.EXPECT().IndexedForBlockStart(blockStart).Return(true) 1588 1589 h2 := doc.NewMockOnIndexSeries(ctrl) 1590 h2.EXPECT().ReconciledOnIndexSeries().Return(h2, closer, false) 1591 h2.EXPECT().OnIndexFinalize(blockStart) 1592 h2.EXPECT().OnIndexSuccess(blockStart) 1593 h2.EXPECT().IndexedRange().Return(blockStart, blockStart) 1594 h2.EXPECT().IndexedForBlockStart(blockStart).Return(true) 1595 1596 batch := NewWriteBatch(testWriteBatchOptionsWithBlockSize(blockSize)) 1597 batch.Append(WriteBatchEntry{ 1598 Timestamp: nowNotBlockStartAligned, 1599 OnIndexSeries: h1, 1600 }, testDoc1()) 1601 batch.Append(WriteBatchEntry{ 1602 Timestamp: nowNotBlockStartAligned, 1603 OnIndexSeries: h2, 1604 }, testDoc2()) 1605 1606 res, err := b.WriteBatch(batch) 1607 require.NoError(t, err) 1608 require.Equal(t, int64(2), res.NumSuccess) 1609 require.Equal(t, int64(0), res.NumError) 1610 1611 seg := testSegment(t, testDoc1DupeID()) 1612 idxResults := result.NewIndexBlockByVolumeType(blockStart) 1613 idxResults.SetBlock(idxpersist.DefaultIndexVolumeType, 1614 result.NewIndexBlock([]result.Segment{result.NewSegment(seg, true)}, 1615 result.NewShardTimeRangesFromRange(blockStart, blockStart.Add(blockSize), 1, 2, 3))) 1616 require.NoError(t, blk.AddResults(idxResults)) 1617 1618 q, err := idx.NewRegexpQuery([]byte("bar"), []byte("b.*")) 1619 require.NoError(t, err) 1620 1621 ctx := context.NewBackground() 1622 // create initial span from a mock tracer and get ctx 1623 mtr := mocktracer.New() 1624 sp := mtr.StartSpan("root") 1625 ctx.SetGoContext(opentracing.ContextWithSpan(stdlibctx.Background(), sp)) 1626 1627 results := NewQueryResults(nil, QueryResultsOptions{}, testOpts) 1628 queryIter, err := b.QueryIter(ctx, Query{q}) 1629 require.NoError(t, err) 1630 err = b.QueryWithIter(ctx, QueryOptions{ 1631 StartInclusive: blockStart, 1632 EndExclusive: blockStart.Add(time.Second), 1633 }, queryIter, results, time.Now().Add(time.Minute), emptyLogFields) 1634 require.NoError(t, err) 1635 require.Equal(t, 2, results.Size()) 1636 1637 rMap := results.Map() 1638 d, ok := rMap.Get(testDoc1().ID) 1639 require.True(t, ok) 1640 t1 := test.DocumentToTagIter(t, d) 1641 require.True(t, ident.NewTagIterMatcher( 1642 ident.MustNewTagStringsIterator("bar", "baz")).Matches( 1643 t1)) 1644 1645 d, ok = rMap.Get(testDoc2().ID) 1646 require.True(t, ok) 1647 t2 := test.DocumentToTagIter(t, d) 1648 require.True(t, ident.NewTagIterMatcher( 1649 ident.MustNewTagStringsIterator("bar", "baz", "some", "more")).Matches( 1650 t2)) 1651 1652 sp.Finish() 1653 spans := mtr.FinishedSpans() 1654 require.Len(t, spans, 6) 1655 require.Equal(t, tracepoint.SearchExecutorIndexSearch, spans[0].OperationName) 1656 require.Equal(t, tracepoint.SearchExecutorIndexSearch, spans[1].OperationName) 1657 require.Equal(t, tracepoint.NSIdxBlockQueryAddDocuments, spans[2].OperationName) 1658 require.Equal(t, tracepoint.NSIdxBlockQueryAddDocuments, spans[3].OperationName) 1659 require.Equal(t, tracepoint.BlockQuery, spans[4].OperationName) 1660 } 1661 1662 func TestBlockE2EInsertAddResultsMergeQuery(t *testing.T) { 1663 ctrl := gomock.NewController(t) 1664 defer ctrl.Finish() 1665 1666 testMD := newTestNSMetadata(t) 1667 blockSize := time.Hour 1668 1669 now := xtime.Now() 1670 blockStart := now.Truncate(blockSize) 1671 1672 nowNotBlockStartAligned := now. 1673 Truncate(blockSize). 1674 Add(time.Minute) 1675 1676 blk, err := NewBlock(blockStart, testMD, BlockOptions{}, 1677 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1678 require.NoError(t, err) 1679 b, ok := blk.(*block) 1680 require.True(t, ok) 1681 1682 h1 := doc.NewMockOnIndexSeries(ctrl) 1683 h1.EXPECT().ReconciledOnIndexSeries().Return(h1, &resource.NoopCloser{}, false) 1684 h1.EXPECT().OnIndexFinalize(blockStart) 1685 h1.EXPECT().OnIndexSuccess(blockStart) 1686 h1.EXPECT().IndexedRange().Return(blockStart, blockStart) 1687 h1.EXPECT().IndexedForBlockStart(blockStart).Return(true) 1688 1689 batch := NewWriteBatch(testWriteBatchOptionsWithBlockSize(blockSize)) 1690 batch.Append(WriteBatchEntry{ 1691 Timestamp: nowNotBlockStartAligned, 1692 OnIndexSeries: h1, 1693 }, testDoc1()) 1694 1695 res, err := b.WriteBatch(batch) 1696 require.NoError(t, err) 1697 require.Equal(t, int64(1), res.NumSuccess) 1698 require.Equal(t, int64(0), res.NumError) 1699 1700 seg := testSegment(t, testDoc2()) 1701 idxResults := result.NewIndexBlockByVolumeType(blockStart) 1702 idxResults.SetBlock(idxpersist.DefaultIndexVolumeType, 1703 result.NewIndexBlock([]result.Segment{result.NewSegment(seg, true)}, 1704 result.NewShardTimeRangesFromRange(blockStart, blockStart.Add(blockSize), 1, 2, 3))) 1705 require.NoError(t, blk.AddResults(idxResults)) 1706 1707 q, err := idx.NewRegexpQuery([]byte("bar"), []byte("b.*")) 1708 require.NoError(t, err) 1709 1710 ctx := context.NewBackground() 1711 // create initial span from a mock tracer and get ctx 1712 mtr := mocktracer.New() 1713 sp := mtr.StartSpan("root") 1714 ctx.SetGoContext(opentracing.ContextWithSpan(stdlibctx.Background(), sp)) 1715 1716 results := NewQueryResults(nil, QueryResultsOptions{}, testOpts) 1717 queryIter, err := b.QueryIter(ctx, Query{q}) 1718 require.NoError(t, err) 1719 err = b.QueryWithIter(ctx, QueryOptions{ 1720 StartInclusive: blockStart, 1721 EndExclusive: blockStart.Add(time.Second), 1722 }, queryIter, results, time.Now().Add(time.Minute), emptyLogFields) 1723 require.NoError(t, err) 1724 require.Equal(t, 2, results.Size()) 1725 1726 rMap := results.Map() 1727 d, ok := results.Map().Get(testDoc1().ID) 1728 require.True(t, ok) 1729 t1 := test.DocumentToTagIter(t, d) 1730 require.True(t, ident.NewTagIterMatcher( 1731 ident.MustNewTagStringsIterator("bar", "baz")).Matches( 1732 t1)) 1733 1734 d, ok = rMap.Get(testDoc2().ID) 1735 require.True(t, ok) 1736 t2 := test.DocumentToTagIter(t, d) 1737 require.True(t, ident.NewTagIterMatcher( 1738 ident.MustNewTagStringsIterator("bar", "baz", "some", "more")).Matches( 1739 t2)) 1740 1741 sp.Finish() 1742 spans := mtr.FinishedSpans() 1743 require.Len(t, spans, 6) 1744 require.Equal(t, tracepoint.SearchExecutorIndexSearch, spans[0].OperationName) 1745 require.Equal(t, tracepoint.SearchExecutorIndexSearch, spans[1].OperationName) 1746 require.Equal(t, tracepoint.NSIdxBlockQueryAddDocuments, spans[2].OperationName) 1747 require.Equal(t, tracepoint.NSIdxBlockQueryAddDocuments, spans[3].OperationName) 1748 require.Equal(t, tracepoint.BlockQuery, spans[4].OperationName) 1749 } 1750 1751 func TestBlockE2EInsertAddResultsQueryNarrowingBlockRange(t *testing.T) { 1752 ctrl := gomock.NewController(t) 1753 defer ctrl.Finish() 1754 1755 testMD := newTestNSMetadata(t) 1756 blockSize := time.Hour 1757 1758 now := xtime.Now() 1759 blockStart := now.Truncate(blockSize) 1760 1761 nowNotBlockStartAligned := now. 1762 Truncate(blockSize). 1763 Add(time.Minute) 1764 1765 blk, err := NewBlock(blockStart, testMD, BlockOptions{}, 1766 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1767 require.NoError(t, err) 1768 b, ok := blk.(*block) 1769 require.True(t, ok) 1770 1771 closer := &resource.NoopCloser{} 1772 h1 := doc.NewMockOnIndexSeries(ctrl) 1773 h1.EXPECT().ReconciledOnIndexSeries().Return(h1, closer, false) 1774 h1.EXPECT().OnIndexFinalize(blockStart) 1775 h1.EXPECT().OnIndexSuccess(blockStart) 1776 h1.EXPECT().IndexedRange().Return(blockStart, blockStart.Add(2*blockSize)) 1777 h1.EXPECT().IndexedForBlockStart(blockStart).Return(false) 1778 h1.EXPECT().IndexedForBlockStart(blockStart.Add(1 * blockSize)).Return(false) 1779 h1.EXPECT().IndexedForBlockStart(blockStart.Add(2 * blockSize)).Return(true) 1780 1781 h2 := doc.NewMockOnIndexSeries(ctrl) 1782 h2.EXPECT().ReconciledOnIndexSeries().Return(h2, closer, false) 1783 h2.EXPECT().OnIndexFinalize(blockStart) 1784 h2.EXPECT().OnIndexSuccess(blockStart) 1785 h2.EXPECT().IndexedRange().Return(xtime.UnixNano(0), xtime.UnixNano(0)) 1786 1787 batch := NewWriteBatch(testWriteBatchOptionsWithBlockSize(blockSize)) 1788 batch.Append(WriteBatchEntry{ 1789 Timestamp: nowNotBlockStartAligned, 1790 OnIndexSeries: h1, 1791 }, testDoc1()) 1792 batch.Append(WriteBatchEntry{ 1793 Timestamp: nowNotBlockStartAligned, 1794 OnIndexSeries: h2, 1795 }, testDoc2()) 1796 1797 res, err := b.WriteBatch(batch) 1798 require.NoError(t, err) 1799 require.Equal(t, int64(2), res.NumSuccess) 1800 require.Equal(t, int64(0), res.NumError) 1801 1802 seg := testSegment(t, testDoc1DupeID()) 1803 idxResults := result.NewIndexBlockByVolumeType(blockStart) 1804 idxResults.SetBlock(idxpersist.DefaultIndexVolumeType, 1805 result.NewIndexBlock([]result.Segment{result.NewSegment(seg, true)}, 1806 result.NewShardTimeRangesFromRange(blockStart, blockStart.Add(blockSize), 1, 2, 3))) 1807 require.NoError(t, blk.AddResults(idxResults)) 1808 1809 q, err := idx.NewRegexpQuery([]byte("bar"), []byte("b.*")) 1810 require.NoError(t, err) 1811 1812 ctx := context.NewBackground() 1813 // create initial span from a mock tracer and get ctx 1814 mtr := mocktracer.New() 1815 sp := mtr.StartSpan("root") 1816 ctx.SetGoContext(opentracing.ContextWithSpan(stdlibctx.Background(), sp)) 1817 1818 results := NewQueryResults(nil, QueryResultsOptions{}, testOpts) 1819 queryIter, err := b.QueryIter(ctx, Query{q}) 1820 require.NoError(t, err) 1821 err = b.QueryWithIter(ctx, QueryOptions{ 1822 StartInclusive: blockStart.Add(-1000 * blockSize), 1823 EndExclusive: blockStart.Add(1000 * blockSize), 1824 }, queryIter, results, time.Now().Add(time.Minute), emptyLogFields) 1825 require.NoError(t, err) 1826 require.Equal(t, 1, results.Size()) 1827 1828 rMap := results.Map() 1829 d, ok := rMap.Get(testDoc1().ID) 1830 require.True(t, ok) 1831 t1 := test.DocumentToTagIter(t, d) 1832 require.True(t, ident.NewTagIterMatcher( 1833 ident.MustNewTagStringsIterator("bar", "baz")).Matches( 1834 t1)) 1835 1836 _, ok = rMap.Get(testDoc2().ID) 1837 require.False(t, ok) 1838 1839 sp.Finish() 1840 spans := mtr.FinishedSpans() 1841 require.Len(t, spans, 5) 1842 require.Equal(t, tracepoint.SearchExecutorIndexSearch, spans[0].OperationName) 1843 require.Equal(t, tracepoint.SearchExecutorIndexSearch, spans[1].OperationName) 1844 require.Equal(t, tracepoint.NSIdxBlockQueryAddDocuments, spans[2].OperationName) 1845 require.Equal(t, tracepoint.BlockQuery, spans[3].OperationName) 1846 } 1847 1848 func TestBlockWriteBackgroundCompact(t *testing.T) { 1849 ctrl := gomock.NewController(t) 1850 defer ctrl.Finish() 1851 1852 testMD := newTestNSMetadata(t) 1853 blockSize := time.Hour 1854 1855 now := xtime.Now() 1856 blockStart := now.Truncate(blockSize) 1857 1858 nowNotBlockStartAligned := now. 1859 Truncate(blockSize). 1860 Add(time.Minute) 1861 1862 logger, err := zap.NewDevelopment() 1863 require.NoError(t, err) 1864 testOpts = testOpts.SetInstrumentOptions( 1865 testOpts.InstrumentOptions().SetLogger(logger)) 1866 1867 blk, err := NewBlock(blockStart, testMD, BlockOptions{}, 1868 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1869 require.NoError(t, err) 1870 defer func() { 1871 require.NoError(t, blk.Close()) 1872 }() 1873 1874 b, ok := blk.(*block) 1875 require.True(t, ok) 1876 1877 // Testing compaction only, so mark GC as already running so the test is limited only to compaction. 1878 b.mutableSegments.compact.compactingBackgroundGarbageCollect = true 1879 1880 // First write 1881 h1 := doc.NewMockOnIndexSeries(ctrl) 1882 h1.EXPECT().OnIndexFinalize(blockStart) 1883 h1.EXPECT().OnIndexSuccess(blockStart) 1884 1885 h2 := doc.NewMockOnIndexSeries(ctrl) 1886 h2.EXPECT().OnIndexFinalize(blockStart) 1887 h2.EXPECT().OnIndexSuccess(blockStart) 1888 1889 batch := NewWriteBatch(testWriteBatchOptionsWithBlockSize(blockSize)) 1890 batch.Append(WriteBatchEntry{ 1891 Timestamp: nowNotBlockStartAligned, 1892 OnIndexSeries: h1, 1893 }, testDoc1()) 1894 batch.Append(WriteBatchEntry{ 1895 Timestamp: nowNotBlockStartAligned, 1896 OnIndexSeries: h2, 1897 }, testDoc2()) 1898 1899 res, err := b.WriteBatch(batch) 1900 require.NoError(t, err) 1901 require.Equal(t, int64(2), res.NumSuccess) 1902 require.Equal(t, int64(0), res.NumError) 1903 1904 // Move the segment to background 1905 b.Lock() 1906 b.mutableSegments.maybeMoveForegroundSegmentsToBackgroundWithLock([]compaction.Segment{ 1907 {Segment: b.mutableSegments.foregroundSegments[0].Segment()}, 1908 }) 1909 b.Unlock() 1910 1911 // Second write 1912 h1 = doc.NewMockOnIndexSeries(ctrl) 1913 h1.EXPECT().OnIndexFinalize(blockStart) 1914 h1.EXPECT().OnIndexSuccess(blockStart) 1915 1916 batch = NewWriteBatch(testWriteBatchOptionsWithBlockSize(blockSize)) 1917 batch.Append(WriteBatchEntry{ 1918 Timestamp: nowNotBlockStartAligned, 1919 OnIndexSeries: h1, 1920 }, testDoc3()) 1921 1922 res, err = b.WriteBatch(batch) 1923 require.NoError(t, err) 1924 require.Equal(t, int64(1), res.NumSuccess) 1925 require.Equal(t, int64(0), res.NumError) 1926 1927 // Move last segment to background, this should kick off a background compaction 1928 b.mutableSegments.Lock() 1929 b.mutableSegments.maybeMoveForegroundSegmentsToBackgroundWithLock([]compaction.Segment{ 1930 {Segment: b.mutableSegments.foregroundSegments[0].Segment()}, 1931 }) 1932 require.Equal(t, 2, len(b.mutableSegments.backgroundSegments)) 1933 require.True(t, b.mutableSegments.compact.compactingBackgroundStandard) 1934 b.mutableSegments.Unlock() 1935 1936 // Wait for compaction to finish 1937 for { 1938 b.mutableSegments.RLock() 1939 compacting := b.mutableSegments.compact.compactingBackgroundStandard 1940 b.mutableSegments.RUnlock() 1941 if !compacting { 1942 break 1943 } 1944 time.Sleep(10 * time.Millisecond) 1945 } 1946 1947 // Make sure compacted into a single segment 1948 b.mutableSegments.RLock() 1949 require.Equal(t, 1, len(b.mutableSegments.backgroundSegments)) 1950 require.Equal(t, 3, int(b.mutableSegments.backgroundSegments[0].Segment().Size())) 1951 b.mutableSegments.RUnlock() 1952 } 1953 1954 func TestBlockAggregateAfterClose(t *testing.T) { 1955 testMD := newTestNSMetadata(t) 1956 start := xtime.Now().Truncate(time.Hour) 1957 b, err := NewBlock(start, testMD, BlockOptions{}, 1958 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1959 require.NoError(t, err) 1960 1961 require.Equal(t, start, b.StartTime()) 1962 require.Equal(t, start.Add(time.Hour), b.EndTime()) 1963 require.NoError(t, b.Close()) 1964 1965 _, err = b.AggregateIter(context.NewBackground(), AggregateResultsOptions{}) 1966 require.Error(t, err) 1967 } 1968 1969 func TestBlockAggregateIterationErr(t *testing.T) { 1970 ctrl := gomock.NewController(t) 1971 defer ctrl.Finish() 1972 1973 testMD := newTestNSMetadata(t) 1974 start := xtime.Now().Truncate(time.Hour) 1975 blk, err := NewBlock(start, testMD, BlockOptions{}, 1976 namespace.NewRuntimeOptionsManager("foo"), testOpts) 1977 require.NoError(t, err) 1978 1979 b, ok := blk.(*block) 1980 require.True(t, ok) 1981 1982 seg1 := segment.NewMockMutableSegment(ctrl) 1983 reader := segment.NewMockReader(ctrl) 1984 reader.EXPECT().Close().Return(nil) 1985 seg1.EXPECT().Reader().Return(reader, nil) 1986 1987 b.mutableSegments.foregroundSegments = []*readableSeg{newReadableSeg(seg1, testOpts)} 1988 iter := NewMockfieldsAndTermsIterator(ctrl) 1989 b.newFieldsAndTermsIteratorFn = func( 1990 _ context.Context, _ segment.Reader, opts fieldsAndTermsIteratorOpts) (fieldsAndTermsIterator, error) { 1991 return iter, nil 1992 } 1993 1994 results := NewAggregateResults(ident.StringID("ns"), AggregateResultsOptions{ 1995 SizeLimit: 3, 1996 Type: AggregateTagNamesAndValues, 1997 }, testOpts) 1998 1999 gomock.InOrder( 2000 iter.EXPECT().Next().Return(true), 2001 iter.EXPECT().Current().Return([]byte("f1"), []byte("t1")), 2002 iter.EXPECT().Next().Return(false), 2003 iter.EXPECT().Err().Return(fmt.Errorf("unknown error")), 2004 ) 2005 2006 ctx := context.NewBackground() 2007 defer ctx.BlockingClose() 2008 2009 aggIter, err := b.AggregateIter(ctx, results.AggregateResultsOptions()) 2010 require.NoError(t, err) 2011 err = b.AggregateWithIter( 2012 ctx, 2013 aggIter, 2014 QueryOptions{SeriesLimit: 3}, 2015 results, 2016 time.Now().Add(time.Minute), 2017 emptyLogFields) 2018 require.Error(t, err) 2019 } 2020 2021 func TestBlockAggregate(t *testing.T) { 2022 ctrl := gomock.NewController(t) 2023 defer ctrl.Finish() 2024 2025 scope := tally.NewTestScope("", nil) 2026 iOpts := instrument.NewOptions().SetMetricsScope(scope) 2027 limitOpts := limits.NewOptions(). 2028 SetInstrumentOptions(iOpts). 2029 SetDocsLimitOpts(limits.LookbackLimitOptions{Limit: 50, Lookback: time.Minute}). 2030 SetBytesReadLimitOpts(limits.LookbackLimitOptions{Lookback: time.Minute}). 2031 SetAggregateDocsLimitOpts(limits.LookbackLimitOptions{Lookback: time.Minute}) 2032 queryLimits, err := limits.NewQueryLimits(limitOpts) 2033 require.NoError(t, err) 2034 testOpts = testOpts.SetInstrumentOptions(iOpts).SetQueryLimits(queryLimits) 2035 2036 // NB: seriesLimit must be higher than the number of fields to be exhaustive. 2037 seriesLimit := 10 2038 testMD := newTestNSMetadata(t) 2039 start := xtime.Now().Truncate(time.Hour) 2040 blk, err := NewBlock(start, testMD, BlockOptions{}, 2041 namespace.NewRuntimeOptionsManager("foo"), testOpts) 2042 require.NoError(t, err) 2043 2044 b, ok := blk.(*block) 2045 require.True(t, ok) 2046 2047 seg1 := segment.NewMockMutableSegment(ctrl) 2048 reader := segment.NewMockReader(ctrl) 2049 reader.EXPECT().Close().Return(nil) 2050 seg1.EXPECT().Reader().Return(reader, nil) 2051 2052 b.mutableSegments.foregroundSegments = []*readableSeg{newReadableSeg(seg1, testOpts)} 2053 iter := NewMockfieldsAndTermsIterator(ctrl) 2054 b.newFieldsAndTermsIteratorFn = func( 2055 _ context.Context, _ segment.Reader, opts fieldsAndTermsIteratorOpts) (fieldsAndTermsIterator, error) { 2056 return iter, nil 2057 } 2058 2059 results := NewAggregateResults(ident.StringID("ns"), AggregateResultsOptions{ 2060 SizeLimit: seriesLimit, 2061 Type: AggregateTagNamesAndValues, 2062 }, testOpts) 2063 2064 ctx := context.NewBackground() 2065 defer ctx.BlockingClose() 2066 2067 // create initial span from a mock tracer and get ctx 2068 mtr := mocktracer.New() 2069 sp := mtr.StartSpan("root") 2070 ctx.SetGoContext(opentracing.ContextWithSpan(stdlibctx.Background(), sp)) 2071 2072 iter.EXPECT().Next().Return(true) 2073 iter.EXPECT().Current().Return([]byte("f1"), []byte("t1")) 2074 iter.EXPECT().Next().Return(true) 2075 iter.EXPECT().Current().Return([]byte("f1"), []byte("t2")) 2076 iter.EXPECT().Next().Return(true) 2077 iter.EXPECT().Current().Return([]byte("f2"), []byte("t1")) 2078 iter.EXPECT().Next().Return(true) 2079 iter.EXPECT().Current().Return([]byte("f1"), []byte("t3")) 2080 iter.EXPECT().Next().Return(false) 2081 iter.EXPECT().Err().Return(nil) 2082 iter.EXPECT().Close().Return(nil) 2083 2084 aggIter, err := b.AggregateIter(ctx, results.AggregateResultsOptions()) 2085 require.NoError(t, err) 2086 err = b.AggregateWithIter( 2087 ctx, 2088 aggIter, 2089 QueryOptions{SeriesLimit: seriesLimit}, 2090 results, 2091 time.Now().Add(time.Minute), 2092 emptyLogFields) 2093 require.NoError(t, err) 2094 2095 assertAggregateResultsMapEquals(t, map[string][]string{ 2096 "f1": {"t1", "t2", "t3"}, 2097 "f2": {"t1"}, 2098 }, results) 2099 2100 sp.Finish() 2101 spans := mtr.FinishedSpans() 2102 require.Len(t, spans, 3) 2103 require.Equal(t, tracepoint.NSIdxBlockAggregateQueryAddDocuments, spans[0].OperationName) 2104 require.Equal(t, tracepoint.BlockAggregate, spans[1].OperationName) 2105 2106 snap := scope.Snapshot() 2107 2108 tallytest.AssertCounterValue(t, 3, snap, 2109 "query-limit.total-docs-matched", map[string]string{"type": "fetch"}) 2110 tallytest.AssertCounterValue(t, 7, snap, 2111 "query-limit.total-docs-matched", map[string]string{"type": "aggregate"}) 2112 } 2113 2114 func TestBlockAggregateWithAggregateLimits(t *testing.T) { 2115 ctrl := gomock.NewController(t) 2116 defer ctrl.Finish() 2117 2118 seriesLimit := 100 2119 scope := tally.NewTestScope("", nil) 2120 iOpts := instrument.NewOptions().SetMetricsScope(scope) 2121 limitOpts := limits.NewOptions(). 2122 SetInstrumentOptions(iOpts). 2123 SetDocsLimitOpts(limits.LookbackLimitOptions{Lookback: time.Minute}). 2124 SetBytesReadLimitOpts(limits.LookbackLimitOptions{Lookback: time.Minute}). 2125 SetAggregateDocsLimitOpts(limits.LookbackLimitOptions{ 2126 Limit: int64(seriesLimit), 2127 Lookback: time.Minute, 2128 }) 2129 queryLimits, err := limits.NewQueryLimits(limitOpts) 2130 require.NoError(t, err) 2131 aggTestOpts := testOpts.SetInstrumentOptions(iOpts).SetQueryLimits(queryLimits) 2132 2133 testMD := newTestNSMetadata(t) 2134 start := xtime.Now().Truncate(time.Hour) 2135 blk, err := NewBlock(start, testMD, BlockOptions{}, 2136 namespace.NewRuntimeOptionsManager("foo"), aggTestOpts) 2137 require.NoError(t, err) 2138 2139 b, ok := blk.(*block) 2140 require.True(t, ok) 2141 2142 seg1 := segment.NewMockMutableSegment(ctrl) 2143 reader := segment.NewMockReader(ctrl) 2144 reader.EXPECT().Close().Return(nil) 2145 seg1.EXPECT().Reader().Return(reader, nil) 2146 2147 b.mutableSegments.foregroundSegments = []*readableSeg{newReadableSeg(seg1, aggTestOpts)} 2148 iter := NewMockfieldsAndTermsIterator(ctrl) 2149 b.newFieldsAndTermsIteratorFn = func( 2150 _ context.Context, _ segment.Reader, opts fieldsAndTermsIteratorOpts) (fieldsAndTermsIterator, error) { 2151 return iter, nil 2152 } 2153 results := NewAggregateResults(ident.StringID("ns"), AggregateResultsOptions{ 2154 SizeLimit: seriesLimit, 2155 Type: AggregateTagNamesAndValues, 2156 }, aggTestOpts) 2157 2158 ctx := context.NewBackground() 2159 defer ctx.BlockingClose() 2160 2161 // create initial span from a mock tracer and get ctx 2162 mtr := mocktracer.New() 2163 sp := mtr.StartSpan("root") 2164 ctx.SetGoContext(opentracing.ContextWithSpan(stdlibctx.Background(), sp)) 2165 2166 // use seriesLimit instead of seriesLimit - 1 since the iterator peeks ahead to check for Done. 2167 for i := 0; i < seriesLimit; i++ { 2168 iter.EXPECT().Next().Return(true) 2169 curr := []byte(fmt.Sprint(i)) 2170 iter.EXPECT().Current().Return([]byte("f1"), curr) 2171 } 2172 2173 aggIter, err := b.AggregateIter(ctx, results.AggregateResultsOptions()) 2174 require.NoError(t, err) 2175 err = b.AggregateWithIter( 2176 ctx, 2177 aggIter, 2178 QueryOptions{SeriesLimit: seriesLimit}, 2179 results, 2180 time.Now().Add(time.Minute), 2181 emptyLogFields) 2182 require.Error(t, err) 2183 assert.True(t, strings.Contains(err.Error(), "query aborted due to limit")) 2184 2185 sp.Finish() 2186 spans := mtr.FinishedSpans() 2187 require.Len(t, spans, 3) 2188 require.Equal(t, tracepoint.NSIdxBlockAggregateQueryAddDocuments, spans[0].OperationName) 2189 require.Equal(t, tracepoint.BlockAggregate, spans[1].OperationName) 2190 2191 snap := scope.Snapshot() 2192 tallytest.AssertCounterValue(t, 1, snap, 2193 "query-limit.total-docs-matched", map[string]string{"type": "fetch"}) 2194 tallytest.AssertCounterValue(t, int64(seriesLimit), snap, 2195 "query-limit.total-docs-matched", map[string]string{"type": "aggregate"}) 2196 } 2197 2198 func TestBlockAggregateNotExhaustive(t *testing.T) { 2199 ctrl := gomock.NewController(t) 2200 defer ctrl.Finish() 2201 2202 testMD := newTestNSMetadata(t) 2203 start := xtime.Now().Truncate(time.Hour) 2204 2205 aggResultsEntryArrayPool := NewAggregateResultsEntryArrayPool(AggregateResultsEntryArrayPoolOpts{ 2206 Options: pool.NewObjectPoolOptions(). 2207 SetSize(aggregateResultsEntryArrayPoolSize), 2208 Capacity: 1, 2209 MaxCapacity: 1, 2210 }) 2211 aggResultsEntryArrayPool.Init() 2212 opts := testOpts.SetAggregateResultsEntryArrayPool(aggResultsEntryArrayPool) 2213 2214 blk, err := NewBlock(start, testMD, BlockOptions{}, 2215 namespace.NewRuntimeOptionsManager("foo"), opts) 2216 require.NoError(t, err) 2217 2218 b, ok := blk.(*block) 2219 require.True(t, ok) 2220 2221 seg1 := segment.NewMockMutableSegment(ctrl) 2222 reader := segment.NewMockReader(ctrl) 2223 reader.EXPECT().Close().Return(nil) 2224 seg1.EXPECT().Reader().Return(reader, nil) 2225 2226 b.mutableSegments.foregroundSegments = []*readableSeg{newReadableSeg(seg1, testOpts)} 2227 iter := NewMockfieldsAndTermsIterator(ctrl) 2228 b.newFieldsAndTermsIteratorFn = func( 2229 _ context.Context, _ segment.Reader, opts fieldsAndTermsIteratorOpts) (fieldsAndTermsIterator, error) { 2230 return iter, nil 2231 } 2232 2233 results := NewAggregateResults(ident.StringID("ns"), AggregateResultsOptions{ 2234 SizeLimit: 1, 2235 Type: AggregateTagNamesAndValues, 2236 }, testOpts) 2237 2238 ctx := context.NewBackground() 2239 defer ctx.BlockingClose() 2240 2241 // create initial span from a mock tracer and get ctx 2242 mtr := mocktracer.New() 2243 sp := mtr.StartSpan("root") 2244 ctx.SetGoContext(opentracing.ContextWithSpan(stdlibctx.Background(), sp)) 2245 2246 gomock.InOrder( 2247 iter.EXPECT().Next().Return(true), 2248 iter.EXPECT().Current().Return([]byte("f1"), []byte("t1")), 2249 iter.EXPECT().Next().Return(true), 2250 // even though there is a limit 1, the iterator peeks ahead so 3 results are actually consumed. 2251 iter.EXPECT().Current().Return([]byte("f2"), []byte("t2")), 2252 iter.EXPECT().Next().Return(true), 2253 iter.EXPECT().Current().Return([]byte("f3"), []byte("f3")), 2254 ) 2255 aggIter, err := b.AggregateIter(ctx, results.AggregateResultsOptions()) 2256 require.NoError(t, err) 2257 err = b.AggregateWithIter( 2258 ctx, 2259 aggIter, 2260 QueryOptions{SeriesLimit: 1}, 2261 results, 2262 time.Now().Add(time.Minute), 2263 emptyLogFields) 2264 require.NoError(t, err) 2265 2266 assertAggregateResultsMapEquals(t, map[string][]string{ 2267 "f1": {}, 2268 }, results) 2269 2270 sp.Finish() 2271 spans := mtr.FinishedSpans() 2272 require.Len(t, spans, 3) 2273 require.Equal(t, tracepoint.NSIdxBlockAggregateQueryAddDocuments, spans[0].OperationName) 2274 require.Equal(t, tracepoint.BlockAggregate, spans[1].OperationName) 2275 } 2276 2277 func TestBlockE2EInsertAggregate(t *testing.T) { 2278 ctrl := gomock.NewController(t) 2279 defer ctrl.Finish() 2280 2281 blockSize := time.Hour 2282 2283 testMD := newTestNSMetadata(t) 2284 now := xtime.Now() 2285 blockStart := now.Truncate(blockSize) 2286 2287 nowNotBlockStartAligned := now. 2288 Truncate(blockSize). 2289 Add(time.Minute) 2290 2291 // Use a larger batch size to simulate large number in a batch 2292 // coming back (to ensure code path for reusing buffers for iterator 2293 // is covered). 2294 testOpts := optionsWithDocsArrayPool(testOpts, 16, 256) 2295 2296 blk, err := NewBlock(blockStart, testMD, 2297 BlockOptions{ 2298 ForegroundCompactorMmapDocsData: true, 2299 BackgroundCompactorMmapDocsData: true, 2300 }, 2301 namespace.NewRuntimeOptionsManager("foo"), 2302 testOpts) 2303 require.NoError(t, err) 2304 b, ok := blk.(*block) 2305 require.True(t, ok) 2306 2307 h1 := doc.NewMockOnIndexSeries(ctrl) 2308 h1.EXPECT().OnIndexFinalize(blockStart) 2309 h1.EXPECT().OnIndexSuccess(blockStart) 2310 2311 h2 := doc.NewMockOnIndexSeries(ctrl) 2312 h2.EXPECT().OnIndexFinalize(blockStart) 2313 h2.EXPECT().OnIndexSuccess(blockStart) 2314 2315 h3 := doc.NewMockOnIndexSeries(ctrl) 2316 h3.EXPECT().OnIndexFinalize(blockStart) 2317 h3.EXPECT().OnIndexSuccess(blockStart) 2318 2319 batch := NewWriteBatch(testWriteBatchOptionsWithBlockSize(blockSize)) 2320 batch.Append(WriteBatchEntry{ 2321 Timestamp: nowNotBlockStartAligned, 2322 OnIndexSeries: h1, 2323 }, testDoc1()) 2324 batch.Append(WriteBatchEntry{ 2325 Timestamp: nowNotBlockStartAligned, 2326 OnIndexSeries: h2, 2327 }, testDoc2()) 2328 batch.Append(WriteBatchEntry{ 2329 Timestamp: nowNotBlockStartAligned, 2330 OnIndexSeries: h3, 2331 }, testDoc3()) 2332 2333 res, err := b.WriteBatch(batch) 2334 require.NoError(t, err) 2335 require.Equal(t, int64(3), res.NumSuccess) 2336 require.Equal(t, int64(0), res.NumError) 2337 2338 results := NewAggregateResults(ident.StringID("ns"), AggregateResultsOptions{ 2339 SizeLimit: 10, 2340 Type: AggregateTagNamesAndValues, 2341 }, testOpts) 2342 2343 ctx := context.NewBackground() 2344 mtr := mocktracer.New() 2345 sp := mtr.StartSpan("root") 2346 ctx.SetGoContext(opentracing.ContextWithSpan(stdlibctx.Background(), sp)) 2347 2348 aggIter, err := b.AggregateIter(ctx, results.AggregateResultsOptions()) 2349 require.NoError(t, err) 2350 err = b.AggregateWithIter( 2351 ctx, 2352 aggIter, 2353 QueryOptions{SeriesLimit: 1000}, 2354 results, 2355 time.Now().Add(time.Minute), 2356 emptyLogFields) 2357 require.NoError(t, err) 2358 assertAggregateResultsMapEquals(t, map[string][]string{ 2359 "bar": {"baz", "qux"}, 2360 "some": {"more", "other"}, 2361 }, results) 2362 2363 results = NewAggregateResults(ident.StringID("ns"), AggregateResultsOptions{ 2364 SizeLimit: 10, 2365 Type: AggregateTagNamesAndValues, 2366 FieldFilter: AggregateFieldFilter{[]byte("bar")}, 2367 }, testOpts) 2368 aggIter, err = b.AggregateIter(ctx, results.AggregateResultsOptions()) 2369 require.NoError(t, err) 2370 err = b.AggregateWithIter( 2371 ctx, 2372 aggIter, 2373 QueryOptions{SeriesLimit: 1000}, 2374 results, 2375 time.Now().Add(time.Minute), 2376 emptyLogFields) 2377 require.NoError(t, err) 2378 assertAggregateResultsMapEquals(t, map[string][]string{ 2379 "bar": {"baz", "qux"}, 2380 }, results) 2381 2382 results = NewAggregateResults(ident.StringID("ns"), AggregateResultsOptions{ 2383 SizeLimit: 10, 2384 Type: AggregateTagNamesAndValues, 2385 FieldFilter: AggregateFieldFilter{[]byte("random")}, 2386 }, testOpts) 2387 aggIter, err = b.AggregateIter(ctx, results.AggregateResultsOptions()) 2388 require.NoError(t, err) 2389 err = b.AggregateWithIter( 2390 ctx, 2391 aggIter, 2392 QueryOptions{SeriesLimit: 1000}, 2393 results, 2394 time.Now().Add(time.Minute), 2395 emptyLogFields) 2396 require.NoError(t, err) 2397 assertAggregateResultsMapEquals(t, map[string][]string{}, results) 2398 2399 sp.Finish() 2400 spans := mtr.FinishedSpans() 2401 require.Len(t, spans, 6) 2402 require.Equal(t, tracepoint.NSIdxBlockAggregateQueryAddDocuments, spans[0].OperationName) 2403 require.Equal(t, tracepoint.BlockAggregate, spans[1].OperationName) 2404 require.Equal(t, tracepoint.NSIdxBlockAggregateQueryAddDocuments, spans[2].OperationName) 2405 require.Equal(t, tracepoint.BlockAggregate, spans[3].OperationName) 2406 require.Equal(t, tracepoint.BlockAggregate, spans[4].OperationName) 2407 } 2408 2409 func assertAggregateResultsMapEquals(t *testing.T, expected map[string][]string, observed AggregateResults) { 2410 aggResultsMap := observed.Map() 2411 // ensure `expected` contained in `observed` 2412 for field, terms := range expected { 2413 entry, ok := aggResultsMap.Get(ident.StringID(field)) 2414 require.True(t, ok, 2415 fmt.Sprintf("field from expected map missing in observed: field=%s", field)) 2416 valuesMap := entry.valuesMap 2417 for _, term := range terms { 2418 _, ok = valuesMap.Get(ident.StringID(term)) 2419 require.True(t, ok, 2420 fmt.Sprintf("term from expected map missing in observed: field=%s, term=%s", field, term)) 2421 } 2422 } 2423 // ensure `observed` contained in `expected` 2424 for _, entry := range aggResultsMap.Iter() { 2425 field := entry.Key() 2426 valuesMap := entry.Value().valuesMap 2427 for _, entry := range valuesMap.Iter() { 2428 term := entry.Key() 2429 slice, ok := expected[field.String()] 2430 require.True(t, ok, 2431 fmt.Sprintf("field from observed map missing in expected: field=%s", field.String())) 2432 found := false 2433 for _, expTerm := range slice { 2434 if expTerm == term.String() { 2435 found = true 2436 } 2437 } 2438 require.True(t, found, 2439 fmt.Sprintf("term from observed map missing in expected: field=%s, term=%s", field.String(), term.String())) 2440 } 2441 } 2442 } 2443 2444 func testSegment(t *testing.T, docs ...doc.Metadata) segment.Segment { 2445 seg, err := mem.NewSegment(testOpts.MemSegmentOptions()) 2446 require.NoError(t, err) 2447 2448 for _, d := range docs { 2449 _, err = seg.Insert(d) 2450 require.NoError(t, err) 2451 } 2452 2453 return seg 2454 } 2455 2456 func testDoc1() doc.Metadata { 2457 return doc.Metadata{ 2458 ID: []byte("foo"), 2459 Fields: []doc.Field{ 2460 { 2461 Name: []byte("bar"), 2462 Value: []byte("baz"), 2463 }, 2464 }, 2465 } 2466 } 2467 2468 func testDoc1DupeID() doc.Metadata { 2469 return doc.Metadata{ 2470 ID: []byte("foo"), 2471 Fields: []doc.Field{ 2472 { 2473 Name: []byte("why"), 2474 Value: []byte("not"), 2475 }, 2476 { 2477 Name: []byte("some"), 2478 Value: []byte("more"), 2479 }, 2480 }, 2481 } 2482 } 2483 2484 func testDoc2() doc.Metadata { 2485 return doc.Metadata{ 2486 ID: []byte("something"), 2487 Fields: []doc.Field{ 2488 { 2489 Name: []byte("bar"), 2490 Value: []byte("baz"), 2491 }, 2492 { 2493 Name: []byte("some"), 2494 Value: []byte("more"), 2495 }, 2496 }, 2497 } 2498 } 2499 2500 func testDoc3() doc.Metadata { 2501 return doc.Metadata{ 2502 ID: []byte("bar"), 2503 Fields: []doc.Field{ 2504 { 2505 Name: []byte("bar"), 2506 Value: []byte("qux"), 2507 }, 2508 { 2509 Name: []byte("some"), 2510 Value: []byte("other"), 2511 }, 2512 }, 2513 } 2514 } 2515 2516 func optionsWithAggResultsPool(capacity int) Options { 2517 pool := NewAggregateResultsEntryArrayPool( 2518 AggregateResultsEntryArrayPoolOpts{ 2519 Capacity: capacity, 2520 }, 2521 ) 2522 2523 pool.Init() 2524 return testOpts.SetAggregateResultsEntryArrayPool(pool) 2525 } 2526 2527 func buildSegment(t *testing.T, term string, fields []string, opts mem.Options) *readableSeg { 2528 seg, err := mem.NewSegment(opts) 2529 require.NoError(t, err) 2530 2531 docFields := make([]doc.Field, 0, len(fields)) 2532 sort.Strings(fields) 2533 for _, field := range fields { 2534 docFields = append(docFields, doc.Field{Name: []byte(term), Value: []byte(field)}) 2535 } 2536 2537 _, err = seg.Insert(doc.Metadata{Fields: docFields}) 2538 require.NoError(t, err) 2539 2540 require.NoError(t, seg.Seal()) 2541 return newReadableSeg(seg, testOpts) 2542 } 2543 2544 func TestBlockAggregateBatching(t *testing.T) { 2545 memOpts := mem.NewOptions() 2546 2547 var ( 2548 batchSizeMap = make(map[string][]string) 2549 batchSizeSegments = make([]*readableSeg, 0, defaultQueryDocsBatchSize) 2550 ) 2551 2552 for i := 0; i < defaultQueryDocsBatchSize; i++ { 2553 fields := make([]string, 0, defaultQueryDocsBatchSize) 2554 for j := 0; j < defaultQueryDocsBatchSize; j++ { 2555 fields = append(fields, fmt.Sprintf("bar_%d", j)) 2556 } 2557 2558 if i == 0 { 2559 batchSizeMap["foo"] = fields 2560 } 2561 2562 batchSizeSegments = append(batchSizeSegments, buildSegment(t, "foo", fields, memOpts)) 2563 } 2564 2565 tests := []struct { 2566 name string 2567 batchSize int 2568 segments []*readableSeg 2569 expectedDocsMatched int64 2570 expectedAggDocsMatched int64 2571 expected map[string][]string 2572 }{ 2573 { 2574 name: "single term multiple fields duplicated across readers", 2575 batchSize: 3, 2576 segments: []*readableSeg{ 2577 buildSegment(t, "foo", []string{"bar", "baz"}, memOpts), 2578 buildSegment(t, "foo", []string{"bar", "baz"}, memOpts), 2579 buildSegment(t, "foo", []string{"bar", "baz"}, memOpts), 2580 }, 2581 expectedDocsMatched: 1, 2582 expectedAggDocsMatched: 9, 2583 expected: map[string][]string{ 2584 "foo": {"bar", "baz"}, 2585 }, 2586 }, 2587 { 2588 name: "multiple term multiple fields", 2589 batchSize: 3, 2590 segments: []*readableSeg{ 2591 buildSegment(t, "foo", []string{"bar", "baz"}, memOpts), 2592 buildSegment(t, "foo", []string{"bag", "bat"}, memOpts), 2593 buildSegment(t, "qux", []string{"bar", "baz"}, memOpts), 2594 }, 2595 expectedDocsMatched: 2, 2596 expectedAggDocsMatched: 9, 2597 expected: map[string][]string{ 2598 "foo": {"bag", "bar", "bat", "baz"}, 2599 "qux": {"bar", "baz"}, 2600 }, 2601 }, 2602 { 2603 name: "term present in first and third reader", 2604 // NB: expecting three batches due to the way batches are split (on the 2605 // first different term ID in a batch), will look like this: 2606 // [{foo [bar baz]} {dog [bar baz]} {qux [bar]} 2607 // [{qux [baz]} {qaz [bar baz]} {foo [bar]}] 2608 // [{foo [baz]}] 2609 batchSize: 3, 2610 segments: []*readableSeg{ 2611 buildSegment(t, "foo", []string{"bar", "baz"}, memOpts), 2612 buildSegment(t, "dog", []string{"bar", "baz"}, memOpts), 2613 buildSegment(t, "qux", []string{"bar", "baz"}, memOpts), 2614 buildSegment(t, "qaz", []string{"bar", "baz"}, memOpts), 2615 buildSegment(t, "foo", []string{"bar", "baz"}, memOpts), 2616 }, 2617 expectedDocsMatched: 7, 2618 expectedAggDocsMatched: 15, 2619 expected: map[string][]string{ 2620 "foo": {"bar", "baz"}, 2621 "dog": {"bar", "baz"}, 2622 "qux": {"bar", "baz"}, 2623 "qaz": {"bar", "baz"}, 2624 }, 2625 }, 2626 { 2627 name: "batch size case", 2628 batchSize: defaultQueryDocsBatchSize, 2629 segments: batchSizeSegments, 2630 expectedDocsMatched: 1, 2631 expectedAggDocsMatched: 256*257 + 2, 2632 expected: batchSizeMap, 2633 }, 2634 } 2635 2636 for _, tt := range tests { 2637 t.Run(tt.name, func(t *testing.T) { 2638 scope := tally.NewTestScope("", nil) 2639 iOpts := instrument.NewOptions().SetMetricsScope(scope) 2640 limitOpts := limits.NewOptions(). 2641 SetInstrumentOptions(iOpts). 2642 SetDocsLimitOpts(limits.LookbackLimitOptions{Lookback: time.Minute}). 2643 SetBytesReadLimitOpts(limits.LookbackLimitOptions{Lookback: time.Minute}). 2644 SetAggregateDocsLimitOpts(limits.LookbackLimitOptions{Lookback: time.Minute}) 2645 queryLimits, err := limits.NewQueryLimits(limitOpts) 2646 require.NoError(t, err) 2647 testOpts = optionsWithAggResultsPool(tt.batchSize). 2648 SetInstrumentOptions(iOpts). 2649 SetQueryLimits(queryLimits) 2650 2651 testMD := newTestNSMetadata(t) 2652 start := xtime.Now().Truncate(time.Hour) 2653 blk, err := NewBlock(start, testMD, BlockOptions{}, 2654 namespace.NewRuntimeOptionsManager("foo"), testOpts) 2655 require.NoError(t, err) 2656 2657 b, ok := blk.(*block) 2658 require.True(t, ok) 2659 2660 // NB: wrap existing aggregate results fn to more easily inspect batch size. 2661 addAggregateResultsFn := b.addAggregateResultsFn 2662 b.addAggregateResultsFn = func( 2663 ctx context.Context, 2664 results AggregateResults, 2665 batch []AggregateResultsEntry, 2666 source []byte, 2667 ) ([]AggregateResultsEntry, int, int, error) { 2668 // NB: since both terms and values count towards the batch size, initialize 2669 // this with batch size to account for terms. 2670 count := len(batch) 2671 for _, entry := range batch { 2672 count += len(entry.Terms) 2673 } 2674 2675 require.True(t, count <= tt.batchSize, 2676 fmt.Sprintf("batch %v exceeds batchSize %d", batch, tt.batchSize)) 2677 2678 return addAggregateResultsFn(ctx, results, batch, source) 2679 } 2680 2681 b.mutableSegments.foregroundSegments = tt.segments 2682 results := NewAggregateResults(ident.StringID("ns"), AggregateResultsOptions{ 2683 Type: AggregateTagNamesAndValues, 2684 }, testOpts) 2685 2686 ctx := context.NewBackground() 2687 defer ctx.BlockingClose() 2688 2689 aggIter, err := b.AggregateIter(ctx, results.AggregateResultsOptions()) 2690 require.NoError(t, err) 2691 err = b.AggregateWithIter( 2692 ctx, 2693 aggIter, 2694 QueryOptions{}, 2695 results, 2696 time.Now().Add(time.Minute), 2697 emptyLogFields) 2698 require.NoError(t, err) 2699 2700 snap := scope.Snapshot() 2701 tallytest.AssertCounterValue(t, tt.expectedDocsMatched, snap, 2702 "query-limit.total-docs-matched", map[string]string{"type": "fetch"}) 2703 tallytest.AssertCounterValue(t, tt.expectedAggDocsMatched, snap, 2704 "query-limit.total-docs-matched", map[string]string{"type": "aggregate"}) 2705 2706 resultsMap := make(map[string][]string, results.Map().Len()) 2707 for _, res := range results.Map().Iter() { 2708 vals := make([]string, 0, res.Value().valuesMap.Len()) 2709 for _, val := range res.Value().valuesMap.Iter() { 2710 vals = append(vals, val.Key().String()) 2711 } 2712 2713 sort.Strings(vals) 2714 resultsMap[res.Key().String()] = vals 2715 } 2716 2717 assert.Equal(t, tt.expected, resultsMap) 2718 }) 2719 } 2720 }