mirror of
https://github.com/talent-plan/tinykv.git
synced 2024-12-26 21:00:12 +08:00
a674bd7ab1
from master to course branch the noop entry is important hint for implementation, else learner will feel confused when test fail.
238 lines
6.7 KiB
Go
238 lines
6.7 KiB
Go
// Copyright 2015 The etcd Authors
|
||
//
|
||
// 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
|
||
|
||
import (
|
||
"errors"
|
||
|
||
pb "github.com/pingcap-incubator/tinykv/proto/pkg/eraftpb"
|
||
)
|
||
|
||
// None is a placeholder node ID used when there is no leader.
|
||
const None uint64 = 0
|
||
|
||
// StateType represents the role of a node in a cluster.
|
||
type StateType uint64
|
||
|
||
const (
|
||
StateFollower StateType = iota
|
||
StateCandidate
|
||
StateLeader
|
||
)
|
||
|
||
var stmap = [...]string{
|
||
"StateFollower",
|
||
"StateCandidate",
|
||
"StateLeader",
|
||
}
|
||
|
||
func (st StateType) String() string {
|
||
return stmap[uint64(st)]
|
||
}
|
||
|
||
// ErrProposalDropped is returned when the proposal is ignored by some cases,
|
||
// so that the proposer can be notified and fail fast.
|
||
var ErrProposalDropped = errors.New("raft proposal dropped")
|
||
|
||
// Config contains the parameters to start a raft.
|
||
type Config struct {
|
||
// ID is the identity of the local raft. ID cannot be 0.
|
||
ID uint64
|
||
|
||
// peers contains the IDs of all nodes (including self) in the raft cluster. It
|
||
// should only be set when starting a new raft cluster. Restarting raft from
|
||
// previous configuration will panic if peers is set. peer is private and only
|
||
// used for testing right now.
|
||
peers []uint64
|
||
|
||
// ElectionTick is the number of Node.Tick invocations that must pass between
|
||
// elections. That is, if a follower does not receive any message from the
|
||
// leader of current term before ElectionTick has elapsed, it will become
|
||
// candidate and start an election. ElectionTick must be greater than
|
||
// HeartbeatTick. We suggest ElectionTick = 10 * HeartbeatTick to avoid
|
||
// unnecessary leader switching.
|
||
ElectionTick int
|
||
// HeartbeatTick is the number of Node.Tick invocations that must pass between
|
||
// heartbeats. That is, a leader sends heartbeat messages to maintain its
|
||
// leadership every HeartbeatTick ticks.
|
||
HeartbeatTick int
|
||
|
||
// Storage is the storage for raft. raft generates entries and states to be
|
||
// stored in storage. raft reads the persisted entries and states out of
|
||
// Storage when it needs. raft reads out the previous state and configuration
|
||
// out of storage when restarting.
|
||
Storage Storage
|
||
// Applied is the last applied index. It should only be set when restarting
|
||
// raft. raft will not return entries to the application smaller or equal to
|
||
// Applied. If Applied is unset when restarting, raft might return previous
|
||
// applied entries. This is a very application dependent configuration.
|
||
Applied uint64
|
||
}
|
||
|
||
func (c *Config) validate() error {
|
||
if c.ID == None {
|
||
return errors.New("cannot use none as id")
|
||
}
|
||
|
||
if c.HeartbeatTick <= 0 {
|
||
return errors.New("heartbeat tick must be greater than 0")
|
||
}
|
||
|
||
if c.ElectionTick <= c.HeartbeatTick {
|
||
return errors.New("election tick must be greater than heartbeat tick")
|
||
}
|
||
|
||
if c.Storage == nil {
|
||
return errors.New("storage cannot be nil")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// Progress represents a follower’s progress in the view of the leader. Leader maintains
|
||
// progresses of all followers, and sends entries to the follower based on its progress.
|
||
type Progress struct {
|
||
Match, Next uint64
|
||
}
|
||
|
||
type Raft struct {
|
||
id uint64
|
||
|
||
Term uint64
|
||
Vote uint64
|
||
|
||
// the log
|
||
RaftLog *RaftLog
|
||
|
||
// log replication progress of each peers
|
||
Prs map[uint64]*Progress
|
||
|
||
// this peer's role
|
||
State StateType
|
||
|
||
// votes records
|
||
votes map[uint64]bool
|
||
|
||
// msgs need to send
|
||
msgs []pb.Message
|
||
|
||
// the leader id
|
||
Lead uint64
|
||
|
||
// heartbeat interval, should send
|
||
heartbeatTimeout int
|
||
// baseline of election interval
|
||
electionTimeout int
|
||
// number of ticks since it reached last heartbeatTimeout.
|
||
// only leader keeps heartbeatElapsed.
|
||
heartbeatElapsed int
|
||
// number of ticks since it reached last electionTimeout
|
||
electionElapsed int
|
||
|
||
// leadTransferee is id of the leader transfer target when its value is not zero.
|
||
// Follow the procedure defined in section 3.10 of Raft phd thesis.
|
||
// (https://web.stanford.edu/~ouster/cgi-bin/papers/OngaroPhD.pdf)
|
||
// (Used in 3A leader transfer)
|
||
leadTransferee uint64
|
||
|
||
// Only one conf change may be pending (in the log, but not yet
|
||
// applied) at a time. This is enforced via PendingConfIndex, which
|
||
// is set to a value >= the log index of the latest pending
|
||
// configuration change (if any). Config changes are only allowed to
|
||
// be proposed if the leader's applied index is greater than this
|
||
// value.
|
||
// (Used in 3A conf change)
|
||
PendingConfIndex uint64
|
||
}
|
||
|
||
// newRaft return a raft peer with the given config
|
||
func newRaft(c *Config) *Raft {
|
||
if err := c.validate(); err != nil {
|
||
panic(err.Error())
|
||
}
|
||
// Your Code Here (2A).
|
||
return nil
|
||
}
|
||
|
||
// sendAppend sends an append RPC with new entries (if any) and the
|
||
// current commit index to the given peer. Returns true if a message was sent.
|
||
func (r *Raft) sendAppend(to uint64) bool {
|
||
// Your Code Here (2A).
|
||
return false
|
||
}
|
||
|
||
// sendHeartbeat sends a heartbeat RPC to the given peer.
|
||
func (r *Raft) sendHeartbeat(to uint64) {
|
||
// Your Code Here (2A).
|
||
}
|
||
|
||
// tick advances the internal logical clock by a single tick.
|
||
func (r *Raft) tick() {
|
||
// Your Code Here (2A).
|
||
}
|
||
|
||
// becomeFollower transform this peer's state to Follower
|
||
func (r *Raft) becomeFollower(term uint64, lead uint64) {
|
||
// Your Code Here (2A).
|
||
}
|
||
|
||
// becomeCandidate transform this peer's state to candidate
|
||
func (r *Raft) becomeCandidate() {
|
||
// Your Code Here (2A).
|
||
}
|
||
|
||
// becomeLeader transform this peer's state to leader
|
||
func (r *Raft) becomeLeader() {
|
||
// Your Code Here (2A).
|
||
// NOTE: Leader should propose a noop entry on its term
|
||
}
|
||
|
||
// Step the entrance of handle message, see `MessageType`
|
||
// on `eraftpb.proto` for what msgs should be handled
|
||
func (r *Raft) Step(m pb.Message) error {
|
||
// Your Code Here (2A).
|
||
switch r.State {
|
||
case StateFollower:
|
||
case StateCandidate:
|
||
case StateLeader:
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// handleAppendEntries handle AppendEntries RPC request
|
||
func (r *Raft) handleAppendEntries(m pb.Message) {
|
||
// Your Code Here (2A).
|
||
}
|
||
|
||
// handleHeartbeat handle Heartbeat RPC request
|
||
func (r *Raft) handleHeartbeat(m pb.Message) {
|
||
// Your Code Here (2A).
|
||
}
|
||
|
||
// handleSnapshot handle Snapshot RPC request
|
||
func (r *Raft) handleSnapshot(m pb.Message) {
|
||
// Your Code Here (2C).
|
||
}
|
||
|
||
// addNode add a new node to raft group
|
||
func (r *Raft) addNode(id uint64) {
|
||
// Your Code Here (3A).
|
||
}
|
||
|
||
// removeNode remove a node from raft group
|
||
func (r *Raft) removeNode(id uint64) {
|
||
// Your Code Here (3A).
|
||
}
|