github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/rangefeed/processor.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 rangefeed 12 13 import ( 14 "context" 15 "fmt" 16 "time" 17 18 "github.com/cockroachdb/cockroach/pkg/roachpb" 19 "github.com/cockroachdb/cockroach/pkg/storage" 20 "github.com/cockroachdb/cockroach/pkg/storage/enginepb" 21 "github.com/cockroachdb/cockroach/pkg/util/hlc" 22 "github.com/cockroachdb/cockroach/pkg/util/log" 23 "github.com/cockroachdb/cockroach/pkg/util/stop" 24 ) 25 26 const ( 27 // defaultPushTxnsInterval is the default interval at which a Processor will 28 // push all transactions in the unresolvedIntentQueue that are above the age 29 // specified by PushTxnsAge. 30 defaultPushTxnsInterval = 250 * time.Millisecond 31 // defaultPushTxnsAge is the default age at which a Processor will begin to 32 // consider a transaction old enough to push. 33 defaultPushTxnsAge = 10 * time.Second 34 // defaultCheckStreamsInterval is the default interval at which a Processor 35 // will check all streams to make sure they have not been canceled. 36 defaultCheckStreamsInterval = 1 * time.Second 37 ) 38 39 // newErrBufferCapacityExceeded creates an error that is returned to subscribers 40 // if the rangefeed processor is not able to keep up with the flow of incoming 41 // events and is forced to drop events in order to not block. 42 func newErrBufferCapacityExceeded() *roachpb.Error { 43 return roachpb.NewError( 44 roachpb.NewRangeFeedRetryError(roachpb.RangeFeedRetryError_REASON_SLOW_CONSUMER), 45 ) 46 } 47 48 // Config encompasses the configuration required to create a Processor. 49 type Config struct { 50 log.AmbientContext 51 Clock *hlc.Clock 52 Span roachpb.RSpan 53 54 TxnPusher TxnPusher 55 // PushTxnsInterval specifies the interval at which a Processor will push 56 // all transactions in the unresolvedIntentQueue that are above the age 57 // specified by PushTxnsAge. 58 PushTxnsInterval time.Duration 59 // PushTxnsAge specifies the age at which a Processor will begin to consider 60 // a transaction old enough to push. 61 PushTxnsAge time.Duration 62 63 // EventChanCap specifies the capacity to give to the Processor's input 64 // channel. 65 EventChanCap int 66 // EventChanTimeout specifies the maximum duration that methods will 67 // wait to send on the Processor's input channel before giving up and 68 // shutting down the Processor. 0 for no timeout. 69 EventChanTimeout time.Duration 70 71 // CheckStreamsInterval specifies interval at which a Processor will check 72 // all streams to make sure they have not been canceled. 73 CheckStreamsInterval time.Duration 74 75 // Metrics is for production monitoring of RangeFeeds. 76 Metrics *Metrics 77 } 78 79 // SetDefaults initializes unset fields in Config to values 80 // suitable for use by a Processor. 81 func (sc *Config) SetDefaults() { 82 if sc.TxnPusher == nil { 83 if sc.PushTxnsInterval != 0 { 84 panic("nil TxnPusher with non-zero PushTxnsInterval") 85 } 86 if sc.PushTxnsAge != 0 { 87 panic("nil TxnPusher with non-zero PushTxnsAge") 88 } 89 } else { 90 if sc.PushTxnsInterval == 0 { 91 sc.PushTxnsInterval = defaultPushTxnsInterval 92 } 93 if sc.PushTxnsAge == 0 { 94 sc.PushTxnsAge = defaultPushTxnsAge 95 } 96 } 97 if sc.CheckStreamsInterval == 0 { 98 sc.CheckStreamsInterval = defaultCheckStreamsInterval 99 } 100 } 101 102 // Processor manages a set of rangefeed registrations and handles the routing of 103 // logical updates to these registrations. While routing logical updates to 104 // rangefeed registrations, the processor performs two important tasks: 105 // 1. it translates logical updates into rangefeed events. 106 // 2. it transforms a range-level closed timestamp to a rangefeed-level resolved 107 // timestamp. 108 type Processor struct { 109 Config 110 reg registry 111 rts resolvedTimestamp 112 113 regC chan registration 114 unregC chan *registration 115 lenReqC chan struct{} 116 lenResC chan int 117 filterReqC chan struct{} 118 filterResC chan *Filter 119 eventC chan event 120 stopC chan *roachpb.Error 121 stoppedC chan struct{} 122 } 123 124 // event is a union of different event types that the Processor goroutine needs 125 // to be informed of. It is used so that all events can be sent over the same 126 // channel, which is necessary to prevent reordering. 127 type event struct { 128 ops []enginepb.MVCCLogicalOp 129 ct hlc.Timestamp 130 initRTS bool 131 syncC chan struct{} 132 // This setting is used in conjunction with syncC in tests in order to ensure 133 // that all registrations have fully finished outputting their buffers. This 134 // has to be done by the processor in order to avoid race conditions with the 135 // registry. Should be used only in tests. 136 testRegCatchupSpan roachpb.Span 137 } 138 139 // NewProcessor creates a new rangefeed Processor. The corresponding goroutine 140 // should be launched using the Start method. 141 func NewProcessor(cfg Config) *Processor { 142 cfg.SetDefaults() 143 cfg.AmbientContext.AddLogTag("rangefeed", nil) 144 return &Processor{ 145 Config: cfg, 146 reg: makeRegistry(), 147 rts: makeResolvedTimestamp(), 148 149 regC: make(chan registration), 150 unregC: make(chan *registration), 151 lenReqC: make(chan struct{}), 152 lenResC: make(chan int), 153 filterReqC: make(chan struct{}), 154 filterResC: make(chan *Filter), 155 eventC: make(chan event, cfg.EventChanCap), 156 stopC: make(chan *roachpb.Error, 1), 157 stoppedC: make(chan struct{}), 158 } 159 } 160 161 // Start launches a goroutine to process rangefeed events and send them to 162 // registrations. 163 // 164 // The provided iterator is used to initialize the rangefeed's resolved 165 // timestamp. It must obey the contract of an iterator used for an 166 // initResolvedTSScan. The Processor promises to clean up the iterator by 167 // calling its Close method when it is finished. If the iterator is nil then 168 // no initialization scan will be performed and the resolved timestamp will 169 // immediately be considered initialized. 170 func (p *Processor) Start(stopper *stop.Stopper, rtsIter storage.SimpleIterator) { 171 ctx := p.AnnotateCtx(context.Background()) 172 stopper.RunWorker(ctx, func(ctx context.Context) { 173 defer close(p.stoppedC) 174 ctx, cancelOutputLoops := context.WithCancel(ctx) 175 defer cancelOutputLoops() 176 177 // Launch an async task to scan over the resolved timestamp iterator and 178 // initialize the unresolvedIntentQueue. Ignore error if quiescing. 179 if rtsIter != nil { 180 initScan := newInitResolvedTSScan(p, rtsIter) 181 err := stopper.RunAsyncTask(ctx, "rangefeed: init resolved ts", initScan.Run) 182 if err != nil { 183 initScan.Cancel() 184 } 185 } else { 186 p.initResolvedTS(ctx) 187 } 188 189 // txnPushTicker periodically pushes the transaction record of all 190 // unresolved intents that are above a certain age, helping to ensure 191 // that the resolved timestamp continues to make progress. 192 var txnPushTicker *time.Ticker 193 var txnPushTickerC <-chan time.Time 194 var txnPushAttemptC chan struct{} 195 if p.PushTxnsInterval > 0 { 196 txnPushTicker = time.NewTicker(p.PushTxnsInterval) 197 txnPushTickerC = txnPushTicker.C 198 defer txnPushTicker.Stop() 199 } 200 201 for { 202 select { 203 204 // Handle new registrations. 205 case r := <-p.regC: 206 if !p.Span.AsRawSpanWithNoLocals().Contains(r.span) { 207 log.Fatalf(ctx, "registration %s not in Processor's key range %v", r, p.Span) 208 } 209 210 // Add the new registration to the registry. 211 p.reg.Register(&r) 212 213 // Publish an updated filter that includes the new registration. 214 p.filterResC <- p.reg.NewFilter() 215 216 // Immediately publish a checkpoint event to the registry. This will be 217 // the first event published to this registration after its initial 218 // catch-up scan completes. 219 r.publish(p.newCheckpointEvent()) 220 221 // Run an output loop for the registry. 222 runOutputLoop := func(ctx context.Context) { 223 r.runOutputLoop(ctx) 224 select { 225 case p.unregC <- &r: 226 case <-p.stoppedC: 227 } 228 } 229 if err := stopper.RunAsyncTask(ctx, "rangefeed: output loop", runOutputLoop); err != nil { 230 if r.catchupIter != nil { 231 r.catchupIter.Close() // clean up 232 } 233 r.disconnect(roachpb.NewError(err)) 234 p.reg.Unregister(&r) 235 } 236 237 // Respond to unregistration requests; these come from registrations that 238 // encounter an error during their output loop. 239 case r := <-p.unregC: 240 p.reg.Unregister(r) 241 242 // Respond to answers about the processor goroutine state. 243 case <-p.lenReqC: 244 p.lenResC <- p.reg.Len() 245 246 // Respond to answers about which operations can be filtered before 247 // reaching the Processor. 248 case <-p.filterReqC: 249 p.filterResC <- p.reg.NewFilter() 250 251 // Transform and route events. 252 case e := <-p.eventC: 253 p.consumeEvent(ctx, e) 254 255 // Check whether any unresolved intents need a push. 256 case <-txnPushTickerC: 257 // Don't perform transaction push attempts until the resolved 258 // timestamp has been initialized. 259 if !p.rts.IsInit() { 260 continue 261 } 262 263 now := p.Clock.Now() 264 before := now.Add(-p.PushTxnsAge.Nanoseconds(), 0) 265 oldTxns := p.rts.intentQ.Before(before) 266 267 if len(oldTxns) > 0 { 268 toPush := make([]enginepb.TxnMeta, len(oldTxns)) 269 for i, txn := range oldTxns { 270 toPush[i] = txn.asTxnMeta() 271 } 272 273 // Set the ticker channel to nil so that it can't trigger a 274 // second concurrent push. Create a push attempt response 275 // channel that is closed when the push attempt completes. 276 txnPushTickerC = nil 277 txnPushAttemptC = make(chan struct{}) 278 279 // Launch an async transaction push attempt that pushes the 280 // timestamp of all transactions beneath the push offset. 281 // Ignore error if quiescing. 282 pushTxns := newTxnPushAttempt(p, toPush, now, txnPushAttemptC) 283 err := stopper.RunAsyncTask(ctx, "rangefeed: pushing old txns", pushTxns.Run) 284 if err != nil { 285 pushTxns.Cancel() 286 } 287 } 288 289 // Update the resolved timestamp based on the push attempt. 290 case <-txnPushAttemptC: 291 // Reset the ticker channel so that it can trigger push attempts 292 // again. Set the push attempt channel back to nil. 293 txnPushTickerC = txnPushTicker.C 294 txnPushAttemptC = nil 295 296 // Close registrations and exit when signaled. 297 case pErr := <-p.stopC: 298 p.reg.DisconnectWithErr(all, pErr) 299 return 300 301 // Exit on stopper. 302 case <-stopper.ShouldQuiesce(): 303 pErr := roachpb.NewError(&roachpb.NodeUnavailableError{}) 304 p.reg.DisconnectWithErr(all, pErr) 305 return 306 } 307 } 308 }) 309 } 310 311 // Stop shuts down the processor and closes all registrations. Safe to call on 312 // nil Processor. It is not valid to restart a processor after it has been 313 // stopped. 314 func (p *Processor) Stop() { 315 p.StopWithErr(nil) 316 } 317 318 // StopWithErr shuts down the processor and closes all registrations with the 319 // specified error. Safe to call on nil Processor. It is not valid to restart a 320 // processor after it has been stopped. 321 func (p *Processor) StopWithErr(pErr *roachpb.Error) { 322 if p == nil { 323 return 324 } 325 // Flush any remaining events before stopping. 326 p.syncEventC() 327 // Send the processor a stop signal. 328 p.sendStop(pErr) 329 } 330 331 func (p *Processor) sendStop(pErr *roachpb.Error) { 332 select { 333 case p.stopC <- pErr: 334 // stopC has non-zero capacity so this should not block unless 335 // multiple callers attempt to stop the Processor concurrently. 336 case <-p.stoppedC: 337 // Already stopped. Do nothing. 338 } 339 } 340 341 // Register registers the stream over the specified span of keys. 342 // 343 // The registration will not observe any events that were consumed before this 344 // method was called. It is undefined whether the registration will observe 345 // events that are consumed concurrently with this call. The channel will be 346 // provided an error when the registration closes. 347 // 348 // The optionally provided "catch-up" iterator is used to read changes from the 349 // engine which occurred after the provided start timestamp. 350 // 351 // If the method returns false, the processor will have been stopped, so calling 352 // Stop is not necessary. If the method returns true, it will also return an 353 // updated operation filter that includes the operations required by the new 354 // registration. 355 // 356 // NOT safe to call on nil Processor. 357 func (p *Processor) Register( 358 span roachpb.RSpan, 359 startTS hlc.Timestamp, 360 catchupIter storage.SimpleIterator, 361 withDiff bool, 362 stream Stream, 363 errC chan<- *roachpb.Error, 364 ) (bool, *Filter) { 365 // Synchronize the event channel so that this registration doesn't see any 366 // events that were consumed before this registration was called. Instead, 367 // it should see these events during its catch up scan. 368 p.syncEventC() 369 370 r := newRegistration( 371 span.AsRawSpanWithNoLocals(), startTS, catchupIter, withDiff, 372 p.Config.EventChanCap, p.Metrics, stream, errC, 373 ) 374 select { 375 case p.regC <- r: 376 // Wait for response. 377 return true, <-p.filterResC 378 case <-p.stoppedC: 379 return false, nil 380 } 381 } 382 383 // Len returns the number of registrations attached to the processor. 384 func (p *Processor) Len() int { 385 if p == nil { 386 return 0 387 } 388 389 // Ask the processor goroutine. 390 select { 391 case p.lenReqC <- struct{}{}: 392 // Wait for response. 393 return <-p.lenResC 394 case <-p.stoppedC: 395 return 0 396 } 397 } 398 399 // Filter returns a new operation filter based on the registrations attached to 400 // the processor. Returns nil if the processor has been stopped already. 401 func (p *Processor) Filter() *Filter { 402 if p == nil { 403 return nil 404 } 405 406 // Ask the processor goroutine. 407 select { 408 case p.filterReqC <- struct{}{}: 409 // Wait for response. 410 return <-p.filterResC 411 case <-p.stoppedC: 412 return nil 413 } 414 } 415 416 // ConsumeLogicalOps informs the rangefeed processor of the set of logical 417 // operations. It returns false if consuming the operations hit a timeout, as 418 // specified by the EventChanTimeout configuration. If the method returns false, 419 // the processor will have been stopped, so calling Stop is not necessary. Safe 420 // to call on nil Processor. 421 func (p *Processor) ConsumeLogicalOps(ops ...enginepb.MVCCLogicalOp) bool { 422 if p == nil { 423 return true 424 } 425 if len(ops) == 0 { 426 return true 427 } 428 return p.sendEvent(event{ops: ops}, p.EventChanTimeout) 429 } 430 431 // ForwardClosedTS indicates that the closed timestamp that serves as the basis 432 // for the rangefeed processor's resolved timestamp has advanced. It returns 433 // false if forwarding the closed timestamp hit a timeout, as specified by the 434 // EventChanTimeout configuration. If the method returns false, the processor 435 // will have been stopped, so calling Stop is not necessary. Safe to call on 436 // nil Processor. 437 func (p *Processor) ForwardClosedTS(closedTS hlc.Timestamp) bool { 438 if p == nil { 439 return true 440 } 441 if closedTS == (hlc.Timestamp{}) { 442 return true 443 } 444 return p.sendEvent(event{ct: closedTS}, p.EventChanTimeout) 445 } 446 447 // sendEvent informs the Processor of a new event. If a timeout is specified, 448 // the method will wait for no longer than that duration before giving up, 449 // shutting down the Processor, and returning false. 0 for no timeout. 450 func (p *Processor) sendEvent(e event, timeout time.Duration) bool { 451 if timeout == 0 { 452 select { 453 case p.eventC <- e: 454 case <-p.stoppedC: 455 // Already stopped. Do nothing. 456 } 457 } else { 458 select { 459 case p.eventC <- e: 460 case <-p.stoppedC: 461 // Already stopped. Do nothing. 462 default: 463 select { 464 case p.eventC <- e: 465 case <-p.stoppedC: 466 // Already stopped. Do nothing. 467 case <-time.After(timeout): 468 // Sending on the eventC channel would have blocked. 469 // Instead, tear down the processor and return immediately. 470 p.sendStop(newErrBufferCapacityExceeded()) 471 return false 472 } 473 } 474 } 475 return true 476 } 477 478 // setResolvedTSInitialized informs the Processor that its resolved timestamp has 479 // all the information it needs to be considered initialized. 480 func (p *Processor) setResolvedTSInitialized() { 481 p.sendEvent(event{initRTS: true}, 0 /* timeout */) 482 } 483 484 // syncEventC synchronizes access to the Processor goroutine, allowing the 485 // caller to establish causality with actions taken by the Processor goroutine. 486 // It does so by flushing the event pipeline. 487 func (p *Processor) syncEventC() { 488 syncC := make(chan struct{}) 489 select { 490 case p.eventC <- event{syncC: syncC}: 491 select { 492 case <-syncC: 493 // Synchronized. 494 case <-p.stoppedC: 495 // Already stopped. Do nothing. 496 } 497 case <-p.stoppedC: 498 // Already stopped. Do nothing. 499 } 500 } 501 502 func (p *Processor) consumeEvent(ctx context.Context, e event) { 503 switch { 504 case len(e.ops) > 0: 505 p.consumeLogicalOps(ctx, e.ops) 506 case e.ct != hlc.Timestamp{}: 507 p.forwardClosedTS(ctx, e.ct) 508 case e.initRTS: 509 p.initResolvedTS(ctx) 510 case e.syncC != nil: 511 if e.testRegCatchupSpan.Valid() { 512 if err := p.reg.waitForCaughtUp(e.testRegCatchupSpan); err != nil { 513 log.Errorf( 514 ctx, 515 "error waiting for registries to catch up during test, results might be impacted: %s", 516 err, 517 ) 518 } 519 } 520 close(e.syncC) 521 default: 522 panic("missing event variant") 523 } 524 } 525 526 func (p *Processor) consumeLogicalOps(ctx context.Context, ops []enginepb.MVCCLogicalOp) { 527 for _, op := range ops { 528 // Publish RangeFeedValue updates, if necessary. 529 switch t := op.GetValue().(type) { 530 case *enginepb.MVCCWriteValueOp: 531 // Publish the new value directly. 532 p.publishValue(ctx, t.Key, t.Timestamp, t.Value, t.PrevValue) 533 534 case *enginepb.MVCCWriteIntentOp: 535 // No updates to publish. 536 537 case *enginepb.MVCCUpdateIntentOp: 538 // No updates to publish. 539 540 case *enginepb.MVCCCommitIntentOp: 541 // Publish the newly committed value. 542 p.publishValue(ctx, t.Key, t.Timestamp, t.Value, t.PrevValue) 543 544 case *enginepb.MVCCAbortIntentOp: 545 // No updates to publish. 546 547 case *enginepb.MVCCAbortTxnOp: 548 // No updates to publish. 549 550 default: 551 panic(fmt.Sprintf("unknown logical op %T", t)) 552 } 553 554 // Determine whether the operation caused the resolved timestamp to 555 // move forward. If so, publish a RangeFeedCheckpoint notification. 556 if p.rts.ConsumeLogicalOp(op) { 557 p.publishCheckpoint(ctx) 558 } 559 } 560 } 561 562 func (p *Processor) forwardClosedTS(ctx context.Context, newClosedTS hlc.Timestamp) { 563 if p.rts.ForwardClosedTS(newClosedTS) { 564 p.publishCheckpoint(ctx) 565 } 566 } 567 568 func (p *Processor) initResolvedTS(ctx context.Context) { 569 if p.rts.Init() { 570 p.publishCheckpoint(ctx) 571 } 572 } 573 574 func (p *Processor) publishValue( 575 ctx context.Context, key roachpb.Key, timestamp hlc.Timestamp, value, prevValue []byte, 576 ) { 577 if !p.Span.ContainsKey(roachpb.RKey(key)) { 578 log.Fatalf(ctx, "key %v not in Processor's key range %v", key, p.Span) 579 } 580 581 var prevVal roachpb.Value 582 if prevValue != nil { 583 prevVal.RawBytes = prevValue 584 } 585 var event roachpb.RangeFeedEvent 586 event.MustSetValue(&roachpb.RangeFeedValue{ 587 Key: key, 588 Value: roachpb.Value{ 589 RawBytes: value, 590 Timestamp: timestamp, 591 }, 592 PrevValue: prevVal, 593 }) 594 p.reg.PublishToOverlapping(roachpb.Span{Key: key}, &event) 595 } 596 597 func (p *Processor) publishCheckpoint(ctx context.Context) { 598 // TODO(nvanbenschoten): persist resolvedTimestamp. Give Processor a client.DB. 599 // TODO(nvanbenschoten): rate limit these? send them periodically? 600 601 event := p.newCheckpointEvent() 602 p.reg.PublishToOverlapping(all, event) 603 } 604 605 func (p *Processor) newCheckpointEvent() *roachpb.RangeFeedEvent { 606 // Create a RangeFeedCheckpoint over the Processor's entire span. Each 607 // individual registration will trim this down to just the key span that 608 // it is listening on in registration.maybeStripEvent before publishing. 609 var event roachpb.RangeFeedEvent 610 event.MustSetValue(&roachpb.RangeFeedCheckpoint{ 611 Span: p.Span.AsRawSpanWithNoLocals(), 612 ResolvedTS: p.rts.Get(), 613 }) 614 return &event 615 }