示例#1
0
        public override void Handle(CommandScheduled @event)
        {
            // An entry is considered committed once it has been written to persistant storage and replicated.
            _nodePublisher.PublishEvent(new NodeCommandScheduled
            {
                Command = new CommitEntry
                {
                    EntryIdx  = @event.LogEntry.Index,
                    EntryTerm = @event.LogEntry.Term
                }
            }).Wait();

            @event.Command.Execute(_serviceLocator);
            _nodePublisher.PublishEvent(new NodeCommandScheduled
            {
                Command = new ApplyEntry
                {
                    EntryIdx = @event.LogEntry.Index
                }
            }).Wait();

            if (@event.CompletionSource != null)
            {
                @event.CompletionSource.SetResult(new CommandExecutionResult(true));
            }
        }
示例#2
0
        // TODO: Make this use same buffer and event as RpcLogWriter.
        public void OnNext(ApplyCommandRequested data, long sequence, bool endOfBatch)
        {
            var appliedDifference = data.LogIdx - _node.Data.LastApplied;
            var logsToApply       = EnumerableUtilities.Range(_node.Data.LastApplied + 1, (int)appliedDifference);

            foreach (var logIdx in logsToApply)
            {
                var command = _commandRegister.Get(_node.Data.CurrentTerm, logIdx);

                // The term may have been increased before the command was applied. In which case, rely on log matching to fix.
                if (command == null)
                {
                    continue;
                }

                command.Execute(_serviceLocator);
                _nodePublisher.PublishEvent(new NodeCommandScheduled
                {
                    Command = new ApplyEntry
                    {
                        EntryIdx = logIdx
                    }
                }).Wait();
            }
        }
示例#3
0
        public RequestVoteResponse RequestVote(RequestVoteRequest voteRequest)
        {
            if (voteRequest.Term <= _node.Data.CurrentTerm)
            {
                return(new RequestVoteResponse
                {
                    Term = _node.Data.CurrentTerm,
                    VoteGranted = false
                });
            }

            _nodePublisher.PublishEvent(new NodeCommandScheduled
            {
                Command = new SetNewTerm
                {
                    Term = voteRequest.Term
                }
            }).Wait();

            return(null);
        }
示例#4
0
        public Task <CommandExecutionResult> ExecuteCommand <T>(T command) where T : IRaftCommand, new()
        {
            if (_node.CurrentState != NodeState.Leader)
            {
                throw new NotClusterLeaderException();
            }

            return(_commandPublisher.PublishEvent(
                       new CommandScheduled
            {
                Command = command
            }));
        }
示例#5
0
        public void OnNext(AppendEntriesRequested data, long sequence, bool endOfBatch)
        {
            if (!data.PreviousLogIndex.HasValue || !data.PreviousLogTerm.HasValue)
            {
                return;
            }

            if (data.PreviousLogIndex.Equals(_node.Data.CommitIndex))
            {
                return;
            }

            if (data.PreviousLogIndex > _node.Data.CommitIndex ||
                data.PreviousLogTerm > _node.Data.CurrentTerm)
            {
                throw new InvalidOperationException(
                          "This command is invalid and should not be published to the buffer.");
            }

            var truncateCommandEntry = new TruncateLogCommandEntry
            {
                TruncateFromIndex = data.PreviousLogIndex.Value
            };

            using (var ms = new MemoryStream())
            {
                Serializer.SerializeWithLengthPrefix(ms, truncateCommandEntry, PrefixStyle.Base128);
                _writeDataBlocks.WriteBlock(ms.ToArray());
            }

            _nodePublisher.PublishEvent(
                new NodeCommandScheduled
            {
                Command = new TruncateLog
                {
                    TruncateFromIndex = data.PreviousLogIndex.Value
                }
            });
        }
示例#6
0
        public AppendEntriesResponse AppendEntries(AppendEntriesRequest entriesRequest)
        {
            // If the node term is greater, return before updating timer. Eventually an election will trigger.
            if (_node.Data.CurrentTerm > entriesRequest.Term)
            {
                return new AppendEntriesResponse
                       {
                           Term    = _node.Data.CurrentTerm,
                           Success = false
                       }
            }
            ;

            _timer.ResetTimer();

            if (_node.Data.CurrentTerm < entriesRequest.Term)
            {
                _nodePublisher.PublishEvent(new NodeCommandScheduled
                {
                    Command = new SetNewTerm
                    {
                        Term = entriesRequest.Term
                    }
                }).Wait();
            }

            if (_node.CurrentState == NodeState.Candidate)
            {
                _nodePublisher.PublishEvent(new NodeCommandScheduled
                {
                    Command = new CancelElection()
                }).Wait();
            }

            if (_node.CurrentState != NodeState.Follower)
            {
                throw new FaultException <MultipleLeadersForTermFault>(new MultipleLeadersForTermFault
                {
                    Id = _node.Data.NodeId
                });
            }

            if (_node.Data.Log[entriesRequest.PreviousLogIndex] != entriesRequest.PreviousLogTerm)
            {
                return(new AppendEntriesResponse
                {
                    Term = _node.Data.CurrentTerm,
                    Success = false
                });
            }

            _nodePublisher.PublishEvent(
                new NodeCommandScheduled {
                Command = new SetLeaderInformation
                {
                    LeaderId = entriesRequest.LeaderId
                }
            });

            // TODO: Buffer will be responsible for Log Truncating, Log Writing, Log Applying
            _appendEntriesPublisher.PublishEvent(new AppendEntriesRequested
            {
                PreviousLogIndex = entriesRequest.PreviousLogIndex,
                PreviousLogTerm  = entriesRequest.PreviousLogTerm,
                LeaderCommit     = entriesRequest.LeaderCommit,
                Entries          = entriesRequest.Entries
            });

            return(new AppendEntriesResponse
            {
                Term = _node.Data.CurrentTerm
            });
        }
    }