-
Notifications
You must be signed in to change notification settings - Fork 173
Multiple endpoints for connection. #310
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
clickhouse/client.cpp
Outdated
@@ -194,30 +207,55 @@ class Client::Impl { | |||
std::unique_ptr<InputStream> input_; | |||
std::unique_ptr<OutputStream> output_; | |||
std::unique_ptr<SocketBase> socket_; | |||
std::shared_ptr<EndpointsIteratorBase> endpoints_iterator; |
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.
Do we really need to store iterator here ? Or we can create it as needed. It looks pretty lightweight
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.
For the Round Robin algo, we have to keep the same instance of the object, because otherwise current_index
will be reset to zero every time the object is created.
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 agree with Vasily and believe endpoints iteration-related code could be simplified. Having a base class overridden by a specific algorithm is a good general solution, but in case only one algorithm is implemented, I think it's better to have a dead simple RoundRobinEndpointsIterator endpoints_iterator
without inheritance and smart pointers.
If you still want a general solution, consider using std::unique_ptr
as we seem to have only one user of this field.
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.
IMO general solution with base class looks pretty good here, fixed shared_ptr
to unique_ptr
by now. @Enmk what do you think?
clickhouse/client.cpp
Outdated
@@ -194,30 +207,55 @@ class Client::Impl { | |||
std::unique_ptr<InputStream> input_; | |||
std::unique_ptr<OutputStream> output_; | |||
std::unique_ptr<SocketBase> socket_; | |||
std::shared_ptr<EndpointsIteratorBase> endpoints_iterator; |
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 agree with Vasily and believe endpoints iteration-related code could be simplified. Having a base class overridden by a specific algorithm is a good general solution, but in case only one algorithm is implemented, I think it's better to have a dead simple RoundRobinEndpointsIterator endpoints_iterator
without inheritance and smart pointers.
If you still want a general solution, consider using std::unique_ptr
as we seem to have only one user of this field.
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.
Requires changes, please take a look at previous attempt, since this new one already repeats some of the errors and comments from the old one:
#139
In the last commit, a behavior was added that allows users to reset the connection to a connected endpoint, which is described here. I also checked manually that the client is trying to reconnect to the same endpoint using |
clickhouse/client.h
Outdated
@@ -44,6 +44,19 @@ enum class CompressionMethod { | |||
LZ4 = 1, | |||
}; | |||
|
|||
struct Endpoint { | |||
std::string host; | |||
unsigned int port = 9000; |
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.
unsigned int port = 9000; | |
uint16_t port = 9000; |
Just to make sure that nobody tires to connect to port >65535
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.
So maybe we should change here too?
https://github.com/ClickHouse/clickhouse-cpp/blob/dbbdefcbec5aff8f087bc0979f12ba26d0a06d5f/clickhouse/client.h#LL59C54-L59C54
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.
Yep, I think this is a good idea, even though this alters public interface
clickhouse/base/socket.h
Outdated
@@ -135,7 +136,7 @@ class NonSecureSocketFactory : public SocketFactory { | |||
public: | |||
~NonSecureSocketFactory() override; | |||
|
|||
std::unique_ptr<SocketBase> connect(const ClientOptions& opts) override; | |||
std::unique_ptr<SocketBase> connect(const ClientOptions& opts, const std::string& host, const std::string& port) override; |
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.
std::unique_ptr<SocketBase> connect(const ClientOptions& opts, const std::string& host, const std::string& port) override; | |
std::unique_ptr<SocketBase> connect(const ClientOptions& opts, const Endpoint & endpoint) override; |
Also please modify the call site.
clickhouse/base/socket.h
Outdated
@@ -88,7 +89,7 @@ class SocketFactory { | |||
|
|||
// TODO: move connection-related options to ConnectionOptions structure. | |||
|
|||
virtual std::unique_ptr<SocketBase> connect(const ClientOptions& opts) = 0; | |||
virtual std::unique_ptr<SocketBase> connect(const ClientOptions& opts, const std::string& host, const std::string& port) = 0; |
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.
virtual std::unique_ptr<SocketBase> connect(const ClientOptions& opts, const std::string& host, const std::string& port) = 0; | |
std::unique_ptr<SocketBase> connect(const ClientOptions& opts, const Endpoint & endpoint) = 0; |
clickhouse/base/endpoints_iterator.h
Outdated
virtual void Next() = 0; | ||
// Get the address of current endpoint. | ||
virtual std::string GetHostAddr() const = 0; | ||
|
||
// Get the port of current endpoint. | ||
virtual uint16_t GetPort() const = 0; | ||
|
||
// Reset iterations. | ||
virtual void ResetIterations() = 0; | ||
virtual bool NextIsExist() const = 0; |
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.
virtual void Next() = 0; | |
// Get the address of current endpoint. | |
virtual std::string GetHostAddr() const = 0; | |
// Get the port of current endpoint. | |
virtual uint16_t GetPort() const = 0; | |
// Reset iterations. | |
virtual void ResetIterations() = 0; | |
virtual bool NextIsExist() const = 0; | |
virtual Endpoint Next() = 0; |
Cleaner and much simpler API. We don't need to reset iteration or to check if there are more endpoints left. Consider EndpointsIteratorBase
to be an endless generator of endpoints.
If some new iteration strategy pops up later, that would require changes, we can update the interface accordingly, since it is not part of public API.
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.
Yes, but ResetIterations
is needed in order not to create an infinite loop for endpoint choosing.
Otherwise we have to check it thought opts.endpoints.size()
,which doesn't look like a good solution. Any ideas how to do so?
clickhouse/client.cpp
Outdated
if (opts.host.empty()) | ||
return opts; | ||
|
||
Endpoint endpoint_single({opts.host, opts.port}); |
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.
Endpoint endpoint_single({opts.host, opts.port}); | |
Endpoint default_endpoint({opts.host, opts.port}); |
clickhouse/client.cpp
Outdated
return opts; | ||
|
||
Endpoint endpoint_single({opts.host, opts.port}); | ||
if (std::find(opts.endpoints.begin(), opts.endpoints.end(), endpoint_single) == std::end(opts.endpoints)) { |
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.
IMO it is best to make default_endpoint
first endpoint unconditionally, so it is simple, easy and predictable
clickhouse/client.cpp
Outdated
@@ -64,8 +64,12 @@ struct ClientInfo { | |||
}; | |||
|
|||
std::ostream& operator<<(std::ostream& os, const ClientOptions& opt) { | |||
os << "Client(" << opt.user << '@' << opt.host << ":" << opt.port | |||
<< " ping_before_query:" << opt.ping_before_query | |||
os << "Client("; |
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.
That ignores opt.host
and opt.port
completely, and this operator<<
might be used on unmodified ClientOptions
(by tests or users), making output confusing.
clickhouse/client.cpp
Outdated
@@ -111,6 +115,10 @@ std::unique_ptr<SocketFactory> GetSocketFactory(const ClientOptions& opts) { | |||
return std::make_unique<NonSecureSocketFactory>(); | |||
} | |||
|
|||
std::unique_ptr<EndpointsIteratorBase> GetEndpointsIterator(const std::vector<Endpoint>& endpoints) { |
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.
std::unique_ptr<EndpointsIteratorBase> GetEndpointsIterator(const std::vector<Endpoint>& endpoints) { | |
std::unique_ptr<EndpointsIteratorBase> GetEndpointsIterator(const ClientOptions& options) { |
Discussed in direct messages:
All this is done in the last commit |
Specify multiple hosts for connection, to provide fault tolerance.
The endpoint is selecting according to
EndpointsIterationAlgorithm
, rn only RoundRobin approach is implemented.Ref #139