コード例 #1
0
        public async Task should_issue_gossip_to_gossip_seed()
        {
            HttpRequestMessage?request = null;
            var gossip = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State               = ClusterMessages.VNodeState.Leader,
                        InstanceId          = Guid.NewGuid(),
                        HttpEndPointAddress = IPAddress.Any.ToString(),
                        HttpEndPointPort    = 4444,
                        IsAlive             = true,
                    },
                }
            };

            var handler = new CustomMessageHandler(req => {
                request = req;
                _fixture.CurrentClusterInfo.Members = gossip.Members;
            });

            var gossipSeed = new DnsEndPoint(_fixture.Host, _fixture.Port);

            var sut = new ClusterEndpointDiscoverer(1, new[] {
                gossipSeed,
            }, Timeout.InfiniteTimeSpan, TimeSpan.Zero, NodePreference.Leader, handler);

            await sut.DiscoverAsync();

            Assert.Equal(Uri.UriSchemeHttps, request?.RequestUri.Scheme);
            Assert.Equal(gossipSeed.Host, request?.RequestUri.Host);
            Assert.Equal(gossipSeed.Port, request?.RequestUri.Port);
        }
コード例 #2
0
        public async Task should_issue_gossip_to_gossip_seed()
        {
            HttpRequestMessage request = null;
            var gossip = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State            = ClusterMessages.VNodeState.Leader,
                        InstanceId       = Guid.NewGuid(),
                        ExternalHttpIp   = IPAddress.Any.ToString(),
                        ExternalHttpPort = 4444,
                        IsAlive          = true,
                    },
                }
            };

            var handler = new FakeMessageHandler(req => {
                request = req;
                return(ResponseFromGossip(gossip));
            });

            var gossipSeed = new DnsEndPoint("gossip_seed_endpoint", 1114);

            var sut = new ClusterEndpointDiscoverer(1, new[] {
                gossipSeed,
            }, Timeout.InfiniteTimeSpan, TimeSpan.Zero, NodePreference.Leader, handler);

            await sut.DiscoverAsync();

            Assert.Equal(Uri.UriSchemeHttps, request?.RequestUri.Scheme);
            Assert.Equal(gossipSeed.Host, request?.RequestUri.Host);
            Assert.Equal(gossipSeed.Port, request?.RequestUri.Port);
        }
コード例 #3
0
        public async Task falls_back_to_first_alive_node_if_a_preferred_node_is_not_found(bool useHttps)
        {
            var gossip = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State      = ClusterMessages.VNodeState.Leader,
                        InstanceId = Guid.NewGuid(),
                        EndPoint   = new DnsEndPoint(IPAddress.Any.ToString(), 1111),
                        IsAlive    = false,
                    },
                    new ClusterMessages.MemberInfo {
                        State      = ClusterMessages.VNodeState.Follower,
                        InstanceId = Guid.NewGuid(),
                        EndPoint   = new DnsEndPoint(IPAddress.Any.ToString(), 2222),
                        IsAlive    = true,
                    },
                }
            };

            var sut = new GossipBasedEndpointDiscoverer(
                new EventStoreClientConnectivitySettings {
                MaxDiscoverAttempts = 1,
                GossipTimeout       = System.Threading.Timeout.InfiniteTimeSpan,
                Insecure            = !useHttps,
                DiscoveryInterval   = TimeSpan.Zero,
                NodePreference      = NodePreference.Leader,
                DnsGossipSeeds      = new[] { _gossipSeed }
            }, new CallbackTestGossipClient(gossip));

            var result = await sut.DiscoverAsync();

            Assert.Equal(result.GetPort(),
                         gossip.Members.Last(x => x.State == ClusterMessages.VNodeState.Follower).EndPoint.Port);
        }
