예제 #1
0
        public void SetLeaderIdWhenFollowerAndAppendEntriesRecieved()
        {
            // Arrange
            var message = new AppendEntriesRequest
            {
                Term             = 0,
                PreviousLogIndex = 0,
                PreviousLogTerm  = 0
            };

            var raftNode = Substitute.For <INode>();

            raftNode.CurrentState.Returns(NodeState.Follower);
            raftNode.Data.Returns(new NodeData());

            var timer = Substitute.For <INodeTimer>();
            var appendEntriesPublisher = Substitute.For <IPublishToBuffer <AppendEntriesRequested> >();
            var nodePublisher          = new TestBufferPublisher <NodeCommandScheduled, NodeCommandResult>();

            var service = new RaftService(appendEntriesPublisher, nodePublisher, timer, raftNode);

            // Act
            service.AppendEntries(message);

            // Assert
            nodePublisher.Events.Should().HaveCount(1);
            nodePublisher.Events[0].Command.Should().BeOfType <SetLeaderInformation>();
        }
예제 #2
0
        public void ThrowsWhenAppendEntriesReceivedAndNodeWillNotBeTransitionedToFollower()
        {
            // Arrange
            var message = new AppendEntriesRequest
            {
                Term             = 24,
                PreviousLogIndex = 0,
                PreviousLogTerm  = 0
            };

            var raftNode = Substitute.For <INode>();

            raftNode.CurrentState.Returns(NodeState.Leader);
            raftNode.Data.Returns(new NodeData {
                CurrentTerm = 24
            });

            var timer = Substitute.For <INodeTimer>();
            var appendEntriesPublisher = Substitute.For <IPublishToBuffer <AppendEntriesRequested> >();
            var nodePublisher          = new TestBufferPublisher <NodeCommandScheduled, NodeCommandResult>();

            var service = new RaftService(appendEntriesPublisher, nodePublisher, timer, raftNode);

            // Act
            var actAction = new Action(() => service.AppendEntries(message));

            // Assert
            actAction.ShouldThrow <FaultException <MultipleLeadersForTermFault> >();
        }
예제 #3
0
        public void CancelElectionIfCandidateAndReceiveAppendEntries()
        {
            // Arrange
            var message = new AppendEntriesRequest
            {
                Term             = 0,
                PreviousLogIndex = 0,
                PreviousLogTerm  = 0
            };

            var raftNode = Substitute.For <INode>();

            raftNode.CurrentState.Returns(NodeState.Candidate);
            raftNode.Data.Returns(new NodeData());

            var timer = Substitute.For <INodeTimer>();
            var appendEntriesPublisher = Substitute.For <IPublishToBuffer <AppendEntriesRequested> >();
            var nodePublisher          = new TestBufferPublisher <NodeCommandScheduled, NodeCommandResult>();

            nodePublisher.OnPublish(() => raftNode.CurrentState.Returns(NodeState.Follower));

            var service = new RaftService(appendEntriesPublisher, nodePublisher, timer, raftNode);

            // Act
            service.AppendEntries(message);

            // Assert
            nodePublisher.Events.Should().HaveCount(2);
            nodePublisher.Events[0].Command.Should().BeOfType <CancelElection>();
        }
예제 #4
0
        public void AppendEntriesAmendsTermOnRaftNodeWhenTermIsGreaterThanCurrentTerm()
        {
            // Arrange
            var message = new AppendEntriesRequest
            {
                Term             = 1,
                PreviousLogIndex = 1,
                PreviousLogTerm  = 0
            };

            var raftNode = Substitute.For <INode>();

            raftNode.CurrentState.Returns(NodeState.Follower);

            var timer = Substitute.For <INodeTimer>();
            var appendEntriesPublisher = Substitute.For <IPublishToBuffer <AppendEntriesRequested> >();
            var nodePublisher          = new TestBufferPublisher <NodeCommandScheduled, NodeCommandResult>();

            var service = new RaftService(appendEntriesPublisher, nodePublisher, timer, raftNode);

            var nodeData = new NodeData
            {
                Log = new NodeLog()
            };

            nodeData.Log.SetLogEntry(1, 0);
            raftNode.Data.Returns(nodeData);

            // Act
            service.AppendEntries(message);

            // Assert
            nodePublisher.Events.Should().HaveCount(2);
            nodePublisher.Events[0].Command.Should().BeOfType <SetNewTerm>();
        }
