go.etcd.io/etcd@v3.3.27+incompatible/proxy/grpcproxy/leader.go (about)

     1  // Copyright 2017 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 grpcproxy
    16  
    17  import (
    18  	"context"
    19  	"math"
    20  	"sync"
    21  
    22  	"github.com/coreos/etcd/clientv3"
    23  	"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
    24  
    25  	"golang.org/x/time/rate"
    26  	"google.golang.org/grpc"
    27  )
    28  
    29  const (
    30  	lostLeaderKey  = "__lostleader" // watched to detect leader loss
    31  	retryPerSecond = 10
    32  )
    33  
    34  type leader struct {
    35  	ctx context.Context
    36  	w   clientv3.Watcher
    37  	// mu protects leaderc updates.
    38  	mu       sync.RWMutex
    39  	leaderc  chan struct{}
    40  	disconnc chan struct{}
    41  	donec    chan struct{}
    42  }
    43  
    44  func newLeader(ctx context.Context, w clientv3.Watcher) *leader {
    45  	l := &leader{
    46  		ctx:      clientv3.WithRequireLeader(ctx),
    47  		w:        w,
    48  		leaderc:  make(chan struct{}),
    49  		disconnc: make(chan struct{}),
    50  		donec:    make(chan struct{}),
    51  	}
    52  	// begin assuming leader is lost
    53  	close(l.leaderc)
    54  	go l.recvLoop()
    55  	return l
    56  }
    57  
    58  func (l *leader) recvLoop() {
    59  	defer close(l.donec)
    60  
    61  	limiter := rate.NewLimiter(rate.Limit(retryPerSecond), retryPerSecond)
    62  	rev := int64(math.MaxInt64 - 2)
    63  	for limiter.Wait(l.ctx) == nil {
    64  		wch := l.w.Watch(l.ctx, lostLeaderKey, clientv3.WithRev(rev), clientv3.WithCreatedNotify())
    65  		cresp, ok := <-wch
    66  		if !ok {
    67  			l.loseLeader()
    68  			continue
    69  		}
    70  		if cresp.Err() != nil {
    71  			l.loseLeader()
    72  			if rpctypes.ErrorDesc(cresp.Err()) == grpc.ErrClientConnClosing.Error() {
    73  				close(l.disconnc)
    74  				return
    75  			}
    76  			continue
    77  		}
    78  		l.gotLeader()
    79  		<-wch
    80  		l.loseLeader()
    81  	}
    82  }
    83  
    84  func (l *leader) loseLeader() {
    85  	l.mu.RLock()
    86  	defer l.mu.RUnlock()
    87  	select {
    88  	case <-l.leaderc:
    89  	default:
    90  		close(l.leaderc)
    91  	}
    92  }
    93  
    94  // gotLeader will force update the leadership status to having a leader.
    95  func (l *leader) gotLeader() {
    96  	l.mu.Lock()
    97  	defer l.mu.Unlock()
    98  	select {
    99  	case <-l.leaderc:
   100  		l.leaderc = make(chan struct{})
   101  	default:
   102  	}
   103  }
   104  
   105  func (l *leader) disconnectNotify() <-chan struct{} { return l.disconnc }
   106  
   107  func (l *leader) stopNotify() <-chan struct{} { return l.donec }
   108  
   109  // lostNotify returns a channel that is closed if there has been
   110  // a leader loss not yet followed by a leader reacquire.
   111  func (l *leader) lostNotify() <-chan struct{} {
   112  	l.mu.RLock()
   113  	defer l.mu.RUnlock()
   114  	return l.leaderc
   115  }