Skip to content

Commit c9474f0

Browse files
authored
Merge pull request #2 from jul-stas/shard-awareness
Shard awareness
2 parents 593eaae + ddec859 commit c9474f0

24 files changed

+1472
-45
lines changed

CMakeLists.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
cmake_minimum_required(VERSION 2.8.12)
1+
cmake_minimum_required(VERSION 3.1)
22
project(cassandra C CXX)
33

44
set(CASS_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR})
@@ -43,10 +43,12 @@ option(CASS_USE_KERBEROS "Use Kerberos" OFF)
4343
option(CASS_USE_LIBSSH2 "Use libssh2 for integration tests" OFF)
4444
option(CASS_USE_OPENSSL "Use OpenSSL" ON)
4545
option(CASS_USE_STATIC_LIBS "Link static libraries when building executables" OFF)
46-
option(CASS_USE_STD_ATOMIC "Use C++11 atomics library" OFF)
46+
option(CASS_USE_STD_ATOMIC "Use std::atomic library" ON)
4747
option(CASS_USE_ZLIB "Use zlib" ON)
4848
option(CASS_USE_TIMERFD "Use timerfd (Linux only)" ON)
4949

50+
set(CASS_CPP_STANDARD "11" CACHE STRING "C++ standard (11, 14, 17, etc.)")
51+
5052
# Handle testing dependencies
5153
if(CASS_BUILD_TESTS)
5254
# Enable integration and unit tests
@@ -161,12 +163,10 @@ endif()
161163
# Top-level compiler flags
162164
#------------------------
163165

166+
set (CMAKE_CXX_STANDARD ${CASS_CPP_STANDARD})
167+
164168
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR
165169
"${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
166-
# Enable C++11 support to use std::atomic
167-
if(CASS_USE_STD_ATOMIC)
168-
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
169-
endif()
170170

171171
# OpenSSL is deprecated on later versions of Mac OS X. The long-term solution
172172
# is to provide a CommonCryto implementation.

driver_config.hpp.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#cmakedefine HAVE_KERBEROS
55
#cmakedefine HAVE_OPENSSL
66
#cmakedefine HAVE_STD_ATOMIC
7+
#cmakedefine CASS_CPP_STANDARD @CASS_CPP_STANDARD@
78
#cmakedefine HAVE_BOOST_ATOMIC
89
#cmakedefine HAVE_NOSIGPIPE
910
#cmakedefine HAVE_SIGTIMEDWAIT
File renamed without changes.

licenses/boost-1.0.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Boost Software License - Version 1.0 - August 17th, 2003
2+
3+
Permission is hereby granted, free of charge, to any person or organization
4+
obtaining a copy of the software and accompanying documentation covered by
5+
this license (the "Software") to use, reproduce, display, distribute,
6+
execute, and transmit the Software, and to prepare derivative works of the
7+
Software, and to permit third-parties to whom the Software is furnished to
8+
do so, all subject to the following:
9+
10+
The copyright notices in the Software and this entire statement, including
11+
the above license grant, this restriction and the following disclaimer,
12+
must be included in all copies of the Software, in whole or in part, and
13+
all derivative works of the Software, unless such copies or derivative
14+
works are solely in the form of machine-executable object code generated by
15+
a source language processor.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
20+
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
21+
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
22+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23+
DEALINGS IN THE SOFTWARE.

src/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ else()
3636
endif()
3737
endif()
3838

39+
# Determine `optional` library to include
40+
if(CMAKE_CXX_STANDARD LESS 17)
41+
message(STATUS "Using akrzemi's `optional` implementation")
42+
list(APPEND SOURCES optional/optional_akrzemi.hpp)
43+
else()
44+
message(STATUS "Using std::optional library")
45+
list(APPEND SOURCES optional/optional_std.hpp)
46+
endif()
47+
3948
add_subdirectory(third_party/curl)
4049
add_subdirectory(third_party/hdr_histogram)
4150
add_subdirectory(third_party/http-parser)

src/cluster.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include "speculative_execution.hpp"
2626
#include "utils.hpp"
2727

28+
#include <iostream>
29+
2830
using namespace datastax;
2931
using namespace datastax::internal::core;
3032

