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  }