コード例 #4
0
        public async Task falls_back_to_first_alive_node_if_a_preferred_node_is_not_found()
        {
            var gossip = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State            = ClusterMessages.VNodeState.Leader,
                        InstanceId       = Guid.NewGuid(),
                        ExternalHttpIp   = IPAddress.Any.ToString(),
                        ExternalHttpPort = 1111,
                        IsAlive          = false,
                    },
                    new ClusterMessages.MemberInfo {
                        State            = ClusterMessages.VNodeState.Follower,
                        InstanceId       = Guid.NewGuid(),
                        ExternalHttpIp   = IPAddress.Any.ToString(),
                        ExternalHttpPort = 2222,
                        IsAlive          = true,
                    },
                }
            };
            var handler = new FakeMessageHandler(req => ResponseFromGossip(gossip));

            var sut = new ClusterEndpointDiscoverer(1, new[] {
                new DnsEndPoint("localhost", 1113)
            }, Timeout.InfiniteTimeSpan, TimeSpan.Zero, NodePreference.Leader, handler);

            var result = await sut.DiscoverAsync();

            Assert.Equal(result.Port,
                         gossip.Members.Last(x => x.State == ClusterMessages.VNodeState.Follower).ExternalHttpPort);
        }
コード例 #5
0
        public async Task should_issue_gossip_to_gossip_seed(bool useHttps)
        {
            var endPoint = new DnsEndPoint(IPAddress.Any.ToString(), 4444);
            var gossip   = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State      = ClusterMessages.VNodeState.Leader,
                        InstanceId = Guid.NewGuid(),
                        EndPoint   = endPoint,
                        IsAlive    = true,
                    },
                }
            };

            var sut = new GossipBasedEndpointDiscoverer(
                new EventStoreClientConnectivitySettings {
                MaxDiscoverAttempts = 1,
                GossipTimeout       = System.Threading.Timeout.InfiniteTimeSpan,
                Insecure            = !useHttps,
                DiscoveryInterval   = TimeSpan.Zero,
                NodePreference      = NodePreference.Leader,
                DnsGossipSeeds      = new[] { _gossipSeed }
            }, new CallbackTestGossipClient(gossip));

            var result = await sut.DiscoverAsync();

            Assert.Equal(result, endPoint);
        }
コード例 #6
0
        internal async Task should_not_be_able_to_pick_invalid_node(bool useHttps,
                                                                    ClusterMessages.VNodeState invalidState)
        {
            var gossip = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State      = invalidState,
                        InstanceId = Guid.NewGuid(), EndPoint = new DnsEndPoint(IPAddress.Any.ToString(), 4444),
                        IsAlive    = true,
                    },
                }
            };

            var sut = new GossipBasedEndpointDiscoverer(
                new EventStoreClientConnectivitySettings {
                MaxDiscoverAttempts = 1,
                GossipTimeout       = System.Threading.Timeout.InfiniteTimeSpan,
                Insecure            = !useHttps,
                DiscoveryInterval   = TimeSpan.Zero,
                NodePreference      = NodePreference.Leader,
                DnsGossipSeeds      = new[] { _gossipSeed }
            }, new CallbackTestGossipClient(gossip));

            var ex = await Assert.ThrowsAsync <DiscoveryException>(async() => await sut.DiscoverAsync());

            Assert.Equal(1, ex.MaxDiscoverAttempts);
        }
コード例 #7
0
        public async Task falls_back_to_first_alive_node_if_a_preferred_node_is_not_found()
        {
            var gossip = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State               = ClusterMessages.VNodeState.Leader,
                        InstanceId          = Guid.NewGuid(),
                        HttpEndPointAddress = IPAddress.Any.ToString(),
                        HttpEndPointPort    = 1111,
                        IsAlive             = false,
                    },
                    new ClusterMessages.MemberInfo {
                        State               = ClusterMessages.VNodeState.Follower,
                        InstanceId          = Guid.NewGuid(),
                        HttpEndPointAddress = IPAddress.Any.ToString(),
                        HttpEndPointPort    = 2222,
                        IsAlive             = true,
                    },
                }
            };
            var handler = new CustomMessageHandler(req => {
                _fixture.CurrentClusterInfo.Members = gossip.Members;
            });

            var sut = new ClusterEndpointDiscoverer(1, new[] {
                new DnsEndPoint(_fixture.Host, _fixture.Port)
            }, Timeout.InfiniteTimeSpan, TimeSpan.Zero, NodePreference.Leader, handler);

            var result = await sut.DiscoverAsync();

            Assert.Equal(result.GetPort(),
                         gossip.Members.Last(x => x.State == ClusterMessages.VNodeState.Follower).HttpEndPointPort);
        }
