github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/span/frontier.go (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package span 12 13 import ( 14 "container/heap" 15 "fmt" 16 "strings" 17 18 // Needed for roachpb.Span.String(). 19 _ "github.com/cockroachdb/cockroach/pkg/keys" 20 "github.com/cockroachdb/cockroach/pkg/roachpb" 21 "github.com/cockroachdb/cockroach/pkg/sql/covering" 22 "github.com/cockroachdb/cockroach/pkg/util/hlc" 23 "github.com/cockroachdb/cockroach/pkg/util/interval" 24 ) 25 26 // frontierEntry represents a timestamped span. It is used as the nodes in both 27 // the interval tree and heap needed to keep the Frontier. 28 type frontierEntry struct { 29 id int64 30 keys interval.Range 31 span roachpb.Span 32 ts hlc.Timestamp 33 34 // The index of the item in the frontierHeap, maintained by the 35 // heap.Interface methods. 36 index int 37 } 38 39 // ID implements interval.Interface. 40 func (s *frontierEntry) ID() uintptr { 41 return uintptr(s.id) 42 } 43 44 // Range implements interval.Interface. 45 func (s *frontierEntry) Range() interval.Range { 46 return s.keys 47 } 48 49 func (s *frontierEntry) String() string { 50 return fmt.Sprintf("[%s @ %s]", s.span, s.ts) 51 } 52 53 // frontierHeap implements heap.Interface and holds `frontierEntry`s. Entries 54 // are sorted based on their timestamp such that the oldest will rise to the top 55 // of the heap. 56 type frontierHeap []*frontierEntry 57 58 // Len implements heap.Interface. 59 func (h frontierHeap) Len() int { return len(h) } 60 61 // Less implements heap.Interface. 62 func (h frontierHeap) Less(i, j int) bool { 63 if h[i].ts == h[j].ts { 64 return h[i].span.Key.Compare(h[j].span.Key) < 0 65 } 66 return h[i].ts.Less(h[j].ts) 67 } 68 69 // Swap implements heap.Interface. 70 func (h frontierHeap) Swap(i, j int) { 71 h[i], h[j] = h[j], h[i] 72 h[i].index, h[j].index = i, j 73 } 74 75 // Push implements heap.Interface. 76 func (h *frontierHeap) Push(x interface{}) { 77 n := len(*h) 78 entry := x.(*frontierEntry) 79 entry.index = n 80 *h = append(*h, entry) 81 } 82 83 // Pop implements heap.Interface. 84 func (h *frontierHeap) Pop() interface{} { 85 old := *h 86 n := len(old) 87 entry := old[n-1] 88 entry.index = -1 // for safety 89 old[n-1] = nil // for gc 90 *h = old[0 : n-1] 91 return entry 92 } 93 94 // Frontier tracks the minimum timestamp of a set of spans. 95 type Frontier struct { 96 // tree contains `*frontierEntry` items for the entire current tracked 97 // span set. Any tracked spans that have never been `Forward`ed will have a 98 // zero timestamp. If any entries needed to be split along a tracking 99 // boundary, this has already been done by `insert` before it entered the 100 // tree. 101 tree interval.Tree 102 // minHeap contains the same `*frontierEntry` items as `tree`. Entries 103 // in the heap are sorted first by minimum timestamp and then by lesser 104 // start key. 105 minHeap frontierHeap 106 107 idAlloc int64 108 } 109 110 // MakeFrontier returns a Frontier that tracks the given set of spans. 111 func MakeFrontier(spans ...roachpb.Span) *Frontier { 112 s := &Frontier{tree: interval.NewTree(interval.ExclusiveOverlapper)} 113 for _, span := range spans { 114 e := &frontierEntry{ 115 id: s.idAlloc, 116 keys: span.AsRange(), 117 span: span, 118 ts: hlc.Timestamp{}, 119 } 120 s.idAlloc++ 121 if err := s.tree.Insert(e, true /* fast */); err != nil { 122 panic(err) 123 } 124 heap.Push(&s.minHeap, e) 125 } 126 s.tree.AdjustRanges() 127 return s 128 } 129 130 // Frontier returns the minimum timestamp being tracked. 131 func (f *Frontier) Frontier() hlc.Timestamp { 132 if f.minHeap.Len() == 0 { 133 return hlc.Timestamp{} 134 } 135 return f.minHeap[0].ts 136 } 137 138 // PeekFrontierSpan returns one of the spans at the Frontier. 139 func (f *Frontier) PeekFrontierSpan() roachpb.Span { 140 if f.minHeap.Len() == 0 { 141 return roachpb.Span{} 142 } 143 return f.minHeap[0].span 144 } 145 146 // Forward advances the timestamp for a span. Any part of the span that doesn't 147 // overlap the tracked span set will be ignored. True is returned if the 148 // frontier advanced as a result. 149 // 150 // Note that internally, it may be necessary to use multiple entries to 151 // represent this timestamped span (e.g. if it overlaps with the tracked span 152 // set boundary). Similarly, an entry created by a previous Forward may be 153 // partially overlapped and have to be split into two entries. 154 func (f *Frontier) Forward(span roachpb.Span, ts hlc.Timestamp) bool { 155 prevFrontier := f.Frontier() 156 f.insert(span, ts) 157 return prevFrontier.Less(f.Frontier()) 158 } 159 160 func (f *Frontier) insert(span roachpb.Span, ts hlc.Timestamp) { 161 entryKeys := span.AsRange() 162 overlapping := f.tree.Get(entryKeys) 163 164 // TODO(dan): OverlapCoveringMerge is overkill, do this without it. See 165 // `tscache/treeImpl.Add` for inspiration. 166 entryCov := covering.Covering{{Start: span.Key, End: span.EndKey, Payload: ts}} 167 overlapCov := make(covering.Covering, len(overlapping)) 168 for i, o := range overlapping { 169 spe := o.(*frontierEntry) 170 overlapCov[i] = covering.Range{ 171 Start: spe.span.Key, End: spe.span.EndKey, Payload: spe, 172 } 173 } 174 merged := covering.OverlapCoveringMerge([]covering.Covering{entryCov, overlapCov}) 175 176 toInsert := make([]frontierEntry, 0, len(merged)) 177 for _, m := range merged { 178 // Compute the newest timestamp seen for this span and note whether it's 179 // tracked. There will be either 1 or 2 payloads. If there's 2, it will 180 // be the new span and the old entry. If it's 1 it could be either a new 181 // span (which is untracked and should be ignored) or an old entry which 182 // has been clipped. 183 var mergedTs hlc.Timestamp 184 var tracked bool 185 for _, payload := range m.Payload.([]interface{}) { 186 switch p := payload.(type) { 187 case hlc.Timestamp: 188 if mergedTs.Less(p) { 189 mergedTs = p 190 } 191 case *frontierEntry: 192 tracked = true 193 if mergedTs.Less(p.ts) { 194 mergedTs = p.ts 195 } 196 } 197 } 198 // TODO(dan): Collapse span-adjacent entries with the same value for 199 // timestamp and tracked to save space. 200 if tracked { 201 toInsert = append(toInsert, frontierEntry{ 202 id: f.idAlloc, 203 keys: interval.Range{Start: m.Start, End: m.End}, 204 span: roachpb.Span{Key: m.Start, EndKey: m.End}, 205 ts: mergedTs, 206 }) 207 f.idAlloc++ 208 } 209 } 210 211 // All the entries in `overlapping` have been replaced by updated ones in 212 // `toInsert`, so remove them all from the tree and heap. 213 needAdjust := false 214 if len(overlapping) == 1 { 215 spe := overlapping[0].(*frontierEntry) 216 if err := f.tree.Delete(spe, false /* fast */); err != nil { 217 panic(err) 218 } 219 heap.Remove(&f.minHeap, spe.index) 220 } else { 221 for i := range overlapping { 222 spe := overlapping[i].(*frontierEntry) 223 if err := f.tree.Delete(spe, true /* fast */); err != nil { 224 panic(err) 225 } 226 heap.Remove(&f.minHeap, spe.index) 227 } 228 needAdjust = true 229 } 230 // Then insert! 231 if len(toInsert) == 1 { 232 if err := f.tree.Insert(&toInsert[0], false /* fast */); err != nil { 233 panic(err) 234 } 235 heap.Push(&f.minHeap, &toInsert[0]) 236 } else { 237 for i := range toInsert { 238 if err := f.tree.Insert(&toInsert[i], true /* fast */); err != nil { 239 panic(err) 240 } 241 heap.Push(&f.minHeap, &toInsert[i]) 242 } 243 needAdjust = true 244 } 245 if needAdjust { 246 f.tree.AdjustRanges() 247 } 248 } 249 250 // Entries invokes the given callback with the current timestamp for each 251 // component span in the tracked span set. 252 func (f *Frontier) Entries(fn func(roachpb.Span, hlc.Timestamp)) { 253 f.tree.Do(func(i interval.Interface) bool { 254 spe := i.(*frontierEntry) 255 fn(spe.span, spe.ts) 256 return false 257 }) 258 } 259 260 func (f *Frontier) String() string { 261 var buf strings.Builder 262 f.tree.Do(func(i interval.Interface) bool { 263 if buf.Len() != 0 { 264 buf.WriteString(` `) 265 } 266 buf.WriteString(i.(*frontierEntry).String()) 267 return false 268 }) 269 return buf.String() 270 }