github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/rowexec/windower_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 rowexec
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"math"
    17  	"strings"
    18  	"testing"
    19  
    20  	"github.com/cockroachdb/cockroach/pkg/base"
    21  	"github.com/cockroachdb/cockroach/pkg/settings/cluster"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/execinfra"
    23  	"github.com/cockroachdb/cockroach/pkg/sql/execinfrapb"
    24  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    25  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    26  	"github.com/cockroachdb/cockroach/pkg/storage"
    27  	"github.com/cockroachdb/cockroach/pkg/testutils/distsqlutils"
    28  	"github.com/cockroachdb/cockroach/pkg/util/encoding"
    29  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    30  	"github.com/cockroachdb/cockroach/pkg/util/mon"
    31  )
    32  
    33  const noFilterIdx = -1
    34  
    35  func TestWindowerAccountingForResults(t *testing.T) {
    36  	defer leaktest.AfterTest(t)()
    37  	ctx := context.Background()
    38  	st := cluster.MakeTestingClusterSettings()
    39  	monitor := mon.MakeMonitorWithLimit(
    40  		"test-monitor",
    41  		mon.MemoryResource,
    42  		100000,        /* limit */
    43  		nil,           /* curCount */
    44  		nil,           /* maxHist */
    45  		5000,          /* increment */
    46  		math.MaxInt64, /* noteworthy */
    47  		st,
    48  	)
    49  	evalCtx := tree.MakeTestingEvalContextWithMon(st, &monitor)
    50  	defer evalCtx.Stop(ctx)
    51  	diskMonitor := execinfra.NewTestDiskMonitor(ctx, st)
    52  	defer diskMonitor.Stop(ctx)
    53  	tempEngine, _, err := storage.NewTempEngine(ctx, storage.DefaultStorageEngine, base.DefaultTestTempStorageConfig(st), base.DefaultTestStoreSpec)
    54  	if err != nil {
    55  		t.Fatal(err)
    56  	}
    57  	defer tempEngine.Close()
    58  
    59  	flowCtx := &execinfra.FlowCtx{
    60  		EvalCtx: &evalCtx,
    61  		Cfg: &execinfra.ServerConfig{
    62  			Settings:    st,
    63  			TempStorage: tempEngine,
    64  			DiskMonitor: diskMonitor,
    65  		},
    66  	}
    67  
    68  	post := &execinfrapb.PostProcessSpec{}
    69  	input := execinfra.NewRepeatableRowSource(sqlbase.OneIntCol, sqlbase.MakeIntRows(1000, 1))
    70  	aggSpec := execinfrapb.AggregatorSpec_ARRAY_AGG
    71  	spec := execinfrapb.WindowerSpec{
    72  		PartitionBy: []uint32{},
    73  		WindowFns: []execinfrapb.WindowerSpec_WindowFn{{
    74  			Func:         execinfrapb.WindowerSpec_Func{AggregateFunc: &aggSpec},
    75  			ArgsIdxs:     []uint32{0},
    76  			Ordering:     execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 0}}},
    77  			OutputColIdx: 1,
    78  			FilterColIdx: noFilterIdx,
    79  			Frame: &execinfrapb.WindowerSpec_Frame{
    80  				Mode: execinfrapb.WindowerSpec_Frame_ROWS,
    81  				Bounds: execinfrapb.WindowerSpec_Frame_Bounds{
    82  					Start: execinfrapb.WindowerSpec_Frame_Bound{
    83  						BoundType: execinfrapb.WindowerSpec_Frame_OFFSET_PRECEDING,
    84  						IntOffset: 100,
    85  					},
    86  				},
    87  			},
    88  		}},
    89  	}
    90  	output := distsqlutils.NewRowBuffer(
    91  		sqlbase.OneIntCol, nil, distsqlutils.RowBufferArgs{},
    92  	)
    93  
    94  	d, err := newWindower(flowCtx, 0 /* processorID */, &spec, input, post, output)
    95  	if err != nil {
    96  		t.Fatal(err)
    97  	}
    98  	d.Run(ctx)
    99  	for {
   100  		row, meta := output.Next()
   101  		if row != nil {
   102  			t.Fatalf("unexpectedly received row %+v", row)
   103  		}
   104  		if meta == nil {
   105  			t.Fatalf("unexpectedly didn't receive an OOM error")
   106  		}
   107  		if meta.Err != nil {
   108  			if !strings.Contains(meta.Err.Error(), "memory budget exceeded") {
   109  				t.Fatalf("unexpectedly received an error other than OOM")
   110  			}
   111  			break
   112  		}
   113  	}
   114  }
   115  
   116  type windowTestSpec struct {
   117  	// The column indices of PARTITION BY clause.
   118  	partitionBy []uint32
   119  	// Window function to be computed.
   120  	windowFn windowFnTestSpec
   121  }
   122  
   123  type windowFnTestSpec struct {
   124  	funcName       string
   125  	argsIdxs       []uint32
   126  	columnOrdering sqlbase.ColumnOrdering
   127  }
   128  
   129  func windows(windowTestSpecs []windowTestSpec) ([]execinfrapb.WindowerSpec, error) {
   130  	windows := make([]execinfrapb.WindowerSpec, len(windowTestSpecs))
   131  	for i, spec := range windowTestSpecs {
   132  		windows[i].PartitionBy = spec.partitionBy
   133  		windows[i].WindowFns = make([]execinfrapb.WindowerSpec_WindowFn, 1)
   134  		windowFnSpec := execinfrapb.WindowerSpec_WindowFn{}
   135  		fnSpec, err := CreateWindowerSpecFunc(spec.windowFn.funcName)
   136  		if err != nil {
   137  			return nil, err
   138  		}
   139  		windowFnSpec.Func = fnSpec
   140  		windowFnSpec.ArgsIdxs = spec.windowFn.argsIdxs
   141  		if spec.windowFn.columnOrdering != nil {
   142  			ordCols := make([]execinfrapb.Ordering_Column, 0, len(spec.windowFn.columnOrdering))
   143  			for _, column := range spec.windowFn.columnOrdering {
   144  				ordCols = append(ordCols, execinfrapb.Ordering_Column{
   145  					ColIdx: uint32(column.ColIdx),
   146  					// We need this -1 because encoding.Direction has extra value "_"
   147  					// as zeroth "entry" which its proto equivalent doesn't have.
   148  					Direction: execinfrapb.Ordering_Column_Direction(column.Direction - 1),
   149  				})
   150  			}
   151  			windowFnSpec.Ordering = execinfrapb.Ordering{Columns: ordCols}
   152  		}
   153  		windowFnSpec.FilterColIdx = noFilterIdx
   154  		windows[i].WindowFns[0] = windowFnSpec
   155  	}
   156  	return windows, nil
   157  }
   158  
   159  func BenchmarkWindower(b *testing.B) {
   160  	const numCols = 3
   161  	const numRows = 100000
   162  
   163  	specs, err := windows([]windowTestSpec{
   164  		{ // sum(@1) OVER ()
   165  			partitionBy: []uint32{},
   166  			windowFn: windowFnTestSpec{
   167  				funcName: "SUM",
   168  				argsIdxs: []uint32{0},
   169  			},
   170  		},
   171  		{ // sum(@1) OVER (ORDER BY @3)
   172  			windowFn: windowFnTestSpec{
   173  				funcName:       "SUM",
   174  				argsIdxs:       []uint32{0},
   175  				columnOrdering: sqlbase.ColumnOrdering{{ColIdx: 2, Direction: encoding.Ascending}},
   176  			},
   177  		},
   178  		{ // sum(@1) OVER (PARTITION BY @2)
   179  			partitionBy: []uint32{1},
   180  			windowFn: windowFnTestSpec{
   181  				funcName: "SUM",
   182  				argsIdxs: []uint32{0},
   183  			},
   184  		},
   185  		{ // sum(@1) OVER (PARTITION BY @2 ORDER BY @3)
   186  			partitionBy: []uint32{1},
   187  			windowFn: windowFnTestSpec{
   188  				funcName:       "SUM",
   189  				argsIdxs:       []uint32{0},
   190  				columnOrdering: sqlbase.ColumnOrdering{{ColIdx: 2, Direction: encoding.Ascending}},
   191  			},
   192  		},
   193  	})
   194  	if err != nil {
   195  		b.Fatal(err)
   196  	}
   197  
   198  	ctx := context.Background()
   199  	st := cluster.MakeTestingClusterSettings()
   200  	evalCtx := tree.MakeTestingEvalContext(st)
   201  	defer evalCtx.Stop(ctx)
   202  	diskMonitor := execinfra.NewTestDiskMonitor(ctx, st)
   203  	defer diskMonitor.Stop(ctx)
   204  
   205  	flowCtx := &execinfra.FlowCtx{
   206  		EvalCtx: &evalCtx,
   207  		Cfg: &execinfra.ServerConfig{
   208  			Settings:    st,
   209  			DiskMonitor: diskMonitor,
   210  		},
   211  	}
   212  
   213  	rowsGenerators := []func(int, int) sqlbase.EncDatumRows{
   214  		sqlbase.MakeIntRows,
   215  		func(numRows, numCols int) sqlbase.EncDatumRows {
   216  			return sqlbase.MakeRepeatedIntRows(numRows/100, numRows, numCols)
   217  		},
   218  	}
   219  	skipRepeatedSpecs := map[int]bool{0: true, 1: true}
   220  
   221  	for j, rowsGenerator := range rowsGenerators {
   222  		for i, spec := range specs {
   223  			if skipRepeatedSpecs[i] && j == 1 {
   224  				continue
   225  			}
   226  			runName := fmt.Sprintf("%s() OVER (", spec.WindowFns[0].Func.AggregateFunc.String())
   227  			if len(spec.PartitionBy) > 0 {
   228  				runName = runName + "PARTITION BY"
   229  				if j == 0 {
   230  					runName = runName + "/* SINGLE ROW PARTITIONS */"
   231  				} else {
   232  					runName = runName + "/* MULTIPLE ROWS PARTITIONS */"
   233  				}
   234  			}
   235  			if len(spec.PartitionBy) > 0 && spec.WindowFns[0].Ordering.Columns != nil {
   236  				runName = runName + " "
   237  			}
   238  			if spec.WindowFns[0].Ordering.Columns != nil {
   239  				runName = runName + "ORDER BY"
   240  			}
   241  			runName = runName + ")"
   242  			spec.WindowFns[0].OutputColIdx = 3
   243  
   244  			b.Run(runName, func(b *testing.B) {
   245  				post := &execinfrapb.PostProcessSpec{}
   246  				disposer := &rowDisposer{}
   247  				input := execinfra.NewRepeatableRowSource(sqlbase.ThreeIntCols, rowsGenerator(numRows, numCols))
   248  
   249  				b.SetBytes(int64(8 * numRows * numCols))
   250  				b.ResetTimer()
   251  				for i := 0; i < b.N; i++ {
   252  					d, err := newWindower(flowCtx, 0 /* processorID */, &spec, input, post, disposer)
   253  					if err != nil {
   254  						b.Fatal(err)
   255  					}
   256  					d.Run(context.Background())
   257  					input.Reset()
   258  				}
   259  				b.StopTimer()
   260  			})
   261  		}
   262  	}
   263  }