-
Notifications
You must be signed in to change notification settings - Fork 79
Implement Multinode connection pool #139
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
Conversation
* This commit adds a connection pool that takes a static list of nodes and distributes the load. * trait for setting connection distribution. Defaults to RoundRobin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation looks good. Would sniffing connection pool be implemented on top of this? I think sniffing is a trickier implementation, but it is probably more valuable - the current connection pool implementation may need to change to accommodate interior mutability of a collection of Connection
types
I believe your assessment is correct as far as changing the ConnectionPool interface to account for interior mutability. It seems the only way to do that across threads is a mutex type. I think there’s overhead in requiring every ConnectionPool type to require a Mutex even if it’s not dynamic but it’s only way I see how to do this. Any thoughts? |
Thinking about sniffing further and discussing with folks on the clients team, the connection pool is responsible for managing one or more connections (connections being details about nodes in the cluster such as Uri) and data related to connections such as whether the pool supports sniffing, but the With this in mind, a sniffing connection pool would likely have a pub trait ConnectionPool: Debug + dyn_clone::DynClone + Sync + Send {
/// Gets a reference to the next [Connection]
fn next(&self) -> &Connection;
fn reseedable(&self) -> bool {
false
}
fn reseed(&self, connections: Vec<Connection>) {}
} Would need to play around to get the right type and function design for this. |
* The connection should be owned by the current user of said connection
6609113
to
1e8c4df
Compare
elasticsearch/src/http/transport.rs
Outdated
// NodesInfo::new(&self, NodesInfoParts::None) | ||
// .send() | ||
// .await | ||
// .expect("Could not retrieve nodes for refresh"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is technically a recursive call, which requires returning a futures::future::BoxFuture
, still playing around with making that work without breaking the current send
implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can cheat here and use the reqwest client directly - the .NET/C# client does something similar:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand reseeding the pool would be synchronous with the request? We may have a race condition if two threads have reseedable() == true
and start reseeding.
It would be better for reseeding to be a background periodic operation and send()
can then use the current list of nodes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My current understanding (concurrency is hard so it may be wrong :D), is that if two threads get the reseedable() == true
it'll kick off two serial reseeds as the reseed routine acquires a write lock (I don't predict this to be a likely event but definitely needs to be accounted for. I thought this trade off would be ok accounting for complexity of adding a subroutine for triggering reseeds). The problem I see is reseeding would block the current ConnectionPool
requests until the Write lock is dropped. If you think kicking off multiple serial requests may be problematic I am more than willing to change the implementation to a background operation. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I envisage the implementation doing something like the following:
- An API request is initiated
- The connection pool indicates if it is reseedable and needs reseeding
- If reseeding is needed, a thread-safe signal is set to indicate reseeding is in progress
- A reseed request is started, either in synchronous flow of the request, or on a separate thread. A separate thread is probably preferable as the request that initiates reseeding is not then impacted by the reseeding process. It does make the implementation more complex though.
- Upon receiving the reseed response, a write lock is acquired to update the connection pool. Failure in the reseeding process, should be logged (I think we can add a TODO for this for now and skip, as the client does not currently have logging in place, but should have in future).
- A thread-safe signal is set to indicate reseeding is not in progress
WIP as I still need to implement the |
Really appreciate your effort, @srleyva! I've left some comments |
elasticsearch/src/http/transport.rs
Outdated
// NodesInfo::new(&self, NodesInfoParts::None) | ||
// .send() | ||
// .await | ||
// .expect("Could not retrieve nodes for refresh"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand reseeding the pool would be synchronous with the request? We may have a race condition if two threads have reseedable() == true
and start reseeding.
It would be better for reseeding to be a background periodic operation and send()
can then use the current list of nodes.
1e8c4df
to
4de791d
Compare
Since this is a community submitted pull request, a Jenkins build has not been kicked off automatically. Can an Elastic organization member please verify the contents of this patch and then kick off a build manually? |
0ccc20d
to
fedd490
Compare
First pass at node sniff request following elastic docs. I thought even if aren't sure of the final design, having the request parts up would be easier. Manual tests for round robin load balancing on a local docker-compose ES cluster look quite promising when seeded with
|
7556a70
to
dbfb7c5
Compare
I'll play around with using a background thread to reseed over the next few days. Thanks for your patience and guidance on this as I am still very new to rust. 😄 |
* Style changes per code review
ff48166
to
50f8084
Compare
Hey @russcam is there any update on this PR? Apologies for the lack of communication. Its feature complete according to the original requirements. If there's anything else that's needed please let me know 😄 |
What is holding this PR back? We would like to use the official client, but without at least a way to target multiple elastic nodes, this is not feasible for us. Is there any way I could help out to get this over the finish line? |
I'm also interested in getting this merged. The branch looks stale and based on old dependencies - I'll rebase it on master and update to newer dependencies, and open as a new PR. |
Great! @tommilligan ping me, if you need another hand or pair of eyes. :) |
Superseded by PR #189 |
This commit adds a connection pool that takes a static
list of nodes and distributes the load.
trait for setting connection distribution.
Defaults to RoundRobin.
#57