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