public void CanSendTimeoutNow() { using (var node2Transport = new HttpTransport("node2")) { var node1 = new NodeConnectionInfo { Name = "node1", Uri = new Uri("http://localhost:9079") }; node2Transport.Send(node1, new AppendEntriesRequest { From = "node2", ClusterTopologyId = new Guid("355a589b-cadc-463d-a515-5add2ea47205"), Term = 2, PrevLogIndex = 0, PrevLogTerm = 0, LeaderCommit = 1, Entries = new[] { new LogEntry { Term = 2, Index = 1, Data = new JsonCommandSerializer().Serialize(new DictionaryCommand.Set { Key = "a", Value = 2 }) }, } }); MessageContext context; var gotIt = node2Transport.TryReceiveMessage(_timeout, CancellationToken.None, out context); Assert.True(gotIt); Assert.True(((AppendEntriesResponse)context.Message).Success); var mres = new ManualResetEventSlim(); _raftEngine.StateChanged += state => { if (state == RaftEngineState.CandidateByRequest) mres.Set(); }; node2Transport.Send(node1, new TimeoutNowRequest { Term = 4, From = "node2", ClusterTopologyId = new Guid("355a589b-cadc-463d-a515-5add2ea47205"), }); gotIt = node2Transport.TryReceiveMessage(_timeout, CancellationToken.None, out context); Assert.True(gotIt); Assert.True(context.Message is NothingToDo); Assert.True(mres.Wait(_timeout)); } }
public void CanAskIfCanInstallSnapshot() { using (var node2Transport = new HttpTransport("node2")) { var node1 = new NodeConnectionInfo { Name = "node1", Uri = new Uri("http://localhost:9079") }; node2Transport.Send(node1, new CanInstallSnapshotRequest { From = "node2", ClusterTopologyId = new Guid("355a589b-cadc-463d-a515-5add2ea47205"), Term = 2, Index = 3, }); MessageContext context; var gotIt = node2Transport.TryReceiveMessage(_timeout, CancellationToken.None, out context); Assert.True(gotIt); var msg = (CanInstallSnapshotResponse)context.Message; Assert.True(msg.Success); } }
public void CanSendRequestVotesAndGetReply() { using (var node2Transport = new HttpTransport("node2")) { var node1 = new NodeConnectionInfo { Name = "node1", Uri = new Uri("http://localhost:9079") }; node2Transport.Send(node1, new RequestVoteRequest { TrialOnly = true, From = "node2", ClusterTopologyId = new Guid("355a589b-cadc-463d-a515-5add2ea47205"), Term = 3, LastLogIndex = 2, LastLogTerm = 2, }); MessageContext context; var gotIt = node2Transport.TryReceiveMessage(_timeout, CancellationToken.None, out context); Assert.True(gotIt); Assert.True(context.Message is RequestVoteResponse); } }
public HttpTransportPingTest() { _node1Transport = new HttpTransport("node1"); var node1 = new NodeConnectionInfo { Name = "node1", Uri = new Uri("http://localhost:9079") }; var engineOptions = new RaftEngineOptions(node1, StorageEnvironmentOptions.CreateMemoryOnly(), _node1Transport, new DictionaryStateMachine()) { ElectionTimeout = 60 * 1000, HeartbeatTimeout = 10 * 1000 }; _raftEngine = new RaftEngine(engineOptions); _server = WebApp.Start(new StartOptions { Urls = { "http://+:9079/" } }, builder => { var httpConfiguration = new HttpConfiguration(); RaftWebApiConfig.Load(); httpConfiguration.MapHttpAttributeRoutes(); httpConfiguration.Properties[typeof(HttpTransportBus)] = _node1Transport.Bus; builder.UseWebApi(httpConfiguration); }); }
private static void Main(string[] args) { var options = new TailFeatherCommandLineOptions(); if (Parser.Default.ParseArguments(args, options) == false) { var autoBuild = HelpText.AutoBuild(options); HelpText.DefaultParsingErrorsHandler(options, autoBuild); Console.WriteLine(autoBuild.ToString()); return; } var nodeName = options.NodeName ?? (Environment.MachineName + ":" + options.Port); Console.Title = string.Format("Node name: {0}, port: {1}", nodeName, options.Port); var kvso = StorageEnvironmentOptions.ForPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, options.DataPath, "KeyValue")); using (var statemachine = new KeyValueStateMachine(kvso)) { var storageEnvironmentOptions = StorageEnvironmentOptions.ForPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, options.DataPath, "Raft")); var httpTransport = new HttpTransport(nodeName); var raftEngineOptions = new RaftEngineOptions( new NodeConnectionInfo { Name = nodeName, Uri = new Uri("http://" + Environment.MachineName + ":" + options.Port), }, storageEnvironmentOptions, httpTransport, statemachine ) { ElectionTimeout = 5 * 1000, HeartbeatTimeout = 1000, MaxLogLengthBeforeCompaction = 25 }; if (options.Boostrap) { PersistentState.ClusterBootstrap(raftEngineOptions); Console.WriteLine("Setup node as the cluster seed, exiting..."); return; } using (var raftEngine = new RaftEngine(raftEngineOptions)) { using (WebApp.Start(new StartOptions { Urls = { "http://+:" + options.Port + "/" } }, builder => { var httpConfiguration = new HttpConfiguration(); httpConfiguration.Formatters.Remove(httpConfiguration.Formatters.XmlFormatter); httpConfiguration.Formatters.JsonFormatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented; httpConfiguration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); RaftWebApiConfig.Load(); httpConfiguration.MapHttpAttributeRoutes(); httpConfiguration.Properties[typeof(HttpTransportBus)] = httpTransport.Bus; httpConfiguration.Properties[typeof(RaftEngine)] = raftEngine; builder.UseWebApi(httpConfiguration); })) { Console.WriteLine("Ready @ http://" + Environment.MachineName + ":" + options.Port + "/, press ENTER to stop"); Console.ReadLine(); } } } }
public void CanSendEntries() { using (var node2Transport = new HttpTransport("node2")) { var node1 = new NodeConnectionInfo { Name = "node1", Uri = new Uri("http://localhost:9079") }; node2Transport.Send(node1, new AppendEntriesRequest { From = "node2", ClusterTopologyId = new Guid("355a589b-cadc-463d-a515-5add2ea47205"), Term = 2, PrevLogIndex = 0, PrevLogTerm = 0, LeaderCommit = 1, Entries = new LogEntry[] { new LogEntry { Term = 2, Index = 1, Data = new JsonCommandSerializer().Serialize(new DictionaryCommand.Set { Key = "a", Value = 2 }) }, } }); MessageContext context; var gotIt = node2Transport.TryReceiveMessage(_timeout, CancellationToken.None, out context); Assert.True(gotIt); var appendEntriesResponse = (AppendEntriesResponse)context.Message; Assert.True(appendEntriesResponse.Success); Assert.Equal(2, ((DictionaryStateMachine)_raftEngine.StateMachine).Data["a"]); } }
public void CanInstallSnapshot() { using (var node2Transport = new HttpTransport("node2")) { var node1 = new NodeConnectionInfo { Name = "node1", Uri = new Uri("http://localhost:9079") }; node2Transport.Send(node1, new CanInstallSnapshotRequest { From = "node2", ClusterTopologyId = new Guid("355a589b-cadc-463d-a515-5add2ea47205"), Term = 2, Index = 3, }); MessageContext context; var gotIt = node2Transport.TryReceiveMessage(_timeout, CancellationToken.None, out context); Assert.True(gotIt); Assert.True(context.Message is CanInstallSnapshotResponse); node2Transport.Stream(node1, new InstallSnapshotRequest { From = "node2", ClusterTopologyId = new Guid("355a589b-cadc-463d-a515-5add2ea47205"), Term = 2, Topology = new Topology(new Guid("355a589b-cadc-463d-a515-5add2ea47205")), LastIncludedIndex = 2, LastIncludedTerm = 2, }, stream => { var streamWriter = new StreamWriter(stream); var data = new Dictionary<string, int> { { "a", 2 } }; new JsonSerializer().Serialize(streamWriter, data); streamWriter.Flush(); }); gotIt = node2Transport.TryReceiveMessage(_timeout, CancellationToken.None, out context); Assert.True(gotIt); var appendEntriesResponse = (InstallSnapshotResponse)context.Message; Assert.True(appendEntriesResponse.Success); Assert.Equal(2, ((DictionaryStateMachine)_raftEngine.StateMachine).Data["a"]); } }