internal int FollowerOnAppendEntries(AppendEntries r) { LeaderActiveTime = Zeze.Util.Time.NowUnixMillis; r.Result.Term = Term; r.Result.Success = false; // set default false if (r.Argument.Term < Term) { // 1. Reply false if term < currentTerm (§5.1) r.SendResult(); return(Procedure.LogicError); } var prevLog = ReadLog(r.Argument.PrevLogIndex); if (prevLog == null || prevLog.Term != r.Argument.PrevLogTerm) { // 2. Reply false if log doesn’t contain an entry // at prevLogIndex whose term matches prevLogTerm(§5.3) r.SendResult(); return(Procedure.LogicError); } foreach (var raftLogData in r.Argument.Entries) { var copyLog = RaftLog.Decode(raftLogData, Raft.StateMachine.LogFactory); var conflictCheck = ReadLog(copyLog.Index); if (null != conflictCheck) { if (conflictCheck.Term != copyLog.Term) { // 3. If an existing entry conflicts // with a new one (same index but different terms), // delete the existing entry and all that follow it(§5.3) // raft.pdf 5.3 RemoveLogAndCancelStart(conflictCheck.Index, LastIndex); LastIndex = prevLog.Index; } } else { // 4. Append any new entries not already in the log SaveLog(copyLog); } // 复用这个变量。当冲突需要删除时,精确指到前一个日志。 prevLog = copyLog; } // 5. If leaderCommit > commitIndex, // set commitIndex = min(leaderCommit, index of last new entry) if (r.Argument.LeaderCommit > CommitIndex) { CommitIndex = Math.Min(r.Argument.LeaderCommit, LastRaftLog().Index); TryApply(ReadLog(CommitIndex)); } r.Result.Success = true; logger.Debug("{0}: {1}", Raft.Name, r); r.SendResultCode(0); return(Procedure.Success); }
private void TrySendAppendEntries(Server.ConnectorEx connector, AppendEntries pending) { lock (Raft) { // 按理说,多个Follower设置一次就够了,这里就不做这个处理了。 AppendLogActiveTime = Util.Time.NowUnixMillis; if (connector.Pending != pending) { return; } // 先清除,下面中断(return)不用每次自己清除。 connector.Pending = null; if (false == connector.IsHandshakeDone) { // Hearbeat Will Retry return; } // 【注意】 // 正在安装Snapshot,此时不复制日志,肯定失败。 // 不做这个判断也是可以工作的,算是优化。 if (connector.InstallSnapshotting) { return; } if (connector.NextIndex > LastIndex) { return; } var nextLog = ReadLogReverse(connector.NextIndex); if (nextLog.Index == FirstIndex) { // 已经到了日志开头,此时不会有prev-log,无法复制日志了。 // 这一般发生在Leader进行了Snapshot,但是Follower的日志还更老。 // 新起的Follower也一样。 StartInstallSnapshot(connector); return; } // 现在Index总是递增,但没有确认步长总是为1,这样能处理不为1的情况。 connector.NextIndex = nextLog.Index; connector.Pending = new AppendEntries(); connector.Pending.Argument.Term = Term; connector.Pending.Argument.LeaderId = Raft.Name; connector.Pending.Argument.LeaderCommit = CommitIndex; // 肯定能找到的。 var prevLog = ReadLogReverse(nextLog.Index - 1); connector.Pending.Argument.PrevLogIndex = prevLog.Index; connector.Pending.Argument.PrevLogTerm = prevLog.Term; // 限制一次发送的日志数量,【注意】这个不是raft要求的。 int maxCount = Raft.RaftConfig.MaxAppendEntiresCount; RaftLog lastCopyLog = nextLog; for (var copyLog = nextLog; maxCount > 0 && null != copyLog && copyLog.Index <= LastIndex; copyLog = ReadLogStart(copyLog.Index + 1), --maxCount ) { lastCopyLog = copyLog; connector.Pending.Argument.Entries.Add(new Binary(copyLog.Encode())); } connector.Pending.Argument.LastEntryIndex = lastCopyLog.Index; if (false == connector.Pending.Send( connector.Socket, (p) => ProcessAppendEntriesResult(connector, p), Raft.RaftConfig.AppendEntriesTimeout)) { connector.Pending = null; // Hearbeat Will Retry } } }