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 }