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  }