-
Notifications
You must be signed in to change notification settings - Fork 9.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
raft: Propose in raft node wait the proposal result so we can fail fast while dropping proposal #9137
Conversation
86e45ec
to
3513727
Compare
Codecov Report
@@ Coverage Diff @@
## master #9137 +/- ##
=========================================
Coverage ? 72.47%
=========================================
Files ? 367
Lines ? 31257
Branches ? 0
=========================================
Hits ? 22652
Misses ? 6967
Partials ? 1638
Continue to review full report at Codecov.
|
raft/node.go
Outdated
// ProposeWithCancel proposes that data be appended to the log | ||
// and will invoke the cancel() if the proposal is dropped. | ||
// Application can use this to fail fast when the proposal is dropped or cancelled. | ||
ProposeWithCancel(ctx context.Context, cancel context.CancelFunc, data []byte) error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need to add this? can we improve Propose func so that it would return an error if proposal is dropped?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, the Propose
just send the proposal to chan, and will not wait. If we change the behavior for Propose
to block and return an error it may affect others.
I think that it may be better to add a new method. Any suggestion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly, cancel function is used to cancels the child outside, like:
ctx, cancel := WithCancel(parent)
pass(ctx)
cancel()
So it is strange for me to see this style here, maybe a callback is better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the method name needs to be improved. A new propose method name should make the caller known that a cancel callback will be used to get notified when the proposal is canceled by dropping. (I found there is no way to cancel in context.Context
, so I use the function context.CancelFunc
as callback since It is actually canceled the context.Context
. )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, the Propose just send the proposal to chan, and will not wait.
that is true. however, step func should be fast and non-blocking most of the time. changing Proposal to wait a bit longer for stepping to finish should have very a little impact if any.
Maybe I missed something. What is your concern specifically?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exactly. if we want to make it work, it will return either a callback or a chan, which will be closes if canceled.
Any update? @absolute8511 We need this feature to improve the timeout for Read operation when leader changed. /cc @xiang90 |
3513727
to
7e70fe9
Compare
ea9e414
to
2505eed
Compare
I changed the |
raft/node.go
Outdated
@@ -224,10 +224,15 @@ func RestartNode(c *Config) Node { | |||
return &n | |||
} | |||
|
|||
type proposingMsg struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the message type inside this will not always be proposal.
renaming this to nodeMsg or msgWithResult is better.
raft/node.go
Outdated
case m := <-n.recvc: | ||
err := r.Step(m) | ||
if pm.result != nil { | ||
if err == ErrProposalDropped { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we only propagate ErrProposalDropped
error? we should propagate back all types of errors.
raft/node.go
Outdated
r.Step(m) // raft never returns an error | ||
err := r.Step(m) // raft never returns an error | ||
if pm.result != nil { | ||
if err == ErrProposalDropped { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
raft/node.go
Outdated
// 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 | ||
err := r.Step(m) // raft never returns an error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove the wrong comment
raft/node.go
Outdated
ch := n.recvc | ||
if m.Type == pb.MsgProp { | ||
ch = n.propc | ||
} | ||
|
||
pm := proposingMsg{m: m, result: nil} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need to put nil in result. it would be nil by default.
raft/node.go
Outdated
ch := n.recvc | ||
if m.Type == pb.MsgProp { | ||
ch = n.propc | ||
} | ||
|
||
pm := proposingMsg{m: m, result: nil} | ||
if wait { | ||
pm.result = make(chan error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make(chan error, 1)
raft/node.go
Outdated
propc chan pb.Message | ||
recvc chan pb.Message | ||
propc chan proposingMsg | ||
recvc chan proposingMsg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we really need to change the recvc chan? if the proposal is redirected from other peers, the waiter is actually on another node anyway. there is no easy to propagate back the error even with this change.
probably changing the propc is good enough for the current purpose.
look good to me in general. we probably want to hook this change up with etcd, and do some benchmarks. i want to make sure this PR wont bring in any significant performance issue. |
2505eed
to
068e401
Compare
Yeah, a benchmark is needed. Should it in this PR or a new one? |
Let us do it in the same PR, but in a different commit. Thanks. You can use tools/benchmark to perform benchmarks. |
any update? |
Sorry for the delay, I will start tomorrow after my vacation. |
I looked into the tools/benchmark. What kinds of benchmark do this pr need? I noticed there are some benchmark for write. In this pr I only changed the |
yes. Let us start with that
…On Sat, Feb 3, 2018 at 8:23 AM Vincent Lee ***@***.***> wrote:
@xiang90 <https://github.com/xiang90>
I looked into the tools/benchmark. What kinds of benchmark do this pr
need? I noticed there are some benchmark for write. In this pr I only
changed the Propose behavior, so I just re-run the write benchmark to get
a result or a new benchmark tool need?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#9137 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AERby2YrNfr05HeNJsd-TiM5NbbgYiabks5tRIf7gaJpZM4Rb63X>
.
|
So it seems like these changes won't exactly solve this problem? Just a comment, but node.go; step function:
When the leader changes, n.propc is now a nil channel. Hence the "case ch <- m:" will block and only the "ctc.Done()" will exit this select. In the changes proposed for this request; I don't see this problem solved. Hence I don't see this allow for "fast failures" in this scenario. Seems to me we could do something simple here before we send to 'ch';
|
I run the benchmark on my MacBook using the version with and without this pr and got the below result. I run several times and noticed the result is not very stable. Maybe a standard GCE machine is needed.
Physical machinesMacBook Pro (Retina, 13-inch, Mid 2014) Test without this pretcd Cluster3 etcd at Git commit:
Performance in writing one single key
Test with this pretcd Cluster3 etcd at Git commit:
Performance in writing one single key
|
I re-run the benchmark on standard GCE machine again and use the Physical machinesGCE n1-standard-2(2 vCPU,7.5 GB memory )
Test without this pretcd Cluster3 etcd at Git commit:
Performance in writing one single key
Test with this pretcd Cluster3 etcd at Git commit:
Performance in writing one single key
|
068e401
to
5be3085
Compare
Seem that if we don't have many clients, the PR's performance is better, suprised. |
any update? @xiang90 |
raft/node.go
Outdated
ch := n.recvc | ||
if m.Type == pb.MsgProp { | ||
ch = n.propc | ||
func (n *node) stepWait(ctx context.Context, m pb.Message, wait bool) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename this to stepWithWaitOption. change stepWait to call into stepWithWaitOption(..., true)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we consume the returned error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you comment before:
do we really need to change the recvc chan? if the proposal is redirected from other peers, the waiter is actually on another node anyway. there is no easy to propagate back the error even with this change.
probably changing the propc is good enough for the current purpose.
I leaved this unchanged
@absolute8511 can you please reply to the comment #9137 (comment)? |
In our test by @nolouch, this still can't fail fast when leader changed sometimes. |
I think this is not always true. And this pr should only solve part of the fail fast. Only proposal dropped by the local If dropped by remote currently we just wait. @siddontang could you please give some brief test cases which can't fail fast currently, maybe we need some other pr in future to solve other fail fast issue. |
5be3085
to
bbb9ba9
Compare
You have already explained. If we isolate a follower and rejoin again, the leader will step down, at that time, all the proposed requests can't be notified quickly but have to wait timeout. |
bbb9ba9
to
76fc899
Compare
@xiang90
Have no idea what's wrong with my commit title? |
@absolute8511 rebase with the current master? |
8071ab1
to
cd15aa9
Compare
…st while dropping proposal.
cd15aa9
to
f0dffb4
Compare
@xiang90 |
lgtm |
@absolute8511 doing some archaeological work here and i'm wondering why wasn't |
While the leader is transferring or some other states which the raft node may drop the proposal, we should fail fast to notify the proposal canceled. (a separate pr followed by #9067)
This will be used to fix issue #8975 and #8977