コード例 #8
0
 private HttpResponseMessage ResponseFromGossip(ClusterMessages.ClusterInfo gossip) =>
 new HttpResponseMessage(HttpStatusCode.OK)
 {
     Content = new StringContent(JsonSerializer.Serialize(gossip, new JsonSerializerOptions {
         PropertyNamingPolicy        = JsonNamingPolicy.CamelCase,
         PropertyNameCaseInsensitive = true,
         Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }
     }))
 };
コード例 #9
0
        public async Task should_be_able_to_discover_twice(bool useHttps)
        {
            var firstGossip = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State      = ClusterMessages.VNodeState.Leader,
                        InstanceId = Guid.NewGuid(), EndPoint = new DnsEndPoint(IPAddress.Any.ToString(), 1111),
                        IsAlive    = true,
                    },
                    new ClusterMessages.MemberInfo {
                        State      = ClusterMessages.VNodeState.Follower,
                        InstanceId = Guid.NewGuid(), EndPoint = new DnsEndPoint(IPAddress.Any.ToString(), 2222),
                        IsAlive    = true,
                    },
                }
            };
            var secondGossip = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State      = ClusterMessages.VNodeState.Leader,
                        InstanceId = Guid.NewGuid(), EndPoint = new DnsEndPoint(IPAddress.Any.ToString(), 1111),
                        IsAlive    = false,
                    },
                    new ClusterMessages.MemberInfo {
                        State      = ClusterMessages.VNodeState.Leader,
                        InstanceId = Guid.NewGuid(), EndPoint = new DnsEndPoint(IPAddress.Any.ToString(), 2222),
                        IsAlive    = true,
                    },
                }
            };

            var sut = new GossipBasedEndpointDiscoverer(
                new EventStoreClientConnectivitySettings {
                MaxDiscoverAttempts = 5,
                GossipTimeout       = System.Threading.Timeout.InfiniteTimeSpan,
                Insecure            = !useHttps,
                DiscoveryInterval   = TimeSpan.Zero,
                NodePreference      = NodePreference.Leader,
                DnsGossipSeeds      = new[] { _gossipSeed }
            }, new MultiGossipCallback(firstGossip, secondGossip));

            var result = await sut.DiscoverAsync();

            var expected = firstGossip.Members.First(x => x.EndPoint.Port == 1111);

            Assert.Equal(expected.EndPoint.Host, result.GetHost());
            Assert.Equal(expected.EndPoint.Port, result.GetPort());

            result = await sut.DiscoverAsync();

            expected = secondGossip.Members.First(x => x.EndPoint.Port == 2222);

            Assert.Equal(expected.EndPoint.Host, result.GetHost());
            Assert.Equal(expected.EndPoint.Port, result.GetPort());
        }
コード例 #10
0
        public async Task should_pick_node_based_on_preference(NodePreference preference,
                                                               ClusterMessages.VNodeState expectedState)
        {
            var gossip = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State               = ClusterMessages.VNodeState.Leader,
                        InstanceId          = Guid.NewGuid(),
                        HttpEndPointAddress = IPAddress.Any.ToString(),
                        HttpEndPointPort    = 1111,
                        IsAlive             = true,
                    },
                    new ClusterMessages.MemberInfo {
                        State               = ClusterMessages.VNodeState.Follower,
                        InstanceId          = Guid.NewGuid(),
                        HttpEndPointAddress = IPAddress.Any.ToString(),
                        HttpEndPointPort    = 2222,
                        IsAlive             = true,
                    },
                    new ClusterMessages.MemberInfo {
                        State = expectedState == ClusterMessages.VNodeState.ReadOnlyLeaderless
                                                        ? expectedState
                                                        : ClusterMessages.VNodeState.ReadOnlyReplica,
                        InstanceId          = Guid.NewGuid(),
                        HttpEndPointAddress = IPAddress.Any.ToString(),
                        HttpEndPointPort    = 3333,
                        IsAlive             = true,
                    },
                    new ClusterMessages.MemberInfo {
                        State               = ClusterMessages.VNodeState.Manager,
                        InstanceId          = Guid.NewGuid(),
                        HttpEndPointAddress = IPAddress.Any.ToString(),
                        HttpEndPointPort    = 4444,
                        IsAlive             = true,
                    },
                }
            };
            var handler = new CustomMessageHandler(req => {
                _fixture.CurrentClusterInfo.Members = gossip.Members;
            });

            var sut = new ClusterEndpointDiscoverer(1, new[] {
                new DnsEndPoint(_fixture.Host, _fixture.Port)
            }, Timeout.InfiniteTimeSpan, TimeSpan.Zero, preference, handler);

            var result = await sut.DiscoverAsync();

            Assert.Equal(result.GetPort(),
                         gossip.Members.Last(x => x.State == expectedState).HttpEndPointPort);
        }
