github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ccl/changefeedccl/kvfeed/physical_kv_feed.go (about)

     1  // Copyright 2020 The Cockroach Authors.
     2  //
     3  // Licensed as a CockroachDB Enterprise file under the Cockroach Community
     4  // License (the "License"); you may not use this file except in compliance with
     5  // the License. You may obtain a copy of the License at
     6  //
     7  //     https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt
     8  
     9  package kvfeed
    10  
    11  import (
    12  	"context"
    13  
    14  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    15  	"github.com/cockroachdb/cockroach/pkg/util/ctxgroup"
    16  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    17  	"github.com/cockroachdb/cockroach/pkg/util/log"
    18  )
    19  
    20  // physicalFeedFactory constructs a physical feed which writes into sink and
    21  // runs until the group's context expires.
    22  type physicalFeedFactory interface {
    23  	Run(ctx context.Context, sink EventBufferWriter, cfg physicalConfig) error
    24  }
    25  
    26  type physicalConfig struct {
    27  	Spans     []roachpb.Span
    28  	Timestamp hlc.Timestamp
    29  	WithDiff  bool
    30  }
    31  
    32  type rangefeedFactory func(
    33  	ctx context.Context,
    34  	span roachpb.Span,
    35  	startFrom hlc.Timestamp,
    36  	withDiff bool,
    37  	eventC chan<- *roachpb.RangeFeedEvent,
    38  ) error
    39  
    40  type rangefeed struct {
    41  	memBuf EventBufferWriter
    42  	cfg    physicalConfig
    43  	eventC chan *roachpb.RangeFeedEvent
    44  }
    45  
    46  func (p rangefeedFactory) Run(
    47  	ctx context.Context, sink EventBufferWriter, cfg physicalConfig,
    48  ) error {
    49  	// To avoid blocking raft, RangeFeed puts all entries in a server side
    50  	// buffer. But to keep things simple, it's a small fixed-sized buffer. This
    51  	// means we need to ingest everything we get back as quickly as possible, so
    52  	// we throw it in a buffer here to pick up the slack between RangeFeed and
    53  	// the sink.
    54  	//
    55  	// TODO(dan): Right now, there are two buffers in the changefeed flow when
    56  	// using RangeFeeds, one here and the usual one between the KVFeed and the
    57  	// rest of the changefeed (the latter of which is implemented with an
    58  	// unbuffered channel, and so doesn't actually buffer). Ideally, we'd have
    59  	// one, but the structure of the KVFeed code right now makes this hard.
    60  	// Specifically, when a schema change happens, we need a barrier where we
    61  	// flush out every change before the schema change timestamp before we start
    62  	// emitting any changes from after the schema change. The KVFeed's
    63  	// `schemaFeed` is responsible for detecting and enforcing these , but the
    64  	// after-KVFeed buffer doesn't have access to any of this state. A cleanup is
    65  	// in order.
    66  	feed := rangefeed{
    67  		memBuf: sink,
    68  		cfg:    cfg,
    69  		eventC: make(chan *roachpb.RangeFeedEvent, 128),
    70  	}
    71  	g := ctxgroup.WithContext(ctx)
    72  	g.GoCtx(feed.addEventsToBuffer)
    73  	for _, span := range cfg.Spans {
    74  		span := span
    75  		g.GoCtx(func(ctx context.Context) error {
    76  			return p(ctx, span, cfg.Timestamp, cfg.WithDiff, feed.eventC)
    77  		})
    78  	}
    79  	return g.Wait()
    80  }
    81  
    82  func (p *rangefeed) addEventsToBuffer(ctx context.Context) error {
    83  	var backfillTimestamp hlc.Timestamp
    84  	for {
    85  		select {
    86  		case e := <-p.eventC:
    87  			switch t := e.GetValue().(type) {
    88  			case *roachpb.RangeFeedValue:
    89  				kv := roachpb.KeyValue{Key: t.Key, Value: t.Value}
    90  				var prevVal roachpb.Value
    91  				if p.cfg.WithDiff {
    92  					prevVal = t.PrevValue
    93  				}
    94  				if err := p.memBuf.AddKV(ctx, kv, prevVal, backfillTimestamp); err != nil {
    95  					return err
    96  				}
    97  			case *roachpb.RangeFeedCheckpoint:
    98  				if !t.ResolvedTS.IsEmpty() && t.ResolvedTS.Less(p.cfg.Timestamp) {
    99  					// RangeFeed happily forwards any closed timestamps it receives as
   100  					// soon as there are no outstanding intents under them.
   101  					// Changefeeds don't care about these at all, so throw them out.
   102  					continue
   103  				}
   104  				if err := p.memBuf.AddResolved(ctx, t.Span, t.ResolvedTS, false); err != nil {
   105  					return err
   106  				}
   107  			default:
   108  				log.Fatalf(ctx, "unexpected RangeFeedEvent variant %v", t)
   109  			}
   110  		case <-ctx.Done():
   111  			return ctx.Err()
   112  		}
   113  	}
   114  }