Documentation
¶
Index ¶
- Constants
- Variables
- type AppendEntriesInput
- type AppendEntriesOutput
- type Client
- type DeleteInput
- type Entry
- type Follower
- type GetInput
- type GetOutput
- type Leader
- type Log
- type Node
- func (n *Node) AppendEntries(input AppendEntriesInput) (AppendEntriesOutput, error)
- func (n *Node) Delete(input DeleteInput) error
- func (n *Node) Get(input GetInput) (GetOutput, error)
- func (n *Node) RequestVote(input RequestVoteInput) (RequestVoteOutput, error)
- func (n *Node) Set(input SetInput) error
- type NotLeaderError
- type Peer
- type Protocol
- type RequestVoteInput
- type RequestVoteOutput
- type SetInput
- type State
- type Term
- type Voter
Constants ¶
const ( // StateFollower indicates the node is currently a follower and only responds to requests. StateFollower = "follower" // StateCandidate indicates the node is currently a candidate and is requesting votes from the other nodes. StateCandidate = "candidate" // StateLeader indicates the node is the leader and is servicing user requests along with replicating log entires. StateLeader = "leader" )
Variables ¶
var ErrLeaderUnknown = errors.New("leader node not found")
ErrLeaderUnknown is returned if the node is not the leader and doesn't know where the leader is.
var ErrNotFound = errors.New("not found")
ErrNotFound is returned if something was not found (usually a key).
Functions ¶
This section is empty.
Types ¶
type AppendEntriesInput ¶
type AppendEntriesInput struct {
// Term is the leaders term.
Term Term `json:"term"`
// ID is the leaders id so follower can redirect clients.
ID Peer `json:"id"`
// PreviousLogIndex is the index of log entry immediately preceding new ones.
PreviousLogIndex int `json:"previous_log_index"`
// PreviousLogTerm is the term of prevLogIndex entry.
PreviousLogTerm Term `json:"previous_log_term"`
// Entries is a log entries to store (empty for heartbeat; may send more than one for efficiency).
Entries []Entry `json:"entries"`
// CommitIndex is the leader’s commitIndex.
CommitIndex int `json:"commit_index"`
}
AppendEntriesInput encapsulates the request options available for the 'AppendEntries' API.
type AppendEntriesOutput ¶
type AppendEntriesOutput struct {
// Term is the current term, for leader to update itself.
Term Term `json:"term"`
// Success is a bollean set to true if follower contained an entry matching prevLogIndex and prevLogTerm.
Success bool `json:"success"`
}
AppendEntriesOutput encapsulates the response options available for the 'AppendEntries' API.
type Client ¶
type Client interface {
// Address returns the address client is using to communicate to the node.
Address() *url.URL
// AppendEntries is invoked by leader to replicate log entries (§5.3); also used as heartbeat.
AppendEntries(ctx context.Context, input AppendEntriesInput) (output AppendEntriesOutput, err error)
// RequestVote is invoked by candidates to gather votes.
RequestVote(ctx context.Context, input RequestVoteInput) (output RequestVoteOutput, err error)
}
Client is a client allowing a node to communicate to other nodes in the cluster.
type DeleteInput ¶
type DeleteInput struct {
// Key is the key being deleted.
Key string `json:"key"`
}
DeleteInput encapsulates the request options for the 'Delete' API.
type Entry ¶
type Entry struct {
// Term is the term when the log entry was applied.
Term Term `json:"term"`
// Key is the key being mutated.
Key string `json:"key"`
// Value is the value being mutated.
Value any `json:"value"`
// System indicates that this is a system event and shouldn't be returned to users.
//
//nolint:godox
// TODO(jamesl33): Use to allow log compaction/node addition/removal.
System bool `json:"system"`
// Deleted indicates that this is a deletion.
Deleted bool `json:"deleted"`
}
Entry in the replicated log.
type Follower ¶
type Follower interface {
// AppendEntries heartbeats/replicates the log to the node.
//
// 1. Reply false if term < currentTerm.
// 2. Reply false if log doesn’t contain an entry at prevLogIndex whose term matches prevLogTerm.
// 3. If an existing entry conflicts with a new one (same index but different terms), delete the existing entry and
// all that follow it.
// 4. Append any new entries not already in the log
// 5. If leaderCommit > commitIndex, set commitIndex = min(leaderCommit, index of last new entry)
AppendEntries(input AppendEntriesInput) (output AppendEntriesOutput, err error)
}
Follower is the API for heartbeating/log replication to a node in the cluster.
- Respond to RPCs from candidates and leaders
- If election timeout elapses without receiving AppendEntries RPC from current leader or granting vote to candidate: convert to candidate
type GetInput ¶
type GetInput struct {
// Key is the get being fetched.
Key string `json:"key"`
}
GetInput encapsulates the request options for the 'Get' API.
type GetOutput ¶
type GetOutput struct {
// Key is the key that was fetched.
Key string `json:"key"`
// Value is the value associated with the key.
Value any `json:"value"`
}
GetOutput encapsulates the response options for the 'Get' API.
type Leader ¶
type Leader interface {
// Set a key/value pair.
Set(input SetInput) (err error)
// Get a key value pair.
Get(input GetInput) (output GetOutput, err error)
// Delete a key.
Delete(input DeleteInput) (err error)
}
Leader is the API for servicing client requests to the replicated log.
Upon election: send initial empty AppendEntries RPCs (heartbeat) to each server; repeat during idle periods to prevent election timeouts.
If command received from client: append entry to local log, respond after entry applied to state machine.
If last log index ≥ nextIndex for a follower: send AppendEntries RPC with log entries starting at nextIndex:
- If successful: update nextIndex and matchIndex for follower.
- If AppendEntries fails because of log inconsistency: decrement nextIndex and retry.
If there exists an N such that N > commitIndex, a majority of matchIndex[i] ≥ N, and log[N].term == currentTerm: set commitIndex = N.
type Log ¶
type Log []Entry
Log is a replicated log where entries contain a command (key/value pair) which is applied to a state machine after having been replicated to a quorum of nodes.
type Node ¶
type Node struct {
// contains filtered or unexported fields
}
Node implements the 'Protocol' interface exposing a distributed key/value data store using Raft.
func (*Node) AppendEntries ¶
func (n *Node) AppendEntries(input AppendEntriesInput) (AppendEntriesOutput, error)
AppendEntries performs client side log replication and transitions state where required.
func (*Node) Delete ¶
func (n *Node) Delete(input DeleteInput) error
Delete adds an entry to the log indicating the deletion of a key.
NOTE: This is a tombstone; both the entry and the tombstone will remain indefinitely.
func (*Node) Get ¶
Get returns an entry from the log if it exists (and is not deleted).
NOTE: System entries will not be returned.
func (*Node) RequestVote ¶
func (n *Node) RequestVote(input RequestVoteInput) (RequestVoteOutput, error)
RequestVote casts this nodes vote and transitions state where required.
type NotLeaderError ¶
NotLeaderError is returned if the node is not the leader but does know where the leader is.
func (NotLeaderError) Error ¶
func (n NotLeaderError) Error() string
Error implements the 'error' interface returning a useful error indicating whether the leader node is.
type Protocol ¶
Protocol is a definition of the Raft consensus protocol for managing a replicated log.
All Nodes:
- If commitIndex > lastApplied: increment lastApplied, apply log[lastApplied] to state machine.
- If RPC request or response contains term T > currentTerm: set currentTerm = T, convert to follower.
Followers:
- Respond to RPCs from candidates and leaders
- If election timeout elapses without receiving AppendEntries RPC from current leader or granting vote to candidate: convert to candidate
Candidates:
On conversion to candidate, start election:
- Increment currentTerm.
- Vote for self.
- Reset election timer.
- Send RequestVote RPCs to all other servers.
If votes received from majority of servers: become leader
If AppendEntries RPC received from new leader: convert to follower
If election timeout elapses: start new election
Leaders:
Upon election: send initial empty AppendEntries RPCs (heartbeat) to each server; repeat during idle periods to prevent election timeouts.
If command received from client: append entry to local log, respond after entry applied to state machine.
If last log index ≥ nextIndex for a follower: send AppendEntries RPC with log entries starting at nextIndex:
- If successful: update nextIndex and matchIndex for follower.
- If AppendEntries fails because of log inconsistency: decrement nextIndex and retry.
If there exists an N such that N > commitIndex, a majority of matchIndex[i] ≥ N, and log[N].term == currentTerm: set commitIndex = N.
type RequestVoteInput ¶
type RequestVoteInput struct {
// Term is the candidate’s term.
Term Term `json:"term"`
// CandidateID is the id of the candidate requesting the vote.
CandidateID Peer `json:"candidate_id"`
// LastLogIndex is the index of candidate’s last log entry.
LastLogIndex int `json:"last_log_index"`
// LastLogTerm is the term of candidate’s last log entry.
LastLogTerm Term `json:"last_log_term"`
}
RequestVoteInput encapsulates the request options available for the 'RequestVote' API.
type RequestVoteOutput ¶
type RequestVoteOutput struct {
// Term is the current term, for candidate to update itself.
Term Term `json:"term"`
// Granted is set to true to indicate that the candidate received the vote.
Granted bool `json:"granted"`
}
RequestVoteOutput encapsulates the response options for the 'RequestVote' API.
type SetInput ¶
type SetInput struct {
// Key is the key being mutated.
Key string `json:"key"`
// Value is the new value that will be associated with the key.
Value any `json:"value"`
}
SetInput encapsulates the request options for the 'Set' API.
type Term ¶
type Term int
Term is an election term, each term must only have one leader; new elections increment the term.
type Voter ¶
type Voter interface {
// RequestVote requests a vote from the node.
//
// 1. Reply false if term < currentTerm.
// 2. If votedFor is null or candidateId, and candidate’s log is at least as up-to-date as receiver’s log, grant
// vote.
RequestVote(input RequestVoteInput) (output RequestVoteOutput, err error)
}
Voter is the API for requesting a vote from a node in the cluster.
On conversion to candidate, start election:
- Increment currentTerm.
- Vote for self.
- Reset election timer.
- Send RequestVote RPCs to all other servers.
If votes received from majority of servers: become leader
If AppendEntries RPC received from new leader: convert to follower
If election timeout elapses: start new election