|
@@ -76,10 +76,12 @@ type Node interface {
|
|
|
// Tick increments the internal logical clock for the Node by a single tick. Election
|
|
// Tick increments the internal logical clock for the Node by a single tick. Election
|
|
|
// timeouts and heartbeat timeouts are in units of ticks.
|
|
// timeouts and heartbeat timeouts are in units of ticks.
|
|
|
Tick()
|
|
Tick()
|
|
|
- // Campaign causes the Node to transition to candidate state and start campaigning to become leader
|
|
|
|
|
|
|
+ // Campaign causes the Node to transition to candidate state and start campaigning to become leader.
|
|
|
Campaign(ctx context.Context) error
|
|
Campaign(ctx context.Context) error
|
|
|
// Propose proposes that data be appended to the log.
|
|
// Propose proposes that data be appended to the log.
|
|
|
Propose(ctx context.Context, data []byte) error
|
|
Propose(ctx context.Context, data []byte) error
|
|
|
|
|
+ // Configure proposes config change. Only one config can be in the process of going through consensus at a time.
|
|
|
|
|
+ Configure(ctx context.Context, data []byte) error
|
|
|
// Step advances the state machine using the given message. ctx.Err() will be returned, if any.
|
|
// Step advances the state machine using the given message. ctx.Err() will be returned, if any.
|
|
|
Step(ctx context.Context, msg pb.Message) error
|
|
Step(ctx context.Context, msg pb.Message) error
|
|
|
// Ready returns a channel that returns the current point-in-time state
|
|
// Ready returns a channel that returns the current point-in-time state
|
|
@@ -88,6 +90,12 @@ type Node interface {
|
|
|
Stop()
|
|
Stop()
|
|
|
// Compact
|
|
// Compact
|
|
|
Compact(d []byte)
|
|
Compact(d []byte)
|
|
|
|
|
+ // AddNode adds a node with given id into peer list.
|
|
|
|
|
+ // TODO: reject existed node
|
|
|
|
|
+ AddNode(id int64)
|
|
|
|
|
+ // RemoveNode removes a node with give id from peer list.
|
|
|
|
|
+ // TODO: reject unexisted node
|
|
|
|
|
+ RemoveNode(id int64)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// StartNode returns a new Node given a unique raft id, a list of raft peers, and
|
|
// StartNode returns a new Node given a unique raft id, a list of raft peers, and
|
|
@@ -114,11 +122,22 @@ func RestartNode(id int64, peers []int64, election, heartbeat int, snapshot *pb.
|
|
|
return &n
|
|
return &n
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const (
|
|
|
|
|
+ confAdd = iota
|
|
|
|
|
+ confRemove
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+type conf struct {
|
|
|
|
|
+ typ int
|
|
|
|
|
+ id int64
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// node is the canonical implementation of the Node interface
|
|
// node is the canonical implementation of the Node interface
|
|
|
type node struct {
|
|
type node struct {
|
|
|
propc chan pb.Message
|
|
propc chan pb.Message
|
|
|
recvc chan pb.Message
|
|
recvc chan pb.Message
|
|
|
compactc chan []byte
|
|
compactc chan []byte
|
|
|
|
|
+ confc chan conf
|
|
|
readyc chan Ready
|
|
readyc chan Ready
|
|
|
tickc chan struct{}
|
|
tickc chan struct{}
|
|
|
done chan struct{}
|
|
done chan struct{}
|
|
@@ -129,6 +148,7 @@ func newNode() node {
|
|
|
propc: make(chan pb.Message),
|
|
propc: make(chan pb.Message),
|
|
|
recvc: make(chan pb.Message),
|
|
recvc: make(chan pb.Message),
|
|
|
compactc: make(chan []byte),
|
|
compactc: make(chan []byte),
|
|
|
|
|
+ confc: make(chan conf),
|
|
|
readyc: make(chan Ready),
|
|
readyc: make(chan Ready),
|
|
|
tickc: make(chan struct{}),
|
|
tickc: make(chan struct{}),
|
|
|
done: make(chan struct{}),
|
|
done: make(chan struct{}),
|
|
@@ -167,6 +187,7 @@ func (n *node) run(r *raft) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
select {
|
|
|
|
|
+ // TODO: buffer the config propose if there exists one
|
|
|
case m := <-propc:
|
|
case m := <-propc:
|
|
|
m.From = r.id
|
|
m.From = r.id
|
|
|
r.Step(m)
|
|
r.Step(m)
|
|
@@ -174,6 +195,15 @@ func (n *node) run(r *raft) {
|
|
|
r.Step(m) // raft never returns an error
|
|
r.Step(m) // raft never returns an error
|
|
|
case d := <-n.compactc:
|
|
case d := <-n.compactc:
|
|
|
r.compact(d)
|
|
r.compact(d)
|
|
|
|
|
+ case c := <-n.confc:
|
|
|
|
|
+ switch c.typ {
|
|
|
|
|
+ case confAdd:
|
|
|
|
|
+ r.addNode(c.id)
|
|
|
|
|
+ case confRemove:
|
|
|
|
|
+ r.removeNode(c.id)
|
|
|
|
|
+ default:
|
|
|
|
|
+ panic("unexpected conf type")
|
|
|
|
|
+ }
|
|
|
case <-n.tickc:
|
|
case <-n.tickc:
|
|
|
r.tick()
|
|
r.tick()
|
|
|
case readyc <- rd:
|
|
case readyc <- rd:
|
|
@@ -186,6 +216,10 @@ func (n *node) run(r *raft) {
|
|
|
if !IsEmptySnap(rd.Snapshot) {
|
|
if !IsEmptySnap(rd.Snapshot) {
|
|
|
prevSnapi = rd.Snapshot.Index
|
|
prevSnapi = rd.Snapshot.Index
|
|
|
}
|
|
}
|
|
|
|
|
+ // TODO(yichengq): we assume that all committed config
|
|
|
|
|
+ // entries will be applied to make things easy for now.
|
|
|
|
|
+ // TODO(yichengq): it may have race because applied is set
|
|
|
|
|
+ // before entries are applied.
|
|
|
r.raftLog.resetNextEnts()
|
|
r.raftLog.resetNextEnts()
|
|
|
r.raftLog.resetUnstable()
|
|
r.raftLog.resetUnstable()
|
|
|
r.msgs = nil
|
|
r.msgs = nil
|
|
@@ -212,6 +246,10 @@ func (n *node) Propose(ctx context.Context, data []byte) error {
|
|
|
return n.Step(ctx, pb.Message{Type: msgProp, Entries: []pb.Entry{{Data: data}}})
|
|
return n.Step(ctx, pb.Message{Type: msgProp, Entries: []pb.Entry{{Data: data}}})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+func (n *node) Configure(ctx context.Context, data []byte) error {
|
|
|
|
|
+ return n.Step(ctx, pb.Message{Type: msgProp, Entries: []pb.Entry{{Type: EntryConfig, Data: data}}})
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// Step advances the state machine using msgs. The ctx.Err() will be returned,
|
|
// Step advances the state machine using msgs. The ctx.Err() will be returned,
|
|
|
// if any.
|
|
// if any.
|
|
|
func (n *node) Step(ctx context.Context, m pb.Message) error {
|
|
func (n *node) Step(ctx context.Context, m pb.Message) error {
|
|
@@ -241,6 +279,20 @@ func (n *node) Compact(d []byte) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+func (n *node) AddNode(id int64) {
|
|
|
|
|
+ select {
|
|
|
|
|
+ case n.confc <- conf{typ: confAdd, id: id}:
|
|
|
|
|
+ case <-n.done:
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (n *node) RemoveNode(id int64) {
|
|
|
|
|
+ select {
|
|
|
|
|
+ case n.confc <- conf{typ: confRemove, id: id}:
|
|
|
|
|
+ case <-n.done:
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func newReady(r *raft, prevSoftSt *SoftState, prevHardSt pb.HardState, prevSnapi int64) Ready {
|
|
func newReady(r *raft, prevSoftSt *SoftState, prevHardSt pb.HardState, prevSnapi int64) Ready {
|
|
|
rd := Ready{
|
|
rd := Ready{
|
|
|
Entries: r.raftLog.unstableEnts(),
|
|
Entries: r.raftLog.unstableEnts(),
|