|
@@ -1,20 +1,35 @@
|
|
|
package raft
|
|
package raft
|
|
|
|
|
|
|
|
-import "sync"
|
|
|
|
|
-
|
|
|
|
|
type Interface interface {
|
|
type Interface interface {
|
|
|
Step(m Message)
|
|
Step(m Message)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+type tick int
|
|
|
|
|
+
|
|
|
type Node struct {
|
|
type Node struct {
|
|
|
- lk sync.Mutex
|
|
|
|
|
- sm *stateMachine
|
|
|
|
|
|
|
+ // election timeout and heartbeat timeout in tick
|
|
|
|
|
+ election tick
|
|
|
|
|
+ heartbeat tick
|
|
|
|
|
+
|
|
|
|
|
+ // elapsed ticks after the last reset
|
|
|
|
|
+ elapsed tick
|
|
|
|
|
+ sm *stateMachine
|
|
|
|
|
+
|
|
|
|
|
+ next Interface
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func New(k, addr int, next Interface) *Node {
|
|
|
|
|
|
|
+func New(k, addr int, heartbeat, election tick, next Interface) *Node {
|
|
|
|
|
+ if election < heartbeat*3 {
|
|
|
|
|
+ panic("election is least three times as heartbeat [election: %d, heartbeat: %d]")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
n := &Node{
|
|
n := &Node{
|
|
|
- sm: newStateMachine(k, addr),
|
|
|
|
|
|
|
+ sm: newStateMachine(k, addr),
|
|
|
|
|
+ next: next,
|
|
|
|
|
+ heartbeat: heartbeat,
|
|
|
|
|
+ election: election,
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
return n
|
|
return n
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -25,15 +40,42 @@ func (n *Node) Propose(data []byte) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (n *Node) Step(m Message) {
|
|
func (n *Node) Step(m Message) {
|
|
|
- n.lk.Lock()
|
|
|
|
|
- defer n.lk.Unlock()
|
|
|
|
|
n.sm.Step(m)
|
|
n.sm.Step(m)
|
|
|
|
|
+ ms := n.sm.Msgs()
|
|
|
|
|
+ for _, m := range ms {
|
|
|
|
|
+ // reset elapsed in two cases:
|
|
|
|
|
+ // msgAppResp -> heard from the leader of the same term
|
|
|
|
|
+ // msgVoteResp with grant -> heard from the candidate the node voted for
|
|
|
|
|
+ switch m.Type {
|
|
|
|
|
+ case msgAppResp:
|
|
|
|
|
+ n.elapsed = 0
|
|
|
|
|
+ case msgVoteResp:
|
|
|
|
|
+ if m.Index >= 0 {
|
|
|
|
|
+ n.elapsed = 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ n.next.Step(m)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Next advances the commit index and returns any new
|
|
// Next advances the commit index and returns any new
|
|
|
// commitable entries.
|
|
// commitable entries.
|
|
|
func (n *Node) Next() []Entry {
|
|
func (n *Node) Next() []Entry {
|
|
|
- n.lk.Lock()
|
|
|
|
|
- defer n.lk.Unlock()
|
|
|
|
|
return n.sm.nextEnts()
|
|
return n.sm.nextEnts()
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+// Tick triggers the node to do a tick.
|
|
|
|
|
+// If the current elapsed is greater or equal than the timeout,
|
|
|
|
|
+// node will send corresponding message to the statemachine.
|
|
|
|
|
+func (n *Node) Tick() {
|
|
|
|
|
+ timeout, msgType := n.election, msgHup
|
|
|
|
|
+ if n.sm.state == stateLeader {
|
|
|
|
|
+ timeout, msgType = n.heartbeat, msgBeat
|
|
|
|
|
+ }
|
|
|
|
|
+ if n.elapsed >= timeout {
|
|
|
|
|
+ n.Step(Message{Type: msgType})
|
|
|
|
|
+ n.elapsed = 0
|
|
|
|
|
+ } else {
|
|
|
|
|
+ n.elapsed++
|
|
|
|
|
+ }
|
|
|
|
|
+}
|