コード例 #11
0
        public async Task should_pick_node_based_on_preference(NodePreference preference,
                                                               ClusterMessages.VNodeState expectedState)
        {
            var gossip = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State            = ClusterMessages.VNodeState.Leader,
                        InstanceId       = Guid.NewGuid(),
                        ExternalHttpIp   = IPAddress.Any.ToString(),
                        ExternalHttpPort = 1111,
                        IsAlive          = true,
                    },
                    new ClusterMessages.MemberInfo {
                        State            = ClusterMessages.VNodeState.Follower,
                        InstanceId       = Guid.NewGuid(),
                        ExternalHttpIp   = IPAddress.Any.ToString(),
                        ExternalHttpPort = 2222,
                        IsAlive          = true,
                    },
                    new ClusterMessages.MemberInfo {
                        State = expectedState == ClusterMessages.VNodeState.ReadOnlyLeaderless
                                                        ? expectedState
                                                        : ClusterMessages.VNodeState.ReadOnlyReplica,
                        InstanceId       = Guid.NewGuid(),
                        ExternalHttpIp   = IPAddress.Any.ToString(),
                        ExternalHttpPort = 3333,
                        IsAlive          = true,
                    },
                    new ClusterMessages.MemberInfo {
                        State            = ClusterMessages.VNodeState.Manager,
                        InstanceId       = Guid.NewGuid(),
                        ExternalHttpIp   = IPAddress.Any.ToString(),
                        ExternalHttpPort = 4444,
                        IsAlive          = true,
                    },
                }
            };
            var handler = new FakeMessageHandler(req => ResponseFromGossip(gossip));

            var sut = new ClusterEndpointDiscoverer(1, new[] {
                new DnsEndPoint("localhost", 1113)
            }, Timeout.InfiniteTimeSpan, TimeSpan.Zero, preference, handler);

            var result = await sut.DiscoverAsync();

            Assert.Equal(result.Port,
                         gossip.Members.Last(x => x.State == expectedState).ExternalHttpPort);
        }
コード例 #12
0
        internal async Task should_pick_node_based_on_preference(bool useHttps, NodePreference preference,
                                                                 ClusterMessages.VNodeState expectedState)
        {
            var gossip = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State      = ClusterMessages.VNodeState.Leader,
                        InstanceId = Guid.NewGuid(), EndPoint = new DnsEndPoint(IPAddress.Any.ToString(), 1111),
                        IsAlive    = true,
                    },
                    new ClusterMessages.MemberInfo {
                        State      = ClusterMessages.VNodeState.Follower,
                        InstanceId = Guid.NewGuid(), EndPoint = new DnsEndPoint(IPAddress.Any.ToString(), 2222),
                        IsAlive    = true,
                    },
                    new ClusterMessages.MemberInfo {
                        State = expectedState == ClusterMessages.VNodeState.ReadOnlyLeaderless
                                                        ? expectedState
                                                        : ClusterMessages.VNodeState.ReadOnlyReplica,
                        InstanceId = Guid.NewGuid(), EndPoint = new DnsEndPoint(IPAddress.Any.ToString(), 3333),
                        IsAlive    = true,
                    },
                    new ClusterMessages.MemberInfo {
                        State      = ClusterMessages.VNodeState.Manager,
                        InstanceId = Guid.NewGuid(), EndPoint = new DnsEndPoint(IPAddress.Any.ToString(), 4444),
                        IsAlive    = true,
                    },
                }
            };

            var sut = new GossipBasedEndpointDiscoverer(
                new EventStoreClientConnectivitySettings {
                MaxDiscoverAttempts = 1,
                GossipTimeout       = System.Threading.Timeout.InfiniteTimeSpan,
                Insecure            = !useHttps,
                DiscoveryInterval   = TimeSpan.Zero,
                NodePreference      = preference,
                DnsGossipSeeds      = new[] { _gossipSeed }
            }, new CallbackTestGossipClient(gossip));

            var result = await sut.DiscoverAsync();

            Assert.Equal(result.GetPort(),
                         gossip.Members.Last(x => x.State == expectedState).EndPoint.Port);
        }
