-
Notifications
You must be signed in to change notification settings - Fork 30
Sentinel #70
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
base: master
Are you sure you want to change the base?
Sentinel #70
Conversation
I forgot to add. Here is how you'd use the API: // direct connection, paired, reconnecting
use redis_async::client::{RedisConnectionBuilder, paired::PairedConnectionBuilder};
let client = RedisConnectionBuilder::new("url")
.username("hello")
.password("world")
.paired_reconnecting()
.await
.unwrap();
let response = client.send(data).await.unwrap();
// connecting through sentinel, pubsub, not reconnecting
use redis_async::client::{SentinelConnectionBuilder, pubsub::PubsubConnectionBuilder};
let client = SentinelConnectionBuilder::new(vec!["host 1", "host 2"], "master_name")
.redis_username("foo")
.redis_password("bar")
.pubsub_connect()
.await
.unwrap();
let stream = client.subscribe("message").await.unwrap(); |
Hello, Thanks for this 👍 This is quite a big PR so I'll some more comments later once I have the chance to go in to some of the details in a bit more detail. But some high-level comments: Regarding reconnection - I think I would prefer if the default path maintained backward compatibility, just to reduce the risk of users not noticing the modified behaviour. But I could be persuaded otherwise if non-reconnecting clients was the idiom, but I think most Redis clients I've used for other programming languages have automatically handled reconnection. So on balance, I think that should probably be the default and non-reconnecting made the option. I do like the new reconnection logic. It's much cleaner than what was there before, which grew organically for several versions and is currently quite gnarly. One thing I'm not sure about how the library should behave in future is the let set_future = con.send(resp_array!["SET", "x", "12"]);
let get_result = con.send(resp_array!["GET", "x"]).await; Secondly, it enabled the implicit pipelining to occur (as described in the project's README). So I'm not sure where we should go with this. On the one hand, by making
My first thought for this one is it should be fail-safe in the sense that we don't want it to be possible for the state the be I'll check out the PR locally in more detail over the next few days as time allows. Thanks again 👍 |
Agree. Let's not confuse people.
Do you mean sending multiple messages without waiting for responses? Like this?
I believe this still works, but you have to fire multiple futures at once. It might be even more intuitive, if you compare it to sleep for example:
My thinking here was that we'll probably want to detect disconnects in paired connect anyway to cancel/fail request futures. And if we're doing that, it would be really easy to notify reconnect. In addition, complex connections could trigger reconnect if they find themselves in some unrecoverable state. |
This PR is far from being ready for merge. Its purpose it to show how we might make the library more flexible and add support for Sentinel. Check out issue 67 for the background.
This is a pretty big change to the internals of the library. Public API has also changed, although mostly in terms of types - I wanted to keep the feel of the original.
So, what has changed?
Builders
Previously, there was only one builder:
ConnectionBuilder
. It did not hold data necessary for connecting to Redis through sentinel (redis master name, sentinels address list, credentials). Now there are two builders:RedisConnectionBuilder
andSentinelConnectionBuilder
. They have different fields and methods, but implement the same trait:ConnectionBuilder
.ConnectionBuilder
trait requires one method,connect
, which establishes a primitive (RespConnection
) connection to Redis. WhileRedisConnectionBuilder
does nothing fancy (simply calls the oldconnect_with_auth
function),SentinelConnectionBuilder
asks all sentinels for Redis master address and connects to it after double checking Redis role.Similarly to how it worked before, any type implementing
ConnectionBuilder
can start paired or pubsub connection by callingpaired_connect
orpubsub_connect
on the builder. The only difference is that now users will have to add an appropriate trait to the scope, that is eitherclient::paired::PairedConnectionBuilder
orclient::pubsub::PubsubConnectionBuilder
. Internally, these methods create primitive connection and upgrade it to a complex connection (Paired/Pubsub).Reconnecting
Until now, one couldn't opt out of reconnecting in
PairedConnection
andPubsubConnection
. That is not the case anymore. Both connection types can be created fromRespConnection
and error channel. More on that in a second. I've addedReconnect
struct holding builder and connection state.Reconnect
is generic overConnectionBuilder
andComplexConnection
.ComplexConnection
is a trait with pretty loose requirements: implemeneters must be able to construct themselves fromRespConnection
and error channel. Error channel is a oneshot sender that informsReconnect
struct that the connection should be recreated (using the builder). Users can access the currently active complex connection by calling.current
onReconnecting
. Because we make few assumptions about the complex connection, it's easy to useReconnect
with your own code. For convenience,PairedConnection
addssend
andsend_and_forget
toReconnect
holdingPairedConnection
.PubsubConnection
could do the same.Side effects
I made functions/methods like
paired_connect
return non-reconnecting connections and addedpaired_reconnecting
. People migrating to a new version might not notice that. Maybe we should keep old functions as they were and addpaired_non_reconneting
instead?Considerations
Is error channel a good way of informing
Reconnect
about errors? Maybe we should wrapRespConnection
with some struct that spies on the tcp channel and and pass that to complex connection? There might be a use case for having that level control in complex connection.Maybe
Reconnect
should not be generic overConnectionBuilder
but instead storeBox<dyn ConnectionBuilder>
? This way we wouldn't have to mention the builder type when describing the type of a Connection, e.g.vs
I think we can afford that. We'd possibly have to relax trait bounds.
What do you think of this design?