public AppendEntryResponse AppendEntryRPCHandler(AppendEntry entry) { //Check the log check to prevent a intermittent term increase with no back tracking, TODO check whether this causes potentially concurrency issues if (entry.Term < _nodeStorage.CurrentTerm && entry.LeaderCommit <= NodeStateService.CommitIndex) { Logger.LogDebug(NodeStateService.GetNodeLogId() + "Rejected RPC from " + entry.LeaderId + " due to lower term " + entry.Term + "<" + _nodeStorage.CurrentTerm); return(new AppendEntryResponse() { ConflictName = AppendEntriesExceptionNames.OldTermException, NodeId = _nodeStorage.Id, IsSuccessful = false }); } //Reset the timer if the append is from a valid term ResetTimer(_electionTimeoutTimer, ClusterOptions.ElectionTimeoutMs, ClusterOptions.ElectionTimeoutMs); //If you are a leader or candidate, swap to a follower if (NodeStateService.Role == NodeState.Candidate || NodeStateService.Role == NodeState.Leader) { Logger.LogDebug(NodeStateService.GetNodeLogId() + " detected node " + entry.LeaderId + " is further ahead. Changing to follower"); SetNodeRole(NodeState.Follower); } if (NodeStateService.CurrentLeader != entry.LeaderId) { Logger.LogDebug(NodeStateService.GetNodeLogId() + "Detected uncontacted leader, discovering leader now."); //Reset the current leader NodeStateService.CurrentLeader = entry.LeaderId; } if (entry.LeaderCommit > NodeStateService.LatestLeaderCommit) { Logger.LogDebug(NodeStateService.GetNodeLogId() + "Detected leader commit of " + entry.LeaderCommit + " commiting data on node."); NodeStateService.LatestLeaderCommit = entry.LeaderCommit; } // If your log entry is not within the last snapshot, then check the validity of the previous log index if (_nodeStorage.LastSnapshotIncludedIndex < entry.PrevLogIndex) { var previousEntry = _nodeStorage.GetLogAtIndex(entry.PrevLogIndex); if (previousEntry == null && entry.PrevLogIndex != 0) { Logger.LogDebug(NodeStateService.GetNodeLogId() + "Missing previous entry at index " + entry.PrevLogIndex + " from term " + entry.PrevLogTerm + " does not exist."); return(new AppendEntryResponse() { IsSuccessful = false, ConflictingTerm = null, ConflictName = AppendEntriesExceptionNames.MissingLogEntryException, FirstTermIndex = null, NodeId = _nodeStorage.Id, LastLogEntryIndex = _nodeStorage.GetTotalLogCount() }); } } else { if (_nodeStorage.LastSnapshotIncludedTerm != entry.PrevLogTerm) { Logger.LogDebug(NodeStateService.GetNodeLogId() + "Inconsistency found in the node snapshot and leaders logs, log " + entry.PrevLogIndex + " from term " + entry.PrevLogTerm + " does not exist."); return(new AppendEntryResponse() { ConflictName = AppendEntriesExceptionNames.ConflictingLogEntryException, IsSuccessful = false, ConflictingTerm = entry.PrevLogTerm, NodeId = _nodeStorage.Id, FirstTermIndex = 0 //always set to 0 as snapshots are assumed to be from 0 > n }); } } _nodeStorage.SetCurrentTerm(entry.Term); foreach (var log in entry.Entries) { var existingEnty = _nodeStorage.GetLogAtIndex(log.Index); if (existingEnty != null && existingEnty.Term != log.Term) { Logger.LogDebug(NodeStateService.GetNodeLogId() + "Found inconsistent logs in state, deleting logs from index " + log.Index); _nodeStorage.DeleteLogsFromIndex(log.Index); break; } } NodeStateService.InCluster = true; DateTime time = DateTime.Now; if (entry.Entries != null && _nodeStorage.GetLogAtIndex(entry.PrevLogIndex) != null && entry.Entries.Count() > 0 && entry.Entries.First().Index > 1 && _nodeStorage.GetLogAtIndex(entry.Entries.First().Index - 1).Term != entry.PrevLogTerm) { Logger.LogInformation(NodeStateService.GetNodeLogId() + "Last term does not match, log " + entry.PrevLogIndex + " from term " + entry.PrevLogTerm + " does not exist."); return(new AppendEntryResponse() { ConflictName = AppendEntriesExceptionNames.ConflictingLogEntryException, IsSuccessful = false, ConflictingTerm = entry.PrevLogTerm, NodeId = _nodeStorage.Id, FirstTermIndex = 0//always set to 0 as snapshots are assumed to be from 0 > n }); } foreach (var log in entry.Entries) { try { if (!_nodeStorage.LogExists(log.Index)) { Logger.LogDebug(NodeStateService.GetNodeLogId() + " adding to node storage " + log); _nodeStorage.AddLog(log); } } catch (MissingLogEntryException e) { Logger.LogError(NodeStateService.GetNodeLogId() + " failed to add log " + log.Index + " with exception " + e.Message + Environment.NewLine + e.StackTrace); return(new AppendEntryResponse() { IsSuccessful = false, ConflictingTerm = null, ConflictName = AppendEntriesExceptionNames.MissingLogEntryException, FirstTermIndex = null, NodeId = _nodeStorage.Id, LastLogEntryIndex = _nodeStorage.GetTotalLogCount() }); } catch (Exception e) { Logger.LogError(NodeStateService.GetNodeLogId() + " failed to add log " + log.Index + " with exception " + e.Message + Environment.NewLine + e.StackTrace); return(new AppendEntryResponse() { IsSuccessful = false, ConflictingTerm = null, ConflictName = AppendEntriesExceptionNames.LogAppendingException, FirstTermIndex = null, NodeId = _nodeStorage.Id, LastLogEntryIndex = _nodeStorage.GetTotalLogCount() }); } } return(new AppendEntryResponse() { IsSuccessful = true, NodeId = _nodeStorage.Id, }); }