Пример #1
0
        public void Follower_as_a_single_node_becomes_leader_automatically()
        {
            var hub = new InMemoryTransportHub();
            var storageEnvironmentOptions = StorageEnvironmentOptions.CreateMemoryOnly();

            storageEnvironmentOptions.OwnsPagers = false;

            var raftEngineOptions = new RaftEngineOptions(
                new NodeConnectionInfo {
                Name = "node1"
            },
                storageEnvironmentOptions,
                hub.CreateTransportFor("node1"),
                new DictionaryStateMachine()
                )
            {
                ElectionTimeout  = 1000,
                HeartbeatTimeout = 1000 / 6
            };

            PersistentState.ClusterBootstrap(raftEngineOptions);
            storageEnvironmentOptions.OwnsPagers = true;

            using (var raftNode = new RaftEngine(raftEngineOptions))
            {
                Assert.Equal(RaftEngineState.Leader, raftNode.State);
            }
        }
Пример #2
0
        public RaftNode(int port)
        {
            _name = "node-" + port;
            _url  = string.Format("http://{0}:{1}", Environment.MachineName, port);

            var nodeTransport = new HttpTransport(_name);

            var node1 = new NodeConnectionInfo {
                Name = _name, Uri = new Uri(_url)
            };
            var engineOptions = new RaftEngineOptions(
                node1,
                StorageEnvironmentOptions.CreateMemoryOnly(),
                nodeTransport,
                new DictionaryStateMachine());

            engineOptions.ElectionTimeout  *= 2;
            engineOptions.HeartbeatTimeout *= 2;

            _raftEngine = new RaftEngine(engineOptions);

            _server = WebApp.Start(new StartOptions
            {
                Urls = { string.Format("http://+:{0}/", port) }
            }, builder =>
            {
                var httpConfiguration = new HttpConfiguration();
                RaftWebApiConfig.Load();
                httpConfiguration.MapHttpAttributeRoutes();
                httpConfiguration.Properties[typeof(HttpTransportBus)] = nodeTransport.Bus;
                builder.UseWebApi(httpConfiguration);
            });
        }
Пример #3
0
		public void Follower_as_a_single_node_becomes_leader_automatically()
		{
			var hub = new InMemoryTransportHub();
			var storageEnvironmentOptions = StorageEnvironmentOptions.CreateMemoryOnly();
			storageEnvironmentOptions.OwnsPagers = false;
			
			var raftEngineOptions = new RaftEngineOptions(
				new NodeConnectionInfo { Name = "node1" }, 
				storageEnvironmentOptions,
				hub.CreateTransportFor("node1"),
				new DictionaryStateMachine()
				)
			{
				ElectionTimeout = 1000,
				HeartbeatTimeout = 1000/6
			};

			PersistentState.ClusterBootstrap(raftEngineOptions);
			storageEnvironmentOptions.OwnsPagers = true;

			using (var raftNode = new RaftEngine(raftEngineOptions))
			{
				Assert.Equal(RaftEngineState.Leader, raftNode.State);
			}
		}
Пример #4
0
        public HttpTransportPingTest()
        {
            _node1Transport = new HttpTransport("node1", _requestsTimeout, CancellationToken.None);

            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);

            NonAdminHttp.EnsureCanListenToWhenInNonAdminContext(9079);

            _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);
            });
        }
Пример #5
0
 public static void SetTopologyExplicitly(RaftEngineOptions options, Topology topology, bool throwIfTopologyExists)
 {
     using (var ps = new PersistentState("ClusterBootstrap", options.StorageOptions, CancellationToken.None))
     {
         if (ps.GetCurrentTopology().HasVoters&& throwIfTopologyExists)
         {
             throw new InvalidOperationException("Cannot set topology on a cluster that already have a topology");
         }
         ps.SetCurrentTopology(topology, 0);
     }
 }
Пример #6
0
        public static ClusterManager Create(DocumentDatabase systemDatabase, DatabasesLandlord databasesLandlord)
        {
            if (systemDatabase == null)
            {
                throw new ArgumentNullException("systemDatabase");
            }

            if (databasesLandlord == null)
            {
                throw new ArgumentNullException("databasesLandlord");
            }

            DatabaseHelper.AssertSystemDatabase(systemDatabase);

            var configuration      = systemDatabase.Configuration;
            var nodeConnectionInfo = CreateSelfConnection(systemDatabase);

            StorageEnvironmentOptions options;

            if (configuration.Core.RunInMemory == false)
            {
                var directoryPath = Path.Combine(configuration.Core.DataDirectory ?? AppDomain.CurrentDomain.BaseDirectory, "Raft");
                if (Directory.Exists(directoryPath) == false)
                {
                    Directory.CreateDirectory(directoryPath);
                }

                options = StorageEnvironmentOptions.ForPath(directoryPath);
            }
            else
            {
                options = StorageEnvironmentOptions.CreateMemoryOnly(configuration.Storage.TempPath);
            }

            var transport         = new HttpTransport(nodeConnectionInfo.Name, systemDatabase.WorkContext.CancellationToken);
            var stateMachine      = new ClusterStateMachine(systemDatabase, databasesLandlord);
            var raftEngineOptions = new RaftEngineOptions(nodeConnectionInfo, options, transport, stateMachine)
            {
                ElectionTimeout              = (int)configuration.Cluster.ElectionTimeout.AsTimeSpan.TotalMilliseconds,
                HeartbeatTimeout             = (int)configuration.Cluster.HeartbeatTimeout.AsTimeSpan.TotalMilliseconds,
                MaxLogLengthBeforeCompaction = configuration.Cluster.MaxLogLengthBeforeCompaction,
                MaxEntriesPerRequest         = configuration.Cluster.MaxEntriesPerRequest,
                MaxStepDownDrainTime         = configuration.Cluster.MaxStepDownDrainTime.AsTimeSpan
            };
            var raftEngine = new RaftEngine(raftEngineOptions);

            stateMachine.RaftEngine = raftEngine;

            return(new ClusterManager(raftEngine));
        }
