*: add support for lease create and revoke

Basic support for lease operations like create and revoke.
We still need to:
1. attach keys to leases in KV implmentation if lease field is set
2. leader periodically removes expired leases
3. leader serves keepAlive requests and follower forwards keepAlive
requests to leader.
This commit is contained in:
Xiang Li
2016-01-05 13:49:25 -08:00
parent db60cdc42c
commit d9ca929a33
13 changed files with 451 additions and 141 deletions

View File

@ -0,0 +1,123 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command
import (
"fmt"
"os"
"strconv"
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/spf13/cobra"
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
"github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
)
// NewLeaseCommand returns the cobra command for "lease".
func NewLeaseCommand() *cobra.Command {
lc := &cobra.Command{
Use: "lease",
Short: "lease is used to manage leases.",
}
lc.AddCommand(NewLeaseCreateCommand())
lc.AddCommand(NewLeaseRevokeCommand())
return lc
}
// NewLeaseCreateCommand returns the cobra command for "lease create".
func NewLeaseCreateCommand() *cobra.Command {
lc := &cobra.Command{
Use: "create",
Short: "create is used to create leases.",
Run: leaseCreateCommandFunc,
}
return lc
}
// leaseCreateCommandFunc executes the "lease create" command.
func leaseCreateCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 1 {
ExitWithError(ExitBadArgs, fmt.Errorf("lease create command needs TTL arguement."))
}
ttl, err := strconv.ParseInt(args[0], 10, 64)
if err != nil {
ExitWithError(ExitBadArgs, fmt.Errorf("bad TTL (%v)", err))
}
endpoint, err := cmd.Flags().GetString("endpoint")
if err != nil {
ExitWithError(ExitError, err)
}
conn, err := grpc.Dial(endpoint)
if err != nil {
ExitWithError(ExitBadConnection, err)
}
lease := pb.NewLeaseClient(conn)
req := &pb.LeaseCreateRequest{TTL: ttl}
resp, err := lease.LeaseCreate(context.Background(), req)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to create lease (%v)\n", err)
return
}
fmt.Printf("lease %016x created with TTL(%ds)\n", resp.ID, resp.TTL)
}
// NewLeaseRevokeCommand returns the cobra command for "lease revoke".
func NewLeaseRevokeCommand() *cobra.Command {
lc := &cobra.Command{
Use: "revoke",
Short: "revoke is used to revoke leases.",
Run: leaseRevokeCommandFunc,
}
return lc
}
// leaseRevokeCommandFunc executes the "lease create" command.
func leaseRevokeCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 1 {
ExitWithError(ExitBadArgs, fmt.Errorf("lease revoke command needs 1 argument"))
}
id, err := strconv.ParseInt(args[0], 16, 64)
if err != nil {
ExitWithError(ExitBadArgs, fmt.Errorf("bad lease ID arg (%v), expecting ID in Hex", err))
}
endpoint, err := cmd.Flags().GetString("endpoint")
if err != nil {
ExitWithError(ExitError, err)
}
conn, err := grpc.Dial(endpoint)
if err != nil {
ExitWithError(ExitBadConnection, err)
}
lease := pb.NewLeaseClient(conn)
req := &pb.LeaseRevokeRequest{ID: id}
_, err = lease.LeaseRevoke(context.Background(), req)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to revoke lease (%v)\n", err)
return
}
fmt.Printf("lease %016x revoked\n", id)
}

View File

@ -51,6 +51,7 @@ func init() {
command.NewCompactionCommand(), command.NewCompactionCommand(),
command.NewWatchCommand(), command.NewWatchCommand(),
command.NewVersionCommand(), command.NewVersionCommand(),
command.NewLeaseCommand(),
) )
} }

View File

@ -324,6 +324,7 @@ func startEtcd(cfg *config) (<-chan struct{}, error) {
grpcServer := grpc.NewServer() grpcServer := grpc.NewServer()
etcdserverpb.RegisterKVServer(grpcServer, v3rpc.NewKVServer(s)) etcdserverpb.RegisterKVServer(grpcServer, v3rpc.NewKVServer(s))
etcdserverpb.RegisterWatchServer(grpcServer, v3rpc.NewWatchServer(s)) etcdserverpb.RegisterWatchServer(grpcServer, v3rpc.NewWatchServer(s))
etcdserverpb.RegisterLeaseServer(grpcServer, v3rpc.NewLeaseServer(s))
go func() { plog.Fatal(grpcServer.Serve(v3l)) }() go func() { plog.Fatal(grpcServer.Serve(v3l)) }()
} }

View File

@ -21,7 +21,8 @@ import (
) )
var ( var (
ErrEmptyKey = grpc.Errorf(codes.InvalidArgument, "key is not provided") ErrEmptyKey = grpc.Errorf(codes.InvalidArgument, "key is not provided")
ErrCompacted = grpc.Errorf(codes.OutOfRange, storage.ErrCompacted.Error()) ErrCompacted = grpc.Errorf(codes.OutOfRange, storage.ErrCompacted.Error())
ErrFutureRev = grpc.Errorf(codes.OutOfRange, storage.ErrFutureRev.Error()) ErrFutureRev = grpc.Errorf(codes.OutOfRange, storage.ErrFutureRev.Error())
ErrLeaseNotFound = grpc.Errorf(codes.NotFound, "requested lease not found")
) )

View File

