Esempio n. 1
0
File: Log.cs Progetto: e2wugui/zeze
        public void EndReceiveInstallSnapshot(FileStream s, InstallSnapshot r)
        {
            lock (Raft)
            {
                // 6. If existing log entry has same index and term as snapshot’s
                // last included entry, retain log entries following it and reply
                var last = ReadLog(r.Argument.LastIncludedIndex);
                if (null != last && last.Term == r.Argument.LastIncludedTerm)
                {
                    // 这里全部保留更简单吧,否则如果没有applied,那不就糟了吗?
                    // RemoveLogReverse(r.Argument.LastIncludedIndex - 1);
                    return;
                }
                // 7. Discard the entire log
                // 整个删除,那么下一次AppendEnties又会找不到prev。不就xxx了吗?
                // 我的想法是,InstallSnapshot 最后一个 trunk 带上 LastIncludedLog,
                // 接收者清除log,并把这条日志插入(这个和系统初始化时插入的Index=0的日志道理差不多)。
                // 【除了快照最后包含的日志,其他都删除。】
                var lastIncludedLog = RaftLog.Decode(r.Argument.LastIncludedLog, Raft.StateMachine.LogFactory);
                SaveLog(lastIncludedLog);
                // follower 没有并发请求需要处理,在锁内删除。
                RemoveLogReverse(lastIncludedLog.Index - 1, FirstIndex);
                RemoveLogAndCancelStart(lastIncludedLog.Index + 1, LastIndex);
                LastIndex   = lastIncludedLog.Index;
                FirstIndex  = lastIncludedLog.Index;
                CommitIndex = FirstIndex;
                LastApplied = FirstIndex;

                // 8. Reset state machine using snapshot contents (and load
                // snapshot’s cluster configuration)
                Raft.StateMachine.LoadFromSnapshot(s.Name);
                logger.Debug("{0} EndReceiveInstallSnapshot Path={1}", Raft.Name, s.Name);
            }
        }
Esempio n. 2
0
File: Log.cs Progetto: e2wugui/zeze
        private void InstallSnapshot(string path, Server.ConnectorEx connector)
        {
            // 整个安装成功结束时设置。中间Break(return)不设置。
            // 后面 finally 里面使用这个标志
            bool InstallSuccess = false;

            logger.Debug("{0} InstallSnapshot Start... Path={1} ToConnector={2}",
                         Raft.Name, path, connector.Name);
            try
            {
                var  snapshotFile = new FileStream(path, FileMode.Open);
                long offset       = 0;
                var  buffer       = new byte[32 * 1024];
                var  FirstLog     = ReadLog(FirstIndex);
                var  trunkArg     = new InstallSnapshotArgument();
                trunkArg.Term              = Term;
                trunkArg.LeaderId          = Raft.LeaderId;
                trunkArg.LastIncludedIndex = FirstLog.Index;
                trunkArg.LastIncludedTerm  = FirstLog.Term;

                while (!trunkArg.Done && Raft.IsLeader)
                {
                    int rc = snapshotFile.Read(buffer);
                    trunkArg.Offset = offset;
                    trunkArg.Data   = new Binary(buffer, 0, rc);
                    trunkArg.Done   = rc < buffer.Length;
                    offset         += rc;

                    if (trunkArg.Done)
                    {
                        trunkArg.LastIncludedLog = new Binary(FirstLog.Encode());
                    }

                    while (Raft.IsLeader)
                    {
                        connector.HandshakeDoneEvent.WaitOne();
                        var future = new TaskCompletionSource <int>();
                        var r      = new InstallSnapshot()
                        {
                            Argument = trunkArg
                        };
                        if (!r.Send(connector.Socket,
                                    (_) =>
                        {
                            future.SetResult(0);
                            return(Procedure.Success);
                        }))
                        {
                            continue;
                        }
                        future.Task.Wait();
                        if (r.IsTimeout)
                        {
                            continue;
                        }

                        lock (Raft)
                        {
                            if (this.TrySetTerm(r.Result.Term))
                            {
                                // new term found.
                                Raft.ConvertStateTo(Raft.RaftState.Follower);
                                return;
                            }
                        }

                        switch (r.ResultCode)
                        {
                        case global::Zeze.Raft.InstallSnapshot.ResultCodeNewOffset:
                            break;

                        default:
                            logger.Warn($"InstallSnapshot Break ResultCode={r.ResultCode}");
                            return;
                        }

                        if (r.Result.Offset >= 0)
                        {
                            if (r.Result.Offset > snapshotFile.Length)
                            {
                                logger.Error($"InstallSnapshot.Result.Offset Too Big.{r.Result.Offset}/{snapshotFile.Length}");
                                return; // 中断安装。
                            }
                            offset = r.Result.Offset;
                            snapshotFile.Seek(offset, SeekOrigin.Begin);
                        }
                        break;
                    }
                }
                InstallSuccess = Raft.IsLeader;
                logger.Debug("{0} InstallSnapshot [SUCCESS] Path={1} ToConnector={2}",
                             Raft.Name, path, connector.Name);
            }
            finally
            {
                lock (Raft)
                {
                    connector.InstallSnapshotting = false;
                    InstallSnapshotting.Remove(connector.Name);
                    if (InstallSuccess)
                    {
                        // 安装完成,重新初始化,使得以后的AppendEnties能继续工作。
                        // = FirstIndex + 1,防止Index跳着分配,使用ReadLogStart。
                        var next = ReadLogStart(FirstIndex + 1);
                        connector.NextIndex = next == null ? FirstIndex + 1 : next.Index;
                    }
                }
            }
        }