go.etcd.io/etcd@v3.3.27+incompatible/etcdserver/api/v3rpc/lease.go (about)

     1  // Copyright 2016 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package v3rpc
    16  
    17  import (
    18  	"context"
    19  	"io"
    20  
    21  	"github.com/coreos/etcd/etcdserver"
    22  	"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
    23  	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
    24  	"github.com/coreos/etcd/lease"
    25  )
    26  
    27  type LeaseServer struct {
    28  	hdr header
    29  	le  etcdserver.Lessor
    30  }
    31  
    32  func NewLeaseServer(s *etcdserver.EtcdServer) pb.LeaseServer {
    33  	return &LeaseServer{le: s, hdr: newHeader(s)}
    34  }
    35  
    36  func (ls *LeaseServer) LeaseGrant(ctx context.Context, cr *pb.LeaseGrantRequest) (*pb.LeaseGrantResponse, error) {
    37  	resp, err := ls.le.LeaseGrant(ctx, cr)
    38  
    39  	if err != nil {
    40  		return nil, togRPCError(err)
    41  	}
    42  	ls.hdr.fill(resp.Header)
    43  	return resp, nil
    44  }
    45  
    46  func (ls *LeaseServer) LeaseRevoke(ctx context.Context, rr *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error) {
    47  	resp, err := ls.le.LeaseRevoke(ctx, rr)
    48  	if err != nil {
    49  		return nil, togRPCError(err)
    50  	}
    51  	ls.hdr.fill(resp.Header)
    52  	return resp, nil
    53  }
    54  
    55  func (ls *LeaseServer) LeaseTimeToLive(ctx context.Context, rr *pb.LeaseTimeToLiveRequest) (*pb.LeaseTimeToLiveResponse, error) {
    56  	resp, err := ls.le.LeaseTimeToLive(ctx, rr)
    57  	if err != nil && err != lease.ErrLeaseNotFound {
    58  		return nil, togRPCError(err)
    59  	}
    60  	if err == lease.ErrLeaseNotFound {
    61  		resp = &pb.LeaseTimeToLiveResponse{
    62  			Header: &pb.ResponseHeader{},
    63  			ID:     rr.ID,
    64  			TTL:    -1,
    65  		}
    66  	}
    67  	ls.hdr.fill(resp.Header)
    68  	return resp, nil
    69  }
    70  
    71  func (ls *LeaseServer) LeaseLeases(ctx context.Context, rr *pb.LeaseLeasesRequest) (*pb.LeaseLeasesResponse, error) {
    72  	resp, err := ls.le.LeaseLeases(ctx, rr)
    73  	if err != nil && err != lease.ErrLeaseNotFound {
    74  		return nil, togRPCError(err)
    75  	}
    76  	if err == lease.ErrLeaseNotFound {
    77  		resp = &pb.LeaseLeasesResponse{
    78  			Header: &pb.ResponseHeader{},
    79  			Leases: []*pb.LeaseStatus{},
    80  		}
    81  	}
    82  	ls.hdr.fill(resp.Header)
    83  	return resp, nil
    84  }
    85  
    86  func (ls *LeaseServer) LeaseKeepAlive(stream pb.Lease_LeaseKeepAliveServer) (err error) {
    87  	errc := make(chan error, 1)
    88  	go func() {
    89  		errc <- ls.leaseKeepAlive(stream)
    90  	}()
    91  	select {
    92  	case err = <-errc:
    93  	case <-stream.Context().Done():
    94  		// the only server-side cancellation is noleader for now.
    95  		err = stream.Context().Err()
    96  		if err == context.Canceled {
    97  			err = rpctypes.ErrGRPCNoLeader
    98  		}
    99  	}
   100  	return err
   101  }
   102  
   103  func (ls *LeaseServer) leaseKeepAlive(stream pb.Lease_LeaseKeepAliveServer) error {
   104  	for {
   105  		req, err := stream.Recv()
   106  		if err == io.EOF {
   107  			return nil
   108  		}
   109  		if err != nil {
   110  			if isClientCtxErr(stream.Context().Err(), err) {
   111  				plog.Debugf("failed to receive lease keepalive request from gRPC stream (%q)", err.Error())
   112  			} else {
   113  				plog.Warningf("failed to receive lease keepalive request from gRPC stream (%q)", err.Error())
   114  			}
   115  			return err
   116  		}
   117  
   118  		// Create header before we sent out the renew request.
   119  		// This can make sure that the revision is strictly smaller or equal to
   120  		// when the keepalive happened at the local server (when the local server is the leader)
   121  		// or remote leader.
   122  		// Without this, a lease might be revoked at rev 3 but client can see the keepalive succeeded
   123  		// at rev 4.
   124  		resp := &pb.LeaseKeepAliveResponse{ID: req.ID, Header: &pb.ResponseHeader{}}
   125  		ls.hdr.fill(resp.Header)
   126  
   127  		ttl, err := ls.le.LeaseRenew(stream.Context(), lease.LeaseID(req.ID))
   128  		if err == lease.ErrLeaseNotFound {
   129  			err = nil
   130  			ttl = 0
   131  		}
   132  
   133  		if err != nil {
   134  			return togRPCError(err)
   135  		}
   136  
   137  		resp.TTL = ttl
   138  		err = stream.Send(resp)
   139  		if err != nil {
   140  			if isClientCtxErr(stream.Context().Err(), err) {
   141  				plog.Debugf("failed to send lease keepalive response to gRPC stream (%q)", err.Error())
   142  			} else {
   143  				plog.Warningf("failed to send lease keepalive response to gRPC stream (%q)", err.Error())
   144  			}
   145  			return err
   146  		}
   147  	}
   148  }