@ -0,0 +1,45 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v3rpc
import (
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
"github.com/coreos/etcd/etcdserver"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
)
type LeaseServer struct {
le etcdserver.Lessor
}
func NewLeaseServer(le etcdserver.Lessor) pb.LeaseServer {
return &LeaseServer{le: le}
}
func (ls *LeaseServer) LeaseCreate(ctx context.Context, cr *pb.LeaseCreateRequest) (*pb.LeaseCreateResponse, error) {
return ls.le.LeaseCreate(ctx, cr)
}
func (ls *LeaseServer) LeaseRevoke(ctx context.Context, rr *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error) {
r, err := ls.le.LeaseRevoke(ctx, rr)
if err != nil {
return nil, ErrLeaseNotFound
}
return r, nil
}
func (ls *LeaseServer) LeaseKeepAlive(stream pb.Lease_LeaseKeepAliveServer) error {
panic("not implemented")
}

View File

@ -24,6 +24,8 @@ type InternalRaftRequest struct {
DeleteRange *DeleteRangeRequest `protobuf:"bytes,5,opt,name=delete_range" json:"delete_range,omitempty"` DeleteRange *DeleteRangeRequest `protobuf:"bytes,5,opt,name=delete_range" json:"delete_range,omitempty"`
Txn *TxnRequest `protobuf:"bytes,6,opt,name=txn" json:"txn,omitempty"` Txn *TxnRequest `protobuf:"bytes,6,opt,name=txn" json:"txn,omitempty"`
Compaction *CompactionRequest `protobuf:"bytes,7,opt,name=compaction" json:"compaction,omitempty"` Compaction *CompactionRequest `protobuf:"bytes,7,opt,name=compaction" json:"compaction,omitempty"`
LeaseCreate *LeaseCreateRequest `protobuf:"bytes,8,opt,name=lease_create" json:"lease_create,omitempty"`
LeaseRevoke *LeaseRevokeRequest `protobuf:"bytes,9,opt,name=lease_revoke" json:"lease_revoke,omitempty"`
} }
func (m *InternalRaftRequest) Reset() { *m = InternalRaftRequest{} } func (m *InternalRaftRequest) Reset() { *m = InternalRaftRequest{} }
@ -117,6 +119,26 @@ func (m *InternalRaftRequest) MarshalTo(data []byte) (int, error) {
} }
i += n6 i += n6
} }
if m.LeaseCreate != nil {
data[i] = 0x42
i++
i = encodeVarintRaftInternal(data, i, uint64(m.LeaseCreate.Size()))
n7, err := m.LeaseCreate.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n7
}
if m.LeaseRevoke != nil {
data[i] = 0x4a
i++
i = encodeVarintRaftInternal(data, i, uint64(m.LeaseRevoke.Size()))
n8, err := m.LeaseRevoke.MarshalTo(data[i:])
if err != nil {
return 0, err
}
i += n8
}
return i, nil return i, nil
} }
@ -195,6 +217,14 @@ func (m *InternalRaftRequest) Size() (n int) {
l = m.Compaction.Size() l = m.Compaction.Size()
n += 1 + l + sovRaftInternal(uint64(l)) n += 1 + l + sovRaftInternal(uint64(l))
} }
if m.LeaseCreate != nil {
l = m.LeaseCreate.Size()
n += 1 + l + sovRaftInternal(uint64(l))
}
if m.LeaseRevoke != nil {
l = m.LeaseRevoke.Size()
n += 1 + l + sovRaftInternal(uint64(l))
}
return n return n
} }
@ -432,6 +462,66 @@ func (m *InternalRaftRequest) Unmarshal(data []byte) error {
return err return err
} }
iNdEx = postIndex iNdEx = postIndex
case 8:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field LeaseCreate", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.LeaseCreate == nil {
m.LeaseCreate = &LeaseCreateRequest{}
}
if err := m.LeaseCreate.Unmarshal(data[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 9:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field LeaseRevoke", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthRaftInternal
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.LeaseRevoke == nil {
m.LeaseRevoke = &LeaseRevokeRequest{}
}
if err := m.LeaseRevoke.Unmarshal(data[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default: default:
var sizeOfWire int var sizeOfWire int
for { for {

View File

@ -15,11 +15,15 @@ option (gogoproto.goproto_getters_all) = false;
message InternalRaftRequest { message InternalRaftRequest {
uint64 ID = 1; uint64 ID = 1;
Request v2 = 2; Request v2 = 2;
RangeRequest range = 3; RangeRequest range = 3;
PutRequest put = 4; PutRequest put = 4;
DeleteRangeRequest delete_range = 5; DeleteRangeRequest delete_range = 5;
TxnRequest txn = 6; TxnRequest txn = 6;
CompactionRequest compaction = 7; CompactionRequest compaction = 7;
LeaseCreateRequest lease_create = 8;
LeaseRevokeRequest lease_revoke = 9;
} }
message EmptyResponse { message EmptyResponse {

View File

@ -441,7 +441,7 @@ func (m *WatchResponse) GetEvents() []*storagepb.Event {
type LeaseCreateRequest struct { type LeaseCreateRequest struct {
// advisory ttl in seconds // advisory ttl in seconds
Ttl int64 `protobuf:"varint,1,opt,name=ttl,proto3" json:"ttl,omitempty"` TTL int64 `protobuf:"varint,1,opt,proto3" json:"TTL,omitempty"`
} }
func (m *LeaseCreateRequest) Reset() { *m = LeaseCreateRequest{} } func (m *LeaseCreateRequest) Reset() { *m = LeaseCreateRequest{} }
@ -449,10 +449,10 @@ func (m *LeaseCreateRequest) String() string { return proto.CompactTextString(m)
func (*LeaseCreateRequest) ProtoMessage() {} func (*LeaseCreateRequest) ProtoMessage() {}
type LeaseCreateResponse struct { type LeaseCreateResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"` Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`
LeaseId int64 `protobuf:"varint,2,opt,name=lease_id,proto3" json:"lease_id,omitempty"` ID int64 `protobuf:"varint,2,opt,proto3" json:"ID,omitempty"`
// server decided ttl in second // server decided ttl in second
Ttl int64 `protobuf:"varint,3,opt,name=ttl,proto3" json:"ttl,omitempty"` TTL int64 `protobuf:"varint,3,opt,proto3" json:"TTL,omitempty"`
Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"`
} }
@ -468,7 +468,7 @@ func (m *LeaseCreateResponse) GetHeader() *ResponseHeader {
} }
type LeaseRevokeRequest struct { type LeaseRevokeRequest struct {
LeaseId int64 `protobuf:"varint,1,opt,name=lease_id,proto3" json:"lease_id,omitempty"` ID int64 `protobuf:"varint,1,opt,proto3" json:"ID,omitempty"`
} }
func (m *LeaseRevokeRequest) Reset() { *m = LeaseRevokeRequest{} } func (m *LeaseRevokeRequest) Reset() { *m = LeaseRevokeRequest{} }
@ -491,7 +491,7 @@ func (m *LeaseRevokeResponse) GetHeader() *ResponseHeader {
} }
type LeaseKeepAliveRequest struct { type LeaseKeepAliveRequest struct {
LeaseId int64 `protobuf:"varint,1,opt,name=lease_id,proto3" json:"lease_id,omitempty"` ID int64 `protobuf:"varint,1,opt,proto3" json:"ID,omitempty"`
} }
func (m *LeaseKeepAliveRequest) Reset() { *m = LeaseKeepAliveRequest{} } func (m *LeaseKeepAliveRequest) Reset() { *m = LeaseKeepAliveRequest{} }
@ -499,9 +499,8 @@ func (m *LeaseKeepAliveRequest) String() string { return proto.CompactTextString
func (*LeaseKeepAliveRequest) ProtoMessage() {} func (*LeaseKeepAliveRequest) ProtoMessage() {}
type LeaseKeepAliveResponse struct { type LeaseKeepAliveResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"` Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`
LeaseId int64 `protobuf:"varint,2,opt,name=lease_id,proto3" json:"lease_id,omitempty"` TTL int64 `protobuf:"varint,2,opt,proto3" json:"TTL,omitempty"`
Ttl int64 `protobuf:"varint,3,opt,name=ttl,proto3" json:"ttl,omitempty"`
} }
func (m *LeaseKeepAliveResponse) Reset() { *m = LeaseKeepAliveResponse{} } func (m *LeaseKeepAliveResponse) Reset() { *m = LeaseKeepAliveResponse{} }
@ -1741,10 +1740,10 @@ func (m *LeaseCreateRequest) MarshalTo(data []byte) (int, error) {
_ = i _ = i
var l int var l int
_ = l _ = l
if m.Ttl != 0 { if m.TTL != 0 {
data[i] = 0x8 data[i] = 0x8
i++ i++
i = encodeVarintRpc(data, i, uint64(m.Ttl)) i = encodeVarintRpc(data, i, uint64(m.TTL))
} }
return i, nil return i, nil
} }
@ -1774,15 +1773,15 @@ func (m *LeaseCreateResponse) MarshalTo(data []byte) (int, error) {
} }
i += n15 i += n15
} }
if m.LeaseId != 0 { if m.ID != 0 {
data[i] = 0x10 data[i] = 0x10
i++ i++
i = encodeVarintRpc(data, i, uint64(m.LeaseId)) i = encodeVarintRpc(data, i, uint64(m.ID))
} }
if m.Ttl != 0 { if m.TTL != 0 {
data[i] = 0x18 data[i] = 0x18
i++ i++
i = encodeVarintRpc(data, i, uint64(m.Ttl)) i = encodeVarintRpc(data, i, uint64(m.TTL))
} }
if len(m.Error) > 0 { if len(m.Error) > 0 {
data[i] = 0x22 data[i] = 0x22
@ -1808,10 +1807,10 @@ func (m *LeaseRevokeRequest) MarshalTo(data []byte) (int, error) {
_ = i _ = i
var l int var l int
_ = l _ = l
if m.LeaseId != 0 { if m.ID != 0 {
data[i] = 0x8 data[i] = 0x8
i++ i++
i = encodeVarintRpc(data, i, uint64(m.LeaseId)) i = encodeVarintRpc(data, i, uint64(m.ID))
} }
return i, nil return i, nil
} }
@ -1859,10 +1858,10 @@ func (m *LeaseKeepAliveRequest) MarshalTo(data []byte) (int, error) {
_ = i _ = i
var l int var l int
_ = l _ = l
if m.LeaseId != 0 { if m.ID != 0 {
data[i] = 0x8 data[i] = 0x8
i++ i++
i = encodeVarintRpc(data, i, uint64(m.LeaseId)) i = encodeVarintRpc(data, i, uint64(m.ID))
} }
return i, nil return i, nil
} }
@ -1892,15 +1891,10 @@ func (m *LeaseKeepAliveResponse) MarshalTo(data []byte) (int, error) {
} }
i += n17 i += n17
} }
if m.LeaseId != 0 { if m.TTL != 0 {
data[i] = 0x10 data[i] = 0x10
i++ i++
i = encodeVarintRpc(data, i, uint64(m.LeaseId)) i = encodeVarintRpc(data, i, uint64(m.TTL))
}
if m.Ttl != 0 {
data[i] = 0x18
i++
i = encodeVarintRpc(data, i, uint64(m.Ttl))
} }
return i, nil return i, nil
} }
@ -2258,8 +2252,8 @@ func (m *WatchResponse) Size() (n int) {
func (m *LeaseCreateRequest) Size() (n int) { func (m *LeaseCreateRequest) Size() (n int) {
var l int var l int
_ = l _ = l
if m.Ttl != 0 { if m.TTL != 0 {
n += 1 + sovRpc(uint64(m.Ttl)) n += 1 + sovRpc(uint64(m.TTL))
} }
return n return n
} }
@ -2271,11 +2265,11 @@ func (m *LeaseCreateResponse) Size() (n int) {
l = m.Header.Size() l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l)) n += 1 + l + sovRpc(uint64(l))
} }
if m.LeaseId != 0 { if m.ID != 0 {
n += 1 + sovRpc(uint64(m.LeaseId)) n += 1 + sovRpc(uint64(m.ID))
} }
if m.Ttl != 0 { if m.TTL != 0 {
n += 1 + sovRpc(uint64(m.Ttl)) n += 1 + sovRpc(uint64(m.TTL))
} }
l = len(m.Error) l = len(m.Error)
if l > 0 { if l > 0 {
@ -2287,8 +2281,8 @@ func (m *LeaseCreateResponse) Size() (n int) {
func (m *LeaseRevokeRequest) Size() (n int) { func (m *LeaseRevokeRequest) Size() (n int) {
var l int var l int
_ = l _ = l
if m.LeaseId != 0 { if m.ID != 0 {
n += 1 + sovRpc(uint64(m.LeaseId)) n += 1 + sovRpc(uint64(m.ID))
} }
return n return n
} }
@ -2306,8 +2300,8 @@ func (m *LeaseRevokeResponse) Size() (n int) {
func (m *LeaseKeepAliveRequest) Size() (n int) { func (m *LeaseKeepAliveRequest) Size() (n int) {
var l int var l int
_ = l _ = l
if m.LeaseId != 0 { if m.ID != 0 {
n += 1 + sovRpc(uint64(m.LeaseId)) n += 1 + sovRpc(uint64(m.ID))
} }
return n return n
} }
@ -2319,11 +2313,8 @@ func (m *LeaseKeepAliveResponse) Size() (n int) {
l = m.Header.Size() l = m.Header.Size()
n += 1 + l + sovRpc(uint64(l)) n += 1 + l + sovRpc(uint64(l))
} }
if m.LeaseId != 0 { if m.TTL != 0 {
n += 1 + sovRpc(uint64(m.LeaseId)) n += 1 + sovRpc(uint64(m.TTL))
}
if m.Ttl != 0 {
n += 1 + sovRpc(uint64(m.Ttl))
} }
return n return n
} }
@ -4351,16 +4342,16 @@ func (m *LeaseCreateRequest) Unmarshal(data []byte) error {
switch fieldNum { switch fieldNum {
case 1: case 1:
if wireType != 0 { if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Ttl", wireType) return fmt.Errorf("proto: wrong wireType = %d for field TTL", wireType)
} }
m.Ttl = 0 m.TTL = 0
for shift := uint(0); ; shift += 7 { for shift := uint(0); ; shift += 7 {
if iNdEx >= l { if iNdEx >= l {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
b := data[iNdEx] b := data[iNdEx]
iNdEx++ iNdEx++
m.Ttl |= (int64(b) & 0x7F) << shift m.TTL |= (int64(b) & 0x7F) << shift
if b < 0x80 { if b < 0x80 {
break break
} }
@ -4442,32 +4433,32 @@ func (m *LeaseCreateResponse) Unmarshal(data []byte) error {
iNdEx = postIndex iNdEx = postIndex
case 2: case 2:
if wireType != 0 { if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field LeaseId", wireType) return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
} }
m.LeaseId = 0 m.ID = 0
for shift := uint(0); ; shift += 7 { for shift := uint(0); ; shift += 7 {
if iNdEx >= l { if iNdEx >= l {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
b := data[iNdEx] b := data[iNdEx]
iNdEx++ iNdEx++
m.LeaseId |= (int64(b) & 0x7F) << shift m.ID |= (int64(b) & 0x7F) << shift
if b < 0x80 { if b < 0x80 {
break break
} }
} }
case 3: case 3:
if wireType != 0 { if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Ttl", wireType) return fmt.Errorf("proto: wrong wireType = %d for field TTL", wireType)
} }
m.Ttl = 0 m.TTL = 0
for shift := uint(0); ; shift += 7 { for shift := uint(0); ; shift += 7 {
if iNdEx >= l { if iNdEx >= l {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
b := data[iNdEx] b := data[iNdEx]
iNdEx++ iNdEx++
m.Ttl |= (int64(b) & 0x7F) << shift m.TTL |= (int64(b) & 0x7F) << shift
if b < 0x80 { if b < 0x80 {
break break
} }
@ -4545,16 +4536,16 @@ func (m *LeaseRevokeRequest) Unmarshal(data []byte) error {
switch fieldNum { switch fieldNum {
case 1: case 1:
if wireType != 0 { if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field LeaseId", wireType) return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
} }
m.LeaseId = 0 m.ID = 0
for shift := uint(0); ; shift += 7 { for shift := uint(0); ; shift += 7 {
if iNdEx >= l { if iNdEx >= l {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
b := data[iNdEx] b := data[iNdEx]
iNdEx++ iNdEx++
m.LeaseId |= (int64(b) & 0x7F) << shift m.ID |= (int64(b) & 0x7F) << shift
if b < 0x80 { if b < 0x80 {
break break
} }
@ -4681,16 +4672,16 @@ func (m *LeaseKeepAliveRequest) Unmarshal(data []byte) error {
switch fieldNum { switch fieldNum {
case 1: case 1:
if wireType != 0 { if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field LeaseId", wireType) return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
} }
m.LeaseId = 0 m.ID = 0
for shift := uint(0); ; shift += 7 { for shift := uint(0); ; shift += 7 {
if iNdEx >= l { if iNdEx >= l {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
b := data[iNdEx] b := data[iNdEx]
iNdEx++ iNdEx++
m.LeaseId |= (int64(b) & 0x7F) << shift m.ID |= (int64(b) & 0x7F) << shift
if b < 0x80 { if b < 0x80 {
break break
} }
@ -4772,32 +4763,16 @@ func (m *LeaseKeepAliveResponse) Unmarshal(data []byte) error {
iNdEx = postIndex iNdEx = postIndex
case 2: case 2:
if wireType != 0 { if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field LeaseId", wireType) return fmt.Errorf("proto: wrong wireType = %d for field TTL", wireType)
} }
m.LeaseId = 0 m.TTL = 0
for shift := uint(0); ; shift += 7 { for shift := uint(0); ; shift += 7 {
if iNdEx >= l { if iNdEx >= l {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
b := data[iNdEx] b := data[iNdEx]
iNdEx++ iNdEx++
m.LeaseId |= (int64(b) & 0x7F) << shift m.TTL |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Ttl", wireType)
}
m.Ttl = 0
for shift := uint(0); ; shift += 7 {
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
iNdEx++
m.Ttl |= (int64(b) & 0x7F) << shift
if b < 0x80 { if b < 0x80 {
break break
} }

View File

@ -243,19 +243,19 @@ message WatchResponse {
message LeaseCreateRequest { message LeaseCreateRequest {
// advisory ttl in seconds // advisory ttl in seconds
int64 ttl = 1; int64 TTL = 1;
} }
message LeaseCreateResponse { message LeaseCreateResponse {
ResponseHeader header = 1; ResponseHeader header = 1;
int64 lease_id = 2; int64 ID = 2;
// server decided ttl in second // server decided ttl in second
int64 ttl = 3; int64 TTL = 3;
string error = 4; string error = 4;
} }
message LeaseRevokeRequest { message LeaseRevokeRequest {
int64 lease_id = 1; int64 ID = 1;
} }
message LeaseRevokeResponse { message LeaseRevokeResponse {
@ -263,11 +263,10 @@ message LeaseRevokeResponse {
} }
message LeaseKeepAliveRequest { message LeaseKeepAliveRequest {
int64 lease_id = 1; int64 ID = 1;
} }
message LeaseKeepAliveResponse { message LeaseKeepAliveResponse {
ResponseHeader header = 1; ResponseHeader header = 1;
int64 lease_id = 2; int64 TTL = 2;
int64 ttl = 3;
} }

View File

@ -34,6 +34,7 @@ import (
"github.com/coreos/etcd/etcdserver/etcdhttp/httptypes" "github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb" pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
"github.com/coreos/etcd/etcdserver/stats" "github.com/coreos/etcd/etcdserver/stats"
"github.com/coreos/etcd/lease"
"github.com/coreos/etcd/pkg/fileutil" "github.com/coreos/etcd/pkg/fileutil"
"github.com/coreos/etcd/pkg/idutil" "github.com/coreos/etcd/pkg/idutil"
"github.com/coreos/etcd/pkg/pbutil" "github.com/coreos/etcd/pkg/pbutil"
@ -166,8 +167,9 @@ type EtcdServer struct {
store store.Store store store.Store
kv dstorage.ConsistentWatchableKV kv dstorage.ConsistentWatchableKV
be backend.Backend lessor lease.Lessor
be backend.Backend
stats *stats.ServerStats stats *stats.ServerStats
lstats *stats.LeaderStats lstats *stats.LeaderStats
@ -360,6 +362,7 @@ func NewServer(cfg *ServerConfig) (*EtcdServer, error) {
if cfg.V3demo { if cfg.V3demo {
srv.be = backend.NewDefaultBackend(path.Join(cfg.SnapDir(), databaseFilename)) srv.be = backend.NewDefaultBackend(path.Join(cfg.SnapDir(), databaseFilename))
srv.kv = dstorage.New(srv.be, &srv.consistIndex) srv.kv = dstorage.New(srv.be, &srv.consistIndex)
srv.lessor = lease.NewLessor(uint8(id), srv.be, srv.kv)
} }
// TODO: move transport initialization near the definition of remote // TODO: move transport initialization near the definition of remote
@ -589,6 +592,8 @@ func (s *EtcdServer) applySnapshot(ep *etcdProgress, apply *apply) {
plog.Panicf("rename snapshot file error: %v", err) plog.Panicf("rename snapshot file error: %v", err)
} }
// TODO: recover leassor
newbe := backend.NewDefaultBackend(fn) newbe := backend.NewDefaultBackend(fn)
if err := s.kv.Restore(newbe); err != nil { if err := s.kv.Restore(newbe); err != nil {
plog.Panicf("restore KV error: %v", err) plog.Panicf("restore KV error: %v", err)

View File

@ -34,6 +34,11 @@ type RaftKV interface {
Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.CompactionResponse, error) Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.CompactionResponse, error)
} }
type Lessor interface {
LeaseCreate(ctx context.Context, r *pb.LeaseCreateRequest) (*pb.LeaseCreateResponse, error)
LeaseRevoke(ctx context.Context, r *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error)
}
func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error) { func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error) {
result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Range: r}) result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Range: r})
if err != nil { if err != nil {
@ -74,6 +79,22 @@ func (s *EtcdServer) Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.
return result.resp.(*pb.CompactionResponse), result.err return result.resp.(*pb.CompactionResponse), result.err
} }
func (s *EtcdServer) LeaseCreate(ctx context.Context, r *pb.LeaseCreateRequest) (*pb.LeaseCreateResponse, error) {
result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{LeaseCreate: r})
if err != nil {
return nil, err
}
return result.resp.(*pb.LeaseCreateResponse), result.err
}
func (s *EtcdServer) LeaseRevoke(ctx context.Context, r *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error) {
result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{LeaseRevoke: r})
if err != nil {
return nil, err
}
return result.resp.(*pb.LeaseRevokeResponse), result.err
}
type applyResult struct { type applyResult struct {
resp proto.Message resp proto.Message
err error err error
@ -115,6 +136,7 @@ const (
func (s *EtcdServer) applyV3Request(r *pb.InternalRaftRequest) interface{} { func (s *EtcdServer) applyV3Request(r *pb.InternalRaftRequest) interface{} {
kv := s.getKV() kv := s.getKV()
le := s.lessor
ar := &applyResult{} ar := &applyResult{}
@ -129,6 +151,10 @@ func (s *EtcdServer) applyV3Request(r *pb.InternalRaftRequest) interface{} {
ar.resp, ar.err = applyTxn(kv, r.Txn) ar.resp, ar.err = applyTxn(kv, r.Txn)
case r.Compaction != nil: case r.Compaction != nil:
ar.resp, ar.err = applyCompaction(kv, r.Compaction) ar.resp, ar.err = applyCompaction(kv, r.Compaction)
case r.LeaseCreate != nil:
ar.resp, ar.err = applyLeaseCreate(le, r.LeaseCreate)
case r.LeaseRevoke != nil:
ar.resp, ar.err = applyLeaseRevoke(le, r.LeaseRevoke)
default: default:
panic("not implemented") panic("not implemented")
} }
@ -348,6 +374,18 @@ func applyCompare(txnID int64, kv dstorage.KV, c *pb.Compare) (int64, bool) {
return rev, true return rev, true
} }
func applyLeaseCreate(le lease.Lessor, lc *pb.LeaseCreateRequest) (*pb.LeaseCreateResponse, error) {
l := le.Grant(lc.TTL)
return &pb.LeaseCreateResponse{ID: int64(l.ID), TTL: l.TTL}, nil
}
func applyLeaseRevoke(le lease.Lessor, lc *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error) {
err := le.Revoke(lease.LeaseID(lc.ID))
return &pb.LeaseRevokeResponse{}, err
}
func compareInt64(a, b int64) int { func compareInt64(a, b int64) int {
switch { switch {
case a < b: case a < b:

View File

@ -43,8 +43,17 @@ type DeleteableRange interface {
DeleteRange(key, end []byte) (int64, int64) DeleteRange(key, end []byte) (int64, int64)
} }
// a lessor is the owner of leases. It can grant, revoke, // A Lessor is the owner of leases. It can grant, revoke, renew and modify leases for lessee.
// renew and modify leases for lessee. type Lessor interface {
// Grant grants a lease that expires at least after TTL seconds.
Grant(ttl int64) *Lease
// Revoke revokes a lease with given ID. The item attached to the
// given lease will be removed. If the ID does not exist, an error
// will be returned.
Revoke(id LeaseID) error
}
// lessor implements Lessor interface.
// TODO: use clockwork for testability. // TODO: use clockwork for testability.
type lessor struct { type lessor struct {
mu sync.Mutex mu sync.Mutex
@ -54,7 +63,7 @@ type lessor struct {
// We want to make Grant, Revoke, and FindExpired all O(logN) and // We want to make Grant, Revoke, and FindExpired all O(logN) and
// Renew O(1). // Renew O(1).
// FindExpired and Renew should be the most frequent operations. // FindExpired and Renew should be the most frequent operations.
leaseMap map[LeaseID]*lease leaseMap map[LeaseID]*Lease
// A DeleteableRange the lessor operates on. // A DeleteableRange the lessor operates on.
// When a lease expires, the lessor will delete the // When a lease expires, the lessor will delete the
@ -68,9 +77,19 @@ type lessor struct {
idgen *idutil.Generator idgen *idutil.Generator
} }
func NewLessor(lessorID uint8, b backend.Backend, dr DeleteableRange) *lessor { func NewLessor(lessorID uint8, b backend.Backend, dr DeleteableRange) Lessor {
return newLessor(lessorID, b, dr)
}
func newLessor(lessorID uint8, b backend.Backend, dr DeleteableRange) *lessor {
// ensure the most significant bit of lessorID is 0.
// so all the IDs generated by id generator will be greater than 0.
if int8(lessorID) < 0 {
lessorID = uint8(-int8(lessorID))
}
l := &lessor{ l := &lessor{
leaseMap: make(map[LeaseID]*lease), leaseMap: make(map[LeaseID]*Lease),
b: b, b: b,
dr: dr, dr: dr,
idgen: idutil.NewGenerator(lessorID, time.Now()), idgen: idutil.NewGenerator(lessorID, time.Now()),
@ -80,10 +99,9 @@ func NewLessor(lessorID uint8, b backend.Backend, dr DeleteableRange) *lessor {
return l return l
} }
// Grant grants a lease that expires at least after TTL seconds.
// TODO: when lessor is under high load, it should give out lease // TODO: when lessor is under high load, it should give out lease
// with longer TTL to reduce renew load. // with longer TTL to reduce renew load.
func (le *lessor) Grant(ttl int64) *lease { func (le *lessor) Grant(ttl int64) *Lease {
// TODO: define max TTL // TODO: define max TTL
expiry := time.Now().Add(time.Duration(ttl) * time.Second) expiry := time.Now().Add(time.Duration(ttl) * time.Second)
expiry = minExpiry(time.Now(), expiry) expiry = minExpiry(time.Now(), expiry)
@ -93,7 +111,7 @@ func (le *lessor) Grant(ttl int64) *lease {
le.mu.Lock() le.mu.Lock()
defer le.mu.Unlock() defer le.mu.Unlock()
l := &lease{id: id, ttl: ttl, expiry: expiry, itemSet: make(map[leaseItem]struct{})} l := &Lease{ID: id, TTL: ttl, expiry: expiry, itemSet: make(map[leaseItem]struct{})}
if _, ok := le.leaseMap[id]; ok { if _, ok := le.leaseMap[id]; ok {
panic("lease: unexpected duplicate ID!") panic("lease: unexpected duplicate ID!")
} }
@ -104,9 +122,6 @@ func (le *lessor) Grant(ttl int64) *lease {
return l return l
} }
// Revoke revokes a lease with given ID. The item attached to the
// given lease will be removed. If the ID does not exist, an error
// will be returned.
func (le *lessor) Revoke(id LeaseID) error { func (le *lessor) Revoke(id LeaseID) error {
le.mu.Lock() le.mu.Lock()
defer le.mu.Unlock() defer le.mu.Unlock()
@ -120,7 +135,7 @@ func (le *lessor) Revoke(id LeaseID) error {
le.dr.DeleteRange([]byte(item.key), nil) le.dr.DeleteRange([]byte(item.key), nil)
} }
delete(le.leaseMap, l.id) delete(le.leaseMap, l.ID)
l.removeFrom(le.b) l.removeFrom(le.b)
return nil return nil
@ -138,7 +153,7 @@ func (le *lessor) Renew(id LeaseID) error {
return fmt.Errorf("lease: cannot find lease %x", id) return fmt.Errorf("lease: cannot find lease %x", id)
} }
expiry := time.Now().Add(time.Duration(l.ttl) * time.Second) expiry := time.Now().Add(time.Duration(l.TTL) * time.Second)
l.expiry = minExpiry(time.Now(), expiry) l.expiry = minExpiry(time.Now(), expiry)
return nil return nil
} }
@ -161,13 +176,24 @@ func (le *lessor) Attach(id LeaseID, items []leaseItem) error {
return nil return nil
} }
// findExpiredLeases loops all the leases in the leaseMap and returns the expired func (le *lessor) Recover(b backend.Backend, dr DeleteableRange) {
// leases that needed to be revoked.
func (le *lessor) findExpiredLeases() []*lease {
le.mu.Lock() le.mu.Lock()
defer le.mu.Unlock() defer le.mu.Unlock()
leases := make([]*lease, 0, 16) le.b = b
le.dr = dr
le.leaseMap = make(map[LeaseID]*Lease)
le.initAndRecover()
}
// findExpiredLeases loops all the leases in the leaseMap and returns the expired
// leases that needed to be revoked.
func (le *lessor) findExpiredLeases() []*Lease {
le.mu.Lock()
defer le.mu.Unlock()
leases := make([]*Lease, 0, 16)
now := time.Now() now := time.Now()
for _, l := range le.leaseMap { for _, l := range le.leaseMap {
@ -181,7 +207,7 @@ func (le *lessor) findExpiredLeases() []*lease {
// get gets the lease with given id. // get gets the lease with given id.
// get is a helper fucntion for testing, at least for now. // get is a helper fucntion for testing, at least for now.
func (le *lessor) get(id LeaseID) *lease { func (le *lessor) get(id LeaseID) *Lease {
le.mu.Lock() le.mu.Lock()
defer le.mu.Unlock() defer le.mu.Unlock()
@ -191,7 +217,6 @@ func (le *lessor) get(id LeaseID) *lease {
func (le *lessor) initAndRecover() { func (le *lessor) initAndRecover() {
tx := le.b.BatchTx() tx := le.b.BatchTx()
tx.Lock() tx.Lock()
defer tx.Unlock()
tx.UnsafeCreateBucket(leaseBucketName) tx.UnsafeCreateBucket(leaseBucketName)
_, vs := tx.UnsafeRange(leaseBucketName, int64ToBytes(0), int64ToBytes(math.MaxInt64), 0) _, vs := tx.UnsafeRange(leaseBucketName, int64ToBytes(0), int64ToBytes(math.MaxInt64), 0)
@ -200,33 +225,36 @@ func (le *lessor) initAndRecover() {
var lpb leasepb.Lease var lpb leasepb.Lease
err := lpb.Unmarshal(vs[i]) err := lpb.Unmarshal(vs[i])
if err != nil { if err != nil {
tx.Unlock()
panic("failed to unmarshal lease proto item") panic("failed to unmarshal lease proto item")
} }
id := LeaseID(lpb.ID) ID := LeaseID(lpb.ID)
le.leaseMap[id] = &lease{ le.leaseMap[ID] = &Lease{
id: id, ID: ID,
ttl: lpb.TTL, TTL: lpb.TTL,
// itemSet will be filled in when recover key-value pairs // itemSet will be filled in when recover key-value pairs
expiry: minExpiry(time.Now(), time.Now().Add(time.Second*time.Duration(lpb.TTL))), expiry: minExpiry(time.Now(), time.Now().Add(time.Second*time.Duration(lpb.TTL))),
} }
} }
tx.Unlock()
le.b.ForceCommit() le.b.ForceCommit()
} }
type lease struct { type Lease struct {
id LeaseID ID LeaseID
ttl int64 // time to live in seconds TTL int64 // time to live in seconds
itemSet map[leaseItem]struct{} itemSet map[leaseItem]struct{}
// expiry time in unixnano // expiry time in unixnano
expiry time.Time expiry time.Time
} }
func (l lease) persistTo(b backend.Backend) { func (l Lease) persistTo(b backend.Backend) {
key := int64ToBytes(int64(l.id)) key := int64ToBytes(int64(l.ID))
lpb := leasepb.Lease{ID: int64(l.id), TTL: int64(l.ttl)} lpb := leasepb.Lease{ID: int64(l.ID), TTL: int64(l.TTL)}
val, err := lpb.Marshal() val, err := lpb.Marshal()
if err != nil { if err != nil {
panic("failed to marshal lease proto item") panic("failed to marshal lease proto item")
@ -237,8 +265,8 @@ func (l lease) persistTo(b backend.Backend) {
b.BatchTx().Unlock() b.BatchTx().Unlock()
} }
func (l lease) removeFrom(b backend.Backend) { func (l Lease) removeFrom(b backend.Backend) {
key := int64ToBytes(int64(l.id)) key := int64ToBytes(int64(l.ID))
b.BatchTx().Lock() b.BatchTx().Lock()
b.BatchTx().UnsafeDelete(leaseBucketName, key) b.BatchTx().UnsafeDelete(leaseBucketName, key)

View File

@ -33,10 +33,10 @@ func TestLessorGrant(t *testing.T) {
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
defer be.Close() defer be.Close()
le := NewLessor(1, be, &fakeDeleteable{}) le := newLessor(1, be, &fakeDeleteable{})
l := le.Grant(1) l := le.Grant(1)
gl := le.get(l.id) gl := le.get(l.ID)
if !reflect.DeepEqual(gl, l) { if !reflect.DeepEqual(gl, l) {
t.Errorf("lease = %v, want %v", gl, l) t.Errorf("lease = %v, want %v", gl, l)
@ -46,12 +46,12 @@ func TestLessorGrant(t *testing.T) {
} }
nl := le.Grant(1) nl := le.Grant(1)
if nl.id == l.id { if nl.ID == l.ID {
t.Errorf("new lease.id = %x, want != %x", nl.id, l.id) t.Errorf("new lease.id = %x, want != %x", nl.ID, l.ID)
} }
be.BatchTx().Lock() be.BatchTx().Lock()
_, vs := be.BatchTx().UnsafeRange(leaseBucketName, int64ToBytes(int64(l.id)), nil, 0) _, vs := be.BatchTx().UnsafeRange(leaseBucketName, int64ToBytes(int64(l.ID)), nil, 0)
if len(vs) != 1 { if len(vs) != 1 {
t.Errorf("len(vs) = %d, want 1", len(vs)) t.Errorf("len(vs) = %d, want 1", len(vs))
} }
@ -69,7 +69,7 @@ func TestLessorRevoke(t *testing.T) {
fd := &fakeDeleteable{} fd := &fakeDeleteable{}
le := NewLessor(1, be, fd) le := newLessor(1, be, fd)
// grant a lease with long term (100 seconds) to // grant a lease with long term (100 seconds) to
// avoid early termination during the test. // avoid early termination during the test.
@ -80,18 +80,18 @@ func TestLessorRevoke(t *testing.T) {
{"bar"}, {"bar"},
} }
err := le.Attach(l.id, items) err := le.Attach(l.ID, items)
if err != nil { if err != nil {
t.Fatalf("failed to attach items to the lease: %v", err) t.Fatalf("failed to attach items to the lease: %v", err)
} }
err = le.Revoke(l.id) err = le.Revoke(l.ID)
if err != nil { if err != nil {
t.Fatal("failed to revoke lease:", err) t.Fatal("failed to revoke lease:", err)
} }
if le.get(l.id) != nil { if le.get(l.ID) != nil {
t.Errorf("got revoked lease %x", l.id) t.Errorf("got revoked lease %x", l.ID)
} }
wdeleted := []string{"foo_", "bar_"} wdeleted := []string{"foo_", "bar_"}
@ -100,7 +100,7 @@ func TestLessorRevoke(t *testing.T) {
} }
be.BatchTx().Lock() be.BatchTx().Lock()
_, vs := be.BatchTx().UnsafeRange(leaseBucketName, int64ToBytes(int64(l.id)), nil, 0) _, vs := be.BatchTx().UnsafeRange(leaseBucketName, int64ToBytes(int64(l.ID)), nil, 0)
if len(vs) != 0 { if len(vs) != 0 {
t.Errorf("len(vs) = %d, want 0", len(vs)) t.Errorf("len(vs) = %d, want 0", len(vs))
} }
@ -113,14 +113,14 @@ func TestLessorRenew(t *testing.T) {
defer be.Close() defer be.Close()
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
le := NewLessor(1, be, &fakeDeleteable{}) le := newLessor(1, be, &fakeDeleteable{})
l := le.Grant(5) l := le.Grant(5)
// manually change the ttl field // manually change the ttl field
l.ttl = 10 l.TTL = 10
le.Renew(l.id) le.Renew(l.ID)
l = le.get(l.id) l = le.get(l.ID)
if l.expiry.Sub(time.Now()) < 9*time.Second { if l.expiry.Sub(time.Now()) < 9*time.Second {
t.Errorf("failed to renew the lease") t.Errorf("failed to renew the lease")
@ -134,20 +134,20 @@ func TestLessorRecover(t *testing.T) {
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
defer be.Close() defer be.Close()
le := NewLessor(1, be, &fakeDeleteable{}) le := newLessor(1, be, &fakeDeleteable{})
l1 := le.Grant(10) l1 := le.Grant(10)
l2 := le.Grant(20) l2 := le.Grant(20)
// Create a new lessor with the same backend // Create a new lessor with the same backend
nle := NewLessor(1, be, &fakeDeleteable{}) nle := newLessor(1, be, &fakeDeleteable{})
nl1 := nle.get(l1.id) nl1 := nle.get(l1.ID)
if nl1 == nil || nl1.ttl != l1.ttl { if nl1 == nil || nl1.TTL != l1.TTL {
t.Errorf("nl1 = %v, want nl1.TTL= %d", l1.ttl) t.Errorf("nl1 = %v, want nl1.TTL= %d", nl1.TTL, l1.TTL)
} }
nl2 := nle.get(l2.id) nl2 := nle.get(l2.ID)
if nl2 == nil || nl2.ttl != l2.ttl { if nl2 == nil || nl2.TTL != l2.TTL {
t.Errorf("nl2 = %v, want nl2.TTL= %d", l2.ttl) t.Errorf("nl2 = %v, want nl2.TTL= %d", nl2.TTL, l2.TTL)
} }
} }