예제 #1
0
        public async Task GetSentinelMasterConnectionFailoverTest()
        {
            var conn     = Conn.GetSentinelMasterConnection(ServiceOptions);
            var endpoint = conn.currentSentinelMasterEndPoint.ToString();

            try
            {
                Log("Failover attempted initiated");
                SentinelServerA.SentinelFailover(ServiceName);
                Log("  Success!");
            }
            catch (RedisServerException ex) when(ex.Message.Contains("NOGOODSLAVE"))
            {
                // Retry once
                Log("  Retry initiated");
                await Task.Delay(1000).ForAwait();

                SentinelServerA.SentinelFailover(ServiceName);
                Log("  Retry complete");
            }
            await Task.Delay(2000).ForAwait();

            // Try and complete ASAP
            await UntilCondition(TimeSpan.FromSeconds(10), () => {
                var checkConn = Conn.GetSentinelMasterConnection(ServiceOptions);
                return(endpoint != checkConn.currentSentinelMasterEndPoint.ToString());
            });

            // Post-check for validity
            var conn1 = Conn.GetSentinelMasterConnection(ServiceOptions);

            Assert.NotEqual(endpoint, conn1.currentSentinelMasterEndPoint.ToString());
        }
예제 #2
0
        public async Task GetSentinelMasterConnectionWriteReadFailover()
        {
            var conn = Conn.GetSentinelMasterConnection(new ConfigurationOptions {
                ServiceName = ServiceName
            });
            var       s        = conn.currentSentinelMasterEndPoint.ToString();
            IDatabase db       = conn.GetDatabase();
            var       expected = DateTime.Now.Ticks.ToString();

            db.StringSet("beforeFailOverValue", expected);

            SentinelServerA.SentinelFailover(ServiceName);
            await Task.Delay(2000).ForAwait();

            var conn1 = Conn.GetSentinelMasterConnection(new ConfigurationOptions {
                ServiceName = ServiceName
            });
            var s1 = conn1.currentSentinelMasterEndPoint.ToString();

            var db1    = conn1.GetDatabase();
            var actual = db1.StringGet("beforeFailOverValue");

            Assert.NotNull(s);
            Assert.NotNull(s1);
            Assert.NotEmpty(s);
            Assert.NotEmpty(s1);
            Assert.NotEqual(s, s1);
            Assert.Equal(expected, actual);
        }
예제 #3
0
        private void DoFailover()
        {
            WaitForReady();

            // capture current replica
            var replicas = SentinelServerA.SentinelGetReplicaAddresses(ServiceName);

            Log("Starting failover...");
            var sw = Stopwatch.StartNew();

            SentinelServerA.SentinelFailover(ServiceName);

            // wait until the replica becomes the master
            WaitForReady(expectedMaster: replicas[0]);
            Log($"Time to failover: {sw.Elapsed}");
        }
예제 #4
0
        public async Task GetSentinelMasterConnectionFailoverTest()
        {
            var conn = Conn.GetSentinelMasterConnection(new ConfigurationOptions {
                ServiceName = ServiceName
            });
            var endpoint = conn.currentSentinelMasterEndPoint.ToString();

            SentinelServerA.SentinelFailover(ServiceName);
            await Task.Delay(2000).ForAwait();

            var conn1 = Conn.GetSentinelMasterConnection(new ConfigurationOptions {
                ServiceName = ServiceName
            });
            var endpoint1 = conn1.currentSentinelMasterEndPoint.ToString();

            Assert.NotEqual(endpoint, endpoint1);
        }