예제 #5
0
        public void PublishesTruncateLogCommandToNodeAfterEntrySuccessfullyWritten()
        {
            // Arrange
            var @event = new AppendEntriesRequested
            {
                PreviousLogTerm  = 1,
                PreviousLogIndex = 5
            };

            var nodeData = new NodeData
            {
                CommitIndex = 20,
                CurrentTerm = 2
            };

            var writeDataBlocks = Substitute.For <IWriteDataBlocks>();
            var nodePublisher   = new TestBufferPublisher <NodeCommandScheduled, NodeCommandResult>();

            var raftNode = Substitute.For <INode>();

            raftNode.Data.Returns(nodeData);

            var handler = new RpcLogTruncator(raftNode, writeDataBlocks, nodePublisher);

            // Act
            handler.OnNext(@event, 0L, false);

            // Assert
            writeDataBlocks.Received().WriteBlock(Arg.Any <byte[]>());
            nodePublisher.Events.Should().HaveCount(1);
            nodePublisher.Events[0].Command.Should().BeOfType <TruncateLog>();
            ((TruncateLog)nodePublisher.Events[0].Command).TruncateFromIndex.Should().Be(@event.PreviousLogIndex);
        }
예제 #6
0
        public void PublishesToBufferAfterSafetyChecks()
        {
            // Arrange
            var message = new AppendEntriesRequest
            {
                Term             = 11,
                PreviousLogIndex = 2,
                PreviousLogTerm  = 13,
                LeaderCommit     = 12,
                Entries          = new []
                {
                    BitConverter.GetBytes(100),
                    BitConverter.GetBytes(200)
                }
            };

            var raftNode = Substitute.For <INode>();

            raftNode.CurrentState.Returns(NodeState.Follower);

            var nodeData = new NodeData {
                Log = new NodeLog()
            };

            nodeData.Log.SetLogEntry(message.PreviousLogIndex, message.PreviousLogTerm);

            raftNode.Data.Returns(nodeData);

            var timer = Substitute.For <INodeTimer>();
            var appendEntriesPublisher = Substitute.For <IPublishToBuffer <AppendEntriesRequested> >();
            var nodePublisher          = new TestBufferPublisher <NodeCommandScheduled, NodeCommandResult>();

            var service = new RaftService(appendEntriesPublisher, nodePublisher, timer, raftNode);

            // Act
            service.AppendEntries(message);

            // Assert
            appendEntriesPublisher.Received()
            .PublishEvent(Arg.Is <AppendEntriesRequested>(requested =>
                                                          requested.PreviousLogIndex == message.PreviousLogIndex &&
                                                          requested.PreviousLogTerm == message.PreviousLogTerm &&
                                                          requested.LeaderCommit == message.LeaderCommit &&
                                                          requested.Entries == message.Entries));
        }
예제 #7
0
        public void DoesCallApplyCommandOnRaftNodeWithCorrectLogIdx()
        {
            // Arrange
            const long logIdx = 3L;
            var        @event = TestEventFactory.GetCommandEvent(logIdx, new byte[8]);

            var nodePublisher  = new TestBufferPublisher <NodeCommandScheduled, NodeCommandResult>();
            var serviceLocator = Substitute.For <IServiceLocator>();

            var handler = new CommandFinalizer(serviceLocator, nodePublisher);

            // Act
            handler.Handle(@event);

            // Assert
            nodePublisher.Events.Count.Should().BeGreaterThan(1);
            nodePublisher.Events[1].Command.Should().BeOfType <ApplyEntry>();
            ((ApplyEntry)nodePublisher.Events[1].Command).EntryIdx.Should().Be(logIdx);
        }
예제 #8
0
        public void RequestVoteAmendsTermOnRaftNodeWhenTermIsGreaterThanCurrentTerm()
        {
            // Arrange
            var message = new RequestVoteRequest
            {
                Term = 1
            };

            var raftNode = Substitute.For <INode>();
            var timer    = Substitute.For <INodeTimer>();
            var appendEntriesPublisher = Substitute.For <IPublishToBuffer <AppendEntriesRequested> >();
            var nodePublisher          = new TestBufferPublisher <NodeCommandScheduled, NodeCommandResult>();

            var service = new RaftService(appendEntriesPublisher, nodePublisher, timer, raftNode);

            raftNode.Data.Returns(new NodeData());

            // Act
            service.RequestVote(message);

            // Assert
            nodePublisher.Events.Should().HaveCount(1);
            nodePublisher.Events[0].Command.Should().BeOfType <SetNewTerm>();
        }
예제 #9
0
        public void DoesCommitLogEntryOnRaftNode()
        {
            // Arrange
            const long commitIdx = 3L;
            const long term      = 5L;

            var @event = TestEventFactory.GetCommandEvent(commitIdx, new byte[8]);

            @event.LogEntry.Term = term;

            var serviceLocator = Substitute.For <IServiceLocator>();
            var nodePublisher  = new TestBufferPublisher <NodeCommandScheduled, NodeCommandResult>();

            var handler = new CommandFinalizer(serviceLocator, nodePublisher);

            // Act
            handler.Handle(@event);

            // Assert
            nodePublisher.Events.Count.Should().BeGreaterThan(0);
            nodePublisher.Events[0].Command.Should().BeOfType <CommitEntry>();
            ((CommitEntry)nodePublisher.Events[0].Command).EntryIdx.Should().Be(commitIdx);
            ((CommitEntry)nodePublisher.Events[0].Command).EntryTerm.Should().Be(term);
        }