github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/puller/frontier/frontier.go (about)

     1  // Copyright 2020 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package frontier
    15  
    16  import (
    17  	"bytes"
    18  	"fmt"
    19  	"math"
    20  	"strings"
    21  
    22  	"github.com/pingcap/ticdc/pkg/regionspan"
    23  )
    24  
    25  // Frontier checks resolved event of spans and moves the global resolved ts ahead
    26  type Frontier interface {
    27  	Forward(span regionspan.ComparableSpan, ts uint64)
    28  	Frontier() uint64
    29  	String() string
    30  }
    31  
    32  // spanFrontier tracks the minimum timestamp of a set of spans.
    33  type spanFrontier struct {
    34  	spanList  skipList
    35  	minTsHeap fibonacciHeap
    36  }
    37  
    38  // NewFrontier creates Froniter from the given spans.
    39  func NewFrontier(checkpointTs uint64, spans ...regionspan.ComparableSpan) Frontier {
    40  	// spanFrontier don't support use Nil as the maximum key of End range
    41  	// So we use set it as util.UpperBoundKey, the means the real use case *should not* have a
    42  	// End key bigger than util.UpperBoundKey
    43  	for i, span := range spans {
    44  		spans[i] = span.Hack()
    45  	}
    46  
    47  	s := &spanFrontier{
    48  		spanList: *newSpanList(),
    49  	}
    50  	firstSpan := true
    51  	for _, span := range spans {
    52  		if firstSpan {
    53  			s.spanList.Insert(span.Start, s.minTsHeap.Insert(checkpointTs))
    54  			s.spanList.Insert(span.End, s.minTsHeap.Insert(math.MaxUint64))
    55  			firstSpan = false
    56  			continue
    57  		}
    58  		s.insert(span, checkpointTs)
    59  	}
    60  
    61  	return s
    62  }
    63  
    64  // Frontier return the minimum tiemstamp.
    65  func (s *spanFrontier) Frontier() uint64 {
    66  	return s.minTsHeap.GetMinKey()
    67  }
    68  
    69  // Forward advances the timestamp for a span.
    70  func (s *spanFrontier) Forward(span regionspan.ComparableSpan, ts uint64) {
    71  	span = span.Hack()
    72  	s.insert(span, ts)
    73  }
    74  
    75  func (s *spanFrontier) insert(span regionspan.ComparableSpan, ts uint64) {
    76  	seekRes := s.spanList.Seek(span.Start)
    77  
    78  	// if there is no change in the region span
    79  	// We just need to update the ts corresponding to the span in list
    80  	next := seekRes.Node().Next()
    81  	if next != nil {
    82  		cmpStart := bytes.Compare(seekRes.Node().Key(), span.Start)
    83  		cmpEnd := bytes.Compare(next.Key(), span.End)
    84  		if cmpStart == 0 && cmpEnd == 0 {
    85  			s.minTsHeap.UpdateKey(seekRes.Node().Value(), ts)
    86  			return
    87  		}
    88  	}
    89  
    90  	// regions are merged or split, overwrite span into list
    91  	node := seekRes.Node()
    92  	lastNodeTs := uint64(math.MaxUint64)
    93  	shouldInsertStartNode := true
    94  	if node.Value() != nil {
    95  		lastNodeTs = node.Value().key
    96  	}
    97  	for ; node != nil; node = node.Next() {
    98  		cmpStart := bytes.Compare(node.Key(), span.Start)
    99  		if cmpStart < 0 {
   100  			continue
   101  		}
   102  		if bytes.Compare(node.Key(), span.End) > 0 {
   103  			break
   104  		}
   105  		lastNodeTs = node.Value().key
   106  		if cmpStart == 0 {
   107  			s.minTsHeap.UpdateKey(node.Value(), ts)
   108  			shouldInsertStartNode = false
   109  		} else {
   110  			s.spanList.Remove(seekRes, node)
   111  			s.minTsHeap.Remove(node.Value())
   112  		}
   113  	}
   114  	if shouldInsertStartNode {
   115  		s.spanList.InsertNextToNode(seekRes, span.Start, s.minTsHeap.Insert(ts))
   116  		seekRes.Next()
   117  	}
   118  	s.spanList.InsertNextToNode(seekRes, span.End, s.minTsHeap.Insert(lastNodeTs))
   119  }
   120  
   121  // Entries visit all traced spans.
   122  func (s *spanFrontier) Entries(fn func(key []byte, ts uint64)) {
   123  	s.spanList.Entries(func(n *skipListNode) bool {
   124  		fn(n.Key(), n.Value().key)
   125  		return true
   126  	})
   127  }
   128  
   129  func (s *spanFrontier) String() string {
   130  	var buf strings.Builder
   131  	s.Entries(func(key []byte, ts uint64) {
   132  		if ts == math.MaxUint64 {
   133  			buf.WriteString(fmt.Sprintf("[%s @ Max] ", key))
   134  		} else {
   135  			buf.WriteString(fmt.Sprintf("[%s @ %d] ", key, ts))
   136  		}
   137  	})
   138  	return buf.String()
   139  }