New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issues with Sentinel #1427
Comments
I have a rough time too getting this to work for the Redis Sentinel running on my local machine. I'm glad to hear I'm not the only one. I can't figure out how I can insert records in my Sentinel Cluster. T'm running 2 master Pods and 3 Sentinel and 3 Redis pods on my Cluster. I'm using this piece of code: var options = new ConfigurationOptions() var masterOptions = new ConfigurationOptions()
The GetSentinelMasterConnection method call fails with an exception: "Unable to determine master" |
Ultimately, this is one of the key concerns I had about merging the sentinel code. Fundamentally, we (the core maintainers) aren't routinely sentinel users, so it is hard to say "yes, this is the right API and does what it needs to do". So we held off on the sentinel code for ages. Eventually we merged it, and: it looks like there's already some gaps and problem areas. Now I'm in that awkward "what option is best now" position again :/ |
@mgravell That makes sense. Now I understand why this functionality was only recently merged in. I still believe the API can be made much better with a few "minor" changes. A refinement of my original suggestions would be to have an interface like the following. public interface ISentinel {
IConnectionMultiplexer GetMaster(ConfigurationOptions options = null);
}
var sentinel = ConnectionMultiplexer.ConnectSentinel(new ConfigurationOptions {
ServiceName = _redisSentinelServiceName,
EndPoints = { { _redisSentinelHost, 26379 } }
});
var master = sentinel.GetMaster(); This would largely avoid the confusion caused by having |
@mgravell I get what you are saying and it's not fair for the community to expect it, but you guys are pretty much THE Redis library for .NET and using sentinel is a very common thing to do especially for HA scenarios. If you guys don't personally use it and don't want to maintain it then I would hope that you would defer to the community to do so and trust them. I can try and help out more. As @ctlajoie said, there are definitely some usability issues currently. I ran into the exact same issues. I will try and get a PR together to address these issues without affecting current usages of the library. Any feedback on @ctlajoie 's suggestions before I implement them? |
…r for working with sentinel setups (StackExchange#1427) Fix issue with duplicate endpoints being added in the UpdateSentinelAddressList method (StackExchange#1430).
…r for working with sentinel setups (StackExchange#1427) Fix issue with duplicate endpoints being added in the UpdateSentinelAddressList method (StackExchange#1430). Add string configuration overloads for sentinel connect methods. Remove password from sentinel servers as it seems the windows port does not support it. Add some new tests. Oops commented out the wrong line
…r for working with sentinel setups (StackExchange#1427) Fix issue with duplicate endpoints being added in the UpdateSentinelAddressList method (StackExchange#1430). Add string configuration overloads for sentinel connect methods. Remove password from sentinel servers as it seems the windows port does not support it. Add some new tests. Oops commented out the wrong line Again
…r for working with sentinel setups (StackExchange#1427) Fix issue with duplicate endpoints being added in the UpdateSentinelAddressList method (StackExchange#1430). Add string configuration overloads for sentinel connect methods. Remove password from sentinel servers as it seems the windows port does not support it. Add some new tests.
…r for working with sentinel setups (StackExchange#1427) Fix issue with duplicate endpoints being added in the UpdateSentinelAddressList method (StackExchange#1430). Add string configuration overloads for sentinel connect methods. Remove password from sentinel servers as it seems the windows port does not support it. Add some new tests.
…r for working with sentinel setups (StackExchange#1427) Fix issue with duplicate endpoints being added in the UpdateSentinelAddressList method (StackExchange#1430). Add string configuration overloads for sentinel connect methods. Remove password from sentinel servers as it seems the windows port does not support it. Add some new tests.
There is also this - #1067 (comment) The transparency issues weren't solved. I have tried giving Sentinel a go with
Improving the API is worthy, but I still believe it is important that I could understand this was out of scope of #1067. Should we create a new issue for that? |
|
Totally missed that. |
I'm inclined to agree; if we step back (many) years, this actually was the case in BookSleeve (the ancient ancestor of SE-Redis). That is why the service name existed in the config. Broadly speaking, I'm open to re-enabling this, but I am concerned about it changing semantics, in particular what happens when people have a value in there that currently does nothing. However! I am open to doing something more controlled here, and maybe this is something we should do that changes the direction of #1431 - i.e. instead of adding methods like
with if (config.NewFlag)
return SentinelMasterConnect(config); // this is now a private method
return // ... old logic thoughts? |
How about try
{
// current connection methods
}
catch (SorryIAmNotRedisIAmSentinelException)
{
return SentinelMasterConnect(config); // this is now a private method
} That is, you would not need a new flag. You could tell from the protocol, that the connection string points to Sentinel instances. |
or more simply we could just do: var conn = current connection methods
if (!string.IsNullOrWhiteSpace(config.ServiceName) &&
conn.ServerSelectionStrategy.ServerType == ServerType.Sentinel)
{
using (conn) { return conn.SomeMethod(config.ServiceName); }
}
return conn; If you don't want to do this flip (i.e. you want to connect to the sentinel itself): do not supply a |
Sounds good. Just to be clear, since the method is called That is, will the |
Good question, and I'm open to suggestions; should it be issuing both |
I'd be inclined to it. Can't see now what it would mean for implementation. I would tell though, that Sentinel should only affect the way the pool of As a side note. There could be an opportunity here to introduce load balancing, even it it was a simple round-robin. Currently we are using 3-node Sentinel managed "clusters", the connection strings point to Redis nodes directly, oblivious of Sentinel. Bulk of requests (say 90%) ends up on the slave that occurs first in the connection string (or whatever slave |
(it should already round-robin as long as it knows it is talking to replicas; it can't round-robin to the primary) |
What could cause it not knowing it is talking to replicas? Could that affect, or be affected by, (future implementation of) Sentinel? |
the server not responding to |
however, the thought occurs; if we were getting primary/replica info from sentinel - we could actually pre-seed the expected server types based on which list it is coming from; then it doesn't matter whether the server respects that command |
I am going to try and re-work my PR #1431 to make it so that you can connect to a sentinel managed master with just a connection string and not have to go through a different connect method. I do think that you should be able to explicitly connect to a redis sentinel server though and have the commands limited to the sentinel commands. So I think we should keep the |
That still leaves out the slaves. What would happen here? using var redis = await ConnectionMultiplexer.ConnectAsync("mySentinel:port,ServiceName=someService");
var db = await redis.GetDatabase();
var result = await db.StringGetAsync(someRedisKey, CommandFlags.DemandSlave);
Makes sense. Although I don't think you need extra flags or connect methods, besides the two: ConnectionMultiplexer.ConnectAsync();
ConnectionMultiplexer.SentinelConnectAsync(); The client calling |
If there was to be a new flag, I am thinking, use it to do away with any extra connect methods while helping users to avoid a pitfall. Consider: ConnectionMultiplexer.ConnectAsync("mySentinel:port,ServiceName=someService"); vs (this would allow you to ConnectionMultiplexer.ConnectAsync("mySentinel:port"); vs (here imagine the ConnectionMultiplexer.ConnectAsync("mySentinel:port,ServiceName=someService,SentinelManagedConnection=true"); As a user how would you image these methods to behave under various conditions? |
My view here is that if the server connected to is clearly a sentinel, and if a service name is supplied: it is fine to bounce automatically without any extra change, so no extra config option is needed. Anyone historically using a redis server (not sentinel) endpoint and supplying a service name will be unaffected. Anyone who wants to connect just to the sentinel: shouldn't supply a service name. Re replicas: we should try and identify master and replicas from the sentinel, and prime all the endpoints. The replicstion role (primary/replica) of each can probably be set in advance based on this info. |
I think it’s a completely valid scenario to want to connect directly to the sentinel server without getting redirected. How would I go about doing that? Would we just connect and sniff to see if it’s a sentinel and change the command map on the fly? I am in favor of leaving the other methods for intuitiveness, but also making it possible to connect with just a connection string. I don’t know how the read scenarios currently work. Does the client know about all the masters and slaves and a flag in each command tells it which set of servers to use? |
Just don't set a service name in the config
The library supports that usage, yes. Right now I think the sentinel usage only looks up he master, but it could lookup everything. |
I like direction you took this. Would the presence of a service name in the config also cause the ports to default to 26379? |
@ctlajoie I'd be hesitant to do that; the key aim here is to not break people who may have set that value without knowing the much deferred intent of it, so if their config is currently routing them to a redis-server instance, I don't want to break them. |
@mgravell but that is a brand new config option, isn't it? It seems to me that if we are going to trigger sentinel mode from the servicename setting then it should trigger the default port setting as well. Of course if you specify a port then that would override the default. |
This is not a new config option, so people could today have it specified. So what I suggest is: if all we have is a service name and an endpoint, don't make any guesses until we have spoken to the endpoint. If it is a redis server: ignore the service name and connect like we always have; if it is a sentinel: sure, resolving the service makes sense. |
In Kubernetes it is common to run sentinel and redis in the same pod. Thus they will have the same IP address, but the services are running on different ports. I can only speak for myself, but IMO it would be best to have it attempt to connnect on 26379 first, if a ServiceName is provided. What was the ServiceName property used for, historically? |
…r for working with sentinel setups (StackExchange#1427) Fix issue with duplicate endpoints being added in the UpdateSentinelAddressList method (StackExchange#1430). Add string configuration overloads for sentinel connect methods. Remove password from sentinel servers as it seems the windows port does not support it. Add some new tests.
Scanning through it and can't see how it manages the slaves ("GetMaster" methods), but that can be simply my ignorance. The API seems great now. I can run it through some stress tests on our setup and trigger Sentinel failovers if that would be helpful. |
@mrmartan Are you asking about being able to connect read only to the slaves? |
No, not particularly. That is: var conn = ConnectionMultiplexer.Connect("localhost,serviceName=mymaster");
var db = conn.GetDatabase();
db.StringGet("key", CommandFlag.DemandSlave); would route the Does that make sense? Also here #1427 (comment) and here #1427 (comment) |
@mrmartan yeah, I'm not sure how that works. I was just focused on getting the core sentinel support working. I think there are several more things that would be nice to have like what you are showing where it would send that command to a slave instance. I am thinking that will probably work though. Did you give it a try? |
No, I didn't. I can try tomorrow. |
@mrmartan hmm.. not sure on that. I didn't change any of that part of the code. My PR was just trying to get the basics to work. I would think that if that code currently works in a master/slave setup then it should work for a sentinel setup since sentinel is just a leader election quorum that sits on top of a master/slave config and automatically switches the master. @NickCraver @mgravell thoughts? |
Normally you would connect the multiplexer to |
I see. Although I would argue that giving users a version that can be integrated with 3rd party caching libraries and Sentinel while then working only with masters could lead to some performance pitfalls, i.e. it locks you out of horizontally scaling your sentinel setup. Could you release the fix for #1430 separately? Of course, the decision is ultimately with @mgravell and @NickCraver |
I have yet to try a failover scenarios, but after one quick test it seems to work. |
@mrmartan that's great. Would love to hear how all your testing goes. It would be a big help to get more testing on this. |
That's great. Thanks for testing that out! Did you also verify that failover is working correctly? You would need to ask @NickCraver or @mgravell about how it load balances the slave reads. |
I'll open a new issue about the load balancing. In other news, I think I've found an issue. After forcing a failover ( |
Add SentinelConnect and SentinelMasterConnect to ConnectionMultiplexer for working with sentinel setups (#1427) Fix issue with duplicate endpoints being added in the UpdateSentinelAddressList method (#1430). This change makes it a lot easier and more discoverable how to connect to a sentinel server while also allowing connecting with just a connection string change which allows existing libs that are using SE.Redis to be used in sentinel mode. Adding a `serviceName` parameter to the connection string triggers sentinel mode. It will connect to the sentinel and discover the current master and return a managed connection that follows the master. ```csharp var conn = ConnectionMultiplexer.Connect("localhost,serviceName=mymaster"); var db = conn.GetDatabase(); db.StringSet("key", "value"); ```
We are also facing with strange load balacing on replicas Yellow line is RPS on replica and it's two times more than any other replica (excluding green one, which is Master with write only commands) @mgravell @NickCraver can you help with this? |
I am also observing the same condition. I have three read replicas. First node gets high amount of traffic. 2nd node gets half amount of traffic than first node. 3rd node gets half amount of traffic than 2nd node. Is there any way to distribute same traffic to all the read replicas? |
Since this was scoped to the other issue (resolved now), let's please open any load balancing issues separately, so we can figure out a path there! |
I implemented support for Redis Sentinel in an application yesterday and I had a rough time getting it to work. I had to actually dig into the library code to figure out what I was doing wrong. If you make a few minor tweaks to the API you could entirely avoid the problems I ran into.
Allow me to explain. This is not the exact code I had; I modified it to illustrate the point.
Looks like it should work... but there are 2 problems with this code.
IConnectionMultiplexer
does not have aGetSentinelMasterConnection
function.ConfigurationOptions
MUST haveTieBreaker = ""
to work.I have a few suggestions:
GetSentinelMasterConnection
toIConnectionMultiplexer
ConnectionMultiplexer.ConnectSentinel(...)
function, which setsCommandMap
andTieBreaker
to the required values, and make endpoints default to port 26379.ServiceName
provided in the Sentinel configuration, so I don't have to provide it twiceI realize there may be any number of reasons why these suggestions might not make sense (this isn't my code), but please consider if there's some improvements to be made with regard to the library's Sentinel functionality.
The text was updated successfully, but these errors were encountered: