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

     1  // Copyright 2016 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 kvserver
    12  
    13  import (
    14  	"fmt"
    15  	"reflect"
    16  	"runtime"
    17  	"strings"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/gossip"
    20  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/apply"
    21  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/compactor"
    22  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverpb"
    23  	"github.com/cockroachdb/cockroach/pkg/storage/enginepb"
    24  	"github.com/cockroachdb/cockroach/pkg/util/protoutil"
    25  	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
    26  )
    27  
    28  func funcName(f interface{}) string {
    29  	return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
    30  }
    31  
    32  // TrackRaftProtos instruments proto marshaling to track protos which are
    33  // marshaled downstream of raft. It returns a function that removes the
    34  // instrumentation and returns the list of downstream-of-raft protos.
    35  func TrackRaftProtos() func() []reflect.Type {
    36  	// Grab the name of the function that roots all raft application.
    37  	applyRaftEntryFunc := funcName((*apply.Task).ApplyCommittedEntries)
    38  	// We only need to track protos that could cause replica divergence
    39  	// by being written to disk downstream of raft.
    40  	whitelist := []string{
    41  		// Some raft operations trigger gossip, but we don't require
    42  		// strict consistency there.
    43  		funcName((*gossip.Gossip).AddInfoProto),
    44  		// Compactions are suggested below Raft, but they are local to a store and
    45  		// thus not subject to Raft consistency requirements.
    46  		funcName((*compactor.Compactor).Suggest),
    47  		// Range merges destroy replicas beneath Raft and write replica tombstones,
    48  		// but tombstones are unreplicated and thus not subject to the strict
    49  		// consistency requirements.
    50  		funcName((*Replica).setTombstoneKey),
    51  		// tryReproposeWithNewLeaseIndex is only run on the replica that
    52  		// proposed the command.
    53  		funcName((*Replica).tryReproposeWithNewLeaseIndex),
    54  	}
    55  
    56  	belowRaftProtos := struct {
    57  		syncutil.Mutex
    58  		inner map[reflect.Type]struct{}
    59  	}{
    60  		inner: make(map[reflect.Type]struct{}),
    61  	}
    62  
    63  	// Hard-coded protos for which we don't want to change the encoding. These
    64  	// are not "below raft" in the normal sense, but instead are used as part of
    65  	// conditional put operations. This is a bad practice - see #38308.
    66  	belowRaftProtos.Lock()
    67  	belowRaftProtos.inner[reflect.TypeOf(&kvserverpb.Liveness{})] = struct{}{}
    68  	belowRaftProtos.Unlock()
    69  
    70  	protoutil.Interceptor = func(pb protoutil.Message) {
    71  		t := reflect.TypeOf(pb)
    72  
    73  		// Special handling for MVCCMetadata: we expect MVCCMetadata to be
    74  		// marshaled below raft, but MVCCMetadata.Txn should always be nil in such
    75  		// cases.
    76  		if meta, ok := pb.(*enginepb.MVCCMetadata); ok && meta.Txn != nil {
    77  			protoutil.Interceptor(meta.Txn)
    78  		}
    79  
    80  		belowRaftProtos.Lock()
    81  		_, ok := belowRaftProtos.inner[t]
    82  		belowRaftProtos.Unlock()
    83  		if ok {
    84  			return
    85  		}
    86  
    87  		var pcs [256]uintptr
    88  		if numCallers := runtime.Callers(0, pcs[:]); numCallers == len(pcs) {
    89  			panic(fmt.Sprintf("number of callers %d might have exceeded slice size %d", numCallers, len(pcs)))
    90  		}
    91  		frames := runtime.CallersFrames(pcs[:])
    92  		for {
    93  			f, more := frames.Next()
    94  
    95  			whitelisted := false
    96  			for _, s := range whitelist {
    97  				if strings.Contains(f.Function, s) {
    98  					whitelisted = true
    99  					break
   100  				}
   101  			}
   102  			if whitelisted {
   103  				break
   104  			}
   105  
   106  			if strings.Contains(f.Function, applyRaftEntryFunc) {
   107  				belowRaftProtos.Lock()
   108  				belowRaftProtos.inner[t] = struct{}{}
   109  				belowRaftProtos.Unlock()
   110  				break
   111  			}
   112  			if !more {
   113  				break
   114  			}
   115  		}
   116  	}
   117  
   118  	return func() []reflect.Type {
   119  		protoutil.Interceptor = func(_ protoutil.Message) {}
   120  
   121  		belowRaftProtos.Lock()
   122  		types := make([]reflect.Type, 0, len(belowRaftProtos.inner))
   123  		for t := range belowRaftProtos.inner {
   124  			types = append(types, t)
   125  		}
   126  		belowRaftProtos.Unlock()
   127  
   128  		return types
   129  	}
   130  }