github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/batcheval/result/result.go (about) 1 // Copyright 2014 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 result 12 13 import ( 14 "context" 15 "fmt" 16 17 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverpb" 18 "github.com/cockroachdb/cockroach/pkg/roachpb" 19 "github.com/cockroachdb/cockroach/pkg/util/log" 20 "github.com/cockroachdb/errors" 21 "github.com/kr/pretty" 22 ) 23 24 // LocalResult is data belonging to an evaluated command that is 25 // only used on the node on which the command was proposed. Note that 26 // the proposing node may die before the local results are processed, 27 // so any side effects here are only best-effort. 28 type LocalResult struct { 29 Reply *roachpb.BatchResponse 30 31 // EncounteredIntents stores any intents from other transactions that the 32 // request encountered but did not conflict with. They should be handed off 33 // to asynchronous intent processing on the proposer, so that an attempt to 34 // resolve them is made. 35 EncounteredIntents []roachpb.Intent 36 // AcquiredLocks stores any newly acquired or re-acquired locks. 37 AcquiredLocks []roachpb.LockAcquisition 38 // ResolvedLocks stores any resolved lock spans, either with finalized or 39 // pending statuses. Unlike AcquiredLocks and EncounteredIntents, values in 40 // this slice will represent spans of locks that were resolved. 41 ResolvedLocks []roachpb.LockUpdate 42 // UpdatedTxns stores transaction records that have been updated by 43 // calls to EndTxn, PushTxn, and RecoverTxn. 44 UpdatedTxns []*roachpb.Transaction 45 // EndTxns stores completed transactions. If the transaction 46 // contains unresolved intents, they should be handed off for 47 // asynchronous intent resolution. A bool in each EndTxnIntents 48 // indicates whether or not the intents must be left alone if the 49 // corresponding command/proposal didn't succeed. For example, 50 // resolving intents of a committing txn should not happen if the 51 // commit fails, or we may accidentally make uncommitted values 52 // live. 53 EndTxns []EndTxnIntents 54 55 // When set (in which case we better be the first range), call 56 // GossipFirstRange if the Replica holds the lease. 57 GossipFirstRange bool 58 // Call MaybeGossipSystemConfig. 59 MaybeGossipSystemConfig bool 60 // Call MaybeGossipSystemConfigIfHaveFailure 61 MaybeGossipSystemConfigIfHaveFailure bool 62 // Call MaybeAddToSplitQueue. 63 MaybeAddToSplitQueue bool 64 // Call MaybeGossipNodeLiveness with the specified Span, if set. 65 MaybeGossipNodeLiveness *roachpb.Span 66 // Call maybeWatchForMerge. 67 MaybeWatchForMerge bool 68 69 // Metrics contains counters which are to be passed to the 70 // metrics subsystem. 71 Metrics *Metrics 72 } 73 74 // IsZero reports whether lResult is the zero value. 75 func (lResult *LocalResult) IsZero() bool { 76 // NB: keep in order. 77 return lResult.Reply == nil && 78 lResult.EncounteredIntents == nil && 79 lResult.AcquiredLocks == nil && 80 lResult.ResolvedLocks == nil && 81 lResult.UpdatedTxns == nil && 82 lResult.EndTxns == nil && 83 !lResult.GossipFirstRange && 84 !lResult.MaybeGossipSystemConfig && 85 !lResult.MaybeGossipSystemConfigIfHaveFailure && 86 lResult.MaybeGossipNodeLiveness == nil && 87 !lResult.MaybeWatchForMerge && 88 lResult.Metrics == nil 89 } 90 91 func (lResult *LocalResult) String() string { 92 if lResult == nil { 93 return "LocalResult: nil" 94 } 95 return fmt.Sprintf("LocalResult (reply: %v, "+ 96 "#encountered intents: %d, #acquired locks: %d, #resolved locks: %d"+ 97 "#updated txns: %d #end txns: %d, "+ 98 "GossipFirstRange:%t MaybeGossipSystemConfig:%t "+ 99 "MaybeGossipSystemConfigIfHaveFailure:%t MaybeAddToSplitQueue:%t "+ 100 "MaybeGossipNodeLiveness:%s MaybeWatchForMerge:%t", 101 lResult.Reply, 102 len(lResult.EncounteredIntents), len(lResult.AcquiredLocks), len(lResult.ResolvedLocks), 103 len(lResult.UpdatedTxns), len(lResult.EndTxns), 104 lResult.GossipFirstRange, lResult.MaybeGossipSystemConfig, 105 lResult.MaybeGossipSystemConfigIfHaveFailure, lResult.MaybeAddToSplitQueue, 106 lResult.MaybeGossipNodeLiveness, lResult.MaybeWatchForMerge) 107 } 108 109 // DetachEncounteredIntents returns (and removes) those encountered 110 // intents from the LocalEvalResult which are supposed to be handled. 111 func (lResult *LocalResult) DetachEncounteredIntents() []roachpb.Intent { 112 if lResult == nil { 113 return nil 114 } 115 r := lResult.EncounteredIntents 116 lResult.EncounteredIntents = nil 117 return r 118 } 119 120 // DetachEndTxns returns (and removes) the EndTxnIntent objects from 121 // the local result. If alwaysOnly is true, the slice is filtered to 122 // include only those which have specified returnAlways=true, meaning 123 // the intents should be resolved regardless of whether the 124 // EndTxn command succeeded. 125 func (lResult *LocalResult) DetachEndTxns(alwaysOnly bool) []EndTxnIntents { 126 if lResult == nil { 127 return nil 128 } 129 r := lResult.EndTxns 130 if alwaysOnly { 131 // If alwaysOnly, filter away any !Always EndTxnIntents. 132 r = r[:0] 133 for _, eti := range lResult.EndTxns { 134 if eti.Always { 135 r = append(r, eti) 136 } 137 } 138 } 139 lResult.EndTxns = nil 140 return r 141 } 142 143 // Result is the result of evaluating a KV request. That is, the 144 // proposer (which holds the lease, at least in the case in which the command 145 // will complete successfully) has evaluated the request and is holding on to: 146 // 147 // a) changes to be written to disk when applying the command 148 // b) changes to the state which may require special handling (i.e. code 149 // execution) on all Replicas 150 // c) data which isn't sent to the followers but the proposer needs for tasks 151 // it must run when the command has applied (such as resolving intents). 152 type Result struct { 153 Local LocalResult 154 Replicated kvserverpb.ReplicatedEvalResult 155 WriteBatch *kvserverpb.WriteBatch 156 LogicalOpLog *kvserverpb.LogicalOpLog 157 } 158 159 // IsZero reports whether p is the zero value. 160 func (p *Result) IsZero() bool { 161 if !p.Local.IsZero() { 162 return false 163 } 164 if !p.Replicated.Equal(kvserverpb.ReplicatedEvalResult{}) { 165 return false 166 } 167 if p.WriteBatch != nil { 168 return false 169 } 170 if p.LogicalOpLog != nil { 171 return false 172 } 173 return true 174 } 175 176 // coalesceBool ORs rhs into lhs and then zeroes rhs. 177 func coalesceBool(lhs *bool, rhs *bool) { 178 *lhs = *lhs || *rhs 179 *rhs = false 180 } 181 182 // MergeAndDestroy absorbs the supplied EvalResult while validating that the 183 // resulting EvalResult makes sense. For example, it is forbidden to absorb 184 // two lease updates or log truncations, or multiple splits and/or merges. 185 // 186 // The passed EvalResult must not be used once passed to Merge. 187 func (p *Result) MergeAndDestroy(q Result) error { 188 if q.Replicated.State != nil { 189 if q.Replicated.State.RaftAppliedIndex != 0 { 190 return errors.New("must not specify RaftApplyIndex") 191 } 192 if q.Replicated.State.LeaseAppliedIndex != 0 { 193 return errors.New("must not specify RaftApplyIndex") 194 } 195 if p.Replicated.State == nil { 196 p.Replicated.State = &kvserverpb.ReplicaState{} 197 } 198 if p.Replicated.State.Desc == nil { 199 p.Replicated.State.Desc = q.Replicated.State.Desc 200 } else if q.Replicated.State.Desc != nil { 201 return errors.New("conflicting RangeDescriptor") 202 } 203 q.Replicated.State.Desc = nil 204 205 if p.Replicated.State.Lease == nil { 206 p.Replicated.State.Lease = q.Replicated.State.Lease 207 } else if q.Replicated.State.Lease != nil { 208 return errors.New("conflicting Lease") 209 } 210 q.Replicated.State.Lease = nil 211 212 if p.Replicated.State.TruncatedState == nil { 213 p.Replicated.State.TruncatedState = q.Replicated.State.TruncatedState 214 } else if q.Replicated.State.TruncatedState != nil { 215 return errors.New("conflicting TruncatedState") 216 } 217 q.Replicated.State.TruncatedState = nil 218 219 if q.Replicated.State.GCThreshold != nil { 220 if p.Replicated.State.GCThreshold == nil { 221 p.Replicated.State.GCThreshold = q.Replicated.State.GCThreshold 222 } else { 223 p.Replicated.State.GCThreshold.Forward(*q.Replicated.State.GCThreshold) 224 } 225 q.Replicated.State.GCThreshold = nil 226 } 227 228 if q.Replicated.State.Stats != nil { 229 return errors.New("must not specify Stats") 230 } 231 if (*q.Replicated.State != kvserverpb.ReplicaState{}) { 232 log.Fatalf(context.TODO(), "unhandled EvalResult: %s", 233 pretty.Diff(*q.Replicated.State, kvserverpb.ReplicaState{})) 234 } 235 q.Replicated.State = nil 236 } 237 238 if p.Replicated.Split == nil { 239 p.Replicated.Split = q.Replicated.Split 240 } else if q.Replicated.Split != nil { 241 return errors.New("conflicting Split") 242 } 243 q.Replicated.Split = nil 244 245 if p.Replicated.Merge == nil { 246 p.Replicated.Merge = q.Replicated.Merge 247 } else if q.Replicated.Merge != nil { 248 return errors.New("conflicting Merge") 249 } 250 q.Replicated.Merge = nil 251 252 if p.Replicated.ChangeReplicas == nil { 253 p.Replicated.ChangeReplicas = q.Replicated.ChangeReplicas 254 } else if q.Replicated.ChangeReplicas != nil { 255 return errors.New("conflicting ChangeReplicas") 256 } 257 q.Replicated.ChangeReplicas = nil 258 259 if p.Replicated.ComputeChecksum == nil { 260 p.Replicated.ComputeChecksum = q.Replicated.ComputeChecksum 261 } else if q.Replicated.ComputeChecksum != nil { 262 return errors.New("conflicting ComputeChecksum") 263 } 264 q.Replicated.ComputeChecksum = nil 265 266 if p.Replicated.RaftLogDelta == 0 { 267 p.Replicated.RaftLogDelta = q.Replicated.RaftLogDelta 268 } else if q.Replicated.RaftLogDelta != 0 { 269 return errors.New("conflicting RaftLogDelta") 270 } 271 q.Replicated.RaftLogDelta = 0 272 273 if p.Replicated.AddSSTable == nil { 274 p.Replicated.AddSSTable = q.Replicated.AddSSTable 275 } else if q.Replicated.AddSSTable != nil { 276 return errors.New("conflicting AddSSTable") 277 } 278 q.Replicated.AddSSTable = nil 279 280 if q.Replicated.SuggestedCompactions != nil { 281 if p.Replicated.SuggestedCompactions == nil { 282 p.Replicated.SuggestedCompactions = q.Replicated.SuggestedCompactions 283 } else { 284 p.Replicated.SuggestedCompactions = append(p.Replicated.SuggestedCompactions, q.Replicated.SuggestedCompactions...) 285 } 286 } 287 q.Replicated.SuggestedCompactions = nil 288 289 if p.Replicated.PrevLeaseProposal == nil { 290 p.Replicated.PrevLeaseProposal = q.Replicated.PrevLeaseProposal 291 } else if q.Replicated.PrevLeaseProposal != nil { 292 return errors.New("conflicting lease expiration") 293 } 294 q.Replicated.PrevLeaseProposal = nil 295 296 if p.Local.EncounteredIntents == nil { 297 p.Local.EncounteredIntents = q.Local.EncounteredIntents 298 } else { 299 p.Local.EncounteredIntents = append(p.Local.EncounteredIntents, q.Local.EncounteredIntents...) 300 } 301 q.Local.EncounteredIntents = nil 302 303 if p.Local.AcquiredLocks == nil { 304 p.Local.AcquiredLocks = q.Local.AcquiredLocks 305 } else { 306 p.Local.AcquiredLocks = append(p.Local.AcquiredLocks, q.Local.AcquiredLocks...) 307 } 308 q.Local.AcquiredLocks = nil 309 310 if p.Local.ResolvedLocks == nil { 311 p.Local.ResolvedLocks = q.Local.ResolvedLocks 312 } else { 313 p.Local.ResolvedLocks = append(p.Local.ResolvedLocks, q.Local.ResolvedLocks...) 314 } 315 q.Local.ResolvedLocks = nil 316 317 if p.Local.UpdatedTxns == nil { 318 p.Local.UpdatedTxns = q.Local.UpdatedTxns 319 } else { 320 p.Local.UpdatedTxns = append(p.Local.UpdatedTxns, q.Local.UpdatedTxns...) 321 } 322 q.Local.UpdatedTxns = nil 323 324 if p.Local.EndTxns == nil { 325 p.Local.EndTxns = q.Local.EndTxns 326 } else { 327 p.Local.EndTxns = append(p.Local.EndTxns, q.Local.EndTxns...) 328 } 329 q.Local.EndTxns = nil 330 331 if p.Local.MaybeGossipNodeLiveness == nil { 332 p.Local.MaybeGossipNodeLiveness = q.Local.MaybeGossipNodeLiveness 333 } else if q.Local.MaybeGossipNodeLiveness != nil { 334 return errors.New("conflicting MaybeGossipNodeLiveness") 335 } 336 q.Local.MaybeGossipNodeLiveness = nil 337 338 coalesceBool(&p.Local.GossipFirstRange, &q.Local.GossipFirstRange) 339 coalesceBool(&p.Local.MaybeGossipSystemConfig, &q.Local.MaybeGossipSystemConfig) 340 coalesceBool(&p.Local.MaybeGossipSystemConfigIfHaveFailure, &q.Local.MaybeGossipSystemConfigIfHaveFailure) 341 coalesceBool(&p.Local.MaybeAddToSplitQueue, &q.Local.MaybeAddToSplitQueue) 342 coalesceBool(&p.Local.MaybeWatchForMerge, &q.Local.MaybeWatchForMerge) 343 344 if p.Local.Metrics == nil { 345 p.Local.Metrics = q.Local.Metrics 346 } else if q.Local.Metrics != nil { 347 p.Local.Metrics.Add(*q.Local.Metrics) 348 } 349 q.Local.Metrics = nil 350 351 if q.LogicalOpLog != nil { 352 if p.LogicalOpLog == nil { 353 p.LogicalOpLog = q.LogicalOpLog 354 } else { 355 p.LogicalOpLog.Ops = append(p.LogicalOpLog.Ops, q.LogicalOpLog.Ops...) 356 } 357 } 358 q.LogicalOpLog = nil 359 360 if !q.IsZero() { 361 log.Fatalf(context.TODO(), "unhandled EvalResult: %s", pretty.Diff(q, Result{})) 362 } 363 364 return nil 365 }