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  }