11#include < clickhouse/client.h>
22
3+ #include " clickhouse/base/socket.h"
34#include " readonly_client_test.h"
45#include " connection_failed_client_test.h"
6+ #include " ut/utils_comparison.h"
57#include " utils.h"
8+ #include " ut/roundtrip_column.h"
9+ #include " ut/value_generators.h"
610
711#include < gtest/gtest.h>
812
13+ #include < memory>
914#include < optional>
15+ #include < ostream>
16+ #include < string_view>
1017#include < thread>
1118#include < chrono>
1219
1320using namespace clickhouse ;
1421
22+ namespace clickhouse {
23+ std::ostream & operator << (std::ostream & ostr, const Endpoint & endpoint) {
24+ return ostr << endpoint.host << " :" << endpoint.port ;
25+ }
26+ }
27+
28+ template <typename T>
29+ std::shared_ptr<T> createTableWithOneColumn (Client & client, const std::string & table_name, const std::string & column_name)
30+ {
31+ auto col = std::make_shared<T>();
32+ const auto type_name = col->GetType ().GetName ();
33+
34+ client.Execute (" DROP TEMPORARY TABLE IF EXISTS " + table_name + " ;" );
35+ client.Execute (" CREATE TEMPORARY TABLE IF NOT EXISTS " + table_name + " ( " + column_name + " " + type_name + " )" );
36+
37+ return col;
38+ }
39+
1540// Use value-parameterized tests to run same tests with different client
1641// options.
1742class ClientCase : public testing ::TestWithParam<ClientOptions> {
@@ -27,13 +52,9 @@ class ClientCase : public testing::TestWithParam<ClientOptions> {
2752 template <typename T>
2853 std::shared_ptr<T> createTableWithOneColumn (Block & block)
2954 {
30- auto col = std::make_shared<T>();
31- const auto type_name = col->GetType ().GetName ();
32-
33- client_->Execute (" DROP TEMPORARY TABLE IF EXISTS " + table_name + " ;" );
34- client_->Execute (" CREATE TEMPORARY TABLE IF NOT EXISTS " + table_name + " ( " + column_name + " " + type_name + " )" );
55+ auto col = ::createTableWithOneColumn<T>(*client_, table_name, column_name);
3556
36- block.AppendColumn (" test_column " , col);
57+ block.AppendColumn (column_name , col);
3758
3859 return col;
3960 }
@@ -1352,3 +1373,91 @@ INSTANTIATE_TEST_SUITE_P(ResetConnectionClientTest, ResetConnectionTestCase,
13521373 .SetRetryTimeout (std::chrono::seconds (1 ))
13531374 }
13541375));
1376+
1377+ struct CountingSocketFactoryAdapter : public SocketFactory {
1378+
1379+ using ConnectRequests = std::vector<std::pair<ClientOptions, Endpoint>>;
1380+
1381+ SocketFactory & socket_factory;
1382+ ConnectRequests & connect_requests;
1383+
1384+ CountingSocketFactoryAdapter (SocketFactory & socket_factory, ConnectRequests& connect_requests)
1385+ : socket_factory(socket_factory)
1386+ , connect_requests(connect_requests)
1387+ {}
1388+
1389+ std::unique_ptr<SocketBase> connect (const ClientOptions& opts, const Endpoint& endpoint) {
1390+ connect_requests.emplace_back (opts, endpoint);
1391+
1392+ return socket_factory.connect (opts, endpoint);
1393+ }
1394+
1395+ void sleepFor (const std::chrono::milliseconds& duration) {
1396+ return socket_factory.sleepFor (duration);
1397+ }
1398+
1399+ size_t GetConnectRequestsCount () const {
1400+ return connect_requests.size ();
1401+ }
1402+
1403+ };
1404+
1405+ TEST (SimpleClientTest, issue_335) {
1406+ // Make sure Client connects to server even with ClientOptions.SetSendRetries(0)
1407+ auto vals = MakeStrings ();
1408+ auto col = std::make_shared<ColumnString>(vals);
1409+
1410+ CountingSocketFactoryAdapter::ConnectRequests connect_requests;
1411+ std::unique_ptr<SocketFactory> wrapped_socket_factory = std::make_unique<NonSecureSocketFactory>();
1412+ std::unique_ptr<SocketFactory> socket_factory = std::make_unique<CountingSocketFactoryAdapter>(*wrapped_socket_factory, connect_requests);
1413+
1414+ Client client (ClientOptions (LocalHostEndpoint)
1415+ .SetSendRetries (0 ), // <<=== crucial for reproducing https://github.com/ClickHouse/clickhouse-cpp/issues/335
1416+ std::move (socket_factory));
1417+
1418+ EXPECT_EQ (1u , connect_requests.size ());
1419+ EXPECT_TRUE (CompareRecursive (vals, *RoundtripColumnValuesTyped (client, col)));
1420+
1421+ connect_requests.clear ();
1422+
1423+ client.ResetConnection ();
1424+ EXPECT_EQ (1u , connect_requests.size ());
1425+ EXPECT_TRUE (CompareRecursive (vals, *RoundtripColumnValuesTyped (client, col)));
1426+
1427+ connect_requests.clear ();
1428+
1429+ client.ResetConnectionEndpoint ();
1430+ EXPECT_EQ (1u , connect_requests.size ());
1431+ EXPECT_TRUE (CompareRecursive (vals, *RoundtripColumnValuesTyped (client, col)));
1432+ }
1433+
1434+ TEST (SimpleClientTest, issue_335_reconnects_count) {
1435+ // Make sure that client attempts to connect to each endpoint at least once.
1436+ CountingSocketFactoryAdapter::ConnectRequests connect_requests;
1437+ std::unique_ptr<SocketFactory> wrapped_socket_factory = std::make_unique<NonSecureSocketFactory>();
1438+ std::unique_ptr<SocketFactory> socket_factory = std::make_unique<CountingSocketFactoryAdapter>(*wrapped_socket_factory, connect_requests);
1439+
1440+ const std::vector<Endpoint> endpoints = {
1441+ Endpoint{" foo-invalid-hostname" , 1234 },
1442+ Endpoint{" bar-invalid-hostname" , 4567 },
1443+ };
1444+
1445+ EXPECT_ANY_THROW (
1446+ Client (ClientOptions ()
1447+ .SetEndpoints (endpoints)
1448+ .SetSendRetries (0 ), // <<=== crucial for reproducing https://github.com/ClickHouse/clickhouse-cpp/issues/335
1449+ std::move (socket_factory));
1450+ );
1451+
1452+ EXPECT_EQ (endpoints.size (), connect_requests.size ());
1453+ // make sure there was an attempt to connect to each endpoint at least once.
1454+ for (const auto & endpoint : endpoints)
1455+ {
1456+ auto p = std::find_if (connect_requests.begin (), connect_requests.end (), [&endpoint](const auto & connect_request) {
1457+ return connect_request.second == endpoint;
1458+ });
1459+
1460+ EXPECT_TRUE (connect_requests.end () != p)
1461+ << " \t There was no attempt to connect to endpoint " << endpoint;
1462+ }
1463+ }
0 commit comments