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 }