go.etcd.io/etcd@v3.3.27+incompatible/etcdserver/util.go (about) 1 // Copyright 2015 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package etcdserver 16 17 import ( 18 "fmt" 19 "reflect" 20 "strings" 21 "time" 22 23 pb "github.com/coreos/etcd/etcdserver/etcdserverpb" 24 "github.com/coreos/etcd/etcdserver/membership" 25 "github.com/coreos/etcd/pkg/types" 26 "github.com/coreos/etcd/rafthttp" 27 "github.com/golang/protobuf/proto" 28 ) 29 30 // isConnectedToQuorumSince checks whether the local member is connected to the 31 // quorum of the cluster since the given time. 32 func isConnectedToQuorumSince(transport rafthttp.Transporter, since time.Time, self types.ID, members []*membership.Member) bool { 33 return numConnectedSince(transport, since, self, members) >= (len(members)/2)+1 34 } 35 36 // isConnectedSince checks whether the local member is connected to the 37 // remote member since the given time. 38 func isConnectedSince(transport rafthttp.Transporter, since time.Time, remote types.ID) bool { 39 t := transport.ActiveSince(remote) 40 return !t.IsZero() && t.Before(since) 41 } 42 43 // isConnectedFullySince checks whether the local member is connected to all 44 // members in the cluster since the given time. 45 func isConnectedFullySince(transport rafthttp.Transporter, since time.Time, self types.ID, members []*membership.Member) bool { 46 return numConnectedSince(transport, since, self, members) == len(members) 47 } 48 49 // numConnectedSince counts how many members are connected to the local member 50 // since the given time. 51 func numConnectedSince(transport rafthttp.Transporter, since time.Time, self types.ID, members []*membership.Member) int { 52 connectedNum := 0 53 for _, m := range members { 54 if m.ID == self || isConnectedSince(transport, since, m.ID) { 55 connectedNum++ 56 } 57 } 58 return connectedNum 59 } 60 61 // longestConnected chooses the member with longest active-since-time. 62 // It returns false, if nothing is active. 63 func longestConnected(tp rafthttp.Transporter, membs []types.ID) (types.ID, bool) { 64 var longest types.ID 65 var oldest time.Time 66 for _, id := range membs { 67 tm := tp.ActiveSince(id) 68 if tm.IsZero() { // inactive 69 continue 70 } 71 72 if oldest.IsZero() { // first longest candidate 73 oldest = tm 74 longest = id 75 } 76 77 if tm.Before(oldest) { 78 oldest = tm 79 longest = id 80 } 81 } 82 if uint64(longest) == 0 { 83 return longest, false 84 } 85 return longest, true 86 } 87 88 type notifier struct { 89 c chan struct{} 90 err error 91 } 92 93 func newNotifier() *notifier { 94 return ¬ifier{ 95 c: make(chan struct{}), 96 } 97 } 98 99 func (nc *notifier) notify(err error) { 100 nc.err = err 101 close(nc.c) 102 } 103 104 func warnOfExpensiveRequest(now time.Time, reqStringer fmt.Stringer, respMsg proto.Message, err error) { 105 var resp string 106 if !isNil(respMsg) { 107 resp = fmt.Sprintf("size:%d", proto.Size(respMsg)) 108 } 109 warnOfExpensiveGenericRequest(now, reqStringer, "", resp, err) 110 } 111 112 func warnOfFailedRequest(now time.Time, reqStringer fmt.Stringer, respMsg proto.Message, err error) { 113 var resp string 114 if !isNil(respMsg) { 115 resp = fmt.Sprintf("size:%d", proto.Size(respMsg)) 116 } 117 d := time.Since(now) 118 plog.Warningf("failed to apply request,took %v,request %s,resp %s,err is %v", d, reqStringer.String(), resp, err) 119 } 120 121 func warnOfExpensiveReadOnlyTxnRequest(now time.Time, r *pb.TxnRequest, txnResponse *pb.TxnResponse, err error) { 122 reqStringer := pb.NewLoggableTxnRequest(r) 123 var resp string 124 if !isNil(txnResponse) { 125 var resps []string 126 for _, r := range txnResponse.Responses { 127 switch op := r.Response.(type) { 128 case *pb.ResponseOp_ResponseRange: 129 resps = append(resps, fmt.Sprintf("range_response_count:%d", len(op.ResponseRange.Kvs))) 130 default: 131 // only range responses should be in a read only txn request 132 } 133 } 134 resp = fmt.Sprintf("responses:<%s> size:%d", strings.Join(resps, " "), proto.Size(txnResponse)) 135 } 136 warnOfExpensiveGenericRequest(now, reqStringer, "read-only range ", resp, err) 137 } 138 139 func warnOfExpensiveReadOnlyRangeRequest(now time.Time, reqStringer fmt.Stringer, rangeResponse *pb.RangeResponse, err error) { 140 var resp string 141 if !isNil(rangeResponse) { 142 resp = fmt.Sprintf("range_response_count:%d size:%d", len(rangeResponse.Kvs), proto.Size(rangeResponse)) 143 } 144 warnOfExpensiveGenericRequest(now, reqStringer, "read-only range ", resp, err) 145 } 146 147 func warnOfExpensiveGenericRequest(now time.Time, reqStringer fmt.Stringer, prefix string, resp string, err error) { 148 // TODO: add metrics 149 d := time.Since(now) 150 if d > warnApplyDuration { 151 var result string 152 if err != nil { 153 result = fmt.Sprintf("error:%v", err) 154 } else { 155 result = resp 156 } 157 plog.Warningf("%srequest %q with result %q took too long (%v) to execute", prefix, reqStringer.String(), result, d) 158 slowApplies.Inc() 159 } 160 } 161 162 func isNil(msg proto.Message) bool { 163 return msg == nil || reflect.ValueOf(msg).IsNil() 164 }