From 58b8610024f4aff80076350f2afc532adc96ae05 Mon Sep 17 00:00:00 2001 From: Brandon Philips Date: Thu, 11 Sep 2014 23:31:35 -0700 Subject: [PATCH 1/2] raft: add a doc.go file Explain the package from a high level. --- raft/doc.go | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 raft/doc.go diff --git a/raft/doc.go b/raft/doc.go new file mode 100644 index 000000000..b95eef4f0 --- /dev/null +++ b/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 From 98f9ee36136a75d970babea1ca0d2d952c392e71 Mon Sep 17 00:00:00 2001 From: Brandon Philips Date: Thu, 11 Sep 2014 23:32:46 -0700 Subject: [PATCH 2/2] raft: add useful comments These comments were things I learned about the units, use case or meaning of various fields and functions in the raft package. --- raft/node.go | 8 +++++++- raft/raft.go | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/raft/node.go b/raft/node.go index 275324306..157dbb14d 100644 --- a/raft/node.go +++ b/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{}{}: diff --git a/raft/raft.go b/raft/raft.go index 2245749f0..cec855dff 100644 --- a/raft/raft.go +++ b/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 {