Exemple #1
0
        public async Task <SendLeaderCommandResponse> Receive(ICommand command)
        {
            if (State is Follower)
            {
                await _messageBus.Send(command, LeaderId);
            }

            if (State is Leader)
            {
                _logger.LogDebug("Server Received Command");
                _appendingEntries = true;
                Log.Add(new Log(CurrentTerm, command));
                CommitIndex = Log.Count - 1;

                var remoteServers = GetRemoteServers();

                var tasks = new Task <AppendEntriesResponse> [remoteServers.Count];

                for (int i = 0; i < tasks.Length; i++)
                {
                    var next = NextIndex.FirstOrDefault(x => x.Id == remoteServers[i].Id);
                    if (next == null)
                    {
                        var nextLogIndex = 0;
                        next = new Next(remoteServers[i].Id, nextLogIndex);
                        NextIndex.Add(next);
                    }
                    var match = MatchIndex.FirstOrDefault(x => x.Id == remoteServers[i].Id);
                    if (match == null)
                    {
                        match = new Match(remoteServers[i].Id, 0);
                        MatchIndex.Add(match);
                    }
                    var lastLogIndex = Log.Count > 0 ? Log.Count - 1 : 0;
                    var lastLogTerm  = lastLogIndex > 0 ? Log[match.MatchIndex].Term : 0;

                    // If last log index ≥ nextIndex for a follower: send
                    // AppendEntries RPC with log entries starting at nextIndex
                    if (lastLogIndex >= next.NextIndex)
                    {
                        var log           = Log[next.NextIndex];
                        var appendEntries = new AppendEntries(CurrentTerm, Id, match.MatchIndex, lastLogTerm, log, CommitIndex, remoteServers[i].Id);
                        tasks[i] = _messageBus.Send(appendEntries);
                    }
                }

                Task.WaitAll(tasks);
                int counter = 0;
                foreach (var task in tasks)
                {
                    _logger.LogDebug($"Processing Append entries counter: {counter}");
                    _logger.LogDebug($"Processing Append entries result was: {task.Result.Success} counter: {counter}");
                    await Receive(task.Result);

                    _logger.LogDebug($"Processed Append entries counter: {counter}");
                }
            }

            return(new SendLeaderCommandResponse());
        }
Exemple #2
0
        private void ResetLeaderState()
        {
            NextIndex.Clear();
            MatchIndex.Clear();

            Cluster.GetNodeIdsExcept(NodeId).ForEach(x => {
                NextIndex[x]  = Log.Count;
                MatchIndex[x] = 0;
            });
        }
Exemple #3
0
        /// <summary>
        /// Triggered by the heartbeatTimer. Sends AppendEntries requests in parallel to all the nodes in the cluster.
        /// If there are unreplicated entries, sends them in the request. Sends a simple heartbeat otherwise.
        /// It also checks MatchIndex for any entries replicated in the majority of nodes, and commits them.
        /// </summary>
        /// <param name="arg">Sent by System.Threading.Timer</param>
        private void SendHeartbeats(object arg)
        {
            var nodes = Cluster.GetNodeIdsExcept(NodeId);

            Parallel.ForEach(nodes, nodeId =>
            {
                if (!NextIndex.ContainsKey(nodeId))
                {
                    return;                                 // Prevents errors when testing
                }
                var prevLogIndex = Math.Max(0, NextIndex[nodeId] - 1);
                int prevLogTerm  = (Log.Count > 0) ? prevLogTerm = Log[prevLogIndex].TermNumber : 0;

                List <LogEntry> entries;

                if (Log.Count > NextIndex[nodeId])
                {
                    LogMessage("Log Count: " + Log.Count + " -- Target node[nextIndex]: " + nodeId + " [" + NextIndex[nodeId] + "]");
                    entries = Log.Skip(NextIndex[nodeId]).ToList();
                }
                else
                {
                    // covers Log is empty or no new entries to replicate
                    entries = null;
                }

                var res = Cluster.SendAppendEntriesTo(nodeId, CurrentTerm, NodeId, prevLogIndex, prevLogTerm, entries, CommitIndex);

                CurrentTerm = res.Term;

                if (res.Value)
                {
                    if (entries != null)
                    {
                        // Entry appended
                        LogMessage("Successful AE to " + nodeId + ". Setting nextIndex to " + NextIndex[nodeId]);
                        NextIndex[nodeId]  = Log.Count;
                        MatchIndex[nodeId] = Log.Count - 1;
                    }
                }
                else
                {
                    LogMessage("Failed AE to " + nodeId + ". Setting nextIndex to " + NextIndex[nodeId]);
                    // Entry failed to be appended
                    if (NextIndex[nodeId] > 0)
                    {
                        NextIndex[nodeId]--;
                    }
                }
            });

            // TODO: Do this as new task?
            // Iterate over all uncommitted entries
            for (int i = CommitIndex + 1; i < Log.Count; i++)
            {
                // We add 1 because we know the entry is replicated in this node
                var replicatedIn = MatchIndex.Values.Count(x => x >= i) + 1;
                if (Log[i].TermNumber == CurrentTerm && replicatedIn >= GetMajority())
                {
                    CommitIndex = i;
                    StateMachine.Apply(Log[i].Command);
                    LastApplied = i;
                }
            }
            // (responder a client request)
        }
