github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/cdc/kv/regionlock/range_ts_map.go (about) 1 // Copyright 2024 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 regionlock 15 16 import ( 17 "bytes" 18 "math" 19 20 "github.com/google/btree" 21 "github.com/pingcap/log" 22 ) 23 24 // rangeTsMap represents a map from key range to a timestamp. It supports 25 // range set and calculating min value among a specified range. 26 type rangeTsMap struct { 27 m *btree.BTreeG[rangeTsEntry] 28 start []byte 29 end []byte 30 } 31 32 // newRangeTsMap creates a RangeTsMap. 33 func newRangeTsMap(startKey, endKey []byte, startTs uint64) *rangeTsMap { 34 m := &rangeTsMap{ 35 m: btree.NewG(16, rangeTsEntryLess), 36 start: startKey, 37 end: endKey, 38 } 39 m.set(startKey, endKey, startTs) 40 return m 41 } 42 43 func (m *rangeTsMap) clone() (res *rangeTsMap) { 44 res = &rangeTsMap{ 45 m: btree.NewG(16, rangeTsEntryLess), 46 start: m.start, 47 end: m.end, 48 } 49 m.m.Ascend(func(i rangeTsEntry) bool { 50 res.m.ReplaceOrInsert(i) 51 return true 52 }) 53 return 54 } 55 56 // set the timestamp of range [startKey, endKey) to ts. 57 // RangeLock uses rangeTsMap to store unsubscribed regions. 58 // So `set` should be called after a region gets unlocked from RangeLock. 59 // Note: It leaves the timestamp of range [endKey, +∞) intact. 60 func (m *rangeTsMap) set(startKey, endKey []byte, ts uint64) { 61 startEntry := rangeTsEntryWithKey(startKey) 62 endEntry := rangeTsEntryWithKey(endKey) 63 64 // Check and update the range overlapped with endKey is it exists. 65 if !bytes.Equal(m.end, endKey) && !m.m.Has(endEntry) { 66 var found bool 67 var endKeyOverlapped rangeTsEntry 68 m.m.DescendLessOrEqual(endEntry, func(i rangeTsEntry) bool { 69 found = true 70 endKeyOverlapped.ts = i.ts 71 endKeyOverlapped.isSet = i.isSet 72 return false 73 }) 74 if found { 75 if endKeyOverlapped.isSet { 76 log.Panic("rangeTsMap double set") 77 } 78 endKeyOverlapped.startKey = endKey 79 m.m.ReplaceOrInsert(endKeyOverlapped) 80 } 81 } 82 83 // Check and update the range overlapped with startKey if it exists. 84 if !m.m.Has(startEntry) { 85 var found bool 86 var startKeyOverlapped rangeTsEntry 87 m.m.DescendLessOrEqual(startEntry, func(i rangeTsEntry) bool { 88 found = true 89 startKeyOverlapped.isSet = i.isSet 90 return false 91 }) 92 if found && startKeyOverlapped.isSet { 93 log.Panic("rangeTsMap double set") 94 } 95 } 96 97 // Check and delete all covered ranges. 98 entriesToDelete := make([]rangeTsEntry, 0) 99 m.m.AscendRange(startEntry, endEntry, func(i rangeTsEntry) bool { 100 if i.isSet { 101 log.Panic("rangeTsMap double set") 102 } 103 entriesToDelete = append(entriesToDelete, i) 104 return true 105 }) 106 for _, e := range entriesToDelete { 107 m.m.Delete(e) 108 } 109 110 m.m.ReplaceOrInsert(rangeTsEntry{startKey: startKey, ts: ts, isSet: true}) 111 } 112 113 // RangeLock uses rangeTsMap to store unsubscribed regions. 114 // So `unset` should be called after a region gets locked in RangeLock. 115 func (m *rangeTsMap) unset(startKey, endKey []byte) { 116 var neighbor rangeTsEntry 117 var exist bool 118 startEntry := rangeTsEntryWithKey(startKey) 119 endEntry := rangeTsEntryWithKey(endKey) 120 121 // Check and update the range overlapped with endKey is it exists. 122 if !bytes.Equal(m.end, endKey) { 123 if neighbor, exist = m.m.Get(endEntry); !exist { 124 var found bool 125 var endKeyOverlapped rangeTsEntry 126 m.m.DescendLessOrEqual(endEntry, func(i rangeTsEntry) bool { 127 found = true 128 endKeyOverlapped.ts = i.ts 129 endKeyOverlapped.isSet = i.isSet 130 return false 131 }) 132 if found { 133 if !endKeyOverlapped.isSet { 134 log.Panic("rangeTsMap double unset") 135 } 136 endKeyOverlapped.startKey = endKey 137 m.m.ReplaceOrInsert(endKeyOverlapped) 138 } 139 } else if !neighbor.isSet { 140 m.m.Delete(neighbor) 141 } 142 } 143 144 // Check and update the range overlapped with startKey if it exists. 145 exist = false 146 m.m.DescendLessOrEqual(startEntry, func(i rangeTsEntry) bool { 147 if bytes.Compare(i.startKey, startKey) < 0 { 148 neighbor = i 149 exist = true 150 return false 151 } 152 return true 153 }) 154 shouldInsert := !exist || neighbor.isSet 155 156 // Check and delete all covered ranges. 157 entriesToDelete := make([]rangeTsEntry, 0) 158 m.m.AscendRange(startEntry, endEntry, func(i rangeTsEntry) bool { 159 if !i.isSet { 160 log.Panic("rangeTsMap double unset") 161 } 162 entriesToDelete = append(entriesToDelete, i) 163 return true 164 }) 165 for _, e := range entriesToDelete { 166 m.m.Delete(e) 167 } 168 169 if shouldInsert { 170 m.m.ReplaceOrInsert(rangeTsEntry{startKey: startKey, isSet: false}) 171 } 172 } 173 174 func (m *rangeTsMap) getMinTsInRange(startKey, endKey []byte) uint64 { 175 var ts uint64 = math.MaxUint64 176 177 startEntry := rangeTsEntryWithKey(startKey) 178 if _, ok := m.m.Get(startEntry); !ok { 179 m.m.DescendLessOrEqual(startEntry, func(i rangeTsEntry) bool { 180 if !i.isSet { 181 log.Panic("rangeTsMap get after unset") 182 } 183 ts = i.ts 184 return false 185 }) 186 } 187 188 endEntry := rangeTsEntryWithKey(endKey) 189 m.m.AscendRange(startEntry, endEntry, func(i rangeTsEntry) bool { 190 if !i.isSet { 191 log.Panic("rangeTsMap get after unset") 192 } 193 if ts > i.ts { 194 ts = i.ts 195 } 196 return true 197 }) 198 199 return ts 200 } 201 202 func (m *rangeTsMap) getMinTs() uint64 { 203 var ts uint64 = math.MaxUint64 204 205 m.m.Ascend(func(i rangeTsEntry) bool { 206 if i.isSet && ts > i.ts { 207 ts = i.ts 208 } 209 return true 210 }) 211 212 return ts 213 } 214 215 // rangeTsEntry is the entry of rangeTsMap. 216 type rangeTsEntry struct { 217 // Only startKey is necessary. End key can be inferred by the next item, 218 // since the map always keeps a continuous range. 219 startKey []byte 220 ts uint64 221 222 // rangeTsEntry is used in rangeTsMap. rangeTsMap.set will associate a timestamp to a given key range, 223 // and rangeTsMap.unset will revoke the relationship. `isSet` indicates whether a key range is 224 // generated from a set or unset operation. 225 isSet bool 226 } 227 228 func rangeTsEntryWithKey(key []byte) rangeTsEntry { 229 return rangeTsEntry{startKey: key} 230 } 231 232 func rangeTsEntryLess(a, b rangeTsEntry) bool { 233 return bytes.Compare(a.startKey, b.startKey) < 0 234 }