Browse Source

Merge pull request #1066 from philips/add-raft-doc

add-raft-doc
Brandon Philips 11 years ago
parent
commit
df253a2b14
3 changed files with 75 additions and 2 deletions
  1. 65 0
      raft/doc.go
  2. 7 1
      raft/node.go
  3. 3 1
      raft/raft.go

+ 65 - 0
raft/doc.go

@@ -0,0 +1,65 @@
+// Copyright 2014 CoreOS Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//  http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*
+Package raft provides an implementation of the raft consensus algorithm.
+
+The primary object in raft is a Node. You either start a Node from scratch
+using raft.Start or start a Node from some initial state using raft.Restart.
+
+	n := raft.Start(0x01, []int64{0x02, 0x03}, 3, 1)
+
+Now that you are holding onto a Node you have a few responsibilities:
+
+First, you need to push messages that you receive from other machines into the
+Node with n.Step().
+
+	func recvRaftRPC(ctx context.Context, m raftpb.Message) {
+		n.Step(ctx, m)
+	}
+
+Second, you need to save log entries to storage, process committed log entries
+through your application and then send pending messages to peers by reading the
+channel returned by n.Ready(). It is important that the user persist any
+entries that require stable storage before sending messages to other peers to
+ensure fault-tolerance.
+
+And finally you need to service timeouts with Tick(). Raft has two important
+timeouts: heartbeat and the election timeout. However, internally to the raft
+package time is represented by an abstract "tick". The user is responsible for
+calling Tick() on their raft.Node on a regular interval in order to service
+these timeouts.
+
+The total state machine handling loop will look something like this:
+
+	for {
+		select {
+		case <-s.Ticker:
+			n.Tick()
+		case rd := <-s.Node.Ready():
+			saveToStable(rd.State, rd.Entries)
+			process(rd.CommittedEntries)
+			send(rd.Messages)
+		case <-s.done:
+			return
+		}
+	}
+
+To propose changes to the state machine from your node take your application
+data, serialize it into a byte slice and call:
+
+	n.Propose(ctx, data)
+
+*/
+package raft

+ 7 - 1
raft/node.go

@@ -1,4 +1,3 @@
-// Package raft implements raft.
 package raft
 
 import (
@@ -55,6 +54,8 @@ type Node struct {
 	done   chan struct{}
 }
 
+// Start returns a new Node given a unique raft id, a list of raft peers, and
+// the election and heartbeat timeouts in units of ticks.
 func Start(id int64, peers []int64, election, heartbeat int) Node {
 	n := newNode()
 	r := newRaft(id, peers, election, heartbeat)
@@ -62,6 +63,9 @@ func Start(id int64, peers []int64, election, heartbeat int) Node {
 	return n
 }
 
+// Restart is identical to Start but takes an initial State and a slice of
+// entries. Generally this is used when restarting from a stable storage
+// log.
 func Restart(id int64, peers []int64, election, heartbeat int, st pb.State, ents []pb.Entry) Node {
 	n := newNode()
 	r := newRaft(id, peers, election, heartbeat)
@@ -131,6 +135,8 @@ func (n *Node) run(r *raft) {
 	}
 }
 
+// Tick increments the internal logical clock for this Node. Election timeouts
+// and heartbeat timeouts are in units of ticks.
 func (n *Node) Tick() error {
 	select {
 	case n.tickc <- struct{}{}:

+ 3 - 1
raft/raft.go

@@ -108,7 +108,7 @@ type raft struct {
 	// the leader id
 	lead int64
 
-	elapsed          int
+	elapsed          int // number of ticks since the last msg
 	heartbeatTimeout int
 	electionTimeout  int
 	tick             func()
@@ -258,6 +258,7 @@ func (r *raft) appendEntry(e pb.Entry) {
 	r.maybeCommit()
 }
 
+// tickElection is ran by followers and candidates after r.electionTimeout.
 func (r *raft) tickElection() {
 	r.elapsed++
 	// TODO (xiangli): elctionTimeout should be randomized.
@@ -267,6 +268,7 @@ func (r *raft) tickElection() {
 	}
 }
 
+// tickHeartbeat is ran by leaders to send a msgBeat after r.heartbeatTimeout.
 func (r *raft) tickHeartbeat() {
 	r.elapsed++
 	if r.elapsed > r.heartbeatTimeout {