github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/flowinfra/inbound.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 "io" 16 17 "github.com/cockroachdb/cockroach/pkg/sql/execinfra" 18 "github.com/cockroachdb/cockroach/pkg/sql/execinfrapb" 19 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 20 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 21 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 22 "github.com/cockroachdb/cockroach/pkg/sql/types" 23 "github.com/cockroachdb/cockroach/pkg/util/log" 24 "github.com/cockroachdb/errors" 25 ) 26 27 // InboundStreamHandler is a handler of an inbound stream. 28 type InboundStreamHandler interface { 29 // run is called once a FlowStream RPC is handled and a stream is obtained to 30 // make this stream accessible to the rest of the flow. 31 Run( 32 ctx context.Context, stream execinfrapb.DistSQL_FlowStreamServer, firstMsg *execinfrapb.ProducerMessage, f *FlowBase, 33 ) error 34 // timeout is called with an error, which results in the teardown of the 35 // stream strategy with the given error. 36 // WARNING: timeout may block. 37 Timeout(err error) 38 } 39 40 // RowInboundStreamHandler is an InboundStreamHandler for the row based flow. 41 // It is exported since it is the default for the flow infrastructure. 42 type RowInboundStreamHandler struct { 43 execinfra.RowReceiver 44 } 45 46 var _ InboundStreamHandler = RowInboundStreamHandler{} 47 48 // Run is part of the InboundStreamHandler interface. 49 func (s RowInboundStreamHandler) Run( 50 ctx context.Context, 51 stream execinfrapb.DistSQL_FlowStreamServer, 52 firstMsg *execinfrapb.ProducerMessage, 53 f *FlowBase, 54 ) error { 55 return processInboundStream(ctx, stream, firstMsg, s.RowReceiver, f) 56 } 57 58 // Timeout is part of the InboundStreamHandler interface. 59 func (s RowInboundStreamHandler) Timeout(err error) { 60 s.Push( 61 nil, /* row */ 62 &execinfrapb.ProducerMetadata{Err: err}, 63 ) 64 s.ProducerDone() 65 } 66 67 // processInboundStream receives rows from a DistSQL_FlowStreamServer and sends 68 // them to a RowReceiver. Optionally processes an initial StreamMessage that was 69 // already received (because the first message contains the flow and stream IDs, 70 // it needs to be received before we can get here). 71 func processInboundStream( 72 ctx context.Context, 73 stream execinfrapb.DistSQL_FlowStreamServer, 74 firstMsg *execinfrapb.ProducerMessage, 75 dst execinfra.RowReceiver, 76 f *FlowBase, 77 ) error { 78 79 err := processInboundStreamHelper(ctx, stream, firstMsg, dst, f) 80 81 // err, if set, will also be propagated to the producer 82 // as the last record that the producer gets. 83 if err != nil { 84 log.VEventf(ctx, 1, "inbound stream error: %s", err) 85 return err 86 } 87 log.VEventf(ctx, 1, "inbound stream done") 88 // We are now done. The producer, if it's still around, will receive an EOF 89 // error over its side of the stream. 90 return nil 91 } 92 93 func processInboundStreamHelper( 94 ctx context.Context, 95 stream execinfrapb.DistSQL_FlowStreamServer, 96 firstMsg *execinfrapb.ProducerMessage, 97 dst execinfra.RowReceiver, 98 f *FlowBase, 99 ) error { 100 draining := false 101 var sd StreamDecoder 102 103 sendErrToConsumer := func(err error) { 104 if err != nil { 105 dst.Push(nil, &execinfrapb.ProducerMetadata{Err: err}) 106 } 107 dst.ProducerDone() 108 } 109 110 if firstMsg != nil { 111 if res := processProducerMessage( 112 ctx, stream, dst, &sd, &draining, firstMsg, 113 ); res.err != nil || res.consumerClosed { 114 sendErrToConsumer(res.err) 115 return res.err 116 } 117 } 118 119 // There's two goroutines involved in handling the RPC - the current one (the 120 // "parent"), which is watching for context cancellation, and a "reader" one 121 // that receives messages from the stream. This is all because a stream.Recv() 122 // call doesn't react to context cancellation. The idea is that, if the parent 123 // detects a canceled context, it will return from this RPC handler, which 124 // will cause the stream to be closed. Because the parent cannot wait for the 125 // reader to finish (that being the whole point of the different goroutines), 126 // the reader sending an error to the parent might race with the parent 127 // finishing. In that case, nobody cares about the reader anymore and so its 128 // result channel is buffered. 129 errChan := make(chan error, 1) 130 131 f.GetWaitGroup().Add(1) 132 go func() { 133 defer f.GetWaitGroup().Done() 134 for { 135 msg, err := stream.Recv() 136 if err != nil { 137 if err != io.EOF { 138 // Communication error. 139 err = pgerror.Newf(pgcode.InternalConnectionFailure, "communication error: %s", err) 140 sendErrToConsumer(err) 141 errChan <- err 142 return 143 } 144 // End of the stream. 145 sendErrToConsumer(nil) 146 errChan <- nil 147 return 148 } 149 150 if res := processProducerMessage( 151 ctx, stream, dst, &sd, &draining, msg, 152 ); res.err != nil || res.consumerClosed { 153 sendErrToConsumer(res.err) 154 errChan <- res.err 155 return 156 } 157 } 158 }() 159 160 // Check for context cancellation while reading from the stream on another 161 // goroutine. 162 select { 163 case <-f.GetCtxDone(): 164 return sqlbase.QueryCanceledError 165 case err := <-errChan: 166 return err 167 } 168 } 169 170 // sendDrainSignalToProducer is called when the consumer wants to signal the 171 // producer that it doesn't need any more rows and the producer should drain. A 172 // signal is sent on stream to the producer to ask it to send metadata. 173 func sendDrainSignalToStreamProducer( 174 ctx context.Context, stream execinfrapb.DistSQL_FlowStreamServer, 175 ) error { 176 log.VEvent(ctx, 1, "sending drain signal to producer") 177 sig := execinfrapb.ConsumerSignal{DrainRequest: &execinfrapb.DrainRequest{}} 178 return stream.Send(&sig) 179 } 180 181 // processProducerMessage is a helper function to process data from the producer 182 // and send it along to the consumer. It keeps track of whether or not it's 183 // draining between calls. If err in the result is set (or if the consumer is 184 // closed), the caller must return the error to the producer. 185 func processProducerMessage( 186 ctx context.Context, 187 stream execinfrapb.DistSQL_FlowStreamServer, 188 dst execinfra.RowReceiver, 189 sd *StreamDecoder, 190 draining *bool, 191 msg *execinfrapb.ProducerMessage, 192 ) processMessageResult { 193 err := sd.AddMessage(ctx, msg) 194 if err != nil { 195 return processMessageResult{ 196 err: errors.Wrapf(err, "%s", 197 log.MakeMessage(ctx, "decoding error", nil /* args */)), 198 consumerClosed: false, 199 } 200 } 201 var types []*types.T 202 for { 203 row, meta, err := sd.GetRow(nil /* rowBuf */) 204 if err != nil { 205 return processMessageResult{err: err, consumerClosed: false} 206 } 207 if row == nil && meta == nil { 208 // No more rows in the last message. 209 return processMessageResult{err: nil, consumerClosed: false} 210 } 211 212 if log.V(3) && row != nil { 213 if types == nil { 214 types = sd.Types() 215 } 216 log.Infof(ctx, "inbound stream pushing row %s", row.String(types)) 217 } 218 if *draining && meta == nil { 219 // Don't forward data rows when we're draining. 220 continue 221 } 222 switch dst.Push(row, meta) { 223 case execinfra.NeedMoreRows: 224 continue 225 case execinfra.DrainRequested: 226 // The rest of rows are not needed by the consumer. We'll send a drain 227 // signal to the producer and expect it to quickly send trailing 228 // metadata and close its side of the stream, at which point we also 229 // close the consuming side of the stream and call dst.ProducerDone(). 230 if !*draining { 231 *draining = true 232 if err := sendDrainSignalToStreamProducer(ctx, stream); err != nil { 233 log.Errorf(ctx, "draining error: %s", err) 234 } 235 } 236 case execinfra.ConsumerClosed: 237 return processMessageResult{err: nil, consumerClosed: true} 238 } 239 } 240 } 241 242 type processMessageResult struct { 243 err error 244 consumerClosed bool 245 }