Exemple #4
0
 public void ResetLeaderState()
 {
     NextIndex.Clear();
     MatchIndex.Clear();
 }
Exemple #5
0
        private async Task Receive(AppendEntriesResponse appendEntriesResponse)
        {
            if (State is Leader)
            {
                // If RPC request or response contains term T > currentTerm:
                // set currentTerm = T, convert to follower (§5.1)
                if (appendEntriesResponse.Term > CurrentTerm)
                {
                    BecomeFollowerAndMatchTerm(appendEntriesResponse.Term, appendEntriesResponse.FollowerId);
                }

                // If successful: update nextIndex and matchIndex for
                // follower (§5.3)
                if (State is Leader && appendEntriesResponse.Success)
                {
                    var currentNext = NextIndex.First(x => x.Id == appendEntriesResponse.FollowerId);
                    NextIndex.Remove(currentNext);
                    var nextLogIndex = Log.Count;
                    var next         = new Next(appendEntriesResponse.FollowerId, nextLogIndex);
                    NextIndex.Add(next);

                    var currentMatch = MatchIndex.First(x => x.Id == appendEntriesResponse.FollowerId);
                    MatchIndex.Remove(currentMatch);
                    var match = new Match(appendEntriesResponse.FollowerId, currentNext.NextIndex);
                    MatchIndex.Add(match);
                }

                if (State is Leader && _appendingEntries && appendEntriesResponse.Success)
                {
                    CurrentTermAppendEntriesResponse++;

                    if (CurrentTermAppendEntriesResponse >= (_serversInClusterInCluster.Count / 2) + 1)
                    {
                        if ((CommitIndex == 0 && LastApplied == 0) || CommitIndex > LastApplied)
                        {
                            var entry = Log[Log.Count - 1];
                            LastApplied = Log.Count - 1;
                            await _stateMachine.Apply(entry.Command);
                        }

                        CurrentTermAppendEntriesResponse = 0;
                        _appendingEntries = false;
                    }
                }
                // If AppendEntries fails because of log inconsistency:
                // decrement nextIndex and retry (§5.3)
                else if (State is Leader && _appendingEntries)
                {
                    var lastLogIndex = Log.Count - 1;
                    var lastLogTerm  = Log[lastLogIndex].Term;
                    var next         = NextIndex.First(x => x.Id == appendEntriesResponse.FollowerId);

                    if (lastLogIndex >= next.NextIndex)
                    {
                        var log           = Log[next.NextIndex];
                        var appendEntries = new AppendEntries(CurrentTerm, Id, lastLogIndex, lastLogTerm, log, CommitIndex, appendEntriesResponse.FollowerId);
                        var task          = _messageBus.Send(appendEntries);

                        Task.WaitAll(task);

                        await Receive(task.Result);
                    }
                }
            }
        }
Exemple #6
0
 public PeerState(IPeer peer, MatchIndex matchIndex, NextIndex nextIndex)
 {
     Peer       = peer;
     MatchIndex = matchIndex;
     NextIndex  = nextIndex;
 }
Exemple #7
0
 public void UpdateNextIndex(int nextLogIndexToSendToPeer)
 {
     NextIndex = new NextIndex(Peer, nextLogIndexToSendToPeer);
 }