|
|
@@ -119,6 +119,8 @@ type Response struct {
|
|
|
|
|
|
type ServerV2 interface {
|
|
|
Server
|
|
|
+ Leader() types.ID
|
|
|
+
|
|
|
// Do takes a V2 request and attempts to fulfill it, returning a Response.
|
|
|
Do(ctx context.Context, r pb.Request) (Response, error)
|
|
|
stats.Stats
|
|
|
@@ -127,16 +129,12 @@ type ServerV2 interface {
|
|
|
|
|
|
type ServerV3 interface {
|
|
|
Server
|
|
|
- ID() types.ID
|
|
|
- RaftTimer
|
|
|
+ RaftStatusGetter
|
|
|
}
|
|
|
|
|
|
func (s *EtcdServer) ClientCertAuthEnabled() bool { return s.Cfg.ClientCertAuthEnabled }
|
|
|
|
|
|
type Server interface {
|
|
|
- // Leader returns the ID of the leader Server.
|
|
|
- Leader() types.ID
|
|
|
-
|
|
|
// AddMember attempts to add a member into the cluster. It will return
|
|
|
// ErrIDRemoved if member ID is removed from the cluster, or return
|
|
|
// ErrIDExists if member ID exists in the cluster.
|
|
|
@@ -174,6 +172,9 @@ type EtcdServer struct {
|
|
|
inflightSnapshots int64 // must use atomic operations to access; keep 64-bit aligned.
|
|
|
appliedIndex uint64 // must use atomic operations to access; keep 64-bit aligned.
|
|
|
committedIndex uint64 // must use atomic operations to access; keep 64-bit aligned.
|
|
|
+ term uint64 // must use atomic operations to access; keep 64-bit aligned.
|
|
|
+ lead uint64 // must use atomic operations to access; keep 64-bit aligned.
|
|
|
+
|
|
|
// consistIndex used to hold the offset of current executing entry
|
|
|
// It is initialized to 0 before executing any entry.
|
|
|
consistIndex consistentIndex // must use atomic operations to access; keep 64-bit aligned.
|
|
|
@@ -630,8 +631,6 @@ func (s *EtcdServer) purgeFile() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (s *EtcdServer) ID() types.ID { return s.id }
|
|
|
-
|
|
|
func (s *EtcdServer) Cluster() api.Cluster { return s.cluster }
|
|
|
|
|
|
func (s *EtcdServer) ApplyWait() <-chan struct{} { return s.applyWait.Wait(s.getCommittedIndex()) }
|
|
|
@@ -695,6 +694,8 @@ type etcdProgress struct {
|
|
|
// and helps decouple state machine logic from Raft algorithms.
|
|
|
// TODO: add a state machine interface to apply the commit entries and do snapshot/recover
|
|
|
type raftReadyHandler struct {
|
|
|
+ getLead func() (lead uint64)
|
|
|
+ updateLead func(lead uint64)
|
|
|
updateLeadership func(newLeader bool)
|
|
|
updateCommittedIndex func(uint64)
|
|
|
}
|
|
|
@@ -724,6 +725,8 @@ func (s *EtcdServer) run() {
|
|
|
return
|
|
|
}
|
|
|
rh := &raftReadyHandler{
|
|
|
+ getLead: func() (lead uint64) { return s.getLead() },
|
|
|
+ updateLead: func(lead uint64) { s.setLead(lead) },
|
|
|
updateLeadership: func(newLeader bool) {
|
|
|
if !s.isLeader() {
|
|
|
if s.lessor != nil {
|
|
|
@@ -1098,7 +1101,7 @@ func (s *EtcdServer) StopNotify() <-chan struct{} { return s.done }
|
|
|
func (s *EtcdServer) SelfStats() []byte { return s.stats.JSON() }
|
|
|
|
|
|
func (s *EtcdServer) LeaderStats() []byte {
|
|
|
- lead := atomic.LoadUint64(&s.r.lead)
|
|
|
+ lead := s.getLead()
|
|
|
if lead != uint64(s.id) {
|
|
|
return nil
|
|
|
}
|
|
|
@@ -1218,20 +1221,58 @@ func (s *EtcdServer) UpdateMember(ctx context.Context, memb membership.Member) (
|
|
|
return s.configure(ctx, cc)
|
|
|
}
|
|
|
|
|
|
-// Implement the RaftTimer interface
|
|
|
+func (s *EtcdServer) setCommittedIndex(v uint64) {
|
|
|
+ atomic.StoreUint64(&s.committedIndex, v)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *EtcdServer) getCommittedIndex() uint64 {
|
|
|
+ return atomic.LoadUint64(&s.committedIndex)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *EtcdServer) setAppliedIndex(v uint64) {
|
|
|
+ atomic.StoreUint64(&s.appliedIndex, v)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *EtcdServer) getAppliedIndex() uint64 {
|
|
|
+ return atomic.LoadUint64(&s.appliedIndex)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *EtcdServer) setTerm(v uint64) {
|
|
|
+ atomic.StoreUint64(&s.term, v)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *EtcdServer) getTerm() uint64 {
|
|
|
+ return atomic.LoadUint64(&s.term)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *EtcdServer) setLead(v uint64) {
|
|
|
+ atomic.StoreUint64(&s.lead, v)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *EtcdServer) getLead() uint64 {
|
|
|
+ return atomic.LoadUint64(&s.lead)
|
|
|
+}
|
|
|
+
|
|
|
+// RaftStatusGetter represents etcd server and Raft progress.
|
|
|
+type RaftStatusGetter interface {
|
|
|
+ ID() types.ID
|
|
|
+ Leader() types.ID
|
|
|
+ CommittedIndex() uint64
|
|
|
+ AppliedIndex() uint64
|
|
|
+ Term() uint64
|
|
|
+}
|
|
|
+
|
|
|
+func (s *EtcdServer) ID() types.ID { return s.id }
|
|
|
|
|
|
-func (s *EtcdServer) Index() uint64 { return atomic.LoadUint64(&s.r.index) }
|
|
|
+func (s *EtcdServer) Leader() types.ID { return types.ID(s.getLead()) }
|
|
|
|
|
|
-func (s *EtcdServer) AppliedIndex() uint64 { return atomic.LoadUint64(&s.r.appliedindex) }
|
|
|
+func (s *EtcdServer) Lead() uint64 { return s.getLead() }
|
|
|
|
|
|
-func (s *EtcdServer) Term() uint64 { return atomic.LoadUint64(&s.r.term) }
|
|
|
+func (s *EtcdServer) CommittedIndex() uint64 { return s.getCommittedIndex() }
|
|
|
|
|
|
-// Lead is only for testing purposes.
|
|
|
-// TODO: add Raft server interface to expose raft related info:
|
|
|
-// Index, Term, Lead, Committed, Applied, LastIndex, etc.
|
|
|
-func (s *EtcdServer) Lead() uint64 { return atomic.LoadUint64(&s.r.lead) }
|
|
|
+func (s *EtcdServer) AppliedIndex() uint64 { return s.getAppliedIndex() }
|
|
|
|
|
|
-func (s *EtcdServer) Leader() types.ID { return types.ID(s.Lead()) }
|
|
|
+func (s *EtcdServer) Term() uint64 { return s.getTerm() }
|
|
|
|
|
|
type confChangeResponse struct {
|
|
|
membs []*membership.Member
|
|
|
@@ -1351,6 +1392,8 @@ func (s *EtcdServer) apply(es []raftpb.Entry, confState *raftpb.ConfState) (appl
|
|
|
switch e.Type {
|
|
|
case raftpb.EntryNormal:
|
|
|
s.applyEntryNormal(&e)
|
|
|
+ s.setAppliedIndex(e.Index)
|
|
|
+ s.setTerm(e.Term)
|
|
|
case raftpb.EntryConfChange:
|
|
|
// set the consistent index of current executing entry
|
|
|
if e.Index > s.consistIndex.ConsistentIndex() {
|
|
|
@@ -1360,15 +1403,13 @@ func (s *EtcdServer) apply(es []raftpb.Entry, confState *raftpb.ConfState) (appl
|
|
|
pbutil.MustUnmarshal(&cc, e.Data)
|
|
|
removedSelf, err := s.applyConfChange(cc, confState)
|
|
|
s.setAppliedIndex(e.Index)
|
|
|
+ s.setTerm(e.Term)
|
|
|
shouldStop = shouldStop || removedSelf
|
|
|
s.w.Trigger(cc.ID, &confChangeResponse{s.cluster.Members(), err})
|
|
|
default:
|
|
|
plog.Panicf("entry type should be either EntryNormal or EntryConfChange")
|
|
|
}
|
|
|
- atomic.StoreUint64(&s.r.index, e.Index)
|
|
|
- atomic.StoreUint64(&s.r.term, e.Term)
|
|
|
- appliedt = e.Term
|
|
|
- appliedi = e.Index
|
|
|
+ appliedi, appliedt = e.Index, e.Term
|
|
|
}
|
|
|
return appliedt, appliedi, shouldStop
|
|
|
}
|
|
|
@@ -1381,7 +1422,6 @@ func (s *EtcdServer) applyEntryNormal(e *raftpb.Entry) {
|
|
|
s.consistIndex.setConsistentIndex(e.Index)
|
|
|
shouldApplyV3 = true
|
|
|
}
|
|
|
- defer s.setAppliedIndex(e.Index)
|
|
|
|
|
|
// raft state machine may generate noop entry when leader confirmation.
|
|
|
// skip it in advance to avoid some potential bug in the future
|
|
|
@@ -1670,7 +1710,7 @@ func (s *EtcdServer) parseProposeCtxErr(err error, start time.Time) error {
|
|
|
return ErrTimeoutDueToLeaderFail
|
|
|
}
|
|
|
|
|
|
- lead := types.ID(atomic.LoadUint64(&s.r.lead))
|
|
|
+ lead := types.ID(s.getLead())
|
|
|
switch lead {
|
|
|
case types.ID(raft.None):
|
|
|
// TODO: return error to specify it happens because the cluster does not have leader now
|
|
|
@@ -1715,23 +1755,6 @@ func (s *EtcdServer) restoreAlarms() error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func (s *EtcdServer) getAppliedIndex() uint64 {
|
|
|
- return atomic.LoadUint64(&s.appliedIndex)
|
|
|
-}
|
|
|
-
|
|
|
-func (s *EtcdServer) setAppliedIndex(v uint64) {
|
|
|
- atomic.StoreUint64(&s.appliedIndex, v)
|
|
|
- atomic.StoreUint64(&s.r.appliedindex, v)
|
|
|
-}
|
|
|
-
|
|
|
-func (s *EtcdServer) getCommittedIndex() uint64 {
|
|
|
- return atomic.LoadUint64(&s.committedIndex)
|
|
|
-}
|
|
|
-
|
|
|
-func (s *EtcdServer) setCommittedIndex(v uint64) {
|
|
|
- atomic.StoreUint64(&s.committedIndex, v)
|
|
|
-}
|
|
|
-
|
|
|
// goAttach creates a goroutine on a given function and tracks it using
|
|
|
// the etcdserver waitgroup.
|
|
|
func (s *EtcdServer) goAttach(f func()) {
|