github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/spanz/span.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 spanz 15 16 import ( 17 "bytes" 18 19 "github.com/pingcap/log" 20 "github.com/pingcap/tidb/pkg/ddl" 21 "github.com/pingcap/tidb/pkg/kv" 22 "github.com/pingcap/tidb/pkg/tablecodec" 23 "github.com/pingcap/tidb/pkg/util/codec" 24 "github.com/pingcap/tiflow/cdc/processor/tablepb" 25 "github.com/pingcap/tiflow/pkg/errors" 26 "go.uber.org/zap" 27 ) 28 29 const ( 30 // JobTableID is the id of `tidb_ddl_job`. 31 JobTableID = ddl.JobTableID 32 // JobHistoryID is the id of `tidb_ddl_history` 33 JobHistoryID = ddl.HistoryTableID 34 ) 35 36 // UpperBoundKey represents the maximum value. 37 var UpperBoundKey = []byte{255, 255, 255, 255, 255} 38 39 // HackSpan will set End as UpperBoundKey if End is Nil. 40 func HackSpan(span tablepb.Span) tablepb.Span { 41 if span.StartKey == nil { 42 span.StartKey = []byte{} 43 } 44 45 if span.EndKey == nil { 46 span.EndKey = UpperBoundKey 47 } 48 return span 49 } 50 51 // GetTableRange returns the span to watch for the specified table 52 // Note that returned keys are not in memcomparable format. 53 func GetTableRange(tableID int64) (startKey, endKey []byte) { 54 tablePrefix := tablecodec.GenTablePrefix(tableID) 55 sep := byte('_') 56 recordMarker := byte('r') 57 58 var start, end kv.Key 59 // ignore index keys. 60 start = append(tablePrefix, sep, recordMarker) 61 end = append(tablePrefix, sep, recordMarker+1) 62 return start, end 63 } 64 65 // GetAllDDLSpan return all cdc interested spans for DDL. 66 func GetAllDDLSpan() []tablepb.Span { 67 spans := make([]tablepb.Span, 0, 2) 68 start, end := GetTableRange(JobTableID) 69 spans = append(spans, tablepb.Span{ 70 StartKey: ToComparableKey(start), 71 EndKey: ToComparableKey(end), 72 }) 73 start, end = GetTableRange(JobHistoryID) 74 spans = append(spans, tablepb.Span{ 75 StartKey: ToComparableKey(start), 76 EndKey: ToComparableKey(end), 77 }) 78 return spans 79 } 80 81 // KeyInSpan check if k in the span range. 82 func KeyInSpan(k tablepb.Key, span tablepb.Span) bool { 83 if StartCompare(k, span.StartKey) >= 0 && 84 EndCompare(k, span.EndKey) < 0 { 85 return true 86 } 87 88 return false 89 } 90 91 // StartCompare compares two start keys. 92 // The result will be 0 if lhs==rhs, -1 if lhs < rhs, and +1 if lhs > rhs 93 func StartCompare(lhs []byte, rhs []byte) int { 94 if len(lhs) == 0 && len(rhs) == 0 { 95 return 0 96 } 97 98 // Nil means Negative infinity. 99 // It's difference with EndCompare. 100 if len(lhs) == 0 { 101 return -1 102 } 103 104 if len(rhs) == 0 { 105 return 1 106 } 107 108 return bytes.Compare(lhs, rhs) 109 } 110 111 // EndCompare compares two end keys. 112 // The result will be 0 if lhs==rhs, -1 if lhs < rhs, and +1 if lhs > rhs 113 func EndCompare(lhs []byte, rhs []byte) int { 114 if len(lhs) == 0 && len(rhs) == 0 { 115 return 0 116 } 117 118 // Nil means Positive infinity. 119 // It's difference with StartCompare. 120 if len(lhs) == 0 { 121 return 1 122 } 123 124 if len(rhs) == 0 { 125 return -1 126 } 127 128 return bytes.Compare(lhs, rhs) 129 } 130 131 // Intersect return to intersect part of lhs and rhs span. 132 // Return error if there's no intersect part 133 func Intersect(lhs tablepb.Span, rhs tablepb.Span) (span tablepb.Span, err error) { 134 if len(lhs.StartKey) != 0 && EndCompare(lhs.StartKey, rhs.EndKey) >= 0 || 135 len(rhs.StartKey) != 0 && EndCompare(rhs.StartKey, lhs.EndKey) >= 0 { 136 return tablepb.Span{}, errors.ErrIntersectNoOverlap.GenWithStackByArgs(lhs, rhs) 137 } 138 139 start := lhs.StartKey 140 141 if StartCompare(rhs.StartKey, start) > 0 { 142 start = rhs.StartKey 143 } 144 145 end := lhs.EndKey 146 147 if EndCompare(rhs.EndKey, end) < 0 { 148 end = rhs.EndKey 149 } 150 151 return tablepb.Span{StartKey: start, EndKey: end}, nil 152 } 153 154 // IsSubSpan returns true if the sub span is parents spans 155 func IsSubSpan(sub tablepb.Span, parents ...tablepb.Span) bool { 156 if bytes.Compare(sub.StartKey, sub.EndKey) >= 0 { 157 log.Panic("the sub span is invalid", zap.Reflect("subSpan", sub)) 158 } 159 for _, parent := range parents { 160 if StartCompare(parent.StartKey, sub.StartKey) <= 0 && 161 EndCompare(sub.EndKey, parent.EndKey) <= 0 { 162 return true 163 } 164 } 165 return false 166 } 167 168 // ToSpan returns a span, keys are encoded in memcomparable format. 169 // See: https://github.com/facebook/mysql-5.6/wiki/MyRocks-record-format 170 func ToSpan(startKey, endKey []byte) tablepb.Span { 171 return tablepb.Span{ 172 StartKey: ToComparableKey(startKey), 173 EndKey: ToComparableKey(endKey), 174 } 175 } 176 177 // ToComparableKey returns a memcomparable key. 178 // See: https://github.com/facebook/mysql-5.6/wiki/MyRocks-record-format 179 func ToComparableKey(key []byte) tablepb.Key { 180 return codec.EncodeBytes(nil, key) 181 }