コード例 #13
0
        public async Task should_not_be_able_to_pick_invalid_node(ClusterMessages.VNodeState invalidState)
        {
            var gossip = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State            = invalidState,
                        InstanceId       = Guid.NewGuid(),
                        ExternalHttpIp   = IPAddress.Any.ToString(),
                        ExternalHttpPort = 4444,
                        IsAlive          = true,
                    },
                }
            };

            var handler = new FakeMessageHandler(req => ResponseFromGossip(gossip));

            var sut = new ClusterEndpointDiscoverer(1, new[] { new DnsEndPoint("localhost", 1113), }, Timeout.InfiniteTimeSpan, TimeSpan.Zero, NodePreference.Leader, handler);

            await Assert.ThrowsAsync <DiscoveryException>(() => sut.DiscoverAsync());
        }
コード例 #14
0
        public async Task should_not_be_able_to_pick_invalid_node(ClusterMessages.VNodeState invalidState)
        {
            var gossip = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State               = invalidState,
                        InstanceId          = Guid.NewGuid(),
                        HttpEndPointAddress = IPAddress.Any.ToString(),
                        HttpEndPointPort    = 4444,
                        IsAlive             = true,
                    },
                }
            };

            var handler = new CustomMessageHandler(req => {
                _fixture.CurrentClusterInfo.Members = gossip.Members;
            });

            var sut = new ClusterEndpointDiscoverer(1, new[] { new DnsEndPoint(_fixture.Host, _fixture.Port), }, Timeout.InfiniteTimeSpan, TimeSpan.Zero, NodePreference.Leader, handler);

            await Assert.ThrowsAsync <DiscoveryException>(() => sut.DiscoverAsync());
        }
コード例 #15
0
 public CallbackTestGossipClient(ClusterMessages.ClusterInfo gossip, Action?callback = null)
 {
     _gossip   = gossip;
     _callback = callback;
 }
