-
Notifications
You must be signed in to change notification settings - Fork 399
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
Lease based read-only request without check_quorum
behaves wrong.
#140
Comments
@rustonaut Thanks for this report. 🙇♀️ It's very good and clear. :) You can see that etcd's Raft does similar...: https://github.com/etcd-io/etcd/blob/83304cfc808cf6303d48c45a696f169fae422e68/raft/raft.go#L1010-L1037 case pb.MsgReadIndex:
if r.quorum() > 1 {
if r.raftLog.zeroTermOnErrCompacted(r.raftLog.term(r.raftLog.committed)) != r.Term {
// Reject read only request when this leader has not committed any log entry at its term.
return nil
}
// thinking: use an interally defined context instead of the user given context.
// We can express this in terms of the term and index instead of a user-supplied value.
// This would allow multiple reads to piggyback on the same message.
switch r.readOnly.option {
case ReadOnlySafe:
r.readOnly.addRequest(r.raftLog.committed, m)
r.bcastHeartbeatWithCtx(m.Entries[0].Data)
case ReadOnlyLeaseBased:
ri := r.raftLog.committed
if m.From == None || m.From == r.id { // from local member
r.readStates = append(r.readStates, ReadState{Index: r.raftLog.committed, RequestCtx: m.Entries[0].Data})
} else {
r.send(pb.Message{To: m.From, Type: pb.MsgReadIndexResp, Index: ri, Entries: m.Entries})
}
}
} else {
r.readStates = append(r.readStates, ReadState{Index: r.raftLog.committed, RequestCtx: m.Entries[0].Data})
}
return nil
} I'm going to investigate this a bit more. |
Hi @rustonaut , can you take a look at #141 . I think you are correct. |
I think it's correct, long term we might want to consider to maybe warn if |
141: Correct lease with check quorum. r=Hoverbear a=Hoverbear Fixes #140 . You can see etcd also has this behavior: https://github.com/etcd-io/etcd/blob/83304cfc808cf6303d48c45a696f169fae422e68/raft/raft.go#L1010-L1037 ```go case pb.MsgReadIndex: if r.quorum() > 1 { if r.raftLog.zeroTermOnErrCompacted(r.raftLog.term(r.raftLog.committed)) != r.Term { // Reject read only request when this leader has not committed any log entry at its term. return nil } // thinking: use an interally defined context instead of the user given context. // We can express this in terms of the term and index instead of a user-supplied value. // This would allow multiple reads to piggyback on the same message. switch r.readOnly.option { case ReadOnlySafe: r.readOnly.addRequest(r.raftLog.committed, m) r.bcastHeartbeatWithCtx(m.Entries[0].Data) case ReadOnlyLeaseBased: ri := r.raftLog.committed if m.From == None || m.From == r.id { // from local member r.readStates = append(r.readStates, ReadState{Index: r.raftLog.committed, RequestCtx: m.Entries[0].Data}) } else { r.send(pb.Message{To: m.From, Type: pb.MsgReadIndexResp, Index: ri, Entries: m.Entries}) } } } else { r.readStates = append(r.readStates, ReadState{Index: r.raftLog.committed, RequestCtx: m.Entries[0].Data}) } return nil } ``` Co-authored-by: Hoverbear <operator@hoverbear.org> Co-authored-by: A. Hobden <operator@hoverbear.org>
141: Correct lease with check quorum. r=Hoverbear a=Hoverbear Fixes #140 . You can see etcd also has this behavior: https://github.com/etcd-io/etcd/blob/83304cfc808cf6303d48c45a696f169fae422e68/raft/raft.go#L1010-L1037 ```go case pb.MsgReadIndex: if r.quorum() > 1 { if r.raftLog.zeroTermOnErrCompacted(r.raftLog.term(r.raftLog.committed)) != r.Term { // Reject read only request when this leader has not committed any log entry at its term. return nil } // thinking: use an interally defined context instead of the user given context. // We can express this in terms of the term and index instead of a user-supplied value. // This would allow multiple reads to piggyback on the same message. switch r.readOnly.option { case ReadOnlySafe: r.readOnly.addRequest(r.raftLog.committed, m) r.bcastHeartbeatWithCtx(m.Entries[0].Data) case ReadOnlyLeaseBased: ri := r.raftLog.committed if m.From == None || m.From == r.id { // from local member r.readStates = append(r.readStates, ReadState{Index: r.raftLog.committed, RequestCtx: m.Entries[0].Data}) } else { r.send(pb.Message{To: m.From, Type: pb.MsgReadIndexResp, Index: ri, Entries: m.Entries}) } } } else { r.readStates = append(r.readStates, ReadState{Index: r.raftLog.committed, RequestCtx: m.Entries[0].Data}) } return nil } ``` Co-authored-by: Hoverbear <operator@hoverbear.org> Co-authored-by: A. Hobden <operator@hoverbear.org>
Lease based read-only request without
check_quorum
behaves wrong, at least in outdated leader in a minority partition.In
step_leader
for handlingMsgReadIndex
messages.https://github.com/pingcap/raft-rs/blob/3799cfa482e288a0fc4a3a6c365c6681afd85b3a/src/raft.rs#L1429-L1449
I think line
1437
should useread_index
instead ofself.raft_log.commited
, ifI'm not wrong (and if I'm wrong the
read_index
variable should be created inthe
else
branch to prevent confusion).If my understanding of the code is correct then lease base read requests allow
you to read up to the commit index iff
self.check_quorum
is set as this wouldmake sure that the leader still has the majority behind them and return
INVALID_INDEX
if not (which btw. seems to be kind of a strange, shouldn't then
Config::validate
at lastwarn on using a leas based reads without
check_quorum
).The text was updated successfully, but these errors were encountered: