|
@@ -94,9 +94,12 @@ type Server interface {
|
|
|
Do(command Command) (interface{}, error)
|
|
Do(command Command) (interface{}, error)
|
|
|
TakeSnapshot() error
|
|
TakeSnapshot() error
|
|
|
LoadSnapshot() error
|
|
LoadSnapshot() error
|
|
|
|
|
+ AddEventListener(string, EventListener)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
type server struct {
|
|
type server struct {
|
|
|
|
|
+ *eventDispatcher
|
|
|
|
|
+
|
|
|
name string
|
|
name string
|
|
|
path string
|
|
path string
|
|
|
state string
|
|
state string
|
|
@@ -111,7 +114,7 @@ type server struct {
|
|
|
mutex sync.RWMutex
|
|
mutex sync.RWMutex
|
|
|
syncedPeer map[string]bool
|
|
syncedPeer map[string]bool
|
|
|
|
|
|
|
|
- c chan *event
|
|
|
|
|
|
|
+ c chan *ev
|
|
|
electionTimeout time.Duration
|
|
electionTimeout time.Duration
|
|
|
heartbeatTimeout time.Duration
|
|
heartbeatTimeout time.Duration
|
|
|
|
|
|
|
@@ -123,8 +126,8 @@ type server struct {
|
|
|
connectionString string
|
|
connectionString string
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// An event to be processed by the server's event loop.
|
|
|
|
|
-type event struct {
|
|
|
|
|
|
|
+// An internal event to be processed by the server's event loop.
|
|
|
|
|
+type ev struct {
|
|
|
target interface{}
|
|
target interface{}
|
|
|
returnValue interface{}
|
|
returnValue interface{}
|
|
|
c chan error
|
|
c chan error
|
|
@@ -136,7 +139,11 @@ type event struct {
|
|
|
//
|
|
//
|
|
|
//------------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
-// Creates a new server with a log at the given path.
|
|
|
|
|
|
|
+// Creates a new server with a log at the given path. transporter must
|
|
|
|
|
+// not be nil. stateMachine can be nil if snapshotting and log
|
|
|
|
|
+// compaction is to be disabled. context can be anything (including nil)
|
|
|
|
|
+// and is not used by the raft package except returned by
|
|
|
|
|
+// Server.Context(). connectionString can be anything.
|
|
|
func NewServer(name string, path string, transporter Transporter, stateMachine StateMachine, context interface{}, connectionString string) (Server, error) {
|
|
func NewServer(name string, path string, transporter Transporter, stateMachine StateMachine, context interface{}, connectionString string) (Server, error) {
|
|
|
if name == "" {
|
|
if name == "" {
|
|
|
return nil, errors.New("raft.Server: Name cannot be blank")
|
|
return nil, errors.New("raft.Server: Name cannot be blank")
|
|
@@ -154,12 +161,13 @@ func NewServer(name string, path string, transporter Transporter, stateMachine S
|
|
|
state: Stopped,
|
|
state: Stopped,
|
|
|
peers: make(map[string]*Peer),
|
|
peers: make(map[string]*Peer),
|
|
|
log: newLog(),
|
|
log: newLog(),
|
|
|
- c: make(chan *event, 256),
|
|
|
|
|
|
|
+ c: make(chan *ev, 256),
|
|
|
electionTimeout: DefaultElectionTimeout,
|
|
electionTimeout: DefaultElectionTimeout,
|
|
|
heartbeatTimeout: DefaultHeartbeatTimeout,
|
|
heartbeatTimeout: DefaultHeartbeatTimeout,
|
|
|
maxLogEntriesPerRequest: MaxLogEntriesPerRequest,
|
|
maxLogEntriesPerRequest: MaxLogEntriesPerRequest,
|
|
|
connectionString: connectionString,
|
|
connectionString: connectionString,
|
|
|
}
|
|
}
|
|
|
|
|
+ s.eventDispatcher = newEventDispatcher(s)
|
|
|
|
|
|
|
|
// Setup apply function.
|
|
// Setup apply function.
|
|
|
s.log.ApplyFunc = func(c Command) (interface{}, error) {
|
|
s.log.ApplyFunc = func(c Command) (interface{}, error) {
|
|
@@ -246,19 +254,37 @@ func (s *server) State() string {
|
|
|
func (s *server) setState(state string) {
|
|
func (s *server) setState(state string) {
|
|
|
s.mutex.Lock()
|
|
s.mutex.Lock()
|
|
|
defer s.mutex.Unlock()
|
|
defer s.mutex.Unlock()
|
|
|
|
|
+
|
|
|
|
|
+ // Temporarily store previous values.
|
|
|
|
|
+ prevState := s.state
|
|
|
|
|
+ prevLeader := s.leader
|
|
|
|
|
+
|
|
|
|
|
+ // Update state and leader.
|
|
|
s.state = state
|
|
s.state = state
|
|
|
if state == Leader {
|
|
if state == Leader {
|
|
|
s.leader = s.Name()
|
|
s.leader = s.Name()
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // Dispatch state and leader change events.
|
|
|
|
|
+ if prevState != state {
|
|
|
|
|
+ s.DispatchEvent(newEvent(StateChangeEventType, s.state, prevState))
|
|
|
|
|
+ }
|
|
|
|
|
+ if prevLeader != s.leader {
|
|
|
|
|
+ s.DispatchEvent(newEvent(LeaderChangeEventType, s.leader, prevLeader))
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Retrieves the current term of the server.
|
|
// Retrieves the current term of the server.
|
|
|
func (s *server) Term() uint64 {
|
|
func (s *server) Term() uint64 {
|
|
|
|
|
+ s.mutex.RLock()
|
|
|
|
|
+ defer s.mutex.RUnlock()
|
|
|
return s.currentTerm
|
|
return s.currentTerm
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Retrieves the current commit index of the server.
|
|
// Retrieves the current commit index of the server.
|
|
|
func (s *server) CommitIndex() uint64 {
|
|
func (s *server) CommitIndex() uint64 {
|
|
|
|
|
+ s.log.mutex.RLock()
|
|
|
|
|
+ defer s.log.mutex.RUnlock()
|
|
|
return s.log.commitIndex
|
|
return s.log.commitIndex
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -375,7 +401,7 @@ func init() {
|
|
|
|
|
|
|
|
func (s *server) Start() error {
|
|
func (s *server) Start() error {
|
|
|
// Exit if the server is already running.
|
|
// Exit if the server is already running.
|
|
|
- if s.state != Stopped {
|
|
|
|
|
|
|
+ if s.State() != Stopped {
|
|
|
return errors.New("raft.Server: Server already running")
|
|
return errors.New("raft.Server: Server already running")
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -443,22 +469,34 @@ func (s *server) setCurrentTerm(term uint64, leaderName string, append bool) {
|
|
|
s.mutex.Lock()
|
|
s.mutex.Lock()
|
|
|
defer s.mutex.Unlock()
|
|
defer s.mutex.Unlock()
|
|
|
|
|
|
|
|
- // update the term and clear vote for
|
|
|
|
|
|
|
+ // Store previous values temporarily.
|
|
|
|
|
+ prevState := s.state
|
|
|
|
|
+ prevTerm := s.currentTerm
|
|
|
|
|
+ prevLeader := s.leader
|
|
|
|
|
+
|
|
|
if term > s.currentTerm {
|
|
if term > s.currentTerm {
|
|
|
|
|
+ // update the term and clear vote for
|
|
|
s.state = Follower
|
|
s.state = Follower
|
|
|
s.currentTerm = term
|
|
s.currentTerm = term
|
|
|
s.leader = leaderName
|
|
s.leader = leaderName
|
|
|
s.votedFor = ""
|
|
s.votedFor = ""
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // discover new leader when candidate
|
|
|
|
|
- // save leader name when follower
|
|
|
|
|
- if term == s.currentTerm && s.state != Leader && append {
|
|
|
|
|
|
|
+ } else if term == s.currentTerm && s.state != Leader && append {
|
|
|
|
|
+ // discover new leader when candidate
|
|
|
|
|
+ // save leader name when follower
|
|
|
s.state = Follower
|
|
s.state = Follower
|
|
|
s.leader = leaderName
|
|
s.leader = leaderName
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Dispatch change events.
|
|
|
|
|
+ if prevState != s.state {
|
|
|
|
|
+ s.DispatchEvent(newEvent(StateChangeEventType, s.state, prevState))
|
|
|
|
|
+ }
|
|
|
|
|
+ if prevLeader != s.leader {
|
|
|
|
|
+ s.DispatchEvent(newEvent(LeaderChangeEventType, s.leader, prevLeader))
|
|
|
|
|
+ }
|
|
|
|
|
+ if prevTerm != s.currentTerm {
|
|
|
|
|
+ s.DispatchEvent(newEvent(TermChangeEventType, s.currentTerm, prevTerm))
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------
|
|
//--------------------------------------
|
|
@@ -512,8 +550,8 @@ func (s *server) send(value interface{}) (interface{}, error) {
|
|
|
return event.returnValue, err
|
|
return event.returnValue, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func (s *server) sendAsync(value interface{}) *event {
|
|
|
|
|
- event := &event{target: value, c: make(chan error, 1)}
|
|
|
|
|
|
|
+func (s *server) sendAsync(value interface{}) *ev {
|
|
|
|
|
+ event := &ev{target: value, c: make(chan error, 1)}
|
|
|
s.c <- event
|
|
s.c <- event
|
|
|
return event
|
|
return event
|
|
|
}
|
|
}
|
|
@@ -588,7 +626,13 @@ func (s *server) followerLoop() {
|
|
|
// The event loop that is run when the server is in a Candidate state.
|
|
// The event loop that is run when the server is in a Candidate state.
|
|
|
func (s *server) candidateLoop() {
|
|
func (s *server) candidateLoop() {
|
|
|
lastLogIndex, lastLogTerm := s.log.lastInfo()
|
|
lastLogIndex, lastLogTerm := s.log.lastInfo()
|
|
|
|
|
+
|
|
|
|
|
+ // Clear leader value.
|
|
|
|
|
+ prevLeader := s.leader
|
|
|
s.leader = ""
|
|
s.leader = ""
|
|
|
|
|
+ if prevLeader != s.leader {
|
|
|
|
|
+ s.DispatchEvent(newEvent(LeaderChangeEventType, s.leader, prevLeader))
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
for {
|
|
for {
|
|
|
// Increment current term, vote for self.
|
|
// Increment current term, vote for self.
|
|
@@ -765,7 +809,7 @@ func (s *server) Do(command Command) (interface{}, error) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Processes a command.
|
|
// Processes a command.
|
|
|
-func (s *server) processCommand(command Command, e *event) {
|
|
|
|
|
|
|
+func (s *server) processCommand(command Command, e *ev) {
|
|
|
s.debugln("server.command.process")
|
|
s.debugln("server.command.process")
|
|
|
|
|
|
|
|
// Create an entry for the command in the log.
|
|
// Create an entry for the command in the log.
|
|
@@ -866,7 +910,7 @@ func (s *server) processAppendEntriesRequest(req *AppendEntriesRequest) (*Append
|
|
|
func (s *server) processAppendEntriesResponse(resp *AppendEntriesResponse) {
|
|
func (s *server) processAppendEntriesResponse(resp *AppendEntriesResponse) {
|
|
|
|
|
|
|
|
// If we find a higher term then change to a follower and exit.
|
|
// If we find a higher term then change to a follower and exit.
|
|
|
- if resp.Term > s.currentTerm {
|
|
|
|
|
|
|
+ if resp.Term > s.Term() {
|
|
|
s.setCurrentTerm(resp.Term, "", false)
|
|
s.setCurrentTerm(resp.Term, "", false)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
@@ -914,6 +958,7 @@ func (s *server) processAppendEntriesResponse(resp *AppendEntriesResponse) {
|
|
|
default:
|
|
default:
|
|
|
panic("server unable to send signal to commit channel")
|
|
panic("server unable to send signal to commit channel")
|
|
|
}
|
|
}
|
|
|
|
|
+ entry.commit = nil
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -937,7 +982,7 @@ func (s *server) RequestVote(req *RequestVoteRequest) *RequestVoteResponse {
|
|
|
func (s *server) processRequestVoteRequest(req *RequestVoteRequest) (*RequestVoteResponse, bool) {
|
|
func (s *server) processRequestVoteRequest(req *RequestVoteRequest) (*RequestVoteResponse, bool) {
|
|
|
|
|
|
|
|
// If the request is coming from an old term then reject it.
|
|
// If the request is coming from an old term then reject it.
|
|
|
- if req.Term < s.currentTerm {
|
|
|
|
|
|
|
+ if req.Term < s.Term() {
|
|
|
s.debugln("server.rv.error: stale term")
|
|
s.debugln("server.rv.error: stale term")
|
|
|
return newRequestVoteResponse(s.currentTerm, false), false
|
|
return newRequestVoteResponse(s.currentTerm, false), false
|
|
|
}
|
|
}
|
|
@@ -989,6 +1034,8 @@ func (s *server) AddPeer(name string, connectiongString string) error {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
s.peers[peer.Name] = peer
|
|
s.peers[peer.Name] = peer
|
|
|
|
|
+
|
|
|
|
|
+ s.DispatchEvent(newEvent(AddPeerEventType, name, nil))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Write the configuration to file.
|
|
// Write the configuration to file.
|
|
@@ -1015,6 +1062,8 @@ func (s *server) RemovePeer(name string) error {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
delete(s.peers, name)
|
|
delete(s.peers, name)
|
|
|
|
|
+
|
|
|
|
|
+ s.DispatchEvent(newEvent(RemovePeerEventType, name, nil))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Write the configuration to file.
|
|
// Write the configuration to file.
|
|
@@ -1317,7 +1366,7 @@ func (s *server) readConf() error {
|
|
|
//--------------------------------------
|
|
//--------------------------------------
|
|
|
|
|
|
|
|
func (s *server) debugln(v ...interface{}) {
|
|
func (s *server) debugln(v ...interface{}) {
|
|
|
- debugf("[%s Term:%d] %s", s.name, s.currentTerm, fmt.Sprintln(v...))
|
|
|
|
|
|
|
+ debugf("[%s Term:%d] %s", s.name, s.Term(), fmt.Sprintln(v...))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (s *server) traceln(v ...interface{}) {
|
|
func (s *server) traceln(v ...interface{}) {
|