Пример #7
0
        private RaftEngineOptions CreateNodeOptions(string nodeName, int messageTimeout, StorageEnvironmentOptions storageOptions, params string[] peers)
        {
            var nodeOptions = new RaftEngineOptions(new NodeConnectionInfo {
                Name = nodeName
            },
                                                    storageOptions,
                                                    _inMemoryTransportHub.CreateTransportFor(nodeName),
                                                    new DictionaryStateMachine())
            {
                ElectionTimeout  = messageTimeout,
                HeartbeatTimeout = messageTimeout / 6,
                Stopwatch        = Stopwatch.StartNew()
            };

            return(nodeOptions);
        }
Пример #8
0
        public void Follower_on_timeout_should_become_candidate()
        {
            var storageEnvironmentOptions = StorageEnvironmentOptions.CreateMemoryOnly();

            storageEnvironmentOptions.OwnsPagers = false;

            var nodeOptions = new RaftEngineOptions(new NodeConnectionInfo {
                Name = "real"
            }, storageEnvironmentOptions, _inMemoryTransportHub.CreateTransportFor("real"), new DictionaryStateMachine());

            PersistentState.SetTopologyExplicitly(nodeOptions,
                                                  new Topology(
                                                      new Guid("355a589b-cadc-463d-a515-5add2ea47205"),
                                                      new[]
            {
                new NodeConnectionInfo {
                    Name = "real"
                }, new NodeConnectionInfo {
                    Name = "u2"
                }, new NodeConnectionInfo {
                    Name = "pj"
                },
            }, new NodeConnectionInfo[0], new NodeConnectionInfo[0]), throwIfTopologyExists: true);
            storageEnvironmentOptions.OwnsPagers = true;

            using (var node = new RaftEngine(nodeOptions))
            {
                var timeoutEvent = new ManualResetEventSlim();
                node.StateTimeout += timeoutEvent.Set;

                ForceTimeout("real");

                timeoutEvent.Wait();
                Assert.Equal(RaftEngineState.Candidate, node.State);
            }
        }
Пример #9
0
        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);
            });
        }
Пример #10
0
		public static void SetTopologyExplicitly(RaftEngineOptions options, Topology topology, bool throwIfTopologyExists)
		{
			using (var ps = new PersistentState("ClusterBootstrap", options.StorageOptions, CancellationToken.None))
			{
				if (ps.GetCurrentTopology().HasVoters && throwIfTopologyExists)
					throw new InvalidOperationException("Cannot set topology on a cluster that already have a topology");
				ps.SetCurrentTopology(topology, 0);
			}
		}
Пример #11
0
		public static void ClusterBootstrap(RaftEngineOptions options)
		{
			SetTopologyExplicitly(options,
				new Topology(Guid.NewGuid(), new[] { options.SelfConnection }, Enumerable.Empty<NodeConnectionInfo>(), Enumerable.Empty<NodeConnectionInfo>()),
				throwIfTopologyExists: true);
		}
Пример #12
0
 public static void ClusterBootstrap(RaftEngineOptions options)
 {
     SetTopologyExplicitly(options,
                           new Topology(Guid.NewGuid(), new[] { options.SelfConnection }, Enumerable.Empty <NodeConnectionInfo>(), Enumerable.Empty <NodeConnectionInfo>()),
                           throwIfTopologyExists: true);
 }
Пример #13
0
		private RaftEngineOptions CreateNodeOptions(string nodeName, int messageTimeout, StorageEnvironmentOptions storageOptions, params string[] peers)
		{
			var nodeOptions = new RaftEngineOptions(new NodeConnectionInfo { Name = nodeName },
				storageOptions,
				_inMemoryTransportHub.CreateTransportFor(nodeName),
				new DictionaryStateMachine())
			{
				ElectionTimeout = messageTimeout,
				HeartbeatTimeout = messageTimeout / 6,
				Stopwatch = Stopwatch.StartNew()
			};
			return nodeOptions;
		}
Пример #14
0
		public void Follower_on_timeout_should_become_candidate()
		{
			var storageEnvironmentOptions = StorageEnvironmentOptions.CreateMemoryOnly();
			storageEnvironmentOptions.OwnsPagers = false;

			var nodeOptions = new RaftEngineOptions(new NodeConnectionInfo { Name = "real" }, storageEnvironmentOptions, _inMemoryTransportHub.CreateTransportFor("real"), new DictionaryStateMachine());

			PersistentState.SetTopologyExplicitly(nodeOptions,
				new Topology(
					new Guid("355a589b-cadc-463d-a515-5add2ea47205"),
					new[]
					{
						new NodeConnectionInfo {Name = "real"}, new NodeConnectionInfo {Name = "u2"}, new NodeConnectionInfo {Name = "pj"},
					}, new NodeConnectionInfo[0], new NodeConnectionInfo[0]), throwIfTopologyExists: true);
			storageEnvironmentOptions.OwnsPagers = true;

			using (var node = new RaftEngine(nodeOptions))
			{
				var timeoutEvent = new ManualResetEventSlim();
				node.StateTimeout += timeoutEvent.Set;

				ForceTimeout("real");

				timeoutEvent.Wait();
				Assert.Equal(RaftEngineState.Candidate, node.State);
			}
		}
Пример #15
0
        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();
                    }
                }
            }
        }