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 }