github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/spanset/spanset.go (about)

     1  // Copyright 2017 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 spanset
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"runtime/debug"
    17  	"strings"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/keys"
    20  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    21  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    22  	"github.com/cockroachdb/cockroach/pkg/util/log"
    23  	"github.com/cockroachdb/errors"
    24  )
    25  
    26  // SpanAccess records the intended mode of access in a SpanSet.
    27  type SpanAccess int
    28  
    29  // Constants for SpanAccess. Higher-valued accesses imply lower-level ones.
    30  const (
    31  	SpanReadOnly SpanAccess = iota
    32  	SpanReadWrite
    33  	NumSpanAccess
    34  )
    35  
    36  // String returns a string representation of the SpanAccess.
    37  func (a SpanAccess) String() string {
    38  	switch a {
    39  	case SpanReadOnly:
    40  		return "read"
    41  	case SpanReadWrite:
    42  		return "write"
    43  	default:
    44  		panic("unreachable")
    45  	}
    46  }
    47  
    48  // SpanScope divides access types into local and global keys.
    49  type SpanScope int
    50  
    51  // Constants for span scopes.
    52  const (
    53  	SpanGlobal SpanScope = iota
    54  	SpanLocal
    55  	NumSpanScope
    56  )
    57  
    58  // String returns a string representation of the SpanScope.
    59  func (a SpanScope) String() string {
    60  	switch a {
    61  	case SpanGlobal:
    62  		return "global"
    63  	case SpanLocal:
    64  		return "local"
    65  	default:
    66  		panic("unreachable")
    67  	}
    68  }
    69  
    70  // Span is used to represent a keyspan accessed by a request at a given
    71  // timestamp. A zero timestamp indicates it's a non-MVCC access.
    72  type Span struct {
    73  	roachpb.Span
    74  	Timestamp hlc.Timestamp
    75  }
    76  
    77  // SpanSet tracks the set of key spans touched by a command, broken into MVCC
    78  // and non-MVCC accesses. The set is divided into subsets for access type
    79  // (read-only or read/write) and key scope (local or global; used to facilitate
    80  // use by the separate local and global latches).
    81  // The Span slice for a particular access and scope contains non-overlapping
    82  // spans in increasing key order after calls to SortAndDedup.
    83  type SpanSet struct {
    84  	spans [NumSpanAccess][NumSpanScope][]Span
    85  }
    86  
    87  // String prints a string representation of the SpanSet.
    88  func (s *SpanSet) String() string {
    89  	var buf strings.Builder
    90  	for sa := SpanAccess(0); sa < NumSpanAccess; sa++ {
    91  		for ss := SpanScope(0); ss < NumSpanScope; ss++ {
    92  			for _, cur := range s.GetSpans(sa, ss) {
    93  				fmt.Fprintf(&buf, "%s %s: %s at %s\n",
    94  					sa, ss, cur.Span.String(), cur.Timestamp.String())
    95  			}
    96  		}
    97  	}
    98  	return buf.String()
    99  }
   100  
   101  // Len returns the total number of spans tracked across all accesses and scopes.
   102  func (s *SpanSet) Len() int {
   103  	var count int
   104  	for sa := SpanAccess(0); sa < NumSpanAccess; sa++ {
   105  		for ss := SpanScope(0); ss < NumSpanScope; ss++ {
   106  			count += len(s.GetSpans(sa, ss))
   107  		}
   108  	}
   109  	return count
   110  }
   111  
   112  // Empty returns whether the set contains any spans across all accesses and scopes.
   113  func (s *SpanSet) Empty() bool {
   114  	return s.Len() == 0
   115  }
   116  
   117  // Reserve space for N additional spans.
   118  func (s *SpanSet) Reserve(access SpanAccess, scope SpanScope, n int) {
   119  	existing := s.spans[access][scope]
   120  	s.spans[access][scope] = make([]Span, len(existing), n+cap(existing))
   121  	copy(s.spans[access][scope], existing)
   122  }
   123  
   124  // AddNonMVCC adds a non-MVCC span to the span set. This should typically
   125  // local keys.
   126  func (s *SpanSet) AddNonMVCC(access SpanAccess, span roachpb.Span) {
   127  	s.AddMVCC(access, span, hlc.Timestamp{})
   128  }
   129  
   130  // AddMVCC adds an MVCC span to the span set to be accessed at the given
   131  // timestamp. This should typically be used for MVCC keys, user keys for e.g.
   132  func (s *SpanSet) AddMVCC(access SpanAccess, span roachpb.Span, timestamp hlc.Timestamp) {
   133  	scope := SpanGlobal
   134  	if keys.IsLocal(span.Key) {
   135  		scope = SpanLocal
   136  		timestamp = hlc.Timestamp{}
   137  	}
   138  
   139  	s.spans[access][scope] = append(s.spans[access][scope], Span{Span: span, Timestamp: timestamp})
   140  }
   141  
   142  // Merge merges all spans in s2 into s. s2 is not modified.
   143  func (s *SpanSet) Merge(s2 *SpanSet) {
   144  	for sa := SpanAccess(0); sa < NumSpanAccess; sa++ {
   145  		for ss := SpanScope(0); ss < NumSpanScope; ss++ {
   146  			s.spans[sa][ss] = append(s.spans[sa][ss], s2.spans[sa][ss]...)
   147  		}
   148  	}
   149  	s.SortAndDedup()
   150  }
   151  
   152  // SortAndDedup sorts the spans in the SpanSet and removes any duplicates.
   153  func (s *SpanSet) SortAndDedup() {
   154  	for sa := SpanAccess(0); sa < NumSpanAccess; sa++ {
   155  		for ss := SpanScope(0); ss < NumSpanScope; ss++ {
   156  			s.spans[sa][ss], _ /* distinct */ = mergeSpans(s.spans[sa][ss])
   157  		}
   158  	}
   159  }
   160  
   161  // GetSpans returns a slice of spans with the given parameters.
   162  func (s *SpanSet) GetSpans(access SpanAccess, scope SpanScope) []Span {
   163  	return s.spans[access][scope]
   164  }
   165  
   166  // BoundarySpan returns a span containing all the spans with the given params.
   167  func (s *SpanSet) BoundarySpan(scope SpanScope) roachpb.Span {
   168  	var boundary roachpb.Span
   169  	for sa := SpanAccess(0); sa < NumSpanAccess; sa++ {
   170  		for _, cur := range s.GetSpans(sa, scope) {
   171  			if !boundary.Valid() {
   172  				boundary = cur.Span
   173  				continue
   174  			}
   175  			boundary = boundary.Combine(cur.Span)
   176  		}
   177  	}
   178  	return boundary
   179  }
   180  
   181  // MaxProtectedTimestamp returns the maximum timestamp that is protected across
   182  // all MVCC spans in the SpanSet. ReadWrite spans are protected from their
   183  // declared timestamp forward, so they have no maximum protect timestamp.
   184  // However, ReadOnly are protected only up to their declared timestamp and
   185  // are not protected at later timestamps.
   186  func (s *SpanSet) MaxProtectedTimestamp() hlc.Timestamp {
   187  	maxTS := hlc.MaxTimestamp
   188  	for ss := SpanScope(0); ss < NumSpanScope; ss++ {
   189  		for _, cur := range s.GetSpans(SpanReadOnly, ss) {
   190  			curTS := cur.Timestamp
   191  			if !curTS.IsEmpty() {
   192  				maxTS.Backward(curTS)
   193  			}
   194  		}
   195  	}
   196  	return maxTS
   197  }
   198  
   199  // AssertAllowed calls CheckAllowed and fatals if the access is not allowed.
   200  // Timestamps associated with the spans in the spanset are not considered,
   201  // only the span boundaries are checked.
   202  func (s *SpanSet) AssertAllowed(access SpanAccess, span roachpb.Span) {
   203  	if err := s.CheckAllowed(access, span); err != nil {
   204  		log.Fatalf(context.TODO(), "%v", err)
   205  	}
   206  }
   207  
   208  // CheckAllowed returns an error if the access is not allowed over the given
   209  // keyspan based on the collection of spans in the spanset. Timestamps
   210  // associated with the spans in the spanset are not considered, only the span
   211  // boundaries are checked.
   212  //
   213  // If the provided span contains only an (exclusive) EndKey and has a nil
   214  // (inclusive) Key then Key is considered to be the key previous to EndKey,
   215  // i.e. [,b) will be considered [b.Prev(),b).
   216  //
   217  // TODO(irfansharif): This does not currently work for spans that straddle
   218  // across multiple added spans. Specifically a spanset with spans [a-c) and
   219  // [b-d) added under read only and read write access modes respectively would
   220  // fail at checking if read only access over the span [a-d) was requested. This
   221  // is also a problem if the added spans were read only and the spanset wasn't
   222  // already SortAndDedup-ed.
   223  func (s *SpanSet) CheckAllowed(access SpanAccess, span roachpb.Span) error {
   224  	return s.checkAllowed(access, span, func(_ SpanAccess, _ Span) bool {
   225  		return true
   226  	})
   227  }
   228  
   229  // CheckAllowedAt is like CheckAllowed, except it returns an error if the access
   230  // is not allowed over the given keyspan at the given timestamp.
   231  func (s *SpanSet) CheckAllowedAt(
   232  	access SpanAccess, span roachpb.Span, timestamp hlc.Timestamp,
   233  ) error {
   234  	mvcc := !timestamp.IsEmpty()
   235  	return s.checkAllowed(access, span, func(declAccess SpanAccess, declSpan Span) bool {
   236  		declTimestamp := declSpan.Timestamp
   237  		if declTimestamp.IsEmpty() {
   238  			// When the span is declared as non-MVCC (i.e. with an empty
   239  			// timestamp), it's equivalent to a read/write mutex where we
   240  			// don't consider access timestamps.
   241  			return true
   242  		}
   243  
   244  		switch declAccess {
   245  		case SpanReadOnly:
   246  			switch access {
   247  			case SpanReadOnly:
   248  				// Read spans acquired at a specific timestamp only allow reads
   249  				// at that timestamp and below. Non-MVCC access is not allowed.
   250  				return mvcc && timestamp.LessEq(declTimestamp)
   251  			case SpanReadWrite:
   252  				// NB: should not get here, see checkAllowed.
   253  				panic("unexpected SpanReadWrite access")
   254  			default:
   255  				panic("unexpected span access")
   256  			}
   257  		case SpanReadWrite:
   258  			switch access {
   259  			case SpanReadOnly:
   260  				// Write spans acquired at a specific timestamp allow reads at
   261  				// any timestamp. Non-MVCC access is not allowed.
   262  				return mvcc
   263  			case SpanReadWrite:
   264  				// Write spans acquired at a specific timestamp allow writes at
   265  				// that timestamp of above. Non-MVCC access is not allowed.
   266  				return mvcc && declTimestamp.LessEq(timestamp)
   267  			default:
   268  				panic("unexpected span access")
   269  			}
   270  		default:
   271  			panic("unexpected span access")
   272  		}
   273  	})
   274  }
   275  
   276  func (s *SpanSet) checkAllowed(
   277  	access SpanAccess, span roachpb.Span, check func(SpanAccess, Span) bool,
   278  ) error {
   279  	scope := SpanGlobal
   280  	if (span.Key != nil && keys.IsLocal(span.Key)) ||
   281  		(span.EndKey != nil && keys.IsLocal(span.EndKey)) {
   282  		scope = SpanLocal
   283  	}
   284  
   285  	for ac := access; ac < NumSpanAccess; ac++ {
   286  		for _, cur := range s.spans[ac][scope] {
   287  			if contains(cur.Span, span) && check(ac, cur) {
   288  				return nil
   289  			}
   290  		}
   291  	}
   292  
   293  	return errors.Errorf("cannot %s undeclared span %s\ndeclared:\n%s\nstack:\n%s", access, span, s, debug.Stack())
   294  }
   295  
   296  // contains returns whether s1 contains s2. Unlike Span.Contains, this function
   297  // supports spans with a nil start key and a non-nil end key (e.g. "[nil, c)").
   298  // In this form, s2.Key (inclusive) is considered to be the previous key to
   299  // s2.EndKey (exclusive).
   300  func contains(s1, s2 roachpb.Span) bool {
   301  	if s2.Key != nil {
   302  		// The common case.
   303  		return s1.Contains(s2)
   304  	}
   305  
   306  	// The following is equivalent to:
   307  	//   s1.Contains(roachpb.Span{Key: s2.EndKey.Prev()})
   308  
   309  	if s1.EndKey == nil {
   310  		return s1.Key.IsPrev(s2.EndKey)
   311  	}
   312  
   313  	return s1.Key.Compare(s2.EndKey) < 0 && s1.EndKey.Compare(s2.EndKey) >= 0
   314  }
   315  
   316  // Validate returns an error if any spans that have been added to the set
   317  // are invalid.
   318  func (s *SpanSet) Validate() error {
   319  	for sa := SpanAccess(0); sa < NumSpanAccess; sa++ {
   320  		for ss := SpanScope(0); ss < NumSpanScope; ss++ {
   321  			for _, cur := range s.GetSpans(sa, ss) {
   322  				if len(cur.EndKey) > 0 && cur.Key.Compare(cur.EndKey) >= 0 {
   323  					return errors.Errorf("inverted span %s %s", cur.Key, cur.EndKey)
   324  				}
   325  			}
   326  		}
   327  	}
   328  
   329  	return nil
   330  }