github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/rtree/rtree.go (about) 1 // Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0. 2 3 package rtree 4 5 import ( 6 "bytes" 7 8 "github.com/google/btree" 9 backuppb "github.com/pingcap/kvproto/pkg/backup" 10 "github.com/pingcap/log" 11 12 "github.com/pingcap/br/pkg/logutil" 13 ) 14 15 // Range represents a backup response. 16 type Range struct { 17 StartKey []byte 18 EndKey []byte 19 Files []*backuppb.File 20 } 21 22 // BytesAndKeys returns total bytes and keys in a range. 23 func (rg *Range) BytesAndKeys() (bytes, keys uint64) { 24 for _, f := range rg.Files { 25 bytes += f.TotalBytes 26 keys += f.TotalKvs 27 } 28 return 29 } 30 31 // Intersect returns intersect range in the tree. 32 func (rg *Range) Intersect( 33 start, end []byte, 34 ) (subStart, subEnd []byte, isIntersect bool) { 35 // empty mean the max end key 36 if len(rg.EndKey) != 0 && bytes.Compare(start, rg.EndKey) >= 0 { 37 isIntersect = false 38 return 39 } 40 if len(end) != 0 && bytes.Compare(end, rg.StartKey) <= 0 { 41 isIntersect = false 42 return 43 } 44 isIntersect = true 45 if bytes.Compare(start, rg.StartKey) >= 0 { 46 subStart = start 47 } else { 48 subStart = rg.StartKey 49 } 50 switch { 51 case len(end) == 0: 52 subEnd = rg.EndKey 53 case len(rg.EndKey) == 0: 54 subEnd = end 55 case bytes.Compare(end, rg.EndKey) < 0: 56 subEnd = end 57 default: 58 subEnd = rg.EndKey 59 } 60 return 61 } 62 63 // Contains check if the range contains the given key, [start, end). 64 func (rg *Range) Contains(key []byte) bool { 65 start, end := rg.StartKey, rg.EndKey 66 return bytes.Compare(key, start) >= 0 && 67 (len(end) == 0 || bytes.Compare(key, end) < 0) 68 } 69 70 // Less impls btree.Item. 71 func (rg *Range) Less(than btree.Item) bool { 72 // rg.StartKey < than.StartKey 73 ta := than.(*Range) 74 return bytes.Compare(rg.StartKey, ta.StartKey) < 0 75 } 76 77 var _ btree.Item = &Range{} 78 79 // RangeTree is sorted tree for Ranges. 80 // All the ranges it stored do not overlap. 81 type RangeTree struct { 82 *btree.BTree 83 } 84 85 // NewRangeTree returns an empty range tree. 86 func NewRangeTree() RangeTree { 87 return RangeTree{ 88 BTree: btree.New(32), 89 } 90 } 91 92 // Find is a helper function to find an item that contains the range start 93 // key. 94 func (rangeTree *RangeTree) Find(rg *Range) *Range { 95 var ret *Range 96 rangeTree.DescendLessOrEqual(rg, func(i btree.Item) bool { 97 ret = i.(*Range) 98 return false 99 }) 100 101 if ret == nil || !ret.Contains(rg.StartKey) { 102 return nil 103 } 104 105 return ret 106 } 107 108 // getOverlaps gets the ranges which are overlapped with the specified range range. 109 func (rangeTree *RangeTree) getOverlaps(rg *Range) []*Range { 110 // note that find() gets the last item that is less or equal than the range. 111 // in the case: |_______a_______|_____b_____|___c___| 112 // new range is |______d______| 113 // find() will return Range of range_a 114 // and both startKey of range_a and range_b are less than endKey of range_d, 115 // thus they are regarded as overlapped ranges. 116 found := rangeTree.Find(rg) 117 if found == nil { 118 found = rg 119 } 120 121 var overlaps []*Range 122 rangeTree.AscendGreaterOrEqual(found, func(i btree.Item) bool { 123 over := i.(*Range) 124 if len(rg.EndKey) > 0 && bytes.Compare(rg.EndKey, over.StartKey) <= 0 { 125 return false 126 } 127 overlaps = append(overlaps, over) 128 return true 129 }) 130 return overlaps 131 } 132 133 // Update inserts range into tree and delete overlapping ranges. 134 func (rangeTree *RangeTree) Update(rg Range) { 135 overlaps := rangeTree.getOverlaps(&rg) 136 // Range has backuped, overwrite overlapping range. 137 for _, item := range overlaps { 138 log.Info("delete overlapping range", 139 logutil.Key("startKey", item.StartKey), 140 logutil.Key("endKey", item.EndKey)) 141 rangeTree.Delete(item) 142 } 143 rangeTree.ReplaceOrInsert(&rg) 144 } 145 146 // Put forms a range and inserts it into tree. 147 func (rangeTree *RangeTree) Put( 148 startKey, endKey []byte, files []*backuppb.File, 149 ) { 150 rg := Range{ 151 StartKey: startKey, 152 EndKey: endKey, 153 Files: files, 154 } 155 rangeTree.Update(rg) 156 } 157 158 // InsertRange inserts ranges into the range tree. 159 // It returns a non-nil range if there are soe overlapped ranges. 160 func (rangeTree *RangeTree) InsertRange(rg Range) *Range { 161 out := rangeTree.ReplaceOrInsert(&rg) 162 if out == nil { 163 return nil 164 } 165 return out.(*Range) 166 } 167 168 // GetSortedRanges collects and returns sorted ranges. 169 func (rangeTree *RangeTree) GetSortedRanges() []Range { 170 sortedRanges := make([]Range, 0, rangeTree.Len()) 171 rangeTree.Ascend(func(rg btree.Item) bool { 172 if rg == nil { 173 return false 174 } 175 sortedRanges = append(sortedRanges, *rg.(*Range)) 176 return true 177 }) 178 return sortedRanges 179 } 180 181 // GetIncompleteRange returns missing range covered by startKey and endKey. 182 func (rangeTree *RangeTree) GetIncompleteRange( 183 startKey, endKey []byte, 184 ) []Range { 185 if len(startKey) != 0 && bytes.Equal(startKey, endKey) { 186 return []Range{} 187 } 188 incomplete := make([]Range, 0, 64) 189 requsetRange := Range{StartKey: startKey, EndKey: endKey} 190 lastEndKey := startKey 191 pviot := &Range{StartKey: startKey} 192 if first := rangeTree.Find(pviot); first != nil { 193 pviot.StartKey = first.StartKey 194 } 195 rangeTree.AscendGreaterOrEqual(pviot, func(i btree.Item) bool { 196 rg := i.(*Range) 197 if bytes.Compare(lastEndKey, rg.StartKey) < 0 { 198 start, end, isIntersect := 199 requsetRange.Intersect(lastEndKey, rg.StartKey) 200 if isIntersect { 201 // There is a gap between the last item and the current item. 202 incomplete = 203 append(incomplete, Range{StartKey: start, EndKey: end}) 204 } 205 } 206 lastEndKey = rg.EndKey 207 return len(endKey) == 0 || bytes.Compare(rg.EndKey, endKey) < 0 208 }) 209 210 // Check whether we need append the last range 211 if !bytes.Equal(lastEndKey, endKey) && len(lastEndKey) != 0 && 212 (len(endKey) == 0 || bytes.Compare(lastEndKey, endKey) < 0) { 213 start, end, isIntersect := requsetRange.Intersect(lastEndKey, endKey) 214 if isIntersect { 215 incomplete = 216 append(incomplete, Range{StartKey: start, EndKey: end}) 217 } 218 } 219 return incomplete 220 }