diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 diff --git a/src/Rafty/BecomeLeader.cs b/src/Rafty/BecomeLeader.cs deleted file mode 100644 index 64cbcd4..0000000 --- a/src/Rafty/BecomeLeader.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Rafty -{ - public class BecomeLeader : Message - { - - } -} \ No newline at end of file diff --git a/src/Rafty/Command.cs b/src/Rafty/Command.cs deleted file mode 100644 index ac18cec..0000000 --- a/src/Rafty/Command.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Rafty -{ - public abstract class Command : ICommand - { - - } - - public interface ICommand - { - - } -} \ No newline at end of file diff --git a/src/Rafty/Commands/Command.cs b/src/Rafty/Commands/Command.cs new file mode 100644 index 0000000..af9eaef --- /dev/null +++ b/src/Rafty/Commands/Command.cs @@ -0,0 +1,20 @@ +using System; +using Rafty.Messages; + +namespace Rafty.Commands +{ + public abstract class Command : ICommand, IMessage + { + protected Command() + { + MessageId = Guid.NewGuid(); + } + + public Guid MessageId { get; } + } + + public interface ICommand + { + + } +} \ No newline at end of file diff --git a/src/Rafty/FakeCommand.cs b/src/Rafty/FakeCommand.cs deleted file mode 100644 index 3b84815..0000000 --- a/src/Rafty/FakeCommand.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Rafty -{ - public class FakeCommand : Command - { - public FakeCommand(Guid id) - { - this.Id = id; - - } - public Guid Id { get; set; } - } -} \ No newline at end of file diff --git a/src/Rafty/IReportable.cs b/src/Rafty/Infrastructure/IReportable.cs similarity index 77% rename from src/Rafty/IReportable.cs rename to src/Rafty/Infrastructure/IReportable.cs index 5e451a3..2784f59 100644 --- a/src/Rafty/IReportable.cs +++ b/src/Rafty/Infrastructure/IReportable.cs @@ -1,4 +1,4 @@ -namespace Rafty +namespace Rafty.Infrastructure { public interface IReportable { diff --git a/src/Rafty/RaftyConfigurationExtensions.cs b/src/Rafty/Infrastructure/RaftyConfigurationExtensions.cs similarity index 80% rename from src/Rafty/RaftyConfigurationExtensions.cs rename to src/Rafty/Infrastructure/RaftyConfigurationExtensions.cs index 9155f97..e105277 100644 --- a/src/Rafty/RaftyConfigurationExtensions.cs +++ b/src/Rafty/Infrastructure/RaftyConfigurationExtensions.cs @@ -1,15 +1,17 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Text; -using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Console; using Newtonsoft.Json; +using Rafty.Commands; +using Rafty.Messages; +using Rafty.Messaging; +using Rafty.Raft; +using Rafty.ServiceDiscovery; +using Rafty.State; -namespace Rafty +namespace Rafty.Infrastructure { public static class RaftyConfigurationExtensions { @@ -21,11 +23,11 @@ public static IApplicationBuilder UseRafty(this IApplicationBuilder builder, IStateMachine stateMachine, IServiceRegistry serviceRegistry, ILogger logger, - List remoteServers, + IServersInCluster serversInCluster, string raftyBasePath = null) { builder.UseRaftyForTesting(baseUri, messageSender, messageBus, stateMachine, serviceRegistry, - logger, remoteServers, raftyBasePath); + logger, serversInCluster, raftyBasePath); return builder; } @@ -37,12 +39,12 @@ public static (IApplicationBuilder builder, Server server, ServerInCluster serve IStateMachine stateMachine, IServiceRegistry serviceRegistry, ILogger logger, - List remoteServers, + IServersInCluster serversInCluster, string raftyBasePath = null) { var urlConfig = RaftyUrlConfig.Get(raftyBasePath); - var server = new Server(messageBus, remoteServers, stateMachine, logger); + var server = new Server(messageBus, serversInCluster, stateMachine, logger); serviceRegistry.Register(new RegisterService(RaftyServiceDiscoveryName.Get(), server.Id, baseUri)); @@ -50,7 +52,7 @@ public static (IApplicationBuilder builder, Server server, ServerInCluster serve var serverInCluster = new ServerInCluster(server.Id); - remoteServers.Add(serverInCluster); + serversInCluster.Add(serverInCluster); builder.Map(urlConfig.appendEntriesUrl, app => { @@ -60,7 +62,10 @@ public static (IApplicationBuilder builder, Server server, ServerInCluster serve { var reader = new StreamReader(context.Request.Body); var content = reader.ReadToEnd(); - var appendEntries = JsonConvert.DeserializeObject(content); + var appendEntries = JsonConvert.DeserializeObject(content, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); var appendEntriesResponse = server.Receive(appendEntries); await context.Response.WriteAsync(JsonConvert.SerializeObject(appendEntriesResponse)); } @@ -79,7 +84,10 @@ public static (IApplicationBuilder builder, Server server, ServerInCluster serve { var reader = new StreamReader(context.Request.Body); var content = reader.ReadToEnd(); - var requestVote = JsonConvert.DeserializeObject(content); + var requestVote = JsonConvert.DeserializeObject(content, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); var requestVoteResponse = server.Receive(requestVote); await context.Response.WriteAsync(JsonConvert.SerializeObject(requestVoteResponse)); } @@ -98,7 +106,10 @@ public static (IApplicationBuilder builder, Server server, ServerInCluster serve { var reader = new StreamReader(context.Request.Body); var content = reader.ReadToEnd(); - var command = JsonConvert.DeserializeObject(content); + var command = JsonConvert.DeserializeObject(content, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); var sendCommandToLeaderResponse = server.Receive(command); await context.Response.WriteAsync(JsonConvert.SerializeObject(sendCommandToLeaderResponse)); } diff --git a/src/Rafty/RaftyUrlConfig.cs b/src/Rafty/Infrastructure/RaftyUrlConfig.cs similarity index 96% rename from src/Rafty/RaftyUrlConfig.cs rename to src/Rafty/Infrastructure/RaftyUrlConfig.cs index 4d54ba1..6f434b0 100644 --- a/src/Rafty/RaftyUrlConfig.cs +++ b/src/Rafty/Infrastructure/RaftyUrlConfig.cs @@ -1,4 +1,4 @@ -namespace Rafty +namespace Rafty.Infrastructure { public static class RaftyUrlConfig { diff --git a/src/Rafty/Leader.cs b/src/Rafty/Leader.cs deleted file mode 100644 index e2f308a..0000000 --- a/src/Rafty/Leader.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Rafty -{ - public class Leader : State - { - - } -} \ No newline at end of file diff --git a/src/Rafty/AppendEntries.cs b/src/Rafty/Messages/AppendEntries.cs similarity index 94% rename from src/Rafty/AppendEntries.cs rename to src/Rafty/Messages/AppendEntries.cs index b4da987..c04c754 100644 --- a/src/Rafty/AppendEntries.cs +++ b/src/Rafty/Messages/AppendEntries.cs @@ -1,7 +1,7 @@ using System; -using System.Collections.Generic; +using Rafty.State; -namespace Rafty +namespace Rafty.Messages { public class AppendEntries : Message { diff --git a/src/Rafty/BecomeCandidate.cs b/src/Rafty/Messages/BecomeCandidate.cs similarity index 68% rename from src/Rafty/BecomeCandidate.cs rename to src/Rafty/Messages/BecomeCandidate.cs index 61503e4..2e4c539 100644 --- a/src/Rafty/BecomeCandidate.cs +++ b/src/Rafty/Messages/BecomeCandidate.cs @@ -1,12 +1,12 @@ using System; -namespace Rafty +namespace Rafty.Messages { public class BecomeCandidate : Message { public BecomeCandidate(Guid lastAppendEntriesMessageIdFromLeader) { - this.LastAppendEntriesMessageIdFromLeader = lastAppendEntriesMessageIdFromLeader; + LastAppendEntriesMessageIdFromLeader = lastAppendEntriesMessageIdFromLeader; } public Guid LastAppendEntriesMessageIdFromLeader { get; private set; } } diff --git a/src/Rafty/IMessage.cs b/src/Rafty/Messages/IMessage.cs similarity index 65% rename from src/Rafty/IMessage.cs rename to src/Rafty/Messages/IMessage.cs index 7748a2e..ba799ff 100644 --- a/src/Rafty/IMessage.cs +++ b/src/Rafty/Messages/IMessage.cs @@ -1,7 +1,7 @@ -namespace Rafty -{ - using System; +using System; +namespace Rafty.Messages +{ public interface IMessage { Guid MessageId { get; } diff --git a/src/Rafty/Message.cs b/src/Rafty/Messages/Message.cs similarity index 88% rename from src/Rafty/Message.cs rename to src/Rafty/Messages/Message.cs index 43b17e9..f73f676 100644 --- a/src/Rafty/Message.cs +++ b/src/Rafty/Messages/Message.cs @@ -1,6 +1,6 @@ using System; -namespace Rafty +namespace Rafty.Messages { public class Message : IMessage { diff --git a/src/Rafty/RequestVote.cs b/src/Rafty/Messages/RequestVote.cs similarity index 96% rename from src/Rafty/RequestVote.cs rename to src/Rafty/Messages/RequestVote.cs index cd1ba46..c1434eb 100644 --- a/src/Rafty/RequestVote.cs +++ b/src/Rafty/Messages/RequestVote.cs @@ -1,6 +1,6 @@ using System; -namespace Rafty +namespace Rafty.Messages { public class RequestVote : Message { diff --git a/src/Rafty/SendHeartbeat.cs b/src/Rafty/Messages/SendHeartbeat.cs similarity index 69% rename from src/Rafty/SendHeartbeat.cs rename to src/Rafty/Messages/SendHeartbeat.cs index 762e53c..67d97a6 100644 --- a/src/Rafty/SendHeartbeat.cs +++ b/src/Rafty/Messages/SendHeartbeat.cs @@ -1,4 +1,4 @@ -namespace Rafty +namespace Rafty.Messages { public class SendHeartbeat : Message { diff --git a/src/Rafty/SendLeaderCommand.cs b/src/Rafty/Messages/SendLeaderCommand.cs similarity index 60% rename from src/Rafty/SendLeaderCommand.cs rename to src/Rafty/Messages/SendLeaderCommand.cs index 7ce29eb..7dd02ef 100644 --- a/src/Rafty/SendLeaderCommand.cs +++ b/src/Rafty/Messages/SendLeaderCommand.cs @@ -1,7 +1,7 @@ using System; -using Newtonsoft.Json; +using Rafty.Commands; -namespace Rafty +namespace Rafty.Messages { public class SendLeaderCommand : Message { @@ -11,13 +11,6 @@ public SendLeaderCommand(ICommand command, Guid leaderId) LeaderId = leaderId; } - [JsonConstructor] - public SendLeaderCommand(FakeCommand command, Guid leaderId) - { - Command = command; - LeaderId = leaderId; - } - public ICommand Command { get; private set; } public Guid LeaderId { get; private set; } } diff --git a/src/Rafty/SendToSelf.cs b/src/Rafty/Messages/SendToSelf.cs similarity index 94% rename from src/Rafty/SendToSelf.cs rename to src/Rafty/Messages/SendToSelf.cs index a67f5be..e02450d 100644 --- a/src/Rafty/SendToSelf.cs +++ b/src/Rafty/Messages/SendToSelf.cs @@ -1,4 +1,4 @@ -namespace Rafty +namespace Rafty.Messages { public class SendToSelf : Message { diff --git a/src/Rafty/FakeMessageBus.cs b/src/Rafty/Messaging/FakeMessageBus.cs similarity index 88% rename from src/Rafty/FakeMessageBus.cs rename to src/Rafty/Messaging/FakeMessageBus.cs index bb9ba53..45cf265 100644 --- a/src/Rafty/FakeMessageBus.cs +++ b/src/Rafty/Messaging/FakeMessageBus.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; +using Rafty.Commands; +using Rafty.Messages; +using Rafty.Responses; -namespace Rafty +namespace Rafty.Messaging { - using System.Threading.Tasks; - public class FakeMessageBus : IMessageBus { public List SendToSelfMessages { get; private set; } diff --git a/src/Rafty/HttpClientMessageSender.cs b/src/Rafty/Messaging/HttpClientMessageSender.cs similarity index 82% rename from src/Rafty/HttpClientMessageSender.cs rename to src/Rafty/Messaging/HttpClientMessageSender.cs index 04781bb..f5bd5e5 100644 --- a/src/Rafty/HttpClientMessageSender.cs +++ b/src/Rafty/Messaging/HttpClientMessageSender.cs @@ -3,13 +3,19 @@ using System.Linq; using System.Net.Http; using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; - -namespace Rafty +using Rafty.Commands; +using Rafty.Infrastructure; +using Rafty.Messages; +using Rafty.Raft; +using Rafty.Responses; +using Rafty.ServiceDiscovery; + +namespace Rafty.Messaging { - using System.Threading; - using System.Threading.Tasks; - public class HttpClientMessageSender : IMessageSender { private readonly IServiceRegistry _serviceRegistry; @@ -20,16 +26,17 @@ public class HttpClientMessageSender : IMessageSender private readonly string _appendEntriesUrl; private readonly string _requestVoteUrl; private readonly string _commandUrl; + private readonly ILogger _logger; - public HttpClientMessageSender(IServiceRegistry serviceRegistry, string raftyBasePath = null) + public HttpClientMessageSender(IServiceRegistry serviceRegistry, ILogger logger, string raftyBasePath = null) { var urlConfig = RaftyUrlConfig.Get(raftyBasePath); _appendEntriesUrl = urlConfig.appendEntriesUrl; _requestVoteUrl = urlConfig.requestVoteUrl; _commandUrl = urlConfig.commandUrl; - _serviceRegistry = serviceRegistry; + _logger = logger; _sendToSelfHandlers = new Dictionary> { {typeof(BecomeCandidate), x => _server.Receive((BecomeCandidate) x)}, @@ -43,7 +50,10 @@ public async Task Send(AppendEntries appendEntries) try { var serverToSendMessageTo = _serviceRegistry.Get(RaftyServiceDiscoveryName.Get()).First(x => x.Id == appendEntries.FollowerId); - var json = JsonConvert.SerializeObject(appendEntries); + var json = JsonConvert.SerializeObject(appendEntries, Formatting.None, new JsonSerializerSettings + { + TypeNameHandling= TypeNameHandling.All + }); var httpContent = new StringContent(json); httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); @@ -59,7 +69,7 @@ public async Task Send(AppendEntries appendEntries) } catch (Exception exception) { - Console.WriteLine(exception); + _logger.LogError(new EventId(1), exception, "Error in Send(AppendEntries appendEntries)"); throw; } } @@ -69,7 +79,10 @@ public async Task Send(RequestVote requestVote) try { var serverToSendMessageTo = _serviceRegistry.Get(RaftyServiceDiscoveryName.Get()).First(x => x.Id == requestVote.VoterId); - var json = JsonConvert.SerializeObject(requestVote); + var json = JsonConvert.SerializeObject(requestVote, Formatting.None, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); var httpContent = new StringContent(json); httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); @@ -85,7 +98,7 @@ public async Task Send(RequestVote requestVote) } catch (Exception exception) { - Console.WriteLine(exception); + _logger.LogError(new EventId(1), exception, "Error in Send(RequestVote requestVote)"); throw; } } @@ -95,7 +108,10 @@ public async Task Send(ICommand command, Guid leaderI try { var serverToSendMessageTo = _serviceRegistry.Get(RaftyServiceDiscoveryName.Get()).First(x => x.Id == leaderId); - var json = JsonConvert.SerializeObject(command); + var json = JsonConvert.SerializeObject(command, Formatting.None, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); var httpContent = new StringContent(json); httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); @@ -111,7 +127,7 @@ public async Task Send(ICommand command, Guid leaderI } catch (Exception exception) { - Console.WriteLine(exception); + _logger.LogError(new EventId(1), exception, "Error in Send(ICommand command, Guid leaderId)"); throw; } } @@ -151,7 +167,5 @@ public void SetServer(Server server) { _server = server; } - - } } \ No newline at end of file diff --git a/src/Rafty/IMessageBus.cs b/src/Rafty/Messaging/IMessageBus.cs similarity index 73% rename from src/Rafty/IMessageBus.cs rename to src/Rafty/Messaging/IMessageBus.cs index c71b9d9..d333ac4 100644 --- a/src/Rafty/IMessageBus.cs +++ b/src/Rafty/Messaging/IMessageBus.cs @@ -1,9 +1,11 @@ using System; +using System.Threading.Tasks; +using Rafty.Commands; +using Rafty.Messages; +using Rafty.Responses; -namespace Rafty +namespace Rafty.Messaging { - using System.Threading.Tasks; - public interface IMessageBus { void Publish(SendToSelf message); diff --git a/src/Rafty/IMessageSender.cs b/src/Rafty/Messaging/IMessageSender.cs similarity index 71% rename from src/Rafty/IMessageSender.cs rename to src/Rafty/Messaging/IMessageSender.cs index 1116c12..d8bbde4 100644 --- a/src/Rafty/IMessageSender.cs +++ b/src/Rafty/Messaging/IMessageSender.cs @@ -1,9 +1,12 @@ using System; +using System.Threading.Tasks; +using Rafty.Commands; +using Rafty.Messages; +using Rafty.Raft; +using Rafty.Responses; -namespace Rafty +namespace Rafty.Messaging { - using System.Threading.Tasks; - public interface IMessageSender { void Send(SendToSelf message); diff --git a/src/Rafty/InMemoryBus.cs b/src/Rafty/Messaging/InMemoryBus.cs similarity index 92% rename from src/Rafty/InMemoryBus.cs rename to src/Rafty/Messaging/InMemoryBus.cs index d51fefe..eba5256 100644 --- a/src/Rafty/InMemoryBus.cs +++ b/src/Rafty/Messaging/InMemoryBus.cs @@ -1,12 +1,15 @@ +using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Threading; +using System.Threading.Tasks; +using Rafty.Commands; +using Rafty.Infrastructure; +using Rafty.Messages; +using Rafty.Responses; -namespace Rafty +namespace Rafty.Messaging { - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - public class InMemoryBus : IMessageBus, IReportable { private readonly IMessageSender _messageSender; diff --git a/src/Rafty/Candidate.cs b/src/Rafty/Raft/Candidate.cs similarity index 67% rename from src/Rafty/Candidate.cs rename to src/Rafty/Raft/Candidate.cs index 2b88827..59c4720 100644 --- a/src/Rafty/Candidate.cs +++ b/src/Rafty/Raft/Candidate.cs @@ -1,4 +1,4 @@ -namespace Rafty +namespace Rafty.Raft { public class Candidate : State { diff --git a/src/Rafty/Follower.cs b/src/Rafty/Raft/Follower.cs similarity index 70% rename from src/Rafty/Follower.cs rename to src/Rafty/Raft/Follower.cs index 8b9169e..525cd99 100644 --- a/src/Rafty/Follower.cs +++ b/src/Rafty/Raft/Follower.cs @@ -1,4 +1,4 @@ -namespace Rafty +namespace Rafty.Raft { public class Follower : State { diff --git a/src/Rafty/Raft/IServersInCluster.cs b/src/Rafty/Raft/IServersInCluster.cs new file mode 100644 index 0000000..efecd96 --- /dev/null +++ b/src/Rafty/Raft/IServersInCluster.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using Rafty.Infrastructure; +using Rafty.ServiceDiscovery; + +namespace Rafty.Raft +{ + public interface IServersInCluster + { + int Count { get; } + void Add(ServerInCluster serverInCluster); + void Add(List serverInCluster); + void Remove(ServerInCluster serverInCluster); + bool Contains(Guid id); + List Get(Func predicate); + } + + public class InMemoryServersInCluster : IServersInCluster + { + public InMemoryServersInCluster() + { + All = new List(); + } + + public InMemoryServersInCluster(List remoteServers) + { + All = remoteServers ?? new List(); + } + + public int Count => All.Count; + public List All { get; } + + + public void Add(ServerInCluster serverInCluster) + { + All.Add(serverInCluster); + } + + public void Add(List serverInCluster) + { + All.AddRange(serverInCluster); + } + + public void Remove(ServerInCluster serverInCluster) + { + All.Remove(serverInCluster); + } + + public bool Contains(Guid id) + { + return All.Select(x => x.Id).Contains(id); + } + + public List Get(Func predicate) + { + return All.Where(predicate).ToList(); + } + } +} diff --git a/src/Rafty/Match.cs b/src/Rafty/Raft/Match.cs similarity index 93% rename from src/Rafty/Match.cs rename to src/Rafty/Raft/Match.cs index 8c315b2..6127841 100644 --- a/src/Rafty/Match.cs +++ b/src/Rafty/Raft/Match.cs @@ -1,6 +1,6 @@ using System; -namespace Rafty +namespace Rafty.Raft { public class Match { diff --git a/src/Rafty/Next.cs b/src/Rafty/Raft/Next.cs similarity index 93% rename from src/Rafty/Next.cs rename to src/Rafty/Raft/Next.cs index 5fc7786..cc04615 100644 --- a/src/Rafty/Next.cs +++ b/src/Rafty/Raft/Next.cs @@ -1,6 +1,6 @@ using System; -namespace Rafty +namespace Rafty.Raft { public class Next { diff --git a/src/Rafty/Server.cs b/src/Rafty/Raft/Server.cs similarity index 94% rename from src/Rafty/Server.cs rename to src/Rafty/Raft/Server.cs index e979cd3..2a19529 100644 --- a/src/Rafty/Server.cs +++ b/src/Rafty/Raft/Server.cs @@ -1,28 +1,36 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; - -namespace Rafty +using Rafty.Commands; +using Rafty.Messages; +using Rafty.Messaging; +using Rafty.Responses; +using Rafty.ServiceDiscovery; +using Rafty.State; + +namespace Rafty.Raft { - using System.Threading.Tasks; - public class Server { private readonly IMessageBus _messageBus; - private readonly List _serversInCluster; + private readonly IServersInCluster _serversInClusterInCluster; private readonly IStateMachine _stateMachine; private readonly ILogger _logger; private bool _appendingEntries; private readonly object _lock = new object(); private Guid _lastAppendEntriesMessageId; - public Server(IMessageBus messageBus, List remoteServers, IStateMachine stateMachine, ILogger logger) + public Server(IMessageBus messageBus, + IServersInCluster serversInCluster, + IStateMachine stateMachine, + ILogger logger) { _stateMachine = stateMachine; _logger = logger; _messageBus = messageBus; - _serversInCluster = remoteServers ?? new List(); + _serversInClusterInCluster = serversInCluster; Id = Guid.NewGuid(); Log = new List(); NextIndex = new List(); @@ -42,17 +50,16 @@ public Server(IMessageBus messageBus, List remoteServers, IStat public Guid Id { get; private set; } public int CurrentTermVotes { get; private set; } public int CurrentTermAppendEntriesResponse { get; private set; } - public int CountOfRemoteServers => _serversInCluster.Count; public Guid LeaderId { get; private set; } public RequestVoteResponse Receive(RequestVote requestVote) { _logger.LogDebug($"Server: {Id} received request vote in term: {CurrentTerm}"); - if (!_serversInCluster.Select(x => x.Id).Contains(requestVote.CandidateId)) + if (!_serversInClusterInCluster.Contains(requestVote.CandidateId)) { var remoteServer = new ServerInCluster(requestVote.CandidateId); - _serversInCluster.Add(remoteServer); + _serversInClusterInCluster.Add(remoteServer); } // If RPC request or response contains term T > currentTerm: @@ -124,10 +131,10 @@ public void Receive(BecomeCandidate becomeCandidate) public AppendEntriesResponse Receive(AppendEntries appendEntries) { - if (!_serversInCluster.Select(x => x.Id).Contains(appendEntries.LeaderId)) + if (!_serversInClusterInCluster.Contains(appendEntries.LeaderId)) { var remoteServer = new ServerInCluster(appendEntries.LeaderId); - _serversInCluster.Add(remoteServer); + _serversInClusterInCluster.Add(remoteServer); } if (State is Leader) @@ -258,7 +265,7 @@ public SendLeaderCommandResponse Receive(ICommand command) { _logger.LogDebug("Server Received Command"); _appendingEntries = true; - Log.Add(new Log(CurrentTerm, (FakeCommand)command)); + Log.Add(new Log(CurrentTerm, command)); CommitIndex = Log.Count - 1; var remoteServers = GetRemoteServers(); @@ -351,7 +358,7 @@ private void BecomeLeader() private List GetRemoteServers() { - return _serversInCluster.Where(x => x.Id != Id).ToList(); + return _serversInClusterInCluster.Get(x => x.Id != Id); } private void BecomeFollowerAndMatchTerm(int term, Guid leaderId) @@ -377,7 +384,7 @@ private void Receive(RequestVoteResponse requestVoteResponse) { CurrentTermVotes++; - if (CurrentTermVotes >= (_serversInCluster.Count / 2) + 1) + if (CurrentTermVotes >= (_serversInClusterInCluster.Count / 2) + 1) { BecomeLeader(); } @@ -407,7 +414,6 @@ private void Receive(AppendEntriesResponse appendEntriesResponse) var currentMatch = MatchIndex.First(x => x.Id == appendEntriesResponse.FollowerId); MatchIndex.Remove(currentMatch); - var nextMatchIndex = currentMatch.MatchIndex + 1; var match = new Match(appendEntriesResponse.FollowerId, currentNext.NextIndex); MatchIndex.Add(match); } @@ -416,7 +422,7 @@ private void Receive(AppendEntriesResponse appendEntriesResponse) { CurrentTermAppendEntriesResponse++; - if (CurrentTermAppendEntriesResponse >= (_serversInCluster.Count / 2) + 1) + if (CurrentTermAppendEntriesResponse >= (_serversInClusterInCluster.Count / 2) + 1) { if ((CommitIndex == 0 && LastApplied == 0) || CommitIndex > LastApplied) { diff --git a/src/Rafty/State.cs b/src/Rafty/Raft/State.cs similarity index 69% rename from src/Rafty/State.cs rename to src/Rafty/Raft/State.cs index 8c5b950..9f89fce 100644 --- a/src/Rafty/State.cs +++ b/src/Rafty/Raft/State.cs @@ -1,4 +1,4 @@ -namespace Rafty +namespace Rafty.Raft { public abstract class State { diff --git a/src/Rafty/AppendEntriesResponse.cs b/src/Rafty/Responses/AppendEntriesResponse.cs similarity index 91% rename from src/Rafty/AppendEntriesResponse.cs rename to src/Rafty/Responses/AppendEntriesResponse.cs index fbc19c0..76b39cf 100644 --- a/src/Rafty/AppendEntriesResponse.cs +++ b/src/Rafty/Responses/AppendEntriesResponse.cs @@ -1,6 +1,7 @@ using System; +using Rafty.Messages; -namespace Rafty +namespace Rafty.Responses { public class AppendEntriesResponse : Message { diff --git a/src/Rafty/RequestVoteResponse.cs b/src/Rafty/Responses/RequestVoteResponse.cs similarity index 91% rename from src/Rafty/RequestVoteResponse.cs rename to src/Rafty/Responses/RequestVoteResponse.cs index 44330ba..e76e595 100644 --- a/src/Rafty/RequestVoteResponse.cs +++ b/src/Rafty/Responses/RequestVoteResponse.cs @@ -1,6 +1,7 @@ using System; +using Rafty.Messages; -namespace Rafty +namespace Rafty.Responses { public class RequestVoteResponse : Message { diff --git a/src/Rafty/SendLeaderCommandResponse.cs b/src/Rafty/Responses/SendLeaderCommandResponse.cs similarity index 70% rename from src/Rafty/SendLeaderCommandResponse.cs rename to src/Rafty/Responses/SendLeaderCommandResponse.cs index 772b56e..b54fb3e 100644 --- a/src/Rafty/SendLeaderCommandResponse.cs +++ b/src/Rafty/Responses/SendLeaderCommandResponse.cs @@ -1,4 +1,4 @@ -namespace Rafty +namespace Rafty.Responses { public class SendLeaderCommandResponse { diff --git a/src/Rafty/IServiceRegistry.cs b/src/Rafty/ServiceDiscovery/IServiceRegistry.cs similarity index 84% rename from src/Rafty/IServiceRegistry.cs rename to src/Rafty/ServiceDiscovery/IServiceRegistry.cs index d9de323..3e31a85 100644 --- a/src/Rafty/IServiceRegistry.cs +++ b/src/Rafty/ServiceDiscovery/IServiceRegistry.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Rafty +namespace Rafty.ServiceDiscovery { public interface IServiceRegistry { diff --git a/src/Rafty/RegisterService.cs b/src/Rafty/ServiceDiscovery/RegisterService.cs similarity index 91% rename from src/Rafty/RegisterService.cs rename to src/Rafty/ServiceDiscovery/RegisterService.cs index cce6765..e712fed 100644 --- a/src/Rafty/RegisterService.cs +++ b/src/Rafty/ServiceDiscovery/RegisterService.cs @@ -1,6 +1,6 @@ using System; -namespace Rafty +namespace Rafty.ServiceDiscovery { public class RegisterService { diff --git a/src/Rafty/RemoteServerLocation.cs b/src/Rafty/ServiceDiscovery/RemoteServerLocation.cs similarity index 89% rename from src/Rafty/RemoteServerLocation.cs rename to src/Rafty/ServiceDiscovery/RemoteServerLocation.cs index 4df7f23..f9b463f 100644 --- a/src/Rafty/RemoteServerLocation.cs +++ b/src/Rafty/ServiceDiscovery/RemoteServerLocation.cs @@ -1,6 +1,6 @@ using System; -namespace Rafty +namespace Rafty.ServiceDiscovery { public class RemoteServerLocation { diff --git a/src/Rafty/ServerInCluster.cs b/src/Rafty/ServiceDiscovery/ServerInCluster.cs similarity index 85% rename from src/Rafty/ServerInCluster.cs rename to src/Rafty/ServiceDiscovery/ServerInCluster.cs index a580aac..76b0c79 100644 --- a/src/Rafty/ServerInCluster.cs +++ b/src/Rafty/ServiceDiscovery/ServerInCluster.cs @@ -1,6 +1,6 @@ using System; -namespace Rafty +namespace Rafty.ServiceDiscovery { public class ServerInCluster { diff --git a/src/Rafty/Service.cs b/src/Rafty/ServiceDiscovery/Service.cs similarity index 91% rename from src/Rafty/Service.cs rename to src/Rafty/ServiceDiscovery/Service.cs index 8da1afe..7c9edca 100644 --- a/src/Rafty/Service.cs +++ b/src/Rafty/ServiceDiscovery/Service.cs @@ -1,6 +1,6 @@ using System; -namespace Rafty +namespace Rafty.ServiceDiscovery { public class Service { diff --git a/src/Rafty/ServiceRegistry.cs b/src/Rafty/ServiceDiscovery/ServiceRegistry.cs similarity index 94% rename from src/Rafty/ServiceRegistry.cs rename to src/Rafty/ServiceDiscovery/ServiceRegistry.cs index 75f7f3f..d6814a8 100644 --- a/src/Rafty/ServiceRegistry.cs +++ b/src/Rafty/ServiceDiscovery/ServiceRegistry.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; -namespace Rafty +namespace Rafty.ServiceDiscovery { public class ServiceRegistry : IServiceRegistry { diff --git a/src/Rafty/FakeStateMachine.cs b/src/Rafty/State/FakeStateMachine.cs similarity index 88% rename from src/Rafty/FakeStateMachine.cs rename to src/Rafty/State/FakeStateMachine.cs index 486827f..687a4b5 100644 --- a/src/Rafty/FakeStateMachine.cs +++ b/src/Rafty/State/FakeStateMachine.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; +using Rafty.Commands; -namespace Rafty +namespace Rafty.State { public class FakeStateMachine : IStateMachine { diff --git a/src/Rafty/IStateMachine.cs b/src/Rafty/State/IStateMachine.cs similarity index 66% rename from src/Rafty/IStateMachine.cs rename to src/Rafty/State/IStateMachine.cs index ff324c7..33d108a 100644 --- a/src/Rafty/IStateMachine.cs +++ b/src/Rafty/State/IStateMachine.cs @@ -1,4 +1,6 @@ -namespace Rafty +using Rafty.Commands; + +namespace Rafty.State { public interface IStateMachine { diff --git a/src/Rafty/State/Leader.cs b/src/Rafty/State/Leader.cs new file mode 100644 index 0000000..ead9db4 --- /dev/null +++ b/src/Rafty/State/Leader.cs @@ -0,0 +1,7 @@ +namespace Rafty.State +{ + public class Leader : Raft.State + { + + } +} \ No newline at end of file diff --git a/src/Rafty/Log.cs b/src/Rafty/State/Log.cs similarity index 62% rename from src/Rafty/Log.cs rename to src/Rafty/State/Log.cs index 8b2538f..881b1ac 100644 --- a/src/Rafty/Log.cs +++ b/src/Rafty/State/Log.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; +using Rafty.Commands; -namespace Rafty +namespace Rafty.State { public class Log { @@ -10,13 +11,6 @@ public Log(int term, ICommand command) Command = command; } - [JsonConstructor] - public Log(int term, FakeCommand command) - { - Command = command; - Term = term; - } - public int Term { get; private set; } public ICommand Command { get; private set; } } diff --git a/test/Rafty.AcceptanceTests/AcceptanceTests.cs b/test/Rafty.AcceptanceTests/AcceptanceTests.cs index aa0c23a..06bb15d 100644 --- a/test/Rafty.AcceptanceTests/AcceptanceTests.cs +++ b/test/Rafty.AcceptanceTests/AcceptanceTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using TestStack.BDDfy; using Xunit; @@ -104,8 +105,8 @@ public void after_leader_is_elected_should_persist_command_to_all_servers() this.Given(x => _s.GivenTheFollowingServersAreRunning(remoteServers)) .And(x => _s.ThenANewLeaderIsElected()) .And(x => _s.ThenTheOtherNodesAreFollowers(4)) - .When(x => _s.ACommandIsSentToTheLeader()) - .Then(x => _s.TheCommandIsPersistedToAllStateMachines(0, 5)) + .When(x => _s.AFakeCommandIsSentToTheLeader()) + .Then(x => _s.ThenTheFakeCommandIsPersistedToAllStateMachines(0, 5)) .BDDfy(); } @@ -124,17 +125,39 @@ public void after_leader_is_elected_should_persist_command_to_all_servers_more_t this.Given(x => _s.GivenTheFollowingServersAreRunning(remoteServers)) .And(x => _s.ThenANewLeaderIsElected()) .And(x => _s.ThenTheOtherNodesAreFollowers(4)) - .When(x => _s.ACommandIsSentToTheLeader()) - .Then(x => _s.TheCommandIsPersistedToAllStateMachines(0, 5)) - .When(x => _s.ACommandIsSentToTheLeader()) - .Then(x => _s.TheCommandIsPersistedToAllStateMachines(1, 5)) - .When(x => _s.ACommandIsSentToTheLeader()) - .Then(x => _s.TheCommandIsPersistedToAllStateMachines(2, 5)) + .When(x => _s.AFakeCommandIsSentToTheLeader()) + .Then(x => _s.ThenTheFakeCommandIsPersistedToAllStateMachines(0, 5)) + .When(x => _s.AFakeCommandIsSentToTheLeader()) + .Then(x => _s.ThenTheFakeCommandIsPersistedToAllStateMachines(1, 5)) + .When(x => _s.AFakeCommandIsSentToTheLeader()) + .Then(x => _s.ThenTheFakeCommandIsPersistedToAllStateMachines(2, 5)) .BDDfy(); } [Fact] - public void followe_should_forward_command_to_leader() + public void after_leader_is_elected_should_persist_different_commands_to_all_servers() + { + var remoteServers = new List + { + "http://localhost:5231", + "http://localhost:5232", + "http://localhost:5233", + "http://localhost:5234", + "http://localhost:5235", + }; + + this.Given(x => _s.GivenTheFollowingServersAreRunning(remoteServers)) + .And(x => _s.ThenANewLeaderIsElected()) + .And(x => _s.ThenTheOtherNodesAreFollowers(4)) + .When(x => _s.AFakeCommandIsSentToTheLeader()) + .Then(x => _s.ThenTheFakeCommandIsPersistedToAllStateMachines(0, 5)) + .When(x => _s.AFakeCommandTwoIsSentToTheLeader()) + .Then(x => _s.ThenTheFakeCommandTwoIsPersistedToAllStateMachines(1, 5)) + .BDDfy(); + } + + [Fact] + public void follower_should_forward_command_to_leader() { var remoteServers = new List { @@ -149,7 +172,7 @@ public void followe_should_forward_command_to_leader() .And(x => _s.ThenANewLeaderIsElected()) .And(x => _s.ThenTheOtherNodesAreFollowers(4)) .When(x => _s.ACommandIsSentToAFollower()) - .Then(x => _s.TheCommandIsPersistedToAllStateMachines(0, 5)) + .Then(x => _s.ThenTheFakeCommandIsPersistedToAllStateMachines(0, 5)) .BDDfy(); } @@ -168,15 +191,15 @@ public void after_first_leader_dies_and_new_leader_is_elected_should_be_able_to_ this.Given(x => _s.GivenTheFollowingServersAreRunning(remoteServers)) .And(x => _s.ThenANewLeaderIsElected()) .And(x => _s.ThenTheOtherNodesAreFollowers(4)) - .When(x => _s.ACommandIsSentToTheLeader()) - .Then(x => _s.TheCommandIsPersistedToAllStateMachines(0, 5)) + .When(x => _s.AFakeCommandIsSentToTheLeader()) + .Then(x => _s.ThenTheFakeCommandIsPersistedToAllStateMachines(0, 5)) .And(x => _s.WhenTheLeaderDies()) .And(x => _s.ThenANewLeaderIsElected()) .And(x => _s.ThenTheOtherNodesAreFollowers(3)) - .When(x => _s.ACommandIsSentToTheLeader()) - .Then(x => _s.TheCommandIsPersistedToAllStateMachines(1, 4)) - .When(x => _s.ACommandIsSentToTheLeader()) - .Then(x => _s.TheCommandIsPersistedToAllStateMachines(2, 4)) + .When(x => _s.AFakeCommandIsSentToTheLeader()) + .Then(x => _s.ThenTheFakeCommandIsPersistedToAllStateMachines(1, 4)) + .When(x => _s.AFakeCommandIsSentToTheLeader()) + .Then(x => _s.ThenTheFakeCommandIsPersistedToAllStateMachines(2, 4)) .BDDfy(); } diff --git a/test/Rafty.AcceptanceTests/AcceptanceTestsSteps.cs b/test/Rafty.AcceptanceTests/AcceptanceTestsSteps.cs index 932042a..74ce9ad 100644 --- a/test/Rafty.AcceptanceTests/AcceptanceTestsSteps.cs +++ b/test/Rafty.AcceptanceTests/AcceptanceTestsSteps.cs @@ -6,27 +6,30 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Console; using Newtonsoft.Json; +using Rafty.Infrastructure; +using Rafty.Messaging; +using Rafty.Raft; +using Rafty.ServiceDiscovery; +using Rafty.State; using Shouldly; namespace Rafty.AcceptanceTests { public class AcceptanceTestsSteps : IDisposable { - private List _remoteServers; + private IServersInCluster _serversInCluster; private List _remoteServerLocations; private ServiceRegistry _serviceRegistry; private List _servers; - private FakeCommand _command; + private FakeCommand _fakeCommand; + private FooCommand _fooCommand; public AcceptanceTestsSteps() { - _remoteServers = new List(); + _serversInCluster = new InMemoryServersInCluster(); _serviceRegistry = new ServiceRegistry(); _servers = new List(); } @@ -59,7 +62,7 @@ public Timer GivenIHaveStartedMonitoring() return timer; } - public void TheCommandIsPersistedToAllStateMachines(int index, int serversToCheck) + public void ThenTheFakeCommandIsPersistedToAllStateMachines(int index, int serversToCheck) { var stopWatch = Stopwatch.StartNew(); var updated = new List(); @@ -73,7 +76,7 @@ public void TheCommandIsPersistedToAllStateMachines(int index, int serversToChec if (fakeStateMachine.Commands.Count > 0) { var command = (FakeCommand)fakeStateMachine.Commands[index]; - command.Id.ShouldBe(_command.Id); + command.Id.ShouldBe(_fakeCommand.Id); if (!updated.Contains(server.Server.Id)) { updated.Add(server.Server.Id); @@ -90,7 +93,7 @@ public void TheCommandIsPersistedToAllStateMachines(int index, int serversToChec updated.Count.ShouldBe(serversToCheck); } - public void ACommandIsSentToTheLeader() + public void AFakeCommandIsSentToTheLeader() { var leader = _servers.SingleOrDefault(x => x.Server.State is Leader); while(leader == null) @@ -98,9 +101,12 @@ public void ACommandIsSentToTheLeader() ThenANewLeaderIsElected(); leader = _servers.SingleOrDefault(x => x.Server.State is Leader); } - _command = new FakeCommand(Guid.NewGuid()); + _fakeCommand = new FakeCommand(Guid.NewGuid()); var urlOfLeader = leader.ServerUrl; - var json = JsonConvert.SerializeObject(_command); + var json = JsonConvert.SerializeObject(_fakeCommand, Formatting.None, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); var httpContent = new StringContent(json); using (var httpClient = new HttpClient()) @@ -116,7 +122,7 @@ public void ThenThatServerIsReceivingAndSendingMessages(string baseUrlOfServerTo var result = _servers.First(x => x.ServerUrl == baseUrlOfServerToAssert); result.Server.Id.ShouldNotBe(default(Guid)); - result.Server.CountOfRemoteServers.ShouldBe(6); + _serversInCluster.Count.ShouldBe(6); var termMatchWithLeader = false; var stopWatch = Stopwatch.StartNew(); while (stopWatch.ElapsedMilliseconds < 90000) @@ -158,6 +164,61 @@ public void ThenTheOtherNodesAreFollowers(int expected) fourFollowers.ShouldBeTrue(); } + internal void ThenTheFakeCommandTwoIsPersistedToAllStateMachines(int index, int serversToCheck) + { + var stopWatch = Stopwatch.StartNew(); + var updated = new List(); + + while (stopWatch.ElapsedMilliseconds < 90000) + { + foreach (var server in _servers) + { + var fakeStateMachine = (FakeStateMachine)server.StateMachine; + + if (fakeStateMachine.Commands.Count > 0) + { + var command = (FooCommand)fakeStateMachine.Commands[index]; + command.Description.ShouldBe(_fooCommand.Description); + if (!updated.Contains(server.Server.Id)) + { + updated.Add(server.Server.Id); + } + } + } + + if (updated.Count == serversToCheck) + { + break; + } + } + + updated.Count.ShouldBe(serversToCheck); + } + + internal void AFakeCommandTwoIsSentToTheLeader() + { + var leader = _servers.SingleOrDefault(x => x.Server.State is Leader); + while(leader == null) + { + ThenANewLeaderIsElected(); + leader = _servers.SingleOrDefault(x => x.Server.State is Leader); + } + _fooCommand = new FooCommand("some description....."); + var urlOfLeader = leader.ServerUrl; + var json = JsonConvert.SerializeObject(_fooCommand, Formatting.None, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); + var httpContent = new StringContent(json); + + using (var httpClient = new HttpClient()) + { + httpClient.BaseAddress = new Uri(urlOfLeader); + var response = httpClient.PostAsync("/command", httpContent).Result; + response.EnsureSuccessStatusCode(); + } + } + public void ThenANewLeaderIsElected() { var stopWatch = Stopwatch.StartNew(); @@ -196,7 +257,7 @@ public void WhenTheLeaderDies() serverContainer.MessageSender.Stop(); serverContainer.MessageBus.Stop(); serverContainer.WebHost.Dispose(); - _remoteServers.Remove(serverContainer.ServerInCluster); + _serversInCluster.Remove(serverContainer.ServerInCluster); _servers.Remove(serverContainer); killedLeader = true; break; @@ -237,13 +298,13 @@ private async Task GivenAServerIsRunning(string baseUrl) }) .Configure(app => { - messageSender = new HttpClientMessageSender(_serviceRegistry); + var logger = new ConsoleLogger("ConsoleLogger", (x, y) => true, true); + messageSender = new HttpClientMessageSender(_serviceRegistry, logger); messageBus = new InMemoryBus(messageSender); stateMachine = new FakeStateMachine(); - var logger = new ConsoleLogger("ConsoleLogger", (x, y) => true, true); var result = app.UseRaftyForTesting(new Uri(baseUrl), messageSender, messageBus, stateMachine, - _serviceRegistry, logger, _remoteServers); + _serviceRegistry, logger, _serversInCluster); server = result.server; serverInCluster = result.serverInCluster; @@ -264,7 +325,7 @@ public void Dispose() serverContainer.MessageSender.Stop(); serverContainer.MessageBus.Stop(); serverContainer.WebHost.Dispose(); - _remoteServers.Remove(serverContainer.ServerInCluster); + _serversInCluster.Remove(serverContainer.ServerInCluster); } Thread.Sleep(1000); @@ -280,9 +341,12 @@ public void ACommandIsSentToAFollower() leader = _servers.SingleOrDefault(x => x.Server.State is Leader); follower = _servers.FirstOrDefault(x => x.Server.State is Follower); } - _command = new FakeCommand(Guid.NewGuid()); + _fakeCommand = new FakeCommand(Guid.NewGuid()); var urlOfLeader = follower.ServerUrl; - var json = JsonConvert.SerializeObject(_command); + var json = JsonConvert.SerializeObject(_fakeCommand, Formatting.None, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); var httpContent = new StringContent(json); using (var httpClient = new HttpClient()) diff --git a/test/Rafty.AcceptanceTests/FakeCommand.cs b/test/Rafty.AcceptanceTests/FakeCommand.cs new file mode 100644 index 0000000..340aacf --- /dev/null +++ b/test/Rafty.AcceptanceTests/FakeCommand.cs @@ -0,0 +1,34 @@ +using System; +using Rafty.Commands; + +namespace Rafty.AcceptanceTests +{ + public class FakeCommand : Command + { + public FakeCommand() + { + + } + + public FakeCommand(Guid id) + { + Id = id; + + } + public Guid Id { get; set; } + } + + public class FooCommand : Command + { + public FooCommand() + { + } + + public FooCommand(string description) + { + Description = description; + } + + public string Description {get;set;} + } +} \ No newline at end of file diff --git a/src/Rafty/ServerContainer.cs b/test/Rafty.AcceptanceTests/ServerContainer.cs similarity index 85% rename from src/Rafty/ServerContainer.cs rename to test/Rafty.AcceptanceTests/ServerContainer.cs index f67bd93..458e5a0 100644 --- a/src/Rafty/ServerContainer.cs +++ b/test/Rafty.AcceptanceTests/ServerContainer.cs @@ -1,7 +1,11 @@ -namespace Rafty -{ - using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting; +using Rafty.Messaging; +using Rafty.Raft; +using Rafty.ServiceDiscovery; +using Rafty.State; +namespace Rafty.AcceptanceTests +{ public class ServerContainer { public ServerContainer(IWebHost webHost, Server server, string serverUrl, HttpClientMessageSender messageSender, ServerInCluster serverInCluster, IMessageBus messageBus, IStateMachine stateMachine) diff --git a/test/Rafty.ManualTests/Program.cs b/test/Rafty.ManualTests/Program.cs index 1353f01..5c7db5f 100644 --- a/test/Rafty.ManualTests/Program.cs +++ b/test/Rafty.ManualTests/Program.cs @@ -34,10 +34,10 @@ static int Main(string[] args) Console.WriteLine("ThenTheOtherNodesAreFollowers finished"); steps.ACommandIsSentToAFollower(); - Console.WriteLine("ACommandIsSentToTheLeader finished"); + Console.WriteLine("AFakeCommandIsSentToTheLeader finished"); - steps.TheCommandIsPersistedToAllStateMachines(0, 5); - Console.WriteLine("TheCommandIsPersistedToAllStateMachines finished"); + steps.ThenTheFakeCommandIsPersistedToAllStateMachines(0, 5); + Console.WriteLine("ThenTheFakeCommandIsPersistedToAllStateMachines finished"); //timer.Dispose(); steps.Dispose(); diff --git a/test/Rafty.UnitTests/AllServersTests.cs b/test/Rafty.UnitTests/AllServersTests.cs index d6dc699..96a3554 100644 --- a/test/Rafty.UnitTests/AllServersTests.cs +++ b/test/Rafty.UnitTests/AllServersTests.cs @@ -4,6 +4,12 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging.Console; using Moq; +using Rafty.Messages; +using Rafty.Messaging; +using Rafty.Raft; +using Rafty.Responses; +using Rafty.ServiceDiscovery; +using Rafty.State; using Shouldly; using TestStack.BDDfy; using Xunit; @@ -14,13 +20,13 @@ public class AllServersTests { private Mock_messageBus; private Server _server; - private List _remoteServers; + private IServersInCluster _serversInCluster; private FakeStateMachine _fakeStateMachine; public AllServersTests() { _messageBus = new Mock(); - _remoteServers = new List(); + _serversInCluster = new InMemoryServersInCluster(); } [Fact] @@ -64,7 +70,7 @@ public void server_should_become_follower_if_receives_greater_term_in_append_ent private void GivenTheFollowingRemoteServers(List remoteServers) { - _remoteServers = remoteServers; + _serversInCluster.Add(remoteServers); } private void ServerReceives(BecomeCandidate becomeCandidate) @@ -149,7 +155,7 @@ private void TheResponseIs(AppendEntriesResponse appendEntriesResponse) private void TheRemoteServerCountIs(int expected) { - _remoteServers.Count.ShouldBe(expected); + _serversInCluster.Count.ShouldBe(expected); } private void TheServerIsAFollower() @@ -180,7 +186,7 @@ private void ServerReceives(RequestVote requestVote) private void GivenANewServer() { _fakeStateMachine = new FakeStateMachine(); - _server = new Server(_messageBus.Object, _remoteServers, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); + _server = new Server(_messageBus.Object, _serversInCluster, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); } } } \ No newline at end of file diff --git a/test/Rafty.UnitTests/AppendEntriesTests.cs b/test/Rafty.UnitTests/AppendEntriesTests.cs index 227273e..33ee8db 100644 --- a/test/Rafty.UnitTests/AppendEntriesTests.cs +++ b/test/Rafty.UnitTests/AppendEntriesTests.cs @@ -1,6 +1,13 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.Logging.Console; +using Rafty.AcceptanceTests; +using Rafty.Messages; +using Rafty.Messaging; +using Rafty.Raft; +using Rafty.Responses; +using Rafty.ServiceDiscovery; +using Rafty.State; using Shouldly; using TestStack.BDDfy; using Xunit; @@ -11,10 +18,15 @@ public class AppendEntriesTests { private Server _server; private FakeMessageBus _messageBus; - private List _remoteServers; + private IServersInCluster _serversInCluster; private FakeStateMachine _fakeStateMachine; private AppendEntriesResponse _result; + public AppendEntriesTests() + { + _serversInCluster = new InMemoryServersInCluster(); + } + [Fact] public void server_should_reply_true_if_entries_is_empty() { @@ -198,7 +210,7 @@ private void GivenANewServer() { _fakeStateMachine = new FakeStateMachine(); _messageBus = new FakeMessageBus(); - _server = new Server(_messageBus, _remoteServers, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); + _server = new Server(_messageBus, _serversInCluster, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); } private void ThenTheLogContainsEntriesCount(int expectedCount) diff --git a/test/Rafty.UnitTests/CandidateTests.cs b/test/Rafty.UnitTests/CandidateTests.cs index 668c612..560c721 100644 --- a/test/Rafty.UnitTests/CandidateTests.cs +++ b/test/Rafty.UnitTests/CandidateTests.cs @@ -4,6 +4,12 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging.Console; using Moq; +using Rafty.Messages; +using Rafty.Messaging; +using Rafty.Raft; +using Rafty.Responses; +using Rafty.ServiceDiscovery; +using Rafty.State; using Shouldly; using TestStack.BDDfy; using Xunit; @@ -14,12 +20,13 @@ public class CandidateTests { private Mock _messageBus; private Server _server; - private List _remoteServers; + private InMemoryServersInCluster _serversInCluster; private FakeStateMachine _fakeStateMachine; public CandidateTests() { _messageBus = new Mock(); + _serversInCluster = new InMemoryServersInCluster(); } [Fact] @@ -106,7 +113,7 @@ public void server_should_become_leader_if_votes_received_from_majority_of_serve private void GivenAllServersAppendEntries() { - var response = _remoteServers.Select(remoteServer => Task.FromResult(new AppendEntriesResponse(_server.CurrentTerm, true, remoteServer.Id, _server.Id))).ToList(); + var response = _serversInCluster.All.Select(remoteServer => Task.FromResult(new AppendEntriesResponse(_server.CurrentTerm, true, remoteServer.Id, _server.Id))).ToList(); _messageBus.Setup(x => x.Send(It.IsAny())).ReturnsInOrder(response); } @@ -181,21 +188,21 @@ private void ThenTheServerIsTheLeader() private void TheServerReceivesVotesFromAllRemoteServers() { - var response = _remoteServers.Select(remoteServer => Task.FromResult(new RequestVoteResponse(_server.CurrentTerm, true, remoteServer.Id, _server.Id))).ToList(); + var response = _serversInCluster.All.Select(remoteServer => Task.FromResult(new RequestVoteResponse(_server.CurrentTerm, true, remoteServer.Id, _server.Id))).ToList(); _messageBus.Setup(x => x.Send(It.IsAny())).ReturnsInOrder(response); } private void TheServerDoesNotReceivesVotesFromAllRemoteServers() { - var response = _remoteServers.Select(remoteServer => Task.FromResult(new RequestVoteResponse(_server.CurrentTerm, false, remoteServer.Id, _server.Id))).ToList(); + var response = _serversInCluster.All.Select(remoteServer => Task.FromResult(new RequestVoteResponse(_server.CurrentTerm, false, remoteServer.Id, _server.Id))).ToList(); _messageBus.Setup(x => x.Send(It.IsAny())).ReturnsInOrder(response); } private void GivenTheFollowingRemoteServers(List remoteServers) { - _remoteServers = remoteServers; + _serversInCluster.Add(remoteServers); } private void ThenTheServerRequestsVotesFromAllRemoteServers() @@ -236,7 +243,7 @@ private void TheServerIsACandidate() private void GivenANewServer() { _fakeStateMachine = new FakeStateMachine(); - _server = new Server(_messageBus.Object, _remoteServers, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); + _server = new Server(_messageBus.Object, _serversInCluster, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); } } } diff --git a/test/Rafty.UnitTests/FollowerTests.cs b/test/Rafty.UnitTests/FollowerTests.cs index 86e4dcb..f70515b 100644 --- a/test/Rafty.UnitTests/FollowerTests.cs +++ b/test/Rafty.UnitTests/FollowerTests.cs @@ -2,6 +2,13 @@ using System.Collections.Generic; using Microsoft.Extensions.Logging.Console; using Moq; +using Rafty.AcceptanceTests; +using Rafty.Commands; +using Rafty.Messages; +using Rafty.Messaging; +using Rafty.Raft; +using Rafty.ServiceDiscovery; +using Rafty.State; using Shouldly; using TestStack.BDDfy; using Xunit; @@ -13,12 +20,13 @@ public class FollowerTests private FakeMessageBus _messageBus; private readonly Mock _messageBusMock; private Server _server; - private List _remoteServers; + private IServersInCluster _serversInCluster; private FakeStateMachine _fakeStateMachine; public FollowerTests() { _messageBusMock = new Mock(); + _serversInCluster = new InMemoryServersInCluster(); } [Fact] @@ -67,13 +75,13 @@ private void GivenANewServer() { _fakeStateMachine = new FakeStateMachine(); _messageBus = new FakeMessageBus(); - _server = new Server(_messageBus, _remoteServers, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); + _server = new Server(_messageBus, _serversInCluster, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); } private void GivenANewServer(Mock mock) { _fakeStateMachine = new FakeStateMachine(); - _server = new Server(mock.Object, _remoteServers, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); + _server = new Server(mock.Object, _serversInCluster, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); } private void TheServerIsAFollower() diff --git a/test/Rafty.UnitTests/InitialServerStateTests.cs b/test/Rafty.UnitTests/InitialServerStateTests.cs index 3deace7..abe738a 100644 --- a/test/Rafty.UnitTests/InitialServerStateTests.cs +++ b/test/Rafty.UnitTests/InitialServerStateTests.cs @@ -2,6 +2,10 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging.Console; +using Rafty.Messaging; +using Rafty.Raft; +using Rafty.ServiceDiscovery; +using Rafty.State; using Shouldly; using TestStack.BDDfy; using Xunit; @@ -12,9 +16,14 @@ public class InitialServerStateTests { private Server _server; private FakeMessageBus _messageBus; - private List _remoteServers; + private IServersInCluster _serversInCluster; private FakeStateMachine _fakeStateMachine; + public InitialServerStateTests() + { + _serversInCluster = new InMemoryServersInCluster(); + } + [Fact] public void server_should_have_current_term_of_zero_on_init() { @@ -118,7 +127,7 @@ private void GivenANewServer() { _fakeStateMachine = new FakeStateMachine(); _messageBus = new FakeMessageBus(); - _server = new Server(_messageBus, _remoteServers, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); + _server = new Server(_messageBus, _serversInCluster, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); } private void ThenTheServerHasAnId() diff --git a/test/Rafty.UnitTests/LeaderTests.cs b/test/Rafty.UnitTests/LeaderTests.cs index 5306c28..003c4d9 100644 --- a/test/Rafty.UnitTests/LeaderTests.cs +++ b/test/Rafty.UnitTests/LeaderTests.cs @@ -5,6 +5,13 @@ using Microsoft.Extensions.Logging.Console; using Moq; using Moq.Language.Flow; +using Rafty.AcceptanceTests; +using Rafty.Messages; +using Rafty.Messaging; +using Rafty.Raft; +using Rafty.Responses; +using Rafty.ServiceDiscovery; +using Rafty.State; using Shouldly; using TestStack.BDDfy; using Xunit; @@ -15,7 +22,7 @@ public class LeaderTests { private Mock _messageBus; private Server _server; - private List _remoteServers; + private InMemoryServersInCluster _serversInCluster; private FakeCommand _fakeCommand; private FakeStateMachine _fakeStateMachine; @@ -23,7 +30,7 @@ public class LeaderTests public LeaderTests() { _messageBus = new Mock(); - _remoteServers = new List(); + _serversInCluster = new InMemoryServersInCluster(); } [Fact] @@ -280,7 +287,7 @@ private void ThenTheLastAppliedIs(int expected) private void ThenTheNextIndexIsUpdated(int expected) { - foreach(var remoteSever in _remoteServers.Where(x => x.Id != _server.Id)) + foreach(var remoteSever in _serversInCluster.All.Where(x => x.Id != _server.Id)) { var next = _server.NextIndex.First(x => x.Id == remoteSever.Id); next.NextIndex.ShouldBe(expected); @@ -289,7 +296,7 @@ private void ThenTheNextIndexIsUpdated(int expected) private void ThenTheMatchIndexIsUpdated(int expected) { - foreach(var remoteSever in _remoteServers.Where(x => x.Id != _server.Id)) + foreach(var remoteSever in _serversInCluster.All.Where(x => x.Id != _server.Id)) { var match = _server.MatchIndex.First(x => x.Id == remoteSever.Id); match.MatchIndex.ShouldBe(expected); @@ -298,7 +305,7 @@ private void ThenTheMatchIndexIsUpdated(int expected) private void ThenTheMatchIndexIsInitialisedForEachRemoteServer() { - foreach(var remoteServer in _remoteServers.Where(x => x.Id != _server.Id)) + foreach(var remoteServer in _serversInCluster.All.Where(x => x.Id != _server.Id)) { var match = _server.MatchIndex.First(x => x.Id == remoteServer.Id); match.MatchIndex.ShouldBe(0); @@ -307,7 +314,7 @@ private void ThenTheMatchIndexIsInitialisedForEachRemoteServer() private void ThenTheNextIndexIsInitialisedForEachRemoteServer() { - foreach(var remoteServer in _remoteServers.Where(x => x.Id != _server.Id)) + foreach(var remoteServer in _serversInCluster.All.Where(x => x.Id != _server.Id)) { var next = _server.NextIndex.First(x => x.Id == remoteServer.Id); next.NextIndex.ShouldBe(0); @@ -326,7 +333,7 @@ private void ThenTheCurrentTermAppendEntriesResponseIs(int expected) private void WhenTheServerReceivesAMajorityOfResponses() { - var response = _remoteServers.Select(remoteServer => Task.FromResult(new AppendEntriesResponse(_server.CurrentTerm, true, remoteServer.Id, _server.Id))).ToList(); + var response = _serversInCluster.All.Select(remoteServer => Task.FromResult(new AppendEntriesResponse(_server.CurrentTerm, true, remoteServer.Id, _server.Id))).ToList(); _messageBus.Setup(x => x.Send(It.IsAny())).ReturnsInOrder(response); } @@ -375,18 +382,18 @@ private void ThenTheServerReceivesSendHeartbeat() private void TheServerReceivesAppendEntriesResponsesForCommand() { - var response = _remoteServers.Select(remoteServer => Task.FromResult(new AppendEntriesResponse(_server.CurrentTerm, true, remoteServer.Id, _server.Id))).ToList(); + var response = _serversInCluster.All.Select(remoteServer => Task.FromResult(new AppendEntriesResponse(_server.CurrentTerm, true, remoteServer.Id, _server.Id))).ToList(); _messageBus.Setup(x => x.Send(It.IsAny())).ReturnsInOrder(response); } private void TheServerReceivesAMajorityOfVotes() { - var requestVoteResponses = _remoteServers.Select(x => Task.FromResult(new RequestVoteResponse(0, true, x.Id, Guid.NewGuid()))).ToList(); + var requestVoteResponses = _serversInCluster.All.Select(x => Task.FromResult(new RequestVoteResponse(0, true, x.Id, Guid.NewGuid()))).ToList(); _messageBus.Setup(x => x.Send(It.IsAny())).ReturnsInOrder(requestVoteResponses); - var response = _remoteServers.Select(remoteServer => Task.FromResult(new AppendEntriesResponse(_server.CurrentTerm, true, remoteServer.Id, _server.Id))).ToList(); + var response = _serversInCluster.All.Select(remoteServer => Task.FromResult(new AppendEntriesResponse(_server.CurrentTerm, true, remoteServer.Id, _server.Id))).ToList(); _messageBus.Setup(x => x.Send(It.IsAny())).ReturnsInOrder(response); } @@ -398,7 +405,7 @@ private void ServerReceives(SendHeartbeat heartbeat) private void GivenTheFollowingRemoteServers(List remoteServers) { - _remoteServers = remoteServers; + _serversInCluster.Add(remoteServers); } private void ThenTheCurrentTermVotesAre(int expected) @@ -414,8 +421,8 @@ private void TheServerIsALeader() private void GivenANewServer() { _fakeStateMachine = new FakeStateMachine(); - _server = new Server(_messageBus.Object, _remoteServers, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); - _remoteServers.Add(new ServerInCluster(_server.Id)); + _server = new Server(_messageBus.Object, _serversInCluster, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); + _serversInCluster.Add(new ServerInCluster(_server.Id)); } } diff --git a/test/Rafty.UnitTests/Rafty.UnitTests.csproj b/test/Rafty.UnitTests/Rafty.UnitTests.csproj index ec1f68a..c366b0e 100644 --- a/test/Rafty.UnitTests/Rafty.UnitTests.csproj +++ b/test/Rafty.UnitTests/Rafty.UnitTests.csproj @@ -16,6 +16,7 @@ + diff --git a/test/Rafty.UnitTests/RequestVoteTests.cs b/test/Rafty.UnitTests/RequestVoteTests.cs index 030e6f0..736ff5f 100644 --- a/test/Rafty.UnitTests/RequestVoteTests.cs +++ b/test/Rafty.UnitTests/RequestVoteTests.cs @@ -1,6 +1,13 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.Logging.Console; +using Rafty.AcceptanceTests; +using Rafty.Messages; +using Rafty.Messaging; +using Rafty.Raft; +using Rafty.Responses; +using Rafty.ServiceDiscovery; +using Rafty.State; using Shouldly; using TestStack.BDDfy; using Xunit; @@ -11,10 +18,15 @@ public class RequestVoteTests { private Server _server; private FakeMessageBus _messageBus; - private List _remoteServers; + private IServersInCluster _serversInCluster; private FakeStateMachine _fakeStateMachine; private RequestVoteResponse _result; + public RequestVoteTests() + { + _serversInCluster = new InMemoryServersInCluster(); + } + [Fact] public void server_should_reply_false_if_term_is_less_than_current_term() { @@ -111,7 +123,7 @@ private void GivenANewServer() { _fakeStateMachine = new FakeStateMachine(); _messageBus = new FakeMessageBus(); - _server = new Server(_messageBus, _remoteServers, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); + _server = new Server(_messageBus, _serversInCluster, _fakeStateMachine, new ConsoleLogger("ConsoleLogger", (x, y) => true, true)); } private void GivenTheCurrentTermIs(int term) diff --git a/test/Rafty.UnitTests/ServiceRegistryTests.cs b/test/Rafty.UnitTests/ServiceRegistryTests.cs index e9da865..b73a1e2 100644 --- a/test/Rafty.UnitTests/ServiceRegistryTests.cs +++ b/test/Rafty.UnitTests/ServiceRegistryTests.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using Rafty.Infrastructure; +using Rafty.ServiceDiscovery; using Shouldly; using TestStack.BDDfy; using Xunit;