PageRenderTime 76ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/Rachis/Rachis.Tests/CommandsTests.cs

http://github.com/ayende/ravendb
C# | 169 lines | 135 code | 28 blank | 6 comment | 16 complexity | c63711cf17fe28af97d13366b74d31f4 MD5 | raw file
Possible License(s): GPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, Apache-2.0, BSD-3-Clause, CC-BY-SA-3.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using FizzWare.NBuilder;
  7. using FluentAssertions;
  8. using Rachis.Messages;
  9. using Xunit;
  10. using Xunit.Extensions;
  11. namespace Rachis.Tests
  12. {
  13. public class CommandsTests : RaftTestsBase
  14. {
  15. [Fact]
  16. public void When_command_committed_CompletionTaskSource_is_notified()
  17. {
  18. const int CommandCount = 5;
  19. var leader = CreateNetworkAndGetLeader(3);
  20. var commands = Builder<DictionaryCommand.Set>.CreateListOfSize(CommandCount)
  21. .All()
  22. .With(x => x.Completion = new TaskCompletionSource<object>())
  23. .With(x => x.AssignedIndex = -1)
  24. .Build()
  25. .ToList();
  26. var nonLeaderNode = Nodes.First(x => x.State != RaftEngineState.Leader);
  27. var commitsAppliedEvent = new ManualResetEventSlim();
  28. nonLeaderNode.CommitIndexChanged += (oldIndex, newIndex) =>
  29. {
  30. //CommandCount + 1 --> take into account NOP command that leader sends after election
  31. if (newIndex == CommandCount + 1)
  32. commitsAppliedEvent.Set();
  33. };
  34. commands.ForEach(leader.AppendCommand);
  35. Assert.True(commitsAppliedEvent.Wait(nonLeaderNode.Options.ElectionTimeout * 2));
  36. commands.Should().OnlyContain(cmd => cmd.Completion.Task.Status == TaskStatus.RanToCompletion);
  37. }
  38. //this test is a show-case of how to check for command commit time-out
  39. [Fact]
  40. public void Command_not_committed_after_timeout_CompletionTaskSource_is_notified()
  41. {
  42. const int CommandCount = 5;
  43. var leader = CreateNetworkAndGetLeader(3);
  44. var commands = Builder<DictionaryCommand.Set>.CreateListOfSize(CommandCount)
  45. .All()
  46. .With(x => x.Completion = new TaskCompletionSource<object>())
  47. .With(x => x.AssignedIndex = -1)
  48. .Build()
  49. .ToList();
  50. var nonLeaderNode = Nodes.First(x => x.State != RaftEngineState.Leader);
  51. var commitsAppliedEvent = new ManualResetEventSlim();
  52. nonLeaderNode.CommitIndexChanged += (oldIndex, newIndex) =>
  53. {
  54. //essentially fire event for (CommandCount - 1) + Nop command
  55. if (newIndex == CommandCount)
  56. commitsAppliedEvent.Set();
  57. };
  58. //don't append the last command yet
  59. commands.Take(CommandCount - 1).ToList().ForEach(leader.AppendCommand);
  60. //make sure commands that were appended before network leader disconnection are replicated
  61. Assert.True(commitsAppliedEvent.Wait(nonLeaderNode.Options.ElectionTimeout * 3));
  62. DisconnectNode(leader.Name);
  63. var lastCommand = commands.Last();
  64. var commandCompletionTask = lastCommand.Completion.Task;
  65. leader.AppendCommand(lastCommand);
  66. var aggregateException = Assert.Throws<AggregateException>(() => commandCompletionTask.Wait(leader.Options.ElectionTimeout * 2));
  67. Assert.IsType<TimeoutException>(aggregateException.InnerException);
  68. }
  69. [Theory]
  70. [InlineData(2)]
  71. [InlineData(3)]
  72. public void Leader_AppendCommand_for_first_time_should_distribute_commands_between_nodes(int nodeCount)
  73. {
  74. const int CommandCount = 5;
  75. var commandsToDistribute = Builder<DictionaryCommand.Set>.CreateListOfSize(CommandCount)
  76. .All()
  77. .With(x => x.Completion = null)
  78. .Build()
  79. .ToList();
  80. var leader = CreateNetworkAndGetLeader(nodeCount);
  81. var entriesAppended = new Dictionary<string, List<LogEntry>>();
  82. Nodes.ToList().ForEach(node =>
  83. {
  84. entriesAppended.Add(node.Name, new List<LogEntry>());
  85. node.EntriesAppended += logEntries => entriesAppended[node.Name].AddRange(logEntries);
  86. });
  87. var nonLeaderNode = Nodes.First(x => x.State != RaftEngineState.Leader);
  88. var commitsAppliedEvent = new ManualResetEventSlim();
  89. if (nonLeaderNode.CommitIndex == CommandCount + 1) //precaution
  90. commitsAppliedEvent.Set();
  91. nonLeaderNode.CommitIndexChanged += (oldIndex, newIndex) =>
  92. {
  93. //CommandCount + 1 --> take into account NOP command that leader sends after election
  94. if (newIndex == CommandCount + 1)
  95. commitsAppliedEvent.Set();
  96. };
  97. commandsToDistribute.ForEach(leader.AppendCommand);
  98. var millisecondsTimeout = 10000 * nodeCount;
  99. Assert.True(commitsAppliedEvent.Wait(millisecondsTimeout), "within " + millisecondsTimeout + " sec. non leader node should have all relevant commands committed");
  100. }
  101. [Theory]
  102. [InlineData(3)]
  103. [InlineData(2)]
  104. [InlineData(10)]
  105. public void Leader_AppendCommand_several_times_should_distribute_commands_between_nodes(int nodeCount)
  106. {
  107. const int CommandCount = 5;
  108. var commands = Builder<DictionaryCommand.Set>.CreateListOfSize(CommandCount * 2)
  109. .All()
  110. .With(x => x.Completion = null)
  111. .Build()
  112. .ToList();
  113. var leader = CreateNetworkAndGetLeader(nodeCount, messageTimeout: 10000);
  114. var entriesAppended = new Dictionary<string, List<LogEntry>>();
  115. Nodes.ToList().ForEach(node =>
  116. {
  117. entriesAppended.Add(node.Name, new List<LogEntry>());
  118. node.EntriesAppended += logEntries => entriesAppended[node.Name].AddRange(logEntries);
  119. });
  120. var nonLeaderNode = Nodes.First(x => x.State != RaftEngineState.Leader);
  121. var commitsAppliedEvent = new ManualResetEventSlim();
  122. nonLeaderNode.CommitApplied += (cmd) =>
  123. {
  124. if (cmd.AssignedIndex == commands.Last().AssignedIndex)
  125. commitsAppliedEvent.Set();
  126. };
  127. commands.Take(CommandCount).ToList().ForEach(leader.AppendCommand);
  128. commands.Skip(CommandCount).ToList().ForEach(leader.AppendCommand);
  129. var millisecondsTimeout = 10000 * nodeCount;
  130. Assert.True(commitsAppliedEvent.Wait(millisecondsTimeout), "within " + millisecondsTimeout + " sec. non leader node should have all relevant commands committed");
  131. var committedCommands = nonLeaderNode.PersistentState.LogEntriesAfter(0).Select(x => nonLeaderNode.PersistentState.CommandSerializer.Deserialize(x.Data))
  132. .OfType<DictionaryCommand.Set>().ToList();
  133. Assert.Equal(10, committedCommands.Count);
  134. for (int i = 0; i < 10; i++)
  135. {
  136. Assert.Equal(commands[i].Value, committedCommands[i].Value);
  137. Assert.Equal(commands[i].AssignedIndex, committedCommands[i].AssignedIndex);
  138. }
  139. }
  140. }
  141. }