コード例 #16
0
        public async Task should_be_able_to_discover_twice()
        {
            bool isFirstGossip = true;
            var  firstGossip   = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State               = ClusterMessages.VNodeState.Leader,
                        InstanceId          = Guid.NewGuid(),
                        HttpEndPointAddress = IPAddress.Any.ToString(),
                        HttpEndPointPort    = 1111,
                        IsAlive             = true,
                    },
                    new ClusterMessages.MemberInfo {
                        State               = ClusterMessages.VNodeState.Follower,
                        InstanceId          = Guid.NewGuid(),
                        HttpEndPointAddress = IPAddress.Any.ToString(),
                        HttpEndPointPort    = 2222,
                        IsAlive             = true,
                    },
                }
            };
            var secondGossip = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State               = ClusterMessages.VNodeState.Leader,
                        InstanceId          = Guid.NewGuid(),
                        HttpEndPointAddress = IPAddress.Any.ToString(),
                        HttpEndPointPort    = 1111,
                        IsAlive             = false,
                    },
                    new ClusterMessages.MemberInfo {
                        State               = ClusterMessages.VNodeState.Leader,
                        InstanceId          = Guid.NewGuid(),
                        HttpEndPointAddress = IPAddress.Any.ToString(),
                        HttpEndPointPort    = 2222,
                        IsAlive             = true,
                    },
                }
            };

            var handler = new CustomMessageHandler(req => {
                if (isFirstGossip)
                {
                    isFirstGossip = false;
                    _fixture.CurrentClusterInfo.Members = firstGossip.Members;
                }
                else
                {
                    _fixture.CurrentClusterInfo.Members = secondGossip.Members;
                }
            });

            var gossipSeed = new DnsEndPoint(_fixture.Host, _fixture.Port);

            var sut = new ClusterEndpointDiscoverer(5, new[] {
                gossipSeed,
            }, Timeout.InfiniteTimeSpan, TimeSpan.Zero, NodePreference.Leader, handler);

            var result = await sut.DiscoverAsync();

            var expected = firstGossip.Members.First(x => x.HttpEndPointPort == 1111);

            Assert.Equal(expected.HttpEndPointAddress, result.GetHost());
            Assert.Equal(expected.HttpEndPointPort, result.GetPort());

            result = await sut.DiscoverAsync();

            expected = secondGossip.Members.First(x => x.HttpEndPointPort == 2222);

            Assert.Equal(expected.HttpEndPointAddress, result.GetHost());
            Assert.Equal(expected.HttpEndPointPort, result.GetPort());
        }
コード例 #17
0
 public GossipImplementation(ClusterMessages.ClusterInfo currentClusterInfo)
 {
     _currentClusterInfo = currentClusterInfo;
 }
コード例 #18
0
        public async Task should_be_able_to_discover_twice()
        {
            bool isFirstGossip = true;
            var  firstGossip   = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State            = ClusterMessages.VNodeState.Leader,
                        InstanceId       = Guid.NewGuid(),
                        ExternalHttpIp   = IPAddress.Any.ToString(),
                        ExternalHttpPort = 1111,
                        IsAlive          = true,
                    },
                    new ClusterMessages.MemberInfo {
                        State            = ClusterMessages.VNodeState.Follower,
                        InstanceId       = Guid.NewGuid(),
                        ExternalHttpIp   = IPAddress.Any.ToString(),
                        ExternalHttpPort = 2222,
                        IsAlive          = true,
                    },
                }
            };
            var secondGossip = new ClusterMessages.ClusterInfo {
                Members = new[] {
                    new ClusterMessages.MemberInfo {
                        State            = ClusterMessages.VNodeState.Leader,
                        InstanceId       = Guid.NewGuid(),
                        ExternalHttpIp   = IPAddress.Any.ToString(),
                        ExternalHttpPort = 1111,
                        IsAlive          = false,
                    },
                    new ClusterMessages.MemberInfo {
                        State            = ClusterMessages.VNodeState.Leader,
                        InstanceId       = Guid.NewGuid(),
                        ExternalHttpIp   = IPAddress.Any.ToString(),
                        ExternalHttpPort = 2222,
                        IsAlive          = true,
                    },
                }
            };

            var handler = new FakeMessageHandler(req => {
                if (isFirstGossip)
                {
                    isFirstGossip = false;
                    return(ResponseFromGossip(firstGossip));
                }
                else
                {
                    return(ResponseFromGossip(secondGossip));
                }
            });

            var gossipSeed = new DnsEndPoint("gossip_seed_endpoint", 1114);

            var sut = new ClusterEndpointDiscoverer(5, new[] {
                gossipSeed,
            }, Timeout.InfiniteTimeSpan, TimeSpan.Zero, NodePreference.Leader, handler);

            var result = await sut.DiscoverAsync();

            var expected = firstGossip.Members.First(x => x.ExternalHttpPort == 1111);

            Assert.Equal(expected.ExternalHttpIp, result.Address.ToString());
            Assert.Equal(expected.ExternalHttpPort, result.Port);

            result = await sut.DiscoverAsync();

            expected = secondGossip.Members.First(x => x.ExternalHttpPort == 2222);

            Assert.Equal(expected.ExternalHttpIp, result.Address.ToString());
            Assert.Equal(expected.ExternalHttpPort, result.Port);
        }