github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/state/raft/transport/transport.go (about)

     1  // Package transport provides grpc transport layer for raft.
     2  // All methods are non-blocking.
     3  package transport
     4  
     5  import (
     6  	"context"
     7  	"math"
     8  	"net"
     9  	"sync"
    10  	"time"
    11  
    12  	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
    13  	"google.golang.org/grpc"
    14  	"google.golang.org/grpc/credentials"
    15  
    16  	"github.com/coreos/etcd/raft"
    17  	"github.com/coreos/etcd/raft/raftpb"
    18  	"github.com/docker/swarmkit/log"
    19  	"github.com/pkg/errors"
    20  )
    21  
    22  // ErrIsNotFound indicates that peer was never added to transport.
    23  var ErrIsNotFound = errors.New("peer not found")
    24  
    25  // Raft is interface which represents Raft API for transport package.
    26  type Raft interface {
    27  	ReportUnreachable(id uint64)
    28  	ReportSnapshot(id uint64, status raft.SnapshotStatus)
    29  	IsIDRemoved(id uint64) bool
    30  	UpdateNode(id uint64, addr string)
    31  
    32  	NodeRemoved()
    33  }
    34  
    35  // Config for Transport
    36  type Config struct {
    37  	HeartbeatInterval time.Duration
    38  	SendTimeout       time.Duration
    39  	Credentials       credentials.TransportCredentials
    40  	RaftID            string
    41  
    42  	Raft
    43  }
    44  
    45  // Transport is structure which manages remote raft peers and sends messages
    46  // to them.
    47  type Transport struct {
    48  	config *Config
    49  
    50  	unknownc chan raftpb.Message
    51  
    52  	mu      sync.Mutex
    53  	peers   map[uint64]*peer
    54  	stopped bool
    55  
    56  	ctx    context.Context
    57  	cancel context.CancelFunc
    58  	done   chan struct{}
    59  
    60  	deferredConns map[*grpc.ClientConn]*time.Timer
    61  }
    62  
    63  // New returns new Transport with specified Config.
    64  func New(cfg *Config) *Transport {
    65  	ctx, cancel := context.WithCancel(context.Background())
    66  	if cfg.RaftID != "" {
    67  		ctx = log.WithField(ctx, "raft_id", cfg.RaftID)
    68  	}
    69  	t := &Transport{
    70  		peers:    make(map[uint64]*peer),
    71  		config:   cfg,
    72  		unknownc: make(chan raftpb.Message),
    73  		done:     make(chan struct{}),
    74  		ctx:      ctx,
    75  		cancel:   cancel,
    76  
    77  		deferredConns: make(map[*grpc.ClientConn]*time.Timer),
    78  	}
    79  	go t.run(ctx)
    80  	return t
    81  }
    82  
    83  func (t *Transport) run(ctx context.Context) {
    84  	defer func() {
    85  		log.G(ctx).Debug("stop transport")
    86  		t.mu.Lock()
    87  		defer t.mu.Unlock()
    88  		t.stopped = true
    89  		for _, p := range t.peers {
    90  			p.stop()
    91  			p.cc.Close()
    92  		}
    93  		for cc, timer := range t.deferredConns {
    94  			timer.Stop()
    95  			cc.Close()
    96  		}
    97  		t.deferredConns = nil
    98  		close(t.done)
    99  	}()
   100  	for {
   101  		select {
   102  		case <-ctx.Done():
   103  			return
   104  		default:
   105  		}
   106  
   107  		select {
   108  		case m := <-t.unknownc:
   109  			if err := t.sendUnknownMessage(ctx, m); err != nil {
   110  				log.G(ctx).WithError(err).Warnf("ignored message %s to unknown peer %x", m.Type, m.To)
   111  			}
   112  		case <-ctx.Done():
   113  			return
   114  		}
   115  	}
   116  }
   117  
   118  // Stop stops transport and waits until it finished
   119  func (t *Transport) Stop() {
   120  	t.cancel()
   121  	<-t.done
   122  }
   123  
   124  // Send sends raft message to remote peers.
   125  func (t *Transport) Send(m raftpb.Message) error {
   126  	t.mu.Lock()
   127  	defer t.mu.Unlock()
   128  	if t.stopped {
   129  		return errors.New("transport stopped")
   130  	}
   131  	if t.config.IsIDRemoved(m.To) {
   132  		return errors.Errorf("refusing to send message %s to removed member %x", m.Type, m.To)
   133  	}
   134  	p, ok := t.peers[m.To]
   135  	if !ok {
   136  		log.G(t.ctx).Warningf("sending message %s to an unrecognized member ID %x", m.Type, m.To)
   137  		select {
   138  		// we need to process messages to unknown peers in separate goroutine
   139  		// to not block sender
   140  		case t.unknownc <- m:
   141  		case <-t.ctx.Done():
   142  			return t.ctx.Err()
   143  		default:
   144  			return errors.New("unknown messages queue is full")
   145  		}
   146  		return nil
   147  	}
   148  	if err := p.send(m); err != nil {
   149  		return errors.Wrapf(err, "failed to send message %x to %x", m.Type, m.To)
   150  	}
   151  	return nil
   152  }
   153  
   154  // AddPeer adds new peer with id and address addr to Transport.
   155  // If there is already peer with such id in Transport it will return error if
   156  // address is different (UpdatePeer should be used) or nil otherwise.
   157  func (t *Transport) AddPeer(id uint64, addr string) error {
   158  	t.mu.Lock()
   159  	defer t.mu.Unlock()
   160  	if t.stopped {
   161  		return errors.New("transport stopped")
   162  	}
   163  	if ep, ok := t.peers[id]; ok {
   164  		if ep.address() == addr {
   165  			return nil
   166  		}
   167  		return errors.Errorf("peer %x already added with addr %s", id, ep.addr)
   168  	}
   169  	log.G(t.ctx).Debugf("transport: add peer %x with address %s", id, addr)
   170  	p, err := newPeer(id, addr, t)
   171  	if err != nil {
   172  		return errors.Wrapf(err, "failed to create peer %x with addr %s", id, addr)
   173  	}
   174  	t.peers[id] = p
   175  	return nil
   176  }
   177  
   178  // RemovePeer removes peer from Transport and wait for it to stop.
   179  func (t *Transport) RemovePeer(id uint64) error {
   180  	t.mu.Lock()
   181  	defer t.mu.Unlock()
   182  
   183  	if t.stopped {
   184  		return errors.New("transport stopped")
   185  	}
   186  	p, ok := t.peers[id]
   187  	if !ok {
   188  		return ErrIsNotFound
   189  	}
   190  	delete(t.peers, id)
   191  	cc := p.conn()
   192  	p.stop()
   193  	timer := time.AfterFunc(8*time.Second, func() {
   194  		t.mu.Lock()
   195  		if !t.stopped {
   196  			delete(t.deferredConns, cc)
   197  			cc.Close()
   198  		}
   199  		t.mu.Unlock()
   200  	})
   201  	// store connection and timer for cleaning up on stop
   202  	t.deferredConns[cc] = timer
   203  
   204  	return nil
   205  }
   206  
   207  // UpdatePeer updates peer with new address. It replaces connection immediately.
   208  func (t *Transport) UpdatePeer(id uint64, addr string) error {
   209  	t.mu.Lock()
   210  	defer t.mu.Unlock()
   211  
   212  	if t.stopped {
   213  		return errors.New("transport stopped")
   214  	}
   215  	p, ok := t.peers[id]
   216  	if !ok {
   217  		return ErrIsNotFound
   218  	}
   219  	if err := p.update(addr); err != nil {
   220  		return err
   221  	}
   222  	log.G(t.ctx).Debugf("peer %x updated to address %s", id, addr)
   223  	return nil
   224  }
   225  
   226  // UpdatePeerAddr updates peer with new address, but delays connection creation.
   227  // New address won't be used until first failure on old address.
   228  func (t *Transport) UpdatePeerAddr(id uint64, addr string) error {
   229  	t.mu.Lock()
   230  	defer t.mu.Unlock()
   231  
   232  	if t.stopped {
   233  		return errors.New("transport stopped")
   234  	}
   235  	p, ok := t.peers[id]
   236  	if !ok {
   237  		return ErrIsNotFound
   238  	}
   239  	return p.updateAddr(addr)
   240  }
   241  
   242  // PeerConn returns raw grpc connection to peer.
   243  func (t *Transport) PeerConn(id uint64) (*grpc.ClientConn, error) {
   244  	t.mu.Lock()
   245  	defer t.mu.Unlock()
   246  	p, ok := t.peers[id]
   247  	if !ok {
   248  		return nil, ErrIsNotFound
   249  	}
   250  	p.mu.Lock()
   251  	active := p.active
   252  	p.mu.Unlock()
   253  	if !active {
   254  		return nil, errors.New("peer is inactive")
   255  	}
   256  	return p.conn(), nil
   257  }
   258  
   259  // PeerAddr returns address of peer with id.
   260  func (t *Transport) PeerAddr(id uint64) (string, error) {
   261  	t.mu.Lock()
   262  	defer t.mu.Unlock()
   263  	p, ok := t.peers[id]
   264  	if !ok {
   265  		return "", ErrIsNotFound
   266  	}
   267  	return p.address(), nil
   268  }
   269  
   270  // HealthCheck checks health of particular peer.
   271  func (t *Transport) HealthCheck(ctx context.Context, id uint64) error {
   272  	t.mu.Lock()
   273  	p, ok := t.peers[id]
   274  	t.mu.Unlock()
   275  	if !ok {
   276  		return ErrIsNotFound
   277  	}
   278  	ctx, cancel := t.withContext(ctx)
   279  	defer cancel()
   280  	return p.healthCheck(ctx)
   281  }
   282  
   283  // Active returns true if node was recently active and false otherwise.
   284  func (t *Transport) Active(id uint64) bool {
   285  	t.mu.Lock()
   286  	defer t.mu.Unlock()
   287  	p, ok := t.peers[id]
   288  	if !ok {
   289  		return false
   290  	}
   291  	p.mu.Lock()
   292  	active := p.active
   293  	p.mu.Unlock()
   294  	return active
   295  }
   296  
   297  // LongestActive returns the ID of the peer that has been active for the longest
   298  // length of time.
   299  func (t *Transport) LongestActive() (uint64, error) {
   300  	p, err := t.longestActive()
   301  	if err != nil {
   302  		return 0, err
   303  	}
   304  
   305  	return p.id, nil
   306  }
   307  
   308  // longestActive returns the peer that has been active for the longest length of
   309  // time.
   310  func (t *Transport) longestActive() (*peer, error) {
   311  	var longest *peer
   312  	var longestTime time.Time
   313  	t.mu.Lock()
   314  	defer t.mu.Unlock()
   315  	for _, p := range t.peers {
   316  		becameActive := p.activeTime()
   317  		if becameActive.IsZero() {
   318  			continue
   319  		}
   320  		if longest == nil {
   321  			longest = p
   322  			continue
   323  		}
   324  		if becameActive.Before(longestTime) {
   325  			longest = p
   326  			longestTime = becameActive
   327  		}
   328  	}
   329  	if longest == nil {
   330  		return nil, errors.New("failed to find longest active peer")
   331  	}
   332  	return longest, nil
   333  }
   334  
   335  func (t *Transport) dial(addr string) (*grpc.ClientConn, error) {
   336  	grpcOptions := []grpc.DialOption{
   337  		grpc.WithUnaryInterceptor(grpc_prometheus.UnaryClientInterceptor),
   338  		grpc.WithStreamInterceptor(grpc_prometheus.StreamClientInterceptor),
   339  		grpc.WithBackoffMaxDelay(8 * time.Second),
   340  	}
   341  	if t.config.Credentials != nil {
   342  		grpcOptions = append(grpcOptions, grpc.WithTransportCredentials(t.config.Credentials))
   343  	} else {
   344  		grpcOptions = append(grpcOptions, grpc.WithInsecure())
   345  	}
   346  
   347  	if t.config.SendTimeout > 0 {
   348  		grpcOptions = append(grpcOptions, grpc.WithTimeout(t.config.SendTimeout))
   349  	}
   350  
   351  	// gRPC dialer connects to proxy first. Provide a custom dialer here avoid that.
   352  	// TODO(anshul) Add an option to configure this.
   353  	grpcOptions = append(grpcOptions,
   354  		grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
   355  			return net.DialTimeout("tcp", addr, timeout)
   356  		}))
   357  
   358  	// TODO(dperny): this changes the max received message size for outgoing
   359  	// client connections. this means if the server sends a message larger than
   360  	// this, we will still accept and unmarshal it. i'm unsure what the
   361  	// potential consequences are of setting this to be effectively unbounded,
   362  	// so after docker/swarmkit#2774 is fixed, we should remove this option
   363  	grpcOptions = append(grpcOptions, grpc.WithDefaultCallOptions(
   364  		grpc.MaxCallRecvMsgSize(math.MaxInt32),
   365  	))
   366  
   367  	cc, err := grpc.Dial(addr, grpcOptions...)
   368  	if err != nil {
   369  		return nil, err
   370  	}
   371  
   372  	return cc, nil
   373  }
   374  
   375  func (t *Transport) withContext(ctx context.Context) (context.Context, context.CancelFunc) {
   376  	ctx, cancel := context.WithCancel(ctx)
   377  
   378  	go func() {
   379  		select {
   380  		case <-ctx.Done():
   381  		case <-t.ctx.Done():
   382  			cancel()
   383  		}
   384  	}()
   385  	return ctx, cancel
   386  }
   387  
   388  func (t *Transport) resolvePeer(ctx context.Context, id uint64) (*peer, error) {
   389  	longestActive, err := t.longestActive()
   390  	if err != nil {
   391  		return nil, err
   392  	}
   393  	ctx, cancel := context.WithTimeout(ctx, t.config.SendTimeout)
   394  	defer cancel()
   395  	addr, err := longestActive.resolveAddr(ctx, id)
   396  	if err != nil {
   397  		return nil, err
   398  	}
   399  	return newPeer(id, addr, t)
   400  }
   401  
   402  func (t *Transport) sendUnknownMessage(ctx context.Context, m raftpb.Message) error {
   403  	p, err := t.resolvePeer(ctx, m.To)
   404  	if err != nil {
   405  		return errors.Wrapf(err, "failed to resolve peer")
   406  	}
   407  	defer p.cancel()
   408  	if err := p.sendProcessMessage(ctx, m); err != nil {
   409  		return errors.Wrapf(err, "failed to send message")
   410  	}
   411  	return nil
   412  }