github.com/sym3tri/etcd@v0.2.1-0.20140422215517-a563d82f95d6/server/peer_server.go (about) 1 package server 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "math/rand" 9 "net/http" 10 "net/url" 11 "sort" 12 "strconv" 13 "strings" 14 "sync" 15 "time" 16 17 "github.com/coreos/etcd/third_party/github.com/goraft/raft" 18 "github.com/coreos/etcd/third_party/github.com/gorilla/mux" 19 20 "github.com/coreos/etcd/discovery" 21 etcdErr "github.com/coreos/etcd/error" 22 "github.com/coreos/etcd/log" 23 "github.com/coreos/etcd/metrics" 24 "github.com/coreos/etcd/pkg/btrfs" 25 "github.com/coreos/etcd/store" 26 ) 27 28 const ( 29 // ThresholdMonitorTimeout is the time between log notifications that the 30 // Raft heartbeat is too close to the election timeout. 31 ThresholdMonitorTimeout = 5 * time.Second 32 33 // ActiveMonitorTimeout is the time between checks on the active size of 34 // the cluster. If the active size is different than the actual size then 35 // etcd attempts to promote/demote to bring it to the correct number. 36 ActiveMonitorTimeout = 1 * time.Second 37 38 // PeerActivityMonitorTimeout is the time between checks for dead nodes in 39 // the cluster. 40 PeerActivityMonitorTimeout = 1 * time.Second 41 ) 42 43 const ( 44 peerModeFlag = 0 45 standbyModeFlag = 1 46 ) 47 48 type PeerServerConfig struct { 49 Name string 50 Scheme string 51 URL string 52 SnapshotCount int 53 RetryTimes int 54 RetryInterval float64 55 } 56 57 type PeerServer struct { 58 Config PeerServerConfig 59 clusterConfig *ClusterConfig 60 raftServer raft.Server 61 server *Server 62 joinIndex uint64 63 followersStats *raftFollowersStats 64 serverStats *raftServerStats 65 registry *Registry 66 store store.Store 67 snapConf *snapshotConf 68 mode Mode 69 70 closeChan chan bool 71 timeoutThresholdChan chan interface{} 72 73 standbyPeerURL string 74 standbyClientURL string 75 76 metrics *metrics.Bucket 77 sync.Mutex 78 } 79 80 // TODO: find a good policy to do snapshot 81 type snapshotConf struct { 82 // Etcd will check if snapshot is need every checkingInterval 83 checkingInterval time.Duration 84 85 // The index when the last snapshot happened 86 lastIndex uint64 87 88 // If the incremental number of index since the last snapshot 89 // exceeds the snapshot Threshold, etcd will do a snapshot 90 snapshotThr uint64 91 } 92 93 func NewPeerServer(psConfig PeerServerConfig, registry *Registry, store store.Store, mb *metrics.Bucket, followersStats *raftFollowersStats, serverStats *raftServerStats) *PeerServer { 94 s := &PeerServer{ 95 Config: psConfig, 96 clusterConfig: NewClusterConfig(), 97 registry: registry, 98 store: store, 99 followersStats: followersStats, 100 serverStats: serverStats, 101 102 timeoutThresholdChan: make(chan interface{}, 1), 103 104 metrics: mb, 105 } 106 107 return s 108 } 109 110 func (s *PeerServer) SetRaftServer(raftServer raft.Server) { 111 s.snapConf = &snapshotConf{ 112 checkingInterval: time.Second * 3, 113 // this is not accurate, we will update raft to provide an api 114 lastIndex: raftServer.CommitIndex(), 115 snapshotThr: uint64(s.Config.SnapshotCount), 116 } 117 118 raftServer.AddEventListener(raft.StateChangeEventType, s.raftEventLogger) 119 raftServer.AddEventListener(raft.LeaderChangeEventType, s.raftEventLogger) 120 raftServer.AddEventListener(raft.TermChangeEventType, s.raftEventLogger) 121 raftServer.AddEventListener(raft.AddPeerEventType, s.raftEventLogger) 122 raftServer.AddEventListener(raft.RemovePeerEventType, s.raftEventLogger) 123 raftServer.AddEventListener(raft.HeartbeatIntervalEventType, s.raftEventLogger) 124 raftServer.AddEventListener(raft.ElectionTimeoutThresholdEventType, s.raftEventLogger) 125 126 raftServer.AddEventListener(raft.HeartbeatEventType, s.recordMetricEvent) 127 128 s.raftServer = raftServer 129 } 130 131 // Mode retrieves the current mode of the server. 132 func (s *PeerServer) Mode() Mode { 133 return s.mode 134 } 135 136 // SetMode updates the current mode of the server. 137 // Switching to a peer mode will start the Raft server. 138 // Switching to a standby mode will stop the Raft server. 139 func (s *PeerServer) setMode(mode Mode) { 140 s.mode = mode 141 142 switch mode { 143 case PeerMode: 144 if !s.raftServer.Running() { 145 s.raftServer.Start() 146 } 147 case StandbyMode: 148 if s.raftServer.Running() { 149 s.raftServer.Stop() 150 } 151 } 152 } 153 154 // ClusterConfig retrieves the current cluster configuration. 155 func (s *PeerServer) ClusterConfig() *ClusterConfig { 156 return s.clusterConfig 157 } 158 159 // SetClusterConfig updates the current cluster configuration. 160 // Adjusting the active size will cause the PeerServer to demote peers or 161 // promote standbys to match the new size. 162 func (s *PeerServer) SetClusterConfig(c *ClusterConfig) { 163 // Set minimums. 164 if c.ActiveSize < MinActiveSize { 165 c.ActiveSize = MinActiveSize 166 } 167 if c.PromoteDelay < MinPromoteDelay { 168 c.PromoteDelay = MinPromoteDelay 169 } 170 171 s.clusterConfig = c 172 } 173 174 // Try all possible ways to find clusters to join 175 // Include log data in -data-dir, -discovery and -peers 176 // 177 // Peer discovery follows this order: 178 // 1. previous peers in -data-dir 179 // 2. -discovery 180 // 3. -peers 181 // 182 // TODO(yichengq): RaftServer should be started as late as possible. 183 // Current implementation to start it is not that good, 184 // and should be refactored later. 185 func (s *PeerServer) findCluster(discoverURL string, peers []string) { 186 name := s.Config.Name 187 isNewNode := s.raftServer.IsLogEmpty() 188 189 // Try its best to find possible peers, and connect with them. 190 if !isNewNode { 191 // It is not allowed to join the cluster with existing peer address 192 // This prevents old node joining with different name by mistake. 193 if !s.checkPeerAddressNonconflict() { 194 log.Fatalf("%v is not allowed to join the cluster with existing URL %v", s.Config.Name, s.Config.URL) 195 } 196 197 // Take old nodes into account. 198 allPeers := s.getKnownPeers() 199 // Discover registered peers. 200 // TODO(yichengq): It may mess up discoverURL if this is 201 // set wrong by mistake. This may need to refactor discovery 202 // module. Fix it later. 203 if discoverURL != "" { 204 discoverPeers, _ := s.handleDiscovery(discoverURL) 205 allPeers = append(allPeers, discoverPeers...) 206 } 207 allPeers = append(allPeers, peers...) 208 allPeers = s.removeSelfFromList(allPeers) 209 210 // If there is possible peer list, use it to find cluster. 211 if len(allPeers) > 0 { 212 // TODO(yichengq): joinCluster may fail if there's no leader for 213 // current cluster. It should wait if the cluster is under 214 // leader election, or the node with changed IP cannot join 215 // the cluster then. 216 if err := s.startAsFollower(allPeers, 1); err == nil { 217 log.Debugf("%s joins to the previous cluster %v", name, allPeers) 218 return 219 } 220 221 log.Warnf("%s cannot connect to previous cluster %v", name, allPeers) 222 } 223 224 // TODO(yichengq): Think about the action that should be done 225 // if it cannot connect any of the previous known node. 226 s.raftServer.Start() 227 log.Debugf("%s is restarting the cluster %v", name, allPeers) 228 return 229 } 230 231 // Attempt cluster discovery 232 if discoverURL != "" { 233 discoverPeers, discoverErr := s.handleDiscovery(discoverURL) 234 // It is registered in discover url 235 if discoverErr == nil { 236 // start as a leader in a new cluster 237 if len(discoverPeers) == 0 { 238 log.Debugf("%s is starting a new cluster via discover service", name) 239 s.startAsLeader() 240 } else { 241 log.Debugf("%s is joining a cluster %v via discover service", name, discoverPeers) 242 if err := s.startAsFollower(discoverPeers, s.Config.RetryTimes); err != nil { 243 log.Fatal(err) 244 } 245 } 246 return 247 } 248 log.Warnf("%s failed to connect discovery service[%v]: %v", name, discoverURL, discoverErr) 249 250 if len(peers) == 0 { 251 log.Fatalf("%s, the new leader, must register itself to discovery service as required", name) 252 } 253 } 254 255 if len(peers) > 0 { 256 if err := s.startAsFollower(peers, s.Config.RetryTimes); err != nil { 257 log.Fatalf("%s cannot connect to existing cluster %v", name, peers) 258 } 259 return 260 } 261 262 log.Infof("%s is starting a new cluster.", s.Config.Name) 263 s.startAsLeader() 264 return 265 } 266 267 // Start the raft server 268 func (s *PeerServer) Start(snapshot bool, discoverURL string, peers []string) error { 269 s.Lock() 270 defer s.Unlock() 271 272 // LoadSnapshot 273 if snapshot { 274 err := s.raftServer.LoadSnapshot() 275 276 if err == nil { 277 log.Debugf("%s finished load snapshot", s.Config.Name) 278 } else { 279 log.Debug(err) 280 } 281 } 282 283 s.raftServer.Init() 284 285 // Set NOCOW for data directory in btrfs 286 if btrfs.IsBtrfs(s.raftServer.LogPath()) { 287 if err := btrfs.SetNOCOWFile(s.raftServer.LogPath()); err != nil { 288 log.Warnf("Failed setting NOCOW: %v", err) 289 } 290 } 291 292 s.findCluster(discoverURL, peers) 293 294 s.closeChan = make(chan bool) 295 296 go s.monitorSync() 297 go s.monitorTimeoutThreshold(s.closeChan) 298 go s.monitorActiveSize(s.closeChan) 299 go s.monitorPeerActivity(s.closeChan) 300 301 // open the snapshot 302 if snapshot { 303 go s.monitorSnapshot() 304 } 305 306 return nil 307 } 308 309 func (s *PeerServer) Stop() { 310 s.Lock() 311 defer s.Unlock() 312 313 if s.closeChan != nil { 314 close(s.closeChan) 315 s.closeChan = nil 316 } 317 s.raftServer.Stop() 318 } 319 320 func (s *PeerServer) HTTPHandler() http.Handler { 321 router := mux.NewRouter() 322 323 // internal commands 324 router.HandleFunc("/name", s.NameHttpHandler) 325 router.HandleFunc("/version", s.VersionHttpHandler) 326 router.HandleFunc("/version/{version:[0-9]+}/check", s.VersionCheckHttpHandler) 327 router.HandleFunc("/upgrade", s.UpgradeHttpHandler) 328 router.HandleFunc("/join", s.JoinHttpHandler) 329 router.HandleFunc("/promote", s.PromoteHttpHandler).Methods("POST") 330 router.HandleFunc("/remove/{name:.+}", s.RemoveHttpHandler) 331 router.HandleFunc("/vote", s.VoteHttpHandler) 332 router.HandleFunc("/log", s.GetLogHttpHandler) 333 router.HandleFunc("/log/append", s.AppendEntriesHttpHandler) 334 router.HandleFunc("/snapshot", s.SnapshotHttpHandler) 335 router.HandleFunc("/snapshotRecovery", s.SnapshotRecoveryHttpHandler) 336 router.HandleFunc("/etcdURL", s.EtcdURLHttpHandler) 337 338 router.HandleFunc("/v2/admin/config", s.getClusterConfigHttpHandler).Methods("GET") 339 router.HandleFunc("/v2/admin/config", s.setClusterConfigHttpHandler).Methods("PUT") 340 router.HandleFunc("/v2/admin/machines", s.getMachinesHttpHandler).Methods("GET") 341 router.HandleFunc("/v2/admin/machines/{name}", s.getMachineHttpHandler).Methods("GET") 342 router.HandleFunc("/v2/admin/machines/{name}", s.addMachineHttpHandler).Methods("PUT") 343 router.HandleFunc("/v2/admin/machines/{name}", s.removeMachineHttpHandler).Methods("DELETE") 344 345 return router 346 } 347 348 // Retrieves the underlying Raft server. 349 func (s *PeerServer) RaftServer() raft.Server { 350 return s.raftServer 351 } 352 353 // Associates the client server with the peer server. 354 func (s *PeerServer) SetServer(server *Server) { 355 s.server = server 356 } 357 358 func (s *PeerServer) startAsLeader() { 359 s.raftServer.Start() 360 // leader need to join self as a peer 361 for { 362 c := &JoinCommandV1{ 363 MinVersion: store.MinVersion(), 364 MaxVersion: store.MaxVersion(), 365 Name: s.raftServer.Name(), 366 RaftURL: s.Config.URL, 367 EtcdURL: s.server.URL(), 368 } 369 _, err := s.raftServer.Do(c) 370 if err == nil { 371 break 372 } 373 } 374 log.Debugf("%s start as a leader", s.Config.Name) 375 } 376 377 func (s *PeerServer) startAsFollower(cluster []string, retryTimes int) error { 378 // start as a follower in a existing cluster 379 for i := 0; ; i++ { 380 ok := s.joinCluster(cluster) 381 if ok { 382 break 383 } 384 if i == retryTimes-1 { 385 return fmt.Errorf("Cannot join the cluster via given peers after %x retries", s.Config.RetryTimes) 386 } 387 log.Warnf("%v is unable to join the cluster using any of the peers %v at %dth time. Retrying in %.1f seconds", s.Config.Name, cluster, i, s.Config.RetryInterval) 388 time.Sleep(time.Second * time.Duration(s.Config.RetryInterval)) 389 } 390 391 s.raftServer.Start() 392 return nil 393 } 394 395 // getVersion fetches the peer version of a cluster. 396 func getVersion(t *transporter, versionURL url.URL) (int, error) { 397 resp, _, err := t.Get(versionURL.String()) 398 if err != nil { 399 return 0, err 400 } 401 defer resp.Body.Close() 402 403 body, err := ioutil.ReadAll(resp.Body) 404 if err != nil { 405 return 0, err 406 } 407 408 // Parse version number. 409 version, _ := strconv.Atoi(string(body)) 410 return version, nil 411 } 412 413 // Upgradable checks whether all peers in a cluster support an upgrade to the next store version. 414 func (s *PeerServer) Upgradable() error { 415 nextVersion := s.store.Version() + 1 416 for _, peerURL := range s.registry.PeerURLs(s.raftServer.Leader(), s.Config.Name) { 417 u, err := url.Parse(peerURL) 418 if err != nil { 419 return fmt.Errorf("PeerServer: Cannot parse URL: '%s' (%s)", peerURL, err) 420 } 421 422 t, _ := s.raftServer.Transporter().(*transporter) 423 checkURL := (&url.URL{Host: u.Host, Scheme: s.Config.Scheme, Path: fmt.Sprintf("/version/%d/check", nextVersion)}).String() 424 resp, _, err := t.Get(checkURL) 425 if err != nil { 426 return fmt.Errorf("PeerServer: Cannot check version compatibility: %s", u.Host) 427 } 428 if resp.StatusCode != 200 { 429 return fmt.Errorf("PeerServer: Version %d is not compatible with peer: %s", nextVersion, u.Host) 430 } 431 } 432 433 return nil 434 } 435 436 // checkPeerAddressNonconflict checks whether the peer address has existed with different name. 437 func (s *PeerServer) checkPeerAddressNonconflict() bool { 438 // there exists the (name, peer address) pair 439 if peerURL, ok := s.registry.PeerURL(s.Config.Name); ok { 440 if peerURL == s.Config.URL { 441 return true 442 } 443 } 444 445 // check all existing peer addresses 446 peerURLs := s.registry.PeerURLs(s.raftServer.Leader(), s.Config.Name) 447 for _, peerURL := range peerURLs { 448 if peerURL == s.Config.URL { 449 return false 450 } 451 } 452 return true 453 } 454 455 // Helper function to do discovery and return results in expected format 456 func (s *PeerServer) handleDiscovery(discoverURL string) (peers []string, err error) { 457 peers, err = discovery.Do(discoverURL, s.Config.Name, s.Config.URL) 458 459 // Warn about errors coming from discovery, this isn't fatal 460 // since the user might have provided a peer list elsewhere, 461 // or there is some log in data dir. 462 if err != nil { 463 log.Warnf("Discovery encountered an error: %v", err) 464 return 465 } 466 467 for i := range peers { 468 // Strip the scheme off of the peer if it has one 469 // TODO(bp): clean this up! 470 purl, err := url.Parse(peers[i]) 471 if err == nil { 472 peers[i] = purl.Host 473 } 474 } 475 476 log.Infof("Discovery fetched back peer list: %v", peers) 477 478 return 479 } 480 481 // getKnownPeers gets the previous peers from log 482 func (s *PeerServer) getKnownPeers() []string { 483 peers := s.registry.PeerURLs(s.raftServer.Leader(), s.Config.Name) 484 log.Infof("Peer URLs in log: %s / %s (%s)", s.raftServer.Leader(), s.Config.Name, strings.Join(peers, ",")) 485 486 for i := range peers { 487 u, err := url.Parse(peers[i]) 488 if err != nil { 489 log.Debug("getPrevPeers cannot parse url %v", peers[i]) 490 } 491 peers[i] = u.Host 492 } 493 return peers 494 } 495 496 // removeSelfFromList removes url of the peerServer from the peer list 497 func (s *PeerServer) removeSelfFromList(peers []string) []string { 498 // Remove its own peer address from the peer list to join 499 u, err := url.Parse(s.Config.URL) 500 if err != nil { 501 log.Fatalf("removeSelfFromList cannot parse peer address %v", s.Config.URL) 502 } 503 newPeers := make([]string, 0) 504 for _, v := range peers { 505 if v != u.Host { 506 newPeers = append(newPeers, v) 507 } 508 } 509 return newPeers 510 } 511 512 func (s *PeerServer) joinCluster(cluster []string) bool { 513 for _, peer := range cluster { 514 if len(peer) == 0 { 515 continue 516 } 517 518 err := s.joinByPeer(s.raftServer, peer, s.Config.Scheme) 519 if err == nil { 520 log.Debugf("%s joined the cluster via peer %s", s.Config.Name, peer) 521 return true 522 523 } 524 525 if _, ok := err.(etcdErr.Error); ok { 526 log.Fatal(err) 527 } 528 529 log.Warnf("Attempt to join via %s failed: %s", peer, err) 530 } 531 532 return false 533 } 534 535 // Send join requests to peer. 536 func (s *PeerServer) joinByPeer(server raft.Server, peer string, scheme string) error { 537 // t must be ok 538 t, _ := server.Transporter().(*transporter) 539 540 // Our version must match the leaders version 541 versionURL := url.URL{Host: peer, Scheme: scheme, Path: "/version"} 542 version, err := getVersion(t, versionURL) 543 if err != nil { 544 return fmt.Errorf("Error during join version check: %v", err) 545 } 546 if version < store.MinVersion() || version > store.MaxVersion() { 547 return fmt.Errorf("Unable to join: cluster version is %d; version compatibility is %d - %d", version, store.MinVersion(), store.MaxVersion()) 548 } 549 550 var b bytes.Buffer 551 c := &JoinCommandV2{ 552 MinVersion: store.MinVersion(), 553 MaxVersion: store.MaxVersion(), 554 Name: server.Name(), 555 PeerURL: s.Config.URL, 556 ClientURL: s.server.URL(), 557 } 558 json.NewEncoder(&b).Encode(c) 559 560 joinURL := url.URL{Host: peer, Scheme: scheme, Path: "/v2/admin/machines/" + server.Name()} 561 log.Infof("Send Join Request to %s", joinURL.String()) 562 563 req, _ := http.NewRequest("PUT", joinURL.String(), &b) 564 resp, err := t.client.Do(req) 565 566 for { 567 if err != nil { 568 return fmt.Errorf("Unable to join: %v", err) 569 } 570 if resp != nil { 571 defer resp.Body.Close() 572 573 log.Infof("»»»» %d", resp.StatusCode) 574 if resp.StatusCode == http.StatusOK { 575 var msg joinMessageV2 576 if err := json.NewDecoder(resp.Body).Decode(&msg); err != nil { 577 log.Debugf("Error reading join response: %v", err) 578 return err 579 } 580 s.joinIndex = msg.CommitIndex 581 s.setMode(msg.Mode) 582 583 if msg.Mode == StandbyMode { 584 s.standbyClientURL = resp.Header.Get("X-Leader-Client-URL") 585 s.standbyPeerURL = resp.Header.Get("X-Leader-Peer-URL") 586 } 587 588 return nil 589 } 590 if resp.StatusCode == http.StatusTemporaryRedirect { 591 address := resp.Header.Get("Location") 592 log.Debugf("Send Join Request to %s", address) 593 c := &JoinCommandV2{ 594 MinVersion: store.MinVersion(), 595 MaxVersion: store.MaxVersion(), 596 Name: server.Name(), 597 PeerURL: s.Config.URL, 598 ClientURL: s.server.URL(), 599 } 600 json.NewEncoder(&b).Encode(c) 601 resp, _, err = t.Put(address, &b) 602 603 } else if resp.StatusCode == http.StatusBadRequest { 604 log.Debug("Reach max number peers in the cluster") 605 decoder := json.NewDecoder(resp.Body) 606 err := &etcdErr.Error{} 607 decoder.Decode(err) 608 return *err 609 } else { 610 return fmt.Errorf("Unable to join") 611 } 612 } 613 614 } 615 } 616 617 func (s *PeerServer) Stats() []byte { 618 s.serverStats.LeaderInfo.Uptime = time.Now().Sub(s.serverStats.LeaderInfo.startTime).String() 619 620 // TODO: register state listener to raft to change this field 621 // rather than compare the state each time Stats() is called. 622 if s.RaftServer().State() == raft.Leader { 623 s.serverStats.LeaderInfo.Name = s.RaftServer().Name() 624 } 625 626 queue := s.serverStats.sendRateQueue 627 628 s.serverStats.SendingPkgRate, s.serverStats.SendingBandwidthRate = queue.Rate() 629 630 queue = s.serverStats.recvRateQueue 631 632 s.serverStats.RecvingPkgRate, s.serverStats.RecvingBandwidthRate = queue.Rate() 633 634 b, _ := json.Marshal(s.serverStats) 635 636 return b 637 } 638 639 func (s *PeerServer) PeerStats() []byte { 640 if s.raftServer.State() == raft.Leader { 641 b, _ := json.Marshal(s.followersStats) 642 return b 643 } 644 return nil 645 } 646 647 // raftEventLogger converts events from the Raft server into log messages. 648 func (s *PeerServer) raftEventLogger(event raft.Event) { 649 value := event.Value() 650 prevValue := event.PrevValue() 651 if value == nil { 652 value = "<nil>" 653 } 654 if prevValue == nil { 655 prevValue = "<nil>" 656 } 657 658 switch event.Type() { 659 case raft.StateChangeEventType: 660 log.Infof("%s: state changed from '%v' to '%v'.", s.Config.Name, prevValue, value) 661 case raft.TermChangeEventType: 662 log.Infof("%s: term #%v started.", s.Config.Name, value) 663 case raft.LeaderChangeEventType: 664 log.Infof("%s: leader changed from '%v' to '%v'.", s.Config.Name, prevValue, value) 665 case raft.AddPeerEventType: 666 log.Infof("%s: peer added: '%v'", s.Config.Name, value) 667 case raft.RemovePeerEventType: 668 log.Infof("%s: peer removed: '%v'", s.Config.Name, value) 669 case raft.HeartbeatIntervalEventType: 670 var name = "<unknown>" 671 if peer, ok := value.(*raft.Peer); ok { 672 name = peer.Name 673 } 674 log.Infof("%s: warning: heartbeat timed out: '%v'", s.Config.Name, name) 675 case raft.ElectionTimeoutThresholdEventType: 676 select { 677 case s.timeoutThresholdChan <- value: 678 default: 679 } 680 681 } 682 } 683 684 func (s *PeerServer) recordMetricEvent(event raft.Event) { 685 name := fmt.Sprintf("raft.event.%s", event.Type()) 686 value := event.Value().(time.Duration) 687 (*s.metrics).Timer(name).Update(value) 688 } 689 690 // logSnapshot logs about the snapshot that was taken. 691 func (s *PeerServer) logSnapshot(err error, currentIndex, count uint64) { 692 info := fmt.Sprintf("%s: snapshot of %d events at index %d", s.Config.Name, count, currentIndex) 693 694 if err != nil { 695 log.Infof("%s attempted and failed: %v", info, err) 696 } else { 697 log.Infof("%s completed", info) 698 } 699 } 700 701 func (s *PeerServer) monitorSnapshot() { 702 for { 703 time.Sleep(s.snapConf.checkingInterval) 704 currentIndex := s.RaftServer().CommitIndex() 705 count := currentIndex - s.snapConf.lastIndex 706 if uint64(count) > s.snapConf.snapshotThr { 707 err := s.raftServer.TakeSnapshot() 708 s.logSnapshot(err, currentIndex, count) 709 s.snapConf.lastIndex = currentIndex 710 } 711 } 712 } 713 714 func (s *PeerServer) monitorSync() { 715 ticker := time.Tick(time.Millisecond * 500) 716 for { 717 select { 718 case now := <-ticker: 719 if s.raftServer.State() == raft.Leader { 720 s.raftServer.Do(s.store.CommandFactory().CreateSyncCommand(now)) 721 } 722 } 723 } 724 } 725 726 // monitorTimeoutThreshold groups timeout threshold events together and prints 727 // them as a single log line. 728 func (s *PeerServer) monitorTimeoutThreshold(closeChan chan bool) { 729 for { 730 select { 731 case value := <-s.timeoutThresholdChan: 732 log.Infof("%s: warning: heartbeat near election timeout: %v", s.Config.Name, value) 733 case <-closeChan: 734 return 735 } 736 737 time.Sleep(ThresholdMonitorTimeout) 738 } 739 } 740 741 // monitorActiveSize has the leader periodically check the status of cluster 742 // nodes and swaps them out for standbys as needed. 743 func (s *PeerServer) monitorActiveSize(closeChan chan bool) { 744 for { 745 select { 746 case <-time.After(ActiveMonitorTimeout): 747 case <-closeChan: 748 return 749 } 750 751 // Ignore while this peer is not a leader. 752 if s.raftServer.State() != raft.Leader { 753 continue 754 } 755 756 // Retrieve target active size and actual active size. 757 activeSize := s.ClusterConfig().ActiveSize 758 peerCount := s.registry.PeerCount() 759 standbys := s.registry.Standbys() 760 peers := s.registry.Peers() 761 if index := sort.SearchStrings(peers, s.Config.Name); index < len(peers) && peers[index] == s.Config.Name { 762 peers = append(peers[:index], peers[index+1:]...) 763 } 764 765 // If we have more active nodes than we should then demote. 766 if peerCount > activeSize { 767 peer := peers[rand.Intn(len(peers))] 768 log.Infof("%s: demoting: %v", s.Config.Name, peer) 769 if _, err := s.raftServer.Do(&DemoteCommand{Name: peer}); err != nil { 770 log.Infof("%s: warning: demotion error: %v", s.Config.Name, err) 771 } 772 continue 773 } 774 775 // If we don't have enough active nodes then try to promote a standby. 776 if peerCount < activeSize && len(standbys) > 0 { 777 loop: 778 for _, i := range rand.Perm(len(standbys)) { 779 standby := standbys[i] 780 standbyPeerURL, _ := s.registry.StandbyPeerURL(standby) 781 log.Infof("%s: attempting to promote: %v (%s)", s.Config.Name, standby, standbyPeerURL) 782 783 // Notify standby to promote itself. 784 client := &http.Client{ 785 Transport: &http.Transport{ 786 DisableKeepAlives: false, 787 ResponseHeaderTimeout: ActiveMonitorTimeout, 788 }, 789 } 790 resp, err := client.Post(fmt.Sprintf("%s/promote", standbyPeerURL), "application/json", nil) 791 if err != nil { 792 log.Infof("%s: warning: promotion error: %v", s.Config.Name, err) 793 continue 794 } else if resp.StatusCode != http.StatusOK { 795 log.Infof("%s: warning: promotion failure: %v", s.Config.Name, resp.StatusCode) 796 continue 797 } 798 break loop 799 } 800 } 801 } 802 } 803 804 // monitorPeerActivity has the leader periodically for dead nodes and demotes them. 805 func (s *PeerServer) monitorPeerActivity(closeChan chan bool) { 806 for { 807 select { 808 case <-time.After(PeerActivityMonitorTimeout): 809 case <-closeChan: 810 return 811 } 812 813 // Ignore while this peer is not a leader. 814 if s.raftServer.State() != raft.Leader { 815 continue 816 } 817 818 // Check last activity for all peers. 819 now := time.Now() 820 promoteDelay := time.Duration(s.ClusterConfig().PromoteDelay) * time.Second 821 peers := s.raftServer.Peers() 822 for _, peer := range peers { 823 // If the last response from the peer is longer than the promote delay 824 // then automatically demote the peer. 825 if !peer.LastActivity().IsZero() && now.Sub(peer.LastActivity()) > promoteDelay { 826 log.Infof("%s: demoting node: %v; last activity %v ago", s.Config.Name, peer.Name, now.Sub(peer.LastActivity())) 827 if _, err := s.raftServer.Do(&DemoteCommand{Name: peer.Name}); err != nil { 828 log.Infof("%s: warning: autodemotion error: %v", s.Config.Name, err) 829 } 830 continue 831 } 832 } 833 } 834 } 835 836 // Mode represents whether the server is an active peer or if the server is 837 // simply acting as a standby. 838 type Mode string 839 840 const ( 841 // PeerMode is when the server is an active node in Raft. 842 PeerMode = Mode("peer") 843 844 // StandbyMode is when the server is an inactive, request-forwarding node. 845 StandbyMode = Mode("standby") 846 )