@@ -235,6 +237,10 @@ Cluster::Cluster(const ControlConnection::Ptr& connection, ClusterListener* list
235237
, local_dc_(local_dc)
236238
, supported_options_(supported_options)
237239
, is_recording_events_(settings.disable_events_on_startup) {
240+
static const auto optimized_msg = "===== Using optimized driver!!! =====\n";
241+
std::cout << optimized_msg;
242+
LOG_INFO(optimized_msg);
243+
238244
inc_ref();
239245
connection_->set_listener(this);
240246

src/connection.hpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414
limitations under the License.
1515
*/
1616

17+
#ifndef DATASTAX_INTERNAL_CONNECTION_HPP
18+
#define DATASTAX_INTERNAL_CONNECTION_HPP
19+
1720
#include "event_response.hpp"
1821
#include "request_callback.hpp"
1922
#include "socket.hpp"
2023
#include "stream_manager.hpp"
21-
22-
#ifndef DATASTAX_INTERNAL_CONNECTION_HPP
23-
#define DATASTAX_INTERNAL_CONNECTION_HPP
24+
#include "optional.hpp"
2425

2526
namespace datastax { namespace internal { namespace core {
2627

@@ -195,6 +196,11 @@ class Connection : public RefCounted<Connection> {
195196
*/
196197
void set_listener(ConnectionListener* listener = NULL);
197198

199+
int32_t shard_id() const { return shard_id_; }
200+
void set_shard_id(int32_t shard_id) {
201+
shard_id_ = shard_id;
202+
}
203+
198204
/**
199205
* Start heartbeats to keep the connection alive and to detect a network or
200206
* server-side failure.
@@ -241,6 +247,8 @@ class Connection : public RefCounted<Connection> {
241247
ProtocolVersion protocol_version_;
242248
String keyspace_;
243249

250+
int32_t shard_id_ = 0;
251+
244252
unsigned int idle_timeout_secs_;
245253
unsigned int heartbeat_interval_secs_;
246254
bool heartbeat_outstanding_;

src/connection_pool.cpp

Lines changed: 72 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "utils.hpp"
2323

2424
#include <algorithm>
25+
#include <numeric>
2526

2627
using namespace datastax;
2728
using namespace datastax::internal::core;
@@ -77,34 +78,70 @@ ConnectionPool::ConnectionPool(const Connection::Vec& connections, ConnectionPoo
7778
set_pointer_keys(reconnection_schedules_);
7879
set_pointer_keys(to_flush_);
7980

81+
if (host->sharding_info()) {
82+
const auto hosts_shard_cnt = host->sharding_info()->get_shards_count();
83+
connections_by_shard_.resize(hosts_shard_cnt);
84+
num_connections_per_shard_ = settings_.num_connections_per_host / hosts_shard_cnt
85+
+ (settings_.num_connections_per_host % hosts_shard_cnt ? 1u : 0u);
86+
} else {
87+
connections_by_shard_.resize(1);
88+
num_connections_per_shard_ = settings_.num_connections_per_host;
89+
}
90+
8091
for (Connection::Vec::const_iterator it = connections.begin(), end = connections.end(); it != end;
8192
++it) {
8293
const Connection::Ptr& connection(*it);
8394
if (!connection->is_closing()) {
84-
add_connection(PooledConnection::Ptr(new PooledConnection(this, connection)));
95+
if (connections_by_shard_[connection->shard_id()].size() < num_connections_per_shard_) {
96+
add_connection(PooledConnection::Ptr(new PooledConnection(this, connection)));
97+
} else {
98+
connection->close();
99+
}
85100
}
86101
}
87102

88103
notify_up_or_down();
89104

90105
// We had non-critical errors or some connections closed
91-
assert(connections.size() <= settings_.num_connections_per_host);
92-
size_t needed = settings_.num_connections_per_host - connections_.size();
106+
size_t needed = num_connections_per_shard_ * connections_by_shard_.size()
107+
- std::accumulate(connections_by_shard_.begin(), connections_by_shard_.end(), 0u,
108+
[] (size_t acc, const PooledConnection::Vec& v) {
109+
return acc + v.size();
110+
});
93111
for (size_t i = 0; i < needed; ++i) {
94112
schedule_reconnect();
95113
}
96114
}
97115

98-
PooledConnection::Ptr ConnectionPool::find_least_busy() const {
116+
PooledConnection::Ptr ConnectionPool::find_least_busy(int64_t token) const {
117+
if (token == CASS_INT64_MIN || !host_->sharding_info()) {
118+
// We got a placeholder token, or a sensible token that is useless without the sharding info.
119+
// In both cases we return the least busy connection of the *entire pool* (or NULL).
120+
PooledConnection::Ptr least_busy; // NULL by default
121+
for (const auto& shard_pool : connections_by_shard_) {
122+
for (const auto& conn : shard_pool) {
123+
if (!conn->is_closing()) {
124+
least_busy = least_busy ? std::min(least_busy, conn, least_busy_comp) : conn;
125+
}
126+
}
127+
}
128+
return least_busy;
129+
}
130+
131+
// Otherwise, find the least busy connection pointing to the right shard (if possible)
132+
const auto& shard_pool = connections_by_shard_[host_->sharding_info()->shard_id(token)];
99133
PooledConnection::Vec::const_iterator it =
100-
std::min_element(connections_.begin(), connections_.end(), least_busy_comp);
101-
if (it == connections_.end() || (*it)->is_closing()) {
102-
return PooledConnection::Ptr();
134+
std::min_element(shard_pool.begin(), shard_pool.end(), least_busy_comp);
135+
if (it == shard_pool.end() || (*it)->is_closing()) {
136+
return find_least_busy(CASS_INT64_MIN);
103137
}
104138
return *it;
105139
}
106140

107-
bool ConnectionPool::has_connections() const { return !connections_.empty(); }
141+
bool ConnectionPool::has_connections() const {
142+
return std::any_of(connections_by_shard_.begin(), connections_by_shard_.end(),
143+
[] (const PooledConnection::Vec& v) { return !v.empty(); });
144+
}
108145

109146
void ConnectionPool::flush() {
110147
for (DenseHashSet<PooledConnection*>::const_iterator it = to_flush_.begin(),
@@ -142,8 +179,9 @@ void ConnectionPool::close_connection(PooledConnection* connection, Protected) {
142179
if (metrics_) {
143180
metrics_->total_connections.dec();
144181
}
145-
connections_.erase(std::remove(connections_.begin(), connections_.end(), connection),
146-
connections_.end());
182+
auto& shard_pool = connections_by_shard_[connection->shard_id()];
183+
shard_pool.erase(std::remove(shard_pool.begin(), shard_pool.end(), connection),
184+
shard_pool.end());
147185
to_flush_.erase(connection);
148186

149187
if (close_state_ != CLOSE_STATE_OPEN) {
@@ -161,16 +199,16 @@ void ConnectionPool::add_connection(const PooledConnection::Ptr& connection) {
161199
if (metrics_) {
162200
metrics_->total_connections.inc();
163201
}
164-
connections_.push_back(connection);
202+
const size_t new_connections_shard = connection->shard_id();
203+
LOG_INFO("add_connection: to host %s to shard %ld", host_->address_string().c_str(), new_connections_shard);
204+
connections_by_shard_[new_connections_shard].push_back(connection);
165205
}
166206

167207
void ConnectionPool::notify_up_or_down() {
168-
if ((notify_state_ == NOTIFY_STATE_NEW || notify_state_ == NOTIFY_STATE_UP) &&
169-
connections_.empty()) {
208+
if ((notify_state_ == NOTIFY_STATE_NEW || notify_state_ == NOTIFY_STATE_UP) && !has_connections()) {
170209
notify_state_ = NOTIFY_STATE_DOWN;
171210
listener_->on_pool_down(host_->address());
172-
} else if ((notify_state_ == NOTIFY_STATE_NEW || notify_state_ == NOTIFY_STATE_DOWN) &&
173-
!connections_.empty()) {
211+
} else if ((notify_state_ == NOTIFY_STATE_NEW || notify_state_ == NOTIFY_STATE_DOWN) && has_connections()) {
174212
notify_state_ = NOTIFY_STATE_UP;
175213
listener_->on_pool_up(host_->address());
176214
}
@@ -211,11 +249,12 @@ void ConnectionPool::internal_close() {
211249
// Make copies of connection/connector data structures to prevent iterator
212250
// invalidation.
213251

214-
PooledConnection::Vec connections(connections_);
215-
for (PooledConnection::Vec::iterator it = connections.begin(), end = connections.end();
216-
it != end; ++it) {
217-
(*it)->close();
218-
}
252+
auto connections_per_shards = connections_by_shard_;
253+
std::for_each(connections_per_shards.begin(), connections_per_shards.end(), [] (PooledConnection::Vec& v) {
254+
for (auto& c : v) {
255+
c->close();
256+
}
257+
});
219258

220259
DelayedConnector::Vec pending_connections(pending_connections_);
221260
for (DelayedConnector::Vec::iterator it = pending_connections.begin(),
@@ -232,8 +271,7 @@ void ConnectionPool::internal_close() {
232271
void ConnectionPool::maybe_closed() {
233272
// Remove the pool once all current connections and pending connections
234273
// are terminated.
235-
if (close_state_ == CLOSE_STATE_WAITING_FOR_CONNECTIONS && connections_.empty() &&
236-
pending_connections_.empty()) {
274+
if (close_state_ == CLOSE_STATE_WAITING_FOR_CONNECTIONS && !has_connections() && pending_connections_.empty()) {
237275
close_state_ = CLOSE_STATE_CLOSED;
238276
// Only mark DOWN if it's UP otherwise we might get multiple DOWN events
239277
// when connecting the pool.
@@ -263,9 +301,18 @@ void ConnectionPool::on_reconnect(DelayedConnector* connector) {
263301
}
264302

265303
if (connector->is_ok()) {
266-
add_connection(
267-
PooledConnection::Ptr(new PooledConnection(this, connector->release_connection())));
268-
notify_up_or_down();
304+
PooledConnection::Ptr pooled_conn {new PooledConnection(this, connector->release_connection())};
305+
const size_t new_connections_shard = pooled_conn->shard_id();
306+
if (connections_by_shard_.size() > new_connections_shard
307+
&& connections_by_shard_[new_connections_shard].size() < num_connections_per_shard_) {
308+
add_connection(pooled_conn);
309+
notify_up_or_down();
310+
} else {
311+
LOG_INFO("Reconnection to host %s connected us to shard %ld, reconnecting again",
312+
address().to_string().c_str(), new_connections_shard);
313+
pooled_conn->close();
314+
schedule_reconnect(schedule.release());
315+
}
269316
} else if (!connector->is_canceled()) {
270317
if (connector->is_critical_error()) {
271318
LOG_ERROR("Closing established connection pool to host %s because of the following error: %s",

src/connection_pool.hpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ class ConnectionPool : public RefCounted<ConnectionPool> {
135135
*
136136
* @return The least busy connection or null if no connection is available.
137137
*/
138-
PooledConnection::Ptr find_least_busy() const;
138+
PooledConnection::Ptr find_least_busy(int64_t token) const;
139139

140140
/**
141141
* Determine if the pool has any valid connections.
@@ -213,6 +213,9 @@ class ConnectionPool : public RefCounted<ConnectionPool> {
213213
private:
214214
void notify_up_or_down();
215215
void notify_critical_error(Connector::ConnectionError code, const String& message);
216+
217+
/** Adds connection to the pool. It's the caller's responsibility
218+
* to keep track of the connections count. */
216219
void add_connection(const PooledConnection::Ptr& connection);
217220
void schedule_reconnect(ReconnectionSchedule* schedule = NULL);
218221
void internal_close();
@@ -232,7 +235,8 @@ class ConnectionPool : public RefCounted<ConnectionPool> {
232235

233236
CloseState close_state_;
234237
NotifyState notify_state_;
235-
PooledConnection::Vec connections_;
238+
std::vector<PooledConnection::Vec> connections_by_shard_; /// Index is the shard ID
239+
size_t num_connections_per_shard_;
236240
DelayedConnector::Vec pending_connections_;
237241
DenseHashSet<PooledConnection*> to_flush_;
238242
};

src/connection_pool_manager.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,12 @@ ConnectionPoolManager::ConnectionPoolManager(const ConnectionPool::Map& pools, u
6262
}
6363
}
6464

65-
PooledConnection::Ptr ConnectionPoolManager::find_least_busy(const Address& address) const {
65+
PooledConnection::Ptr ConnectionPoolManager::find_least_busy(const Address& address, int64_t token) const {
6666
ConnectionPool::Map::const_iterator it = pools_.find(address);
6767
if (it == pools_.end()) {
6868
return PooledConnection::Ptr();
6969
}
70-
return it->second->find_least_busy();
70+
return it->second->find_least_busy(token);
7171
}
7272

7373
bool ConnectionPoolManager::has_connections(const Address& address) const {

0 commit comments

Comments
 (0)