Beispiel #1
0
        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);
        }
Beispiel #2
0
        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
                }
            }
        }