StackExchange / StackExchange.Redis

General purpose redis client
https://stackexchange.github.io/StackExchange.Redis/
Other
5.87k stars 1.51k forks source link

Poor message balancing between replicas using Redis Sentinel. #2554

Open Grumov opened 12 months ago

Grumov commented 12 months ago

The current distribution of messages between replicas is uneven. The replica following the master in the list of servers is selected more often than others. In the configuration there is a master and two replicas:

I propose the following fix issue: https://github.com/StackExchange/StackExchange.Redis/pull/2553

NickCraver commented 11 months ago

Ah we do have an issue - great, let's discuss here and hold off on PRs that change the world :)

What does your code/example look like?

Grumov commented 11 months ago

My example looks like: Configuration: 3 Sentinel Redis, 1 Master Redis, 2 Replicas Redis. You can also see all Redis configs in PR tests. I write data to the master and read from replicas, distributing the load between replicas. Since one of the replicas receives a large load, it is overloaded earlier than expected. I assumed according to the Round Robin strategy that the replicas would receive the same number of commands.

var connectionString = $"{TestConfig.Current.SentinelServer}:{TestConfig.Current.SentinelTwoReplicasPortA},serviceName={ServiceOptions.ServiceName},allowAdmin=true";
using var conn = await ConnectionMultiplexer.ConnectAsync(connectionString);

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

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

// Set string value on current primary
var expected = DateTime.Now.Ticks.ToString();
Log("Tick Key: " + expected);
var key = "anyKey";
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();

for (var i = 0; i < 100; i++)
{
    await db.StringGetAsync(key, CommandFlags.PreferReplica);
}
Grumov commented 11 months ago

If we cannot fix the balancing, then maybe you can provide a public interface so that we can make custom server selection strategies.