github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/flowinfra/stream_data_test.go (about)

     1  // Copyright 2016 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 flowinfra
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"math/rand"
    17  	"testing"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/sql/execinfra"
    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/util/leaktest"
    24  	"github.com/cockroachdb/cockroach/pkg/util/randutil"
    25  )
    26  
    27  // The encoder/decoder don't maintain the ordering between rows and metadata
    28  // records.
    29  func testGetDecodedRows(
    30  	tb testing.TB,
    31  	sd *StreamDecoder,
    32  	decodedRows sqlbase.EncDatumRows,
    33  	metas []execinfrapb.ProducerMetadata,
    34  ) (sqlbase.EncDatumRows, []execinfrapb.ProducerMetadata) {
    35  	for {
    36  		row, meta, err := sd.GetRow(nil /* rowBuf */)
    37  		if err != nil {
    38  			tb.Fatal(err)
    39  		}
    40  		if row == nil && meta == nil {
    41  			break
    42  		}
    43  		if row != nil {
    44  			decodedRows = append(decodedRows, row)
    45  		} else {
    46  			metas = append(metas, *meta)
    47  		}
    48  	}
    49  	return decodedRows, metas
    50  }
    51  
    52  func testRowStream(tb testing.TB, rng *rand.Rand, types []*types.T, records []rowOrMeta) {
    53  	var se StreamEncoder
    54  	var sd StreamDecoder
    55  
    56  	var decodedRows sqlbase.EncDatumRows
    57  	var metas []execinfrapb.ProducerMetadata
    58  	numRows := 0
    59  	numMeta := 0
    60  
    61  	se.Init(types)
    62  
    63  	for rowIdx := 0; rowIdx <= len(records); rowIdx++ {
    64  		if rowIdx < len(records) {
    65  			if records[rowIdx].row != nil {
    66  				if err := se.AddRow(records[rowIdx].row); err != nil {
    67  					tb.Fatal(err)
    68  				}
    69  				numRows++
    70  			} else {
    71  				se.AddMetadata(context.Background(), records[rowIdx].meta)
    72  				numMeta++
    73  			}
    74  		}
    75  		// "Send" a message every now and then and once at the end.
    76  		final := (rowIdx == len(records))
    77  		if final || (rowIdx > 0 && rng.Intn(10) == 0) {
    78  			msg := se.FormMessage(context.Background())
    79  			// Make a copy of the data buffer.
    80  			msg.Data.RawBytes = append([]byte(nil), msg.Data.RawBytes...)
    81  			err := sd.AddMessage(context.Background(), msg)
    82  			if err != nil {
    83  				tb.Fatal(err)
    84  			}
    85  			decodedRows, metas = testGetDecodedRows(tb, &sd, decodedRows, metas)
    86  		}
    87  	}
    88  	if len(metas) != numMeta {
    89  		tb.Errorf("expected %d metadata records, got: %d", numMeta, len(metas))
    90  	}
    91  	if len(decodedRows) != numRows {
    92  		tb.Errorf("expected %d rows, got: %d", numRows, len(decodedRows))
    93  	}
    94  }
    95  
    96  type rowOrMeta struct {
    97  	row  sqlbase.EncDatumRow
    98  	meta execinfrapb.ProducerMetadata
    99  }
   100  
   101  // TestStreamEncodeDecode generates random streams of EncDatums and passes them
   102  // through a StreamEncoder and a StreamDecoder
   103  func TestStreamEncodeDecode(t *testing.T) {
   104  	defer leaktest.AfterTest(t)()
   105  	rng, _ := randutil.NewPseudoRand()
   106  	for test := 0; test < 100; test++ {
   107  		rowLen := rng.Intn(20)
   108  		types := sqlbase.RandEncodableColumnTypes(rng, rowLen)
   109  		info := make([]execinfrapb.DatumInfo, rowLen)
   110  		for i := range info {
   111  			info[i].Type = types[i]
   112  			info[i].Encoding = sqlbase.RandDatumEncoding(rng)
   113  		}
   114  		numRows := rng.Intn(100)
   115  		rows := make([]rowOrMeta, numRows)
   116  		for i := range rows {
   117  			if rng.Intn(10) != 0 {
   118  				rows[i].row = make(sqlbase.EncDatumRow, rowLen)
   119  				for j := range rows[i].row {
   120  					rows[i].row[j] = sqlbase.DatumToEncDatum(info[j].Type,
   121  						sqlbase.RandDatum(rng, info[j].Type, true))
   122  				}
   123  			} else {
   124  				rows[i].meta.Err = fmt.Errorf("test error %d", i)
   125  			}
   126  		}
   127  		testRowStream(t, rng, types, rows)
   128  	}
   129  }
   130  
   131  func TestEmptyStreamEncodeDecode(t *testing.T) {
   132  	defer leaktest.AfterTest(t)()
   133  	var se StreamEncoder
   134  	var sd StreamDecoder
   135  	msg := se.FormMessage(context.Background())
   136  	if err := sd.AddMessage(context.Background(), msg); err != nil {
   137  		t.Fatal(err)
   138  	}
   139  	if msg.Header == nil {
   140  		t.Errorf("no header in first message")
   141  	}
   142  	if row, meta, err := sd.GetRow(nil /* rowBuf */); err != nil {
   143  		t.Fatal(err)
   144  	} else if meta != nil || row != nil {
   145  		t.Errorf("received bogus row %v %v", row, meta)
   146  	}
   147  }
   148  
   149  func BenchmarkStreamEncoder(b *testing.B) {
   150  	numRows := 1 << 16
   151  
   152  	for _, numCols := range []int{1, 4, 16, 64} {
   153  		b.Run(fmt.Sprintf("rows=%d,cols=%d", numRows, numCols), func(b *testing.B) {
   154  			b.SetBytes(int64(numRows * numCols * 8))
   155  			cols := sqlbase.MakeIntCols(numCols)
   156  			rows := sqlbase.MakeIntRows(numRows, numCols)
   157  			input := execinfra.NewRepeatableRowSource(cols, rows)
   158  
   159  			b.ResetTimer()
   160  			ctx := context.Background()
   161  
   162  			for i := 0; i < b.N; i++ {
   163  				b.StopTimer()
   164  				input.Reset()
   165  				// Reset the EncDatums' encoded bytes cache.
   166  				for _, row := range rows {
   167  					for j := range row {
   168  						row[j] = sqlbase.EncDatum{
   169  							Datum: row[j].Datum,
   170  						}
   171  					}
   172  				}
   173  				var se StreamEncoder
   174  				se.Init(cols)
   175  				b.StartTimer()
   176  
   177  				// Add rows to the StreamEncoder until the input source is exhausted.
   178  				// "Flush" every outboxBufRows.
   179  				for j := 0; ; j++ {
   180  					row, _ := input.Next()
   181  					if row == nil {
   182  						break
   183  					}
   184  					if err := se.AddRow(row); err != nil {
   185  						b.Fatal(err)
   186  					}
   187  					if j%outboxBufRows == 0 {
   188  						// ignore output
   189  						se.FormMessage(ctx)
   190  					}
   191  				}
   192  			}
   193  		})
   194  	}
   195  }
   196  
   197  func BenchmarkStreamDecoder(b *testing.B) {
   198  	ctx := context.Background()
   199  
   200  	for _, numCols := range []int{1, 4, 16, 64} {
   201  		b.Run(fmt.Sprintf("cols=%d", numCols), func(b *testing.B) {
   202  			b.SetBytes(int64(outboxBufRows * numCols * 8))
   203  			var se StreamEncoder
   204  			colTypes := sqlbase.MakeIntCols(numCols)
   205  			se.Init(colTypes)
   206  			inRow := sqlbase.MakeIntRows(1, numCols)[0]
   207  			for i := 0; i < outboxBufRows; i++ {
   208  				if err := se.AddRow(inRow); err != nil {
   209  					b.Fatal(err)
   210  				}
   211  			}
   212  			msg := se.FormMessage(ctx)
   213  
   214  			for i := 0; i < b.N; i++ {
   215  				var sd StreamDecoder
   216  				if err := sd.AddMessage(ctx, msg); err != nil {
   217  					b.Fatal(err)
   218  				}
   219  				for j := 0; j < outboxBufRows; j++ {
   220  					row, meta, err := sd.GetRow(nil)
   221  					if err != nil {
   222  						b.Fatal(err)
   223  					}
   224  					if row == nil && meta == nil {
   225  						break
   226  					}
   227  				}
   228  			}
   229  		})
   230  	}
   231  }