Documentation
¶
Overview ¶
Package cluster is a leader election mechanism that follows the raft protocol https://raft.github.io/ using http as the transport, up until the use of replicated log. For a more complete raft implementation use https://godoc.org/github.com/coreos/etcd/raft or https://github.com/hashicorp/raft
Leader Election ¶
The process begins with all nodes in FOLLOWER state and waiting for an election timeout. This timeout is recommended to be randomized between 150ms and 300ms. In order to reduce the amount of traffic this will be increased to ElectionTickRange.
After the election timeout, the FOLLOWER becomes a CANDIDATE and begins an election term. It starts by voting for itself and then sends out a RequestVote to all other nodes.
If the receiving nodes haven't voted yet, they will then vote for the candidate with a successful request. The current node will reset it's election timeout and when a candidate has a majority vote it will become a LEADER.
At this point the LEADER will begin sending an AppendEntries request to all other nodes at a rate specified by the heartbeat timeout. The heartbeat timeout should be shorter than the election timeout, preferably by a factor of 10. Followers respond to the AppendEntries, and this term will continue until a follower stops receiving a heartbeat and becomes a candidate.
There is a case where two nodes can be come candidates at the same time, which is referred to a split vote. In this case two nodes will start an election for the same term and each reaches a single follower node before the other. In this case each candidate will have two votes with no more available for the term and no majority. A new election will happen and finally a candidate will become a LEADER. This scenario can happen with an even number of nodes.
Index ¶
- Constants
- Variables
- type AppendEntriesRequest
- type ApplyFunc
- type Cluster
- func (c *Cluster) AppendEntriesHandler() func(w http.ResponseWriter, r *http.Request)
- func (c *Cluster) AppendEntriesRequest() (int, error)
- func (c *Cluster) BroadcastRequest(peers []string, method, route string, body []byte, timeoutMS int) []*http.Response
- func (c *Cluster) IDHandler() func(w http.ResponseWriter, r *http.Request)
- func (c *Cluster) RequestVoteHandler() func(w http.ResponseWriter, r *http.Request)
- func (c *Cluster) RequestVoteRequest() (int, error)
- func (c *Cluster) Routes() []*Route
- func (c *Cluster) Start() error
- func (c *Cluster) StatusHandler() func(w http.ResponseWriter, r *http.Request)
- func (c *Cluster) StepDownHandler() func(w http.ResponseWriter, r *http.Request)
- func (c *Cluster) Stop() error
- type DiscardLogger
- func (d *DiscardLogger) Debugf(format string, v ...interface{})
- func (d *DiscardLogger) Errorf(format string, v ...interface{})
- func (d *DiscardLogger) Errorln(v ...interface{})
- func (d *DiscardLogger) Infof(format string, v ...interface{})
- func (d *DiscardLogger) Printf(format string, v ...interface{})
- func (d *DiscardLogger) Println(v ...interface{})
- func (d *DiscardLogger) Tracef(format string, v ...interface{})
- type LogLogger
- func (d *LogLogger) Debugf(format string, v ...interface{})
- func (d *LogLogger) Errorf(format string, v ...interface{})
- func (d *LogLogger) Errorln(v ...interface{})
- func (d *LogLogger) Infof(format string, v ...interface{})
- func (d *LogLogger) Printf(format string, v ...interface{})
- func (d *LogLogger) Println(v ...interface{})
- func (d *LogLogger) Tracef(format string, v ...interface{})
- type Logger
- type Route
- type State
- func (s *State) AppendEntriesEvent(event ...*AppendEntriesRequest) chan *AppendEntriesRequest
- func (s *State) ElectionTick() <-chan time.Time
- func (s *State) HeartbeatReset(reset ...bool) <-chan struct{}
- func (s *State) HeartbeatTick() <-chan time.Time
- func (s *State) HeartbeatTickRandom() <-chan time.Time
- func (s *State) ID() uint32
- func (s *State) LeaderID(id ...uint32) uint32
- func (s *State) State(state ...int) int
- func (s *State) StateChanged() chan int
- func (s *State) StateString(state int) string
- func (s *State) StepDown(term int)
- func (s *State) String() string
- func (s *State) Term(term ...int) int
- func (s *State) VotedFor(votedFor ...uint32) uint32
- type Status
Constants ¶
const ( // RouteAppendEntries for append entries requests. RouteAppendEntries = "/appendentries" // RouteID for id requests. RouteID = "/id" // RouteRequestVote for request vote requests. RouteRequestVote = "/requestvote" // RouteStatus will render the current nodes full state. RouteStatus = "/status" // RouteStepDown to force a node to step down. RouteStepDown = "/stepdown" )
const ( // UnknownLeaderID is set when a new election is in progress. UnknownLeaderID = uint32(0) // NoVote is set to represent the node has not voted. NoVote = uint32(0) // StateCandidate represents the raft candidate state StateCandidate = iota // StateFollower represents the raft follower state StateFollower // StateLeader represents the raft leader state StateLeader )
Variables ¶
var ( // DefaultOnLeader is a no op function to execute when a node becomes a leader. DefaultOnLeader = func() error { return nil } // DefaultOnFollower is a no op function to execute when a node becomes a follower. DefaultOnFollower = func() error { return nil } // DefaultRoutePrefix is what is prefixed for the cluster routes. (/cluster) DefaultRoutePrefix = "/cluster" // ErrTooFewVotes happens on a RequestVote when the candidate receives less than the // majority of votes. ErrTooFewVotes = errors.New("too few votes") // ErrNewElectionTerm if during RequestVote there is a higher term found. ErrNewElectionTerm = errors.New("newer election term") // ErrLeader is returned when an operation can't be completed on a // leader node. ErrLeader = errors.New("node is the leader") // ErrNotLeader is returned when an operation can't be completed on a // follower or candidate node. ErrNotLeader = errors.New("node is not the leader") )
var ( // DefaultElectionTickRange will set the range of numbers for the election timeout. For example // a value of 1500 will first hash the input Addr to a number from 0 to 1500 and then // add that 1500 to give a result between 1500 and 3000 DefaultElectionTickRange = 4000 // DefaultHeartbeatTickRange will set the range of numbers for the heartbeat timeout. DefaultHeartbeatTickRange = 1000 )
Functions ¶
This section is empty.
Types ¶
type AppendEntriesRequest ¶
type AppendEntriesRequest struct { Term int `json:"term"` LeaderID uint32 `json:"leader_id"` NodeID uint32 `json:"node_id"` }
AppendEntriesRequest represents AppendEntries requests. Replication logging is ignored.
type Cluster ¶
type Cluster struct { // Nodes is a list of all nodes for consensus. Nodes []string // OnLeader is an optional function to execute when becoming a leader. OnLeader func() error // OnFollower is an optional function to execute when becoming a follower. OnFollower func() error // State for holding the raft state. State *State // contains filtered or unexported fields }
Cluster manages the raft FSM and executes OnLeader and OnFollower events.
func (*Cluster) AppendEntriesHandler ¶
func (c *Cluster) AppendEntriesHandler() func(w http.ResponseWriter, r *http.Request)
AppendEntriesHandler (POST) handles append entry requests
func (*Cluster) AppendEntriesRequest ¶
AppendEntriesRequest will broadcast an AppendEntries request to peers. In the raft protocol this deals with appending and processing the replication log, however for leader election this is unused. It returns the current term with any errors.
func (*Cluster) BroadcastRequest ¶
func (c *Cluster) BroadcastRequest(peers []string, method, route string, body []byte, timeoutMS int) []*http.Response
BroadcastRequest will send a request to all other nodes in the system.
func (*Cluster) IDHandler ¶
func (c *Cluster) IDHandler() func(w http.ResponseWriter, r *http.Request)
IDHandler (GET) returns the nodes id.
func (*Cluster) RequestVoteHandler ¶
func (c *Cluster) RequestVoteHandler() func(w http.ResponseWriter, r *http.Request)
RequestVoteHandler (POST) handles requests for votes
func (*Cluster) RequestVoteRequest ¶
RequestVoteRequest will broadcast a request for votes in order to update node to either a follower or leader. If this candidate becomes leader error will return nil. The latest known term is always returned (this could be a newer term from another peer).
func (*Cluster) StatusHandler ¶
func (c *Cluster) StatusHandler() func(w http.ResponseWriter, r *http.Request)
StatusHandler (GET) returns the nodes full state.
func (*Cluster) StepDownHandler ¶
func (c *Cluster) StepDownHandler() func(w http.ResponseWriter, r *http.Request)
StepDownHandler (POST) will force the node to step down to a follower state.
type DiscardLogger ¶
type DiscardLogger struct { }
DiscardLogger is a noop logger.
func (*DiscardLogger) Debugf ¶
func (d *DiscardLogger) Debugf(format string, v ...interface{})
Debugf noop
func (*DiscardLogger) Errorf ¶
func (d *DiscardLogger) Errorf(format string, v ...interface{})
Errorf noop
func (*DiscardLogger) Infof ¶
func (d *DiscardLogger) Infof(format string, v ...interface{})
Infof noop
func (*DiscardLogger) Printf ¶
func (d *DiscardLogger) Printf(format string, v ...interface{})
Printf noop
func (*DiscardLogger) Tracef ¶ added in v0.1.1
func (d *DiscardLogger) Tracef(format string, v ...interface{})
Tracef noop
type LogLogger ¶
LogLogger uses the std lib logger.
type Logger ¶
type Logger interface { Printf(format string, v ...interface{}) Println(v ...interface{}) Infof(format string, v ...interface{}) Debugf(format string, v ...interface{}) Tracef(format string, v ...interface{}) Errorf(format string, v ...interface{}) Errorln(v ...interface{}) }
Logger is for logging to a writer. This is not the raft replication log.
type Route ¶
type Route struct { Path string Handler func(http.ResponseWriter, *http.Request) }
Route holds path and handler information.
type State ¶
type State struct {
// contains filtered or unexported fields
}
State encapsulates the current nodes raft state.
func (*State) AppendEntriesEvent ¶
func (s *State) AppendEntriesEvent(event ...*AppendEntriesRequest) chan *AppendEntriesRequest
AppendEntriesEvent returns a channel for any successful append entries events.
func (*State) ElectionTick ¶
ElectionTick returns a channel with a new random election tick.
func (*State) HeartbeatReset ¶
HeartbeatReset will signal a reset. This works with a listener for HeartbeatTick.
func (*State) HeartbeatTick ¶
HeartbeatTick returns a channel with a heartbeat timeout set to heartbeatTimeoutMS.
func (*State) HeartbeatTickRandom ¶
HeartbeatTickRandom returns a channel with a random heartbeat timeout. 500ms is added to the minimum heartbeatTimeoutMS to compensate for possible network latency.
func (*State) LeaderID ¶
LeaderID will return the states current leader id or if an argument is passed in will set the current LeaderID.
func (*State) State ¶
State will return the states current state or if an argument is passed in will set the state
func (*State) StateChanged ¶
StateChanged returns a channel for any state changes that occur.
func (*State) StateString ¶
StateString returns the current state as a string.
func (*State) StepDown ¶
StepDown will step down the state by resetting to the given term and emitting a state change.