Skip to content

Commit 50e9b03

Browse files
committed
etcdserver: Add response byte size and range response count to took too long warning
1 parent 25f4d80 commit 50e9b03

File tree

4 files changed

+71
-17
lines changed

4 files changed

+71
-17
lines changed

etcdserver/apply.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,12 @@ func (s *EtcdServer) newApplierV3() applierV3 {
109109
}
110110

111111
func (a *applierV3backend) Apply(r *pb.InternalRaftRequest) *applyResult {
112-
defer warnOfExpensiveRequest(a.s.getLogger(), time.Now(), &pb.InternalRaftStringer{Request: r})
113-
114112
ar := &applyResult{}
113+
defer func(start time.Time) {
114+
if ar != nil { // should never be nil, but just to be safe
115+
warnOfExpensiveRequest(a.s.getLogger(), start, &pb.InternalRaftStringer{Request: r}, ar.resp, ar.err)
116+
}
117+
}(time.Now())
115118

116119
// call into a.s.applyV3.F instead of a.F so upper appliers can check individual calls
117120
switch {

etcdserver/apply_v2.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func (a *applierV2store) Sync(r *RequestV2) Response {
114114
// applyV2Request interprets r as a call to v2store.X
115115
// and returns a Response interpreted from v2store.Event
116116
func (s *EtcdServer) applyV2Request(r *RequestV2) Response {
117-
defer warnOfExpensiveRequest(s.getLogger(), time.Now(), r)
117+
defer warnOfExpensiveRequest(s.getLogger(), time.Now(), r, nil, nil)
118118

119119
switch r.Method {
120120
case "POST":

etcdserver/util.go

+54-7
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@ package etcdserver
1616

1717
import (
1818
"fmt"
19+
"strings"
1920
"time"
2021

2122
"github.com/coreos/etcd/etcdserver/api/membership"
2223
"github.com/coreos/etcd/etcdserver/api/rafthttp"
24+
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
2325
"github.com/coreos/etcd/pkg/types"
26+
"github.com/golang/protobuf/proto"
2427

2528
"go.uber.org/zap"
2629
)
@@ -99,15 +102,40 @@ func (nc *notifier) notify(err error) {
99102
close(nc.c)
100103
}
101104

102-
func warnOfExpensiveRequest(lg *zap.Logger, now time.Time, stringer fmt.Stringer) {
103-
warnOfExpensiveGenericRequest(lg, now, stringer, "")
105+
func warnOfExpensiveRequest(lg *zap.Logger, now time.Time, reqStringer fmt.Stringer, respMsg proto.Message, err error) {
106+
var resp string
107+
if respMsg != nil {
108+
resp = fmt.Sprintf("<size:%d>", safeSize(respMsg))
109+
}
110+
warnOfExpensiveGenericRequest(lg, now, reqStringer, "", resp, err)
104111
}
105112

106-
func warnOfExpensiveReadOnlyRangeRequest(lg *zap.Logger, now time.Time, stringer fmt.Stringer) {
107-
warnOfExpensiveGenericRequest(lg, now, stringer, "read-only range ")
113+
func warnOfExpensiveReadOnlyTxnRequest(lg *zap.Logger, now time.Time, reqStringer fmt.Stringer, txnResponse *pb.TxnResponse, err error) {
114+
var resp string
115+
if txnResponse != nil {
116+
var resps []string
117+
for _, r := range txnResponse.Responses {
118+
switch op := r.Response.(type) {
119+
case *pb.ResponseOp_ResponseRange:
120+
resps = append(resps, fmt.Sprintf("<range_response_count:%d>", len(op.ResponseRange.Kvs)))
121+
default:
122+
// only range responses should be in a read only txn request
123+
}
124+
}
125+
resp = fmt.Sprintf("<responses:<%s> size:%d>", strings.Join(resps, " "), safeSize(txnResponse))
126+
}
127+
warnOfExpensiveGenericRequest(lg, now, reqStringer, "read-only range ", resp, err)
108128
}
109129

110-
func warnOfExpensiveGenericRequest(lg *zap.Logger, now time.Time, stringer fmt.Stringer, prefix string) {
130+
func warnOfExpensiveReadOnlyRangeRequest(lg *zap.Logger, now time.Time, reqStringer fmt.Stringer, rangeResponse *pb.RangeResponse, err error) {
131+
var resp string
132+
if rangeResponse != nil {
133+
resp = fmt.Sprintf("<range_response_count:%d size:%d>", len(rangeResponse.Kvs), safeSize(rangeResponse))
134+
}
135+
warnOfExpensiveGenericRequest(lg, now, reqStringer, "read-only range ", resp, err)
136+
}
137+
138+
func warnOfExpensiveGenericRequest(lg *zap.Logger, now time.Time, reqStringer fmt.Stringer, prefix string, resp string, err error) {
111139
// TODO: add metrics
112140
d := time.Since(now)
113141
if d > warnApplyDuration {
@@ -117,11 +145,30 @@ func warnOfExpensiveGenericRequest(lg *zap.Logger, now time.Time, stringer fmt.S
117145
zap.Duration("took", d),
118146
zap.Duration("expected-duration", warnApplyDuration),
119147
zap.String("prefix", prefix),
120-
zap.String("request", stringer.String()),
148+
zap.String("request", reqStringer.String()),
149+
zap.String("response", resp),
150+
zap.Error(err),
121151
)
122152
} else {
123-
plog.Warningf("%srequest %q took too long (%v) to execute", prefix, stringer.String(), d)
153+
var result string
154+
if err != nil {
155+
result = fmt.Sprintf("<error:%v>", err)
156+
} else {
157+
result = resp
158+
}
159+
plog.Warningf("%srequest %q with result %q took too long (%v) to execute", prefix, reqStringer.String(), result, d)
124160
}
125161
slowApplies.Inc()
126162
}
127163
}
164+
165+
// safeSize attempt to calculate size using proto.Size, but recovers from panics and return -1 in those cases.
166+
// TODO: Remove once https://github.com/golang/protobuf/issues/631 is resolved.
167+
func safeSize(msg proto.Message) (size int) {
168+
defer func() {
169+
if r := recover(); r != nil {
170+
size = -1
171+
}
172+
}()
173+
return proto.Size(msg)
174+
}

etcdserver/v3_server.go

+11-7
Original file line numberDiff line numberDiff line change
@@ -86,23 +86,25 @@ type Authenticator interface {
8686
}
8787

8888
func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error) {
89-
defer warnOfExpensiveReadOnlyRangeRequest(s.getLogger(), time.Now(), r)
89+
var resp *pb.RangeResponse
90+
var err error
91+
defer func(start time.Time) {
92+
warnOfExpensiveReadOnlyRangeRequest(s.getLogger(), start, r, resp, err)
93+
}(time.Now())
9094

9195
if !r.Serializable {
92-
err := s.linearizableReadNotify(ctx)
96+
err = s.linearizableReadNotify(ctx)
9397
if err != nil {
9498
return nil, err
9599
}
96100
}
97-
var resp *pb.RangeResponse
98-
var err error
99101
chk := func(ai *auth.AuthInfo) error {
100102
return s.authStore.IsRangePermitted(ai, r.Key, r.RangeEnd)
101103
}
102104

103105
get := func() { resp, err = s.applyV3Base.Range(nil, r) }
104-
if serr := s.doSerialize(ctx, chk, get); serr != nil {
105-
return nil, serr
106+
if err = s.doSerialize(ctx, chk, get); err != nil {
107+
return nil, err
106108
}
107109
return resp, err
108110
}
@@ -137,7 +139,9 @@ func (s *EtcdServer) Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse
137139
return checkTxnAuth(s.authStore, ai, r)
138140
}
139141

140-
defer warnOfExpensiveReadOnlyRangeRequest(s.getLogger(), time.Now(), r)
142+
defer func(start time.Time) {
143+
warnOfExpensiveReadOnlyTxnRequest(s.getLogger(), start, r, resp, err)
144+
}(time.Now())
141145

142146
get := func() { resp, err = s.applyV3Base.Txn(r) }
143147
if serr := s.doSerialize(ctx, chk, get); serr != nil {

0 commit comments

Comments
 (0)