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 }