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); } }
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; } } } }