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

     1  // Copyright 2018 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 execinfra
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/sql/execinfrapb"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    19  	"github.com/cockroachdb/cockroach/pkg/util"
    20  )
    21  
    22  // MetadataTestReceiver is a Processors that is complimentary to
    23  // MetadataTestSender which checks that all metadata emitted by latter is
    24  // received.
    25  type MetadataTestReceiver struct {
    26  	ProcessorBase
    27  	input RowSource
    28  
    29  	// trailingErrMeta stores the error metadata received from the input. We
    30  	// do not return this metadata immediately because metadata propagation errors
    31  	// are prioritized over query errors, which ensures that tests which expect a
    32  	// query error can still fail if they do not properly propagate metadata.
    33  	trailingErrMeta []execinfrapb.ProducerMetadata
    34  
    35  	senders   []string
    36  	rowCounts map[string]rowNumCounter
    37  }
    38  
    39  type rowNumCounter struct {
    40  	expected, actual int32
    41  	seen             util.FastIntSet
    42  	err              error
    43  }
    44  
    45  var _ Processor = &MetadataTestReceiver{}
    46  var _ RowSource = &MetadataTestReceiver{}
    47  
    48  const metadataTestReceiverProcName = "meta receiver"
    49  
    50  // NewMetadataTestReceiver creates a new MetadataTestReceiver.
    51  func NewMetadataTestReceiver(
    52  	flowCtx *FlowCtx,
    53  	processorID int32,
    54  	input RowSource,
    55  	post *execinfrapb.PostProcessSpec,
    56  	output RowReceiver,
    57  	senders []string,
    58  ) (*MetadataTestReceiver, error) {
    59  	mtr := &MetadataTestReceiver{
    60  		input:     input,
    61  		senders:   senders,
    62  		rowCounts: make(map[string]rowNumCounter),
    63  	}
    64  	if err := mtr.Init(
    65  		mtr,
    66  		post,
    67  		input.OutputTypes(),
    68  		flowCtx,
    69  		processorID,
    70  		output,
    71  		nil, /* memMonitor */
    72  		ProcStateOpts{
    73  			InputsToDrain: []RowSource{input},
    74  			TrailingMetaCallback: func(context.Context) []execinfrapb.ProducerMetadata {
    75  				var trailingMeta []execinfrapb.ProducerMetadata
    76  				if mtr.rowCounts != nil {
    77  					if meta := mtr.checkRowNumMetadata(); meta != nil {
    78  						trailingMeta = append(trailingMeta, *meta)
    79  					}
    80  				}
    81  				mtr.InternalClose()
    82  				return trailingMeta
    83  			},
    84  		},
    85  	); err != nil {
    86  		return nil, err
    87  	}
    88  	return mtr, nil
    89  }
    90  
    91  // checkRowNumMetadata examines all of the received RowNum metadata to ensure
    92  // that it has received exactly one of each expected RowNum. If the check
    93  // detects dropped or repeated metadata, it returns error metadata. Otherwise,
    94  // it returns nil.
    95  func (mtr *MetadataTestReceiver) checkRowNumMetadata() *execinfrapb.ProducerMetadata {
    96  	defer func() { mtr.rowCounts = nil }()
    97  
    98  	if len(mtr.rowCounts) != len(mtr.senders) {
    99  		var missingSenders string
   100  		for _, sender := range mtr.senders {
   101  			if _, exists := mtr.rowCounts[sender]; !exists {
   102  				if missingSenders == "" {
   103  					missingSenders = sender
   104  				} else {
   105  					missingSenders += fmt.Sprintf(", %s", sender)
   106  				}
   107  			}
   108  		}
   109  		return &execinfrapb.ProducerMetadata{
   110  			Err: fmt.Errorf(
   111  				"expected %d metadata senders but found %d; missing %s",
   112  				len(mtr.senders), len(mtr.rowCounts), missingSenders,
   113  			),
   114  		}
   115  	}
   116  	for id, cnt := range mtr.rowCounts {
   117  		if cnt.err != nil {
   118  			return &execinfrapb.ProducerMetadata{Err: cnt.err}
   119  		}
   120  		if cnt.expected != cnt.actual {
   121  			return &execinfrapb.ProducerMetadata{
   122  				Err: fmt.Errorf(
   123  					"dropped metadata from sender %s: expected %d RowNum messages but got %d",
   124  					id, cnt.expected, cnt.actual),
   125  			}
   126  		}
   127  		for i := 0; i < int(cnt.expected); i++ {
   128  			if !cnt.seen.Contains(i) {
   129  				return &execinfrapb.ProducerMetadata{
   130  					Err: fmt.Errorf(
   131  						"dropped and repeated metadata from sender %s: have %d messages but missing RowNum #%d",
   132  						id, cnt.expected, i+1),
   133  				}
   134  			}
   135  		}
   136  	}
   137  
   138  	return nil
   139  }
   140  
   141  // Start is part of the RowSource interface.
   142  func (mtr *MetadataTestReceiver) Start(ctx context.Context) context.Context {
   143  	mtr.input.Start(ctx)
   144  	return mtr.StartInternal(ctx, metadataTestReceiverProcName)
   145  }
   146  
   147  // Next is part of the RowSource interface.
   148  //
   149  // This implementation doesn't follow the usual patterns of other processors; it
   150  // makes more limited use of the ProcessorBase's facilities because it needs to
   151  // inspect metadata while draining.
   152  func (mtr *MetadataTestReceiver) Next() (sqlbase.EncDatumRow, *execinfrapb.ProducerMetadata) {
   153  	for {
   154  		if mtr.State == StateTrailingMeta {
   155  			if meta := mtr.popTrailingMeta(); meta != nil {
   156  				return nil, meta
   157  			}
   158  			// If there's no more trailingMeta, we've moved to stateExhausted, and we
   159  			// might return some trailingErrMeta below.
   160  		}
   161  		if mtr.State == StateExhausted {
   162  			if len(mtr.trailingErrMeta) > 0 {
   163  				meta := mtr.trailingErrMeta[0]
   164  				mtr.trailingErrMeta = mtr.trailingErrMeta[1:]
   165  				return nil, &meta
   166  			}
   167  			return nil, nil
   168  		}
   169  
   170  		row, meta := mtr.input.Next()
   171  
   172  		if meta != nil {
   173  			if meta.RowNum != nil {
   174  				rowNum := meta.RowNum
   175  				rcnt, exists := mtr.rowCounts[rowNum.SenderID]
   176  				if !exists {
   177  					rcnt.expected = -1
   178  				}
   179  				if rcnt.err != nil {
   180  					return nil, meta
   181  				}
   182  				if rowNum.LastMsg {
   183  					if rcnt.expected != -1 {
   184  						rcnt.err = fmt.Errorf(
   185  							"repeated metadata from reader %s: received more than one RowNum with LastMsg set",
   186  							rowNum.SenderID)
   187  						mtr.rowCounts[rowNum.SenderID] = rcnt
   188  						return nil, meta
   189  					}
   190  					rcnt.expected = rowNum.RowNum
   191  				} else {
   192  					rcnt.actual++
   193  					rcnt.seen.Add(int(rowNum.RowNum - 1))
   194  				}
   195  				mtr.rowCounts[rowNum.SenderID] = rcnt
   196  			}
   197  			if meta.Err != nil {
   198  				// Keep track of the err in trailingErrMeta, which will be returned
   199  				// after everything else (including ProcessorBase.trailingMeta).
   200  				mtr.trailingErrMeta = append(mtr.trailingErrMeta, *meta)
   201  				continue
   202  			}
   203  
   204  			return nil, meta
   205  		}
   206  
   207  		if row == nil {
   208  			mtr.moveToTrailingMeta()
   209  			continue
   210  		}
   211  
   212  		// We don't use ProcessorBase.ProcessRowHelper() here because we need
   213  		// special handling for errors: this proc never starts draining in order for
   214  		// it to be as unintrusive as possible.
   215  		outRow, ok, err := mtr.Out.ProcessRow(mtr.Ctx, row)
   216  		if err != nil {
   217  			mtr.trailingMeta = append(mtr.trailingMeta, execinfrapb.ProducerMetadata{Err: err})
   218  			continue
   219  		}
   220  		if outRow == nil {
   221  			if !ok {
   222  				mtr.MoveToDraining(nil /* err */)
   223  			}
   224  			continue
   225  		}
   226  
   227  		// Swallow rows if we're draining.
   228  		if mtr.State == StateDraining {
   229  			continue
   230  		}
   231  
   232  		if !ok {
   233  			mtr.MoveToDraining(nil /* err */)
   234  		}
   235  
   236  		return outRow, nil
   237  	}
   238  }
   239  
   240  // ConsumerDone is part of the RowSource interface.
   241  func (mtr *MetadataTestReceiver) ConsumerDone() {
   242  	mtr.input.ConsumerDone()
   243  }
   244  
   245  // ConsumerClosed is part of the RowSource interface.
   246  func (mtr *MetadataTestReceiver) ConsumerClosed() {
   247  	// The consumer is done, Next() will not be called again.
   248  	mtr.InternalClose()
   249  }