github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexec/stats_test.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package colexec 12 13 import ( 14 "context" 15 "testing" 16 "time" 17 18 "github.com/cockroachdb/cockroach/pkg/col/coldata" 19 "github.com/cockroachdb/cockroach/pkg/sql/colexecbase" 20 "github.com/cockroachdb/cockroach/pkg/sql/execinfrapb" 21 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 22 "github.com/cockroachdb/cockroach/pkg/sql/types" 23 "github.com/cockroachdb/cockroach/pkg/testutils/colcontainerutils" 24 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 25 "github.com/cockroachdb/cockroach/pkg/util/timeutil" 26 "github.com/stretchr/testify/require" 27 ) 28 29 // TestNumBatches is a unit test for NumBatches field of VectorizedStats. 30 func TestNumBatches(t *testing.T) { 31 defer leaktest.AfterTest(t)() 32 nBatches := 10 33 noop := NewNoop(makeFiniteChunksSourceWithBatchSize(nBatches, coldata.BatchSize())) 34 vsc := NewVectorizedStatsCollector( 35 noop, 0 /* id */, execinfrapb.ProcessorIDTagKey, true, /* isStall */ 36 timeutil.NewStopWatch(), nil /* memMonitors */, nil, /* diskMonitors */ 37 ) 38 vsc.Init() 39 for { 40 b := vsc.Next(context.Background()) 41 if b.Length() == 0 { 42 break 43 } 44 } 45 require.Equal(t, nBatches, int(vsc.NumBatches)) 46 } 47 48 // TestNumTuples is a unit test for NumTuples field of VectorizedStats. 49 func TestNumTuples(t *testing.T) { 50 defer leaktest.AfterTest(t)() 51 nBatches := 10 52 for _, batchSize := range []int{1, 16, 1024} { 53 noop := NewNoop(makeFiniteChunksSourceWithBatchSize(nBatches, batchSize)) 54 vsc := NewVectorizedStatsCollector( 55 noop, 0 /* id */, execinfrapb.ProcessorIDTagKey, true, /* isStall */ 56 timeutil.NewStopWatch(), nil /* memMonitors */, nil, /* diskMonitors */ 57 ) 58 vsc.Init() 59 for { 60 b := vsc.Next(context.Background()) 61 if b.Length() == 0 { 62 break 63 } 64 } 65 require.Equal(t, nBatches*batchSize, int(vsc.NumTuples)) 66 } 67 } 68 69 // TestVectorizedStatsCollector is an integration test for the 70 // VectorizedStatsCollector. It creates two inputs and feeds them into the 71 // merge joiner and makes sure that all the stats measured on the latter are as 72 // expected. 73 func TestVectorizedStatsCollector(t *testing.T) { 74 defer leaktest.AfterTest(t)() 75 queueCfg, cleanup := colcontainerutils.NewTestingDiskQueueCfg(t, true /* inMem */) 76 defer cleanup() 77 for nBatches := 1; nBatches < 5; nBatches++ { 78 timeSource := timeutil.NewTestTimeSource() 79 mjInputWatch := timeutil.NewTestStopWatch(timeSource.Now) 80 81 leftSource := &timeAdvancingOperator{ 82 OneInputNode: NewOneInputNode(makeFiniteChunksSourceWithBatchSize(nBatches, coldata.BatchSize())), 83 timeSource: timeSource, 84 } 85 leftInput := NewVectorizedStatsCollector( 86 leftSource, 0 /* id */, execinfrapb.ProcessorIDTagKey, true, /* isStall */ 87 timeutil.NewTestStopWatch(timeSource.Now), nil /* memMonitors */, nil, /* diskMonitors */ 88 ) 89 leftInput.SetOutputWatch(mjInputWatch) 90 91 rightSource := &timeAdvancingOperator{ 92 OneInputNode: NewOneInputNode(makeFiniteChunksSourceWithBatchSize(nBatches, coldata.BatchSize())), 93 timeSource: timeSource, 94 } 95 rightInput := NewVectorizedStatsCollector( 96 rightSource, 1 /* id */, execinfrapb.ProcessorIDTagKey, true, /* isStall */ 97 timeutil.NewTestStopWatch(timeSource.Now), nil /* memMonitors */, nil, /* diskMonitors */ 98 ) 99 rightInput.SetOutputWatch(mjInputWatch) 100 101 mergeJoiner, err := newMergeJoinOp( 102 testAllocator, defaultMemoryLimit, queueCfg, 103 colexecbase.NewTestingSemaphore(4), sqlbase.InnerJoin, leftInput, rightInput, 104 []*types.T{types.Int}, []*types.T{types.Int}, 105 []execinfrapb.Ordering_Column{{ColIdx: 0}}, 106 []execinfrapb.Ordering_Column{{ColIdx: 0}}, 107 testDiskAcc, 108 ) 109 if err != nil { 110 t.Fatal(err) 111 } 112 timeAdvancingMergeJoiner := &timeAdvancingOperator{ 113 OneInputNode: NewOneInputNode(mergeJoiner), 114 timeSource: timeSource, 115 } 116 mjStatsCollector := NewVectorizedStatsCollector( 117 timeAdvancingMergeJoiner, 2 /* id */, execinfrapb.ProcessorIDTagKey, false, /* isStall */ 118 mjInputWatch, nil /* memMonitors */, nil, /* diskMonitors */ 119 ) 120 121 // The inputs are identical, so the merge joiner should output nBatches 122 // batches with each having coldata.BatchSize() tuples. 123 mjStatsCollector.Init() 124 batchCount := 0 125 for { 126 b := mjStatsCollector.Next(context.Background()) 127 if b.Length() == 0 { 128 break 129 } 130 require.Equal(t, coldata.BatchSize(), b.Length()) 131 batchCount++ 132 } 133 mjStatsCollector.finalizeStats() 134 135 require.Equal(t, nBatches, batchCount) 136 require.Equal(t, nBatches, int(mjStatsCollector.NumBatches)) 137 require.Equal(t, nBatches*coldata.BatchSize(), int(mjStatsCollector.NumTuples)) 138 // Two inputs are advancing the time source for a total of 2 * nBatches 139 // advances, but these do not count towards merge joiner execution time. 140 // Merge joiner advances the time on its every non-empty batch totaling 141 // nBatches advances that should be accounted for in stats. 142 require.Equal(t, time.Duration(nBatches), mjStatsCollector.Time) 143 } 144 } 145 146 func makeFiniteChunksSourceWithBatchSize(nBatches int, batchSize int) colexecbase.Operator { 147 typs := []*types.T{types.Int} 148 batch := testAllocator.NewMemBatchWithSize(typs, batchSize) 149 vec := batch.ColVec(0).Int64() 150 for i := 0; i < batchSize; i++ { 151 vec[i] = int64(i) 152 } 153 batch.SetLength(batchSize) 154 return newFiniteChunksSource(batch, typs, nBatches, 1 /* matchLen */) 155 } 156 157 // timeAdvancingOperator is an Operator that advances the time source upon 158 // receiving a non-empty batch from its input. It is used for testing only. 159 type timeAdvancingOperator struct { 160 OneInputNode 161 162 timeSource *timeutil.TestTimeSource 163 } 164 165 var _ colexecbase.Operator = &timeAdvancingOperator{} 166 167 func (o *timeAdvancingOperator) Init() { 168 o.input.Init() 169 } 170 171 func (o *timeAdvancingOperator) Next(ctx context.Context) coldata.Batch { 172 b := o.input.Next(ctx) 173 if b.Length() > 0 { 174 o.timeSource.Advance() 175 } 176 return b 177 }