diff --git a/etcdserver/etcdserverpb/rpc.pb.go b/etcdserver/etcdserverpb/rpc.pb.go index 4a06ff49f..05ac13097 100644 --- a/etcdserver/etcdserverpb/rpc.pb.go +++ b/etcdserver/etcdserverpb/rpc.pb.go @@ -232,6 +232,8 @@ func (*DeleteRangeRequest) ProtoMessage() {} type DeleteRangeResponse struct { Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"` + // Deleted is the number of keys that got deleted. + Deleted int64 `protobuf:"varint,2,opt,name=deleted,proto3" json:"deleted,omitempty"` } func (m *DeleteRangeResponse) Reset() { *m = DeleteRangeResponse{} } @@ -2088,6 +2090,11 @@ func (m *DeleteRangeResponse) MarshalTo(data []byte) (int, error) { } i += n3 } + if m.Deleted != 0 { + data[i] = 0x10 + i++ + i = encodeVarintRpc(data, i, uint64(m.Deleted)) + } return i, nil } @@ -3342,6 +3349,9 @@ func (m *DeleteRangeResponse) Size() (n int) { l = m.Header.Size() n += 1 + l + sovRpc(uint64(l)) } + if m.Deleted != 0 { + n += 1 + sovRpc(uint64(m.Deleted)) + } return n } @@ -4696,6 +4706,25 @@ func (m *DeleteRangeResponse) Unmarshal(data []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Deleted", wireType) + } + m.Deleted = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRpc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + m.Deleted |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipRpc(data[iNdEx:]) diff --git a/etcdserver/etcdserverpb/rpc.proto b/etcdserver/etcdserverpb/rpc.proto index ad35b5d12..20fd26b43 100644 --- a/etcdserver/etcdserverpb/rpc.proto +++ b/etcdserver/etcdserverpb/rpc.proto @@ -152,6 +152,8 @@ message DeleteRangeRequest { message DeleteRangeResponse { ResponseHeader header = 1; + // Deleted is the number of keys that got deleted. + int64 deleted = 2; } message RequestUnion { diff --git a/etcdserver/v3demo_server.go b/etcdserver/v3demo_server.go index 5b41f96c4..21bae3a17 100644 --- a/etcdserver/v3demo_server.go +++ b/etcdserver/v3demo_server.go @@ -384,6 +384,7 @@ func applyDeleteRange(txnID int64, kv dstorage.KV, dr *pb.DeleteRangeRequest) (* resp.Header = &pb.ResponseHeader{} var ( + n int64 rev int64 err error ) @@ -393,14 +394,15 @@ func applyDeleteRange(txnID int64, kv dstorage.KV, dr *pb.DeleteRangeRequest) (* } if txnID != noTxn { - _, rev, err = kv.TxnDeleteRange(txnID, dr.Key, dr.RangeEnd) + n, rev, err = kv.TxnDeleteRange(txnID, dr.Key, dr.RangeEnd) if err != nil { return nil, err } } else { - _, rev = kv.DeleteRange(dr.Key, dr.RangeEnd) + n, rev = kv.DeleteRange(dr.Key, dr.RangeEnd) } + resp.Deleted = n resp.Header.Revision = rev return resp, nil } diff --git a/integration/v3_grpc_test.go b/integration/v3_grpc_test.go index 90efa5ca3..8e150d41e 100644 --- a/integration/v3_grpc_test.go +++ b/integration/v3_grpc_test.go @@ -282,42 +282,43 @@ func TestV3DeleteRange(t *testing.T) { end string wantSet [][]byte + deleted int64 }{ // delete middle { []string{"foo", "foo/abc", "fop"}, "foo/", "fop", - [][]byte{[]byte("foo"), []byte("fop")}, + [][]byte{[]byte("foo"), []byte("fop")}, 1, }, // no delete { []string{"foo", "foo/abc", "fop"}, "foo/", "foo/", - [][]byte{[]byte("foo"), []byte("foo/abc"), []byte("fop")}, + [][]byte{[]byte("foo"), []byte("foo/abc"), []byte("fop")}, 0, }, // delete first { []string{"foo", "foo/abc", "fop"}, "fo", "fop", - [][]byte{[]byte("fop")}, + [][]byte{[]byte("fop")}, 2, }, // delete tail { []string{"foo", "foo/abc", "fop"}, "foo/", "fos", - [][]byte{[]byte("foo")}, + [][]byte{[]byte("foo")}, 2, }, // delete exact { []string{"foo", "foo/abc", "fop"}, "foo/abc", "", - [][]byte{[]byte("foo"), []byte("fop")}, + [][]byte{[]byte("foo"), []byte("fop")}, 1, }, // delete none, [x,x) { []string{"foo"}, "foo", "foo", - [][]byte{[]byte("foo")}, + [][]byte{[]byte("foo")}, 0, }, } @@ -341,6 +342,9 @@ func TestV3DeleteRange(t *testing.T) { if err != nil { t.Fatalf("couldn't delete range on test %d (%v)", i, err) } + if tt.deleted != dresp.Deleted { + t.Errorf("expected %d on test %v, got %d", tt.deleted, i, dresp.Deleted) + } rreq := &pb.RangeRequest{Key: []byte{0x0}, RangeEnd: []byte{0xff}} rresp, err := kvc.Range(context.TODO(), rreq)