예제 #5
0
        public async Task GetSentinelMasterConnectionWriteReadFailover()
        {
            Log("Conn:");
            foreach (var server in Conn.GetServerSnapshot().ToArray())
            {
                Log("  Endpoint: " + server.EndPoint);
            }
            Log("Conn Slaves:");
            foreach (var slaves in SentinelServerA.SentinelSlaves(ServiceName))
            {
                foreach (var pair in slaves)
                {
                    Log("  {0}: {1}", pair.Key, pair.Value);
                }
            }

            var conn = Conn.GetSentinelMasterConnection(ServiceOptions);
            var s    = conn.currentSentinelMasterEndPoint.ToString();

            Log("Sentinel Master Endpoint: " + s);
            foreach (var server in conn.GetServerSnapshot().ToArray())
            {
                Log("  Server: " + server.EndPoint);
                Log("    Master Endpoint: " + server.MasterEndPoint);
                Log("    IsSlave: " + server.IsSlave);
                Log("    SlaveReadOnly: " + server.SlaveReadOnly);
                var info = conn.GetServer(server.EndPoint).Info("Replication");
                foreach (var section in info)
                {
                    Log("    Section: " + section.Key);
                    foreach (var pair in section)
                    {
                        Log("        " + pair.Key + ": " + pair.Value);
                    }
                }
            }

            IDatabase db       = conn.GetDatabase();
            var       expected = DateTime.Now.Ticks.ToString();

            Log("Tick Key: " + expected);
            var key = Me();

            db.KeyDelete(key, CommandFlags.FireAndForget);
            db.StringSet(key, expected);

            await UntilCondition(TimeSpan.FromSeconds(10),
                                 () => SentinelServerA.SentinelMaster(ServiceName).ToDictionary()["num-slaves"] != "0"
                                 );

            Log("Conditions met");

            try
            {
                Log("Failover attempted initiated");
                SentinelServerA.SentinelFailover(ServiceName);
                Log("  Success!");
            }
            catch (RedisServerException ex) when(ex.Message.Contains("NOGOODSLAVE"))
            {
                // Retry once
                Log("  Retry initiated");
                await Task.Delay(1000).ForAwait();

                SentinelServerA.SentinelFailover(ServiceName);
                Log("  Retry complete");
            }
            Log("Delaying for failover conditions...");
            await Task.Delay(2000).ForAwait();

            Log("Conditons check...");
            // Spin until complete (with a timeout) - since this can vary
            await UntilCondition(TimeSpan.FromSeconds(20), () =>
            {
                var checkConn = Conn.GetSentinelMasterConnection(ServiceOptions);
                return(s != checkConn.currentSentinelMasterEndPoint.ToString() &&
                       expected == checkConn.GetDatabase().StringGet(key));
            });

            Log("  Conditions met.");

            var conn1 = Conn.GetSentinelMasterConnection(ServiceOptions);
            var s1    = conn1.currentSentinelMasterEndPoint.ToString();

            Log("New master endpoint: " + s1);

            var actual = conn1.GetDatabase().StringGet(key);

            Log("Fetched tick key: " + actual);

            Assert.NotNull(s);
            Assert.NotNull(s1);
            Assert.NotEmpty(s);
            Assert.NotEmpty(s1);
            Assert.NotEqual(s, s1);
            // TODO: Track this down on the test race
            //Assert.Equal(expected, actual);
        }
        public async Task ManagedPrimaryConnectionEndToEndWithFailoverTest()
        {
            var connectionString = $"{TestConfig.Current.SentinelServer}:{TestConfig.Current.SentinelPortA},serviceName={ServiceOptions.ServiceName},allowAdmin=true";
            var conn             = await ConnectionMultiplexer.ConnectAsync(connectionString);

            conn.ConfigurationChanged += (s, e) => Log($"Configuration changed: {e.EndPoint}");
            var sub = conn.GetSubscriber();

            sub.Subscribe("*", (channel, message) => Log($"Sub: {channel}, message:{message}"));

            var db = conn.GetDatabase();
            await db.PingAsync();

            var endpoints = conn.GetEndPoints();

            Assert.Equal(2, endpoints.Length);

            var servers = endpoints.Select(e => conn.GetServer(e)).ToArray();

            Assert.Equal(2, servers.Length);

            var primary = servers.FirstOrDefault(s => !s.IsReplica);

            Assert.NotNull(primary);
            var replica = servers.FirstOrDefault(s => s.IsReplica);

            Assert.NotNull(replica);
            Assert.NotEqual(primary.EndPoint.ToString(), replica.EndPoint.ToString());

            // Set string value on current primary
            var expected = DateTime.Now.Ticks.ToString();

            Log("Tick Key: " + expected);
            var key = Me();
            await db.KeyDeleteAsync(key, CommandFlags.FireAndForget);

            await db.StringSetAsync(key, expected);

            var value = await db.StringGetAsync(key);

            Assert.Equal(expected, value);

            Log("Waiting for first replication check...");
            // force read from replica, replication has some lag
            await WaitForReplicationAsync(servers[0]).ForAwait();

            value = await db.StringGetAsync(key, CommandFlags.DemandReplica);

            Assert.Equal(expected, value);

            Log("Waiting for ready pre-failover...");
            await WaitForReadyAsync();

            // capture current replica
            var replicas = SentinelServerA.SentinelGetReplicaAddresses(ServiceName);

            Log("Starting failover...");
            var sw = Stopwatch.StartNew();

            SentinelServerA.SentinelFailover(ServiceName);

            // There's no point in doing much for 10 seconds - this is a built-in delay of how Sentinel works.
            // The actual completion invoking the replication of the former primary is handled via
            // https://github.com/redis/redis/blob/f233c4c59d24828c77eb1118f837eaee14695f7f/src/sentinel.c#L4799-L4808
            // ...which is invoked by INFO polls every 10 seconds (https://github.com/redis/redis/blob/f233c4c59d24828c77eb1118f837eaee14695f7f/src/sentinel.c#L81)
            // ...which is calling https://github.com/redis/redis/blob/f233c4c59d24828c77eb1118f837eaee14695f7f/src/sentinel.c#L2666
            // However, the quicker iteration on INFO during an o_down does not apply here: https://github.com/redis/redis/blob/f233c4c59d24828c77eb1118f837eaee14695f7f/src/sentinel.c#L3089-L3104
            // So...we're waiting 10 seconds, no matter what. Might as well just idle to be more stable.
            await Task.Delay(TimeSpan.FromSeconds(10));

            // wait until the replica becomes the primary
            Log("Waiting for ready post-failover...");
            await WaitForReadyAsync(expectedPrimary : replicas[0]);

            Log($"Time to failover: {sw.Elapsed}");

            endpoints = conn.GetEndPoints();
            Assert.Equal(2, endpoints.Length);

            servers = endpoints.Select(e => conn.GetServer(e)).ToArray();
            Assert.Equal(2, servers.Length);

            var newPrimary = servers.FirstOrDefault(s => !s.IsReplica);

            Assert.NotNull(newPrimary);
            Assert.Equal(replica.EndPoint.ToString(), newPrimary.EndPoint.ToString());
            var newReplica = servers.FirstOrDefault(s => s.IsReplica);

            Assert.NotNull(newReplica);
            Assert.Equal(primary.EndPoint.ToString(), newReplica.EndPoint.ToString());
            Assert.NotEqual(primary.EndPoint.ToString(), replica.EndPoint.ToString());

            value = await db.StringGetAsync(key);

            Assert.Equal(expected, value);

            Log("Waiting for second replication check...");
            // force read from replica, replication has some lag
            await WaitForReplicationAsync(newPrimary).ForAwait();

            value = await db.StringGetAsync(key, CommandFlags.DemandReplica);

            Assert.Equal(expected, value);
        }