diff --git a/.changelog/11376.txt b/.changelog/11376.txt new file mode 100644 index 000000000000..f3924d102aca --- /dev/null +++ b/.changelog/11376.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +try to transfer leadership to another server when leaving. +``` diff --git a/agent/consul/server.go b/agent/consul/server.go index 4164970f1fb5..d2a9088c8b6a 100644 --- a/agent/consul/server.go +++ b/agent/consul/server.go @@ -17,6 +17,8 @@ import ( "sync/atomic" "time" + "github.com/hashicorp/go-version" + "github.com/armon/go-metrics" connlimit "github.com/hashicorp/go-connlimit" "github.com/hashicorp/go-hclog" @@ -91,6 +93,8 @@ const ( // from Serf with the Catalog. If this is exhausted we will drop updates, // and wait for a periodic reconcile. reconcileChSize = 256 + + LeaderTransferMinVersion = "1.6.0" ) const ( @@ -1003,8 +1007,28 @@ func (s *Server) Leave() error { // removed for some sane period of time. isLeader := s.IsLeader() if isLeader && numPeers > 1 { - if err := s.autopilot.RemoveServer(raft.ServerID(s.config.NodeID)); err != nil { - s.logger.Error("failed to remove ourself as a Raft peer", "error", err) + leadershipTransferVersion := version.Must(version.NewVersion(LeaderTransferMinVersion)) + removeServer := false + if ok, _ := ServersInDCMeetMinimumVersion(s, s.config.Datacenter, leadershipTransferVersion); !ok { + // Transfer leadership to another node then leave the cluster + future := s.raft.LeadershipTransfer() + if err := future.Error(); err != nil { + s.logger.Error("failed to transfer leadership, removing the server", "error", err) + // leadership transfer failed, fallback to removing the server from raft + removeServer = true + } else { + // we are not leader anymore, continue the flow to leave as follower + isLeader = false + } + } else { + // Leadership transfer is not available in the current version, fallback to removing the server from raft + removeServer = true + } + if removeServer { + future := s.raft.RemoveServer(raft.ServerID(s.config.NodeID), 0, 0) + if err := future.Error(); err != nil { + s.logger.Error("failed to remove ourself as raft peer", "error", err) + } } }