Skip to content

Commit

Permalink
etcdserver: reject v3 txns with duplicate put keys
Browse files Browse the repository at this point in the history
An API check to support PR #4363; bad requests didn't return an error.
  • Loading branch information
Anthony Romano committed Feb 2, 2016
1 parent 826df17 commit a30c3ad
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 3 deletions.
1 change: 1 addition & 0 deletions etcdserver/api/v3rpc/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
var (
ErrEmptyKey = grpc.Errorf(codes.InvalidArgument, "key is not provided")
ErrTooManyOps = grpc.Errorf(codes.InvalidArgument, "too many operations in txn request")
ErrDuplicateKey = grpc.Errorf(codes.InvalidArgument, "duplicate key given in txn request")
ErrCompacted = grpc.Errorf(codes.OutOfRange, storage.ErrCompacted.Error())
ErrFutureRev = grpc.Errorf(codes.OutOfRange, storage.ErrFutureRev.Error())
ErrLeaseNotFound = grpc.Errorf(codes.NotFound, "requested lease not found")
Expand Down
27 changes: 27 additions & 0 deletions etcdserver/api/v3rpc/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,40 @@ func checkTxnRequest(r *pb.TxnRequest) error {
return err
}
}
if err := checkRequestPutKeys(r.Success); err != nil {
return err
}

for _, u := range r.Failure {
if err := checkRequestUnion(u); err != nil {
return err
}
}
if err := checkRequestPutKeys(r.Failure); err != nil {
return err
}

return nil
}

// checkRequestPutKeys gives ErrDuplicateKey if the same key is put twice
func checkRequestPutKeys(reqs []*pb.RequestUnion) error {
keys := make(map[string]struct{})
for _, requ := range reqs {
tv, ok := requ.Request.(*pb.RequestUnion_RequestPut)
if !ok {
continue
}
preq := tv.RequestPut
if preq == nil {
continue
}
key := string(preq.Key)
if _, ok := keys[key]; ok {
return ErrDuplicateKey
}
keys[key] = struct{}{}
}
return nil
}

Expand Down
26 changes: 23 additions & 3 deletions integration/v3_grpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,20 +84,27 @@ func TestV3TxnTooManyOps(t *testing.T) {

kvc := clus.RandClient().KV

// unique keys
i := new(int)
keyf := func() []byte {
*i++
return []byte(fmt.Sprintf("key-%d", i))
}

addCompareOps := func(txn *pb.TxnRequest) {
txn.Compare = append(txn.Compare,
&pb.Compare{
Result: pb.Compare_GREATER,
Target: pb.Compare_CREATE,
Key: []byte("bar"),
Key: keyf(),
})
}
addSuccessOps := func(txn *pb.TxnRequest) {
txn.Success = append(txn.Success,
&pb.RequestUnion{
Request: &pb.RequestUnion_RequestPut{
RequestPut: &pb.PutRequest{
Key: []byte("bar"),
Key: keyf(),
Value: []byte("bar"),
},
},
Expand All @@ -108,7 +115,7 @@ func TestV3TxnTooManyOps(t *testing.T) {
&pb.RequestUnion{
Request: &pb.RequestUnion_RequestPut{
RequestPut: &pb.PutRequest{
Key: []byte("bar"),
Key: keyf(),
Value: []byte("bar"),
},
},
Expand All @@ -132,6 +139,19 @@ func TestV3TxnTooManyOps(t *testing.T) {
t.Errorf("#%d: err = %v, want %v", i, err, v3rpc.ErrTooManyOps)
}
}

// equal keys fail earlier
keyf = func() []byte { return []byte("key") }
for i, tt := range tests[1:] {
txn := &pb.TxnRequest{}
tt(txn)
tt(txn)

_, err := kvc.Txn(context.Background(), txn)
if err != v3rpc.ErrDuplicateKey {
t.Errorf("#%d: err = %v, want %v", i, err, v3rpc.ErrDuplicateKey)
}
}
}

// TestV3PutMissingLease ensures that a Put on a key with a bogus lease fails.
Expand Down

0 comments on commit a30c3ad

Please sign in to comment.