raft: support safe readonly request

Implement raft readonly request described in raft thesis 6.4
along with the existing clock/lease based approach.
This commit is contained in:
Xiang Li
2016-08-26 16:03:06 -07:00
parent e53b99588a
commit 710b14ce56
6 changed files with 297 additions and 68 deletions

View File

@@ -60,11 +60,11 @@ type Ready struct {
// HardState will be equal to empty state if there is no update.
pb.HardState
// ReadState can be used for node to serve linearizable read requests locally
// ReadStates can be used for node to serve linearizable read requests locally
// when its applied index is greater than the index in ReadState.
// Note that the readState will be returned when raft receives msgReadIndex.
// The returned is only valid for the request that requested to read.
ReadState
ReadStates []ReadState
// Entries specifies entries to be saved to stable storage BEFORE
// Messages are sent.
@@ -102,7 +102,7 @@ func IsEmptySnap(sp pb.Snapshot) bool {
func (rd Ready) containsUpdates() bool {
return rd.SoftState != nil || !IsEmptyHardState(rd.HardState) ||
!IsEmptySnap(rd.Snapshot) || len(rd.Entries) > 0 ||
len(rd.CommittedEntries) > 0 || len(rd.Messages) > 0 || rd.Index != None
len(rd.CommittedEntries) > 0 || len(rd.Messages) > 0 || len(rd.ReadStates) != 0
}
// Node represents a node in a raft cluster.
@@ -151,11 +151,6 @@ type Node interface {
// Read state has a read index. Once the application advances further than the read
// index, any linearizable read requests issued before the read request can be
// processed safely. The read state will have the same rctx attached.
//
// Note: the current implementation depends on the leader lease. If the clock drift is unbounded,
// leader might keep the lease longer than it should (clock can move backward/pause without any bound).
// ReadIndex is not safe in that case.
// TODO: add clock drift bound into raft configuration.
ReadIndex(ctx context.Context, rctx []byte) error
// Status returns the current status of the raft state machine.
@@ -370,8 +365,7 @@ func (n *node) run(r *raft) {
}
r.msgs = nil
r.readState.Index = None
r.readState.RequestCtx = nil
r.readStates = nil
advancec = n.advancec
case <-advancec:
if prevHardSt.Commit != 0 {
@@ -516,12 +510,8 @@ func newReady(r *raft, prevSoftSt *SoftState, prevHardSt pb.HardState) Ready {
if r.raftLog.unstable.snapshot != nil {
rd.Snapshot = *r.raftLog.unstable.snapshot
}
if r.readState.Index != None {
c := make([]byte, len(r.readState.RequestCtx))
copy(c, r.readState.RequestCtx)
rd.Index = r.readState.Index
rd.RequestCtx = c
if len(r.readStates) != 0 {
rd.ReadStates = r.readStates
}
return rd
}