|
|
@@ -224,9 +224,14 @@ func RestartNode(c *Config) Node {
|
|
|
return &n
|
|
|
}
|
|
|
|
|
|
+type msgWithResult struct {
|
|
|
+ m pb.Message
|
|
|
+ result chan error
|
|
|
+}
|
|
|
+
|
|
|
// node is the canonical implementation of the Node interface
|
|
|
type node struct {
|
|
|
- propc chan pb.Message
|
|
|
+ propc chan msgWithResult
|
|
|
recvc chan pb.Message
|
|
|
confc chan pb.ConfChange
|
|
|
confstatec chan pb.ConfState
|
|
|
@@ -242,7 +247,7 @@ type node struct {
|
|
|
|
|
|
func newNode() node {
|
|
|
return node{
|
|
|
- propc: make(chan pb.Message),
|
|
|
+ propc: make(chan msgWithResult),
|
|
|
recvc: make(chan pb.Message),
|
|
|
confc: make(chan pb.ConfChange),
|
|
|
confstatec: make(chan pb.ConfState),
|
|
|
@@ -271,7 +276,7 @@ func (n *node) Stop() {
|
|
|
}
|
|
|
|
|
|
func (n *node) run(r *raft) {
|
|
|
- var propc chan pb.Message
|
|
|
+ var propc chan msgWithResult
|
|
|
var readyc chan Ready
|
|
|
var advancec chan struct{}
|
|
|
var prevLastUnstablei, prevLastUnstablet uint64
|
|
|
@@ -314,13 +319,18 @@ func (n *node) run(r *raft) {
|
|
|
// TODO: maybe buffer the config propose if there exists one (the way
|
|
|
// described in raft dissertation)
|
|
|
// Currently it is dropped in Step silently.
|
|
|
- case m := <-propc:
|
|
|
+ case pm := <-propc:
|
|
|
+ m := pm.m
|
|
|
m.From = r.id
|
|
|
- r.Step(m)
|
|
|
+ err := r.Step(m)
|
|
|
+ if pm.result != nil {
|
|
|
+ pm.result <- err
|
|
|
+ close(pm.result)
|
|
|
+ }
|
|
|
case m := <-n.recvc:
|
|
|
// filter out response message from unknown From.
|
|
|
if pr := r.getProgress(m.From); pr != nil || !IsResponseMsg(m.Type) {
|
|
|
- r.Step(m) // raft never returns an error
|
|
|
+ r.Step(m)
|
|
|
}
|
|
|
case cc := <-n.confc:
|
|
|
if cc.NodeID == None {
|
|
|
@@ -408,7 +418,7 @@ func (n *node) Tick() {
|
|
|
func (n *node) Campaign(ctx context.Context) error { return n.step(ctx, pb.Message{Type: pb.MsgHup}) }
|
|
|
|
|
|
func (n *node) Propose(ctx context.Context, data []byte) error {
|
|
|
- return n.step(ctx, pb.Message{Type: pb.MsgProp, Entries: []pb.Entry{{Data: data}}})
|
|
|
+ return n.stepWait(ctx, pb.Message{Type: pb.MsgProp, Entries: []pb.Entry{{Data: data}}})
|
|
|
}
|
|
|
|
|
|
func (n *node) Step(ctx context.Context, m pb.Message) error {
|
|
|
@@ -428,22 +438,53 @@ func (n *node) ProposeConfChange(ctx context.Context, cc pb.ConfChange) error {
|
|
|
return n.Step(ctx, pb.Message{Type: pb.MsgProp, Entries: []pb.Entry{{Type: pb.EntryConfChange, Data: data}}})
|
|
|
}
|
|
|
|
|
|
+func (n *node) step(ctx context.Context, m pb.Message) error {
|
|
|
+ return n.stepWithWaitOption(ctx, m, false)
|
|
|
+}
|
|
|
+
|
|
|
+func (n *node) stepWait(ctx context.Context, m pb.Message) error {
|
|
|
+ return n.stepWithWaitOption(ctx, m, true)
|
|
|
+}
|
|
|
+
|
|
|
// Step advances the state machine using msgs. The ctx.Err() will be returned,
|
|
|
// if any.
|
|
|
-func (n *node) step(ctx context.Context, m pb.Message) error {
|
|
|
- ch := n.recvc
|
|
|
- if m.Type == pb.MsgProp {
|
|
|
- ch = n.propc
|
|
|
+func (n *node) stepWithWaitOption(ctx context.Context, m pb.Message, wait bool) error {
|
|
|
+ if m.Type != pb.MsgProp {
|
|
|
+ select {
|
|
|
+ case n.recvc <- m:
|
|
|
+ return nil
|
|
|
+ case <-ctx.Done():
|
|
|
+ return ctx.Err()
|
|
|
+ case <-n.done:
|
|
|
+ return ErrStopped
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ch := n.propc
|
|
|
+ pm := msgWithResult{m: m}
|
|
|
+ if wait {
|
|
|
+ pm.result = make(chan error, 1)
|
|
|
}
|
|
|
-
|
|
|
select {
|
|
|
- case ch <- m:
|
|
|
- return nil
|
|
|
+ case ch <- pm:
|
|
|
+ if !wait {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ case <-ctx.Done():
|
|
|
+ return ctx.Err()
|
|
|
+ case <-n.done:
|
|
|
+ return ErrStopped
|
|
|
+ }
|
|
|
+ select {
|
|
|
+ case rsp := <-pm.result:
|
|
|
+ if rsp != nil {
|
|
|
+ return rsp
|
|
|
+ }
|
|
|
case <-ctx.Done():
|
|
|
return ctx.Err()
|
|
|
case <-n.done:
|
|
|
return ErrStopped
|
|
|
}
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
func (n *node) Ready() <-chan Ready { return n.readyc }
|