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 }