etcdserver: fix nil pointer panic for readonly txn
Backporting https://github.com/etcd-io/etcd/pull/14895 Signed-off-by: Benjamin Wang <wachao@vmware.com>
This commit is contained in:
parent
0b47579263
commit
c1a89973f0
@ -140,7 +140,11 @@ func warnOfExpensiveReadOnlyTxnRequest(lg *zap.Logger, warningApplyDuration time
|
|||||||
for _, r := range txnResponse.Responses {
|
for _, r := range txnResponse.Responses {
|
||||||
switch op := r.Response.(type) {
|
switch op := r.Response.(type) {
|
||||||
case *pb.ResponseOp_ResponseRange:
|
case *pb.ResponseOp_ResponseRange:
|
||||||
resps = append(resps, fmt.Sprintf("range_response_count:%d", len(op.ResponseRange.Kvs)))
|
if op.ResponseRange != nil {
|
||||||
|
resps = append(resps, fmt.Sprintf("range_response_count:%d", len(op.ResponseRange.Kvs)))
|
||||||
|
} else {
|
||||||
|
resps = append(resps, "range_response:nil")
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
// only range responses should be in a read only txn request
|
// only range responses should be in a read only txn request
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zaptest"
|
||||||
|
|
||||||
|
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||||
"go.etcd.io/etcd/client/pkg/v3/types"
|
"go.etcd.io/etcd/client/pkg/v3/types"
|
||||||
"go.etcd.io/etcd/raft/v3/raftpb"
|
"go.etcd.io/etcd/raft/v3/raftpb"
|
||||||
"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
|
"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
|
||||||
@ -110,3 +112,105 @@ type testStringerFunc func() string
|
|||||||
func (s testStringerFunc) String() string {
|
func (s testStringerFunc) String() string {
|
||||||
return s()
|
return s()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestWarnOfExpensiveReadOnlyTxnRequest verifies WarnOfExpensiveReadOnlyTxnRequest
|
||||||
|
// never panic no matter what data the txnResponse contains.
|
||||||
|
func TestWarnOfExpensiveReadOnlyTxnRequest(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
txnResp *pb.TxnResponse
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "all readonly responses",
|
||||||
|
txnResp: &pb.TxnResponse{
|
||||||
|
Responses: []*pb.ResponseOp{
|
||||||
|
{
|
||||||
|
Response: &pb.ResponseOp_ResponseRange{
|
||||||
|
ResponseRange: &pb.RangeResponse{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Response: &pb.ResponseOp_ResponseRange{
|
||||||
|
ResponseRange: &pb.RangeResponse{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all readonly responses with partial nil responses",
|
||||||
|
txnResp: &pb.TxnResponse{
|
||||||
|
Responses: []*pb.ResponseOp{
|
||||||
|
{
|
||||||
|
Response: &pb.ResponseOp_ResponseRange{
|
||||||
|
ResponseRange: &pb.RangeResponse{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Response: &pb.ResponseOp_ResponseRange{
|
||||||
|
ResponseRange: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all readonly responses with all nil responses",
|
||||||
|
txnResp: &pb.TxnResponse{
|
||||||
|
Responses: []*pb.ResponseOp{
|
||||||
|
{
|
||||||
|
Response: &pb.ResponseOp_ResponseRange{
|
||||||
|
ResponseRange: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Response: &pb.ResponseOp_ResponseRange{
|
||||||
|
ResponseRange: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "partial non readonly responses",
|
||||||
|
txnResp: &pb.TxnResponse{
|
||||||
|
Responses: []*pb.ResponseOp{
|
||||||
|
{
|
||||||
|
Response: &pb.ResponseOp_ResponseRange{
|
||||||
|
ResponseRange: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Response: &pb.ResponseOp_ResponsePut{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Response: &pb.ResponseOp_ResponseDeleteRange{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all non readonly responses",
|
||||||
|
txnResp: &pb.TxnResponse{
|
||||||
|
Responses: []*pb.ResponseOp{
|
||||||
|
{
|
||||||
|
Response: &pb.ResponseOp_ResponsePut{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Response: &pb.ResponseOp_ResponseDeleteRange{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
lg := zaptest.NewLogger(t)
|
||||||
|
start := time.Now().Add(-1 * time.Second)
|
||||||
|
// WarnOfExpensiveReadOnlyTxnRequest shouldn't panic.
|
||||||
|
warnOfExpensiveReadOnlyTxnRequest(lg, 0, start, &pb.TxnRequest{}, tc.txnResp, nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user