Skip to content

Commit 7a539df

Browse files
authored
Merge pull request #470 from datastax/CPP-913
CPP-913 Fix: Ensure no duplicates in token map replica sets
2 parents bd32509 + 239075c commit 7a539df

File tree

5 files changed

+184
-19
lines changed

5 files changed

+184
-19
lines changed

src/request_handler.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,24 @@ RequestHandler::RequestHandler(const Request::ConstPtr& request, const ResponseF
167167
, manager_(NULL)
168168
, metrics_(metrics) {}
169169

170+
RequestHandler::~RequestHandler() {
171+
if (Logger::log_level() >= CASS_LOG_TRACE) {
172+
OStringStream ss;
173+
for (RequestTryVec::const_iterator it = request_tries_.begin(), end = request_tries_.end();
174+
it != end; ++it) {
175+
if (it != request_tries_.begin()) ss << ", ";
176+
ss << "(" << it->address << ", ";
177+
if (it->error != CASS_OK) {
178+
ss << cass_error_desc(it->error);
179+
} else {
180+
ss << it->latency;
181+
}
182+
ss << ")";
183+
}
184+
LOG_TRACE("Speculative execution attempts: [%s]", ss.str().c_str());
185+
}
186+
}
187+
170188
void RequestHandler::set_prepared_metadata(const PreparedMetadata::Entry::Ptr& entry) {
171189
wrapper_.set_prepared_metadata(entry);
172190
}
@@ -269,6 +287,10 @@ void RequestHandler::set_response(const Host::Ptr& host, const Response::Ptr& re
269287
metrics_->record_speculative_request(uv_hrtime() - start_time_ns_);
270288
}
271289
}
290+
291+
if (Logger::log_level() >= CASS_LOG_TRACE) {
292+
request_tries_.push_back(RequestTry(host->address(), uv_hrtime() - start_time_ns_));
293+
}
272294
}
273295

274296
void RequestHandler::set_error(CassError code, const String& message) {
@@ -289,6 +311,9 @@ void RequestHandler::set_error(const Host::Ptr& host, CassError code, const Stri
289311
set_error(code, message);
290312
}
291313
}
314+
if (Logger::log_level() >= CASS_LOG_TRACE) {
315+
request_tries_.push_back(RequestTry(host->address(), code));
316+
}
292317
}
293318

294319
void RequestHandler::set_error_with_error_response(const Host::Ptr& host,
@@ -297,6 +322,9 @@ void RequestHandler::set_error_with_error_response(const Host::Ptr& host,
297322
stop_request();
298323
running_executions_--;
299324
future_->set_error_with_response(host->address(), error, code, message);
325+
if (Logger::log_level() >= CASS_LOG_TRACE) {
326+
request_tries_.push_back(RequestTry(host->address(), code));
327+
}
300328
}
301329

302330
void RequestHandler::stop_timer() { timer_.stop(); }

src/request_handler.hpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,28 @@ class ExecutionProfile;
4747
class Timer;
4848
class TokenMap;
4949

50+
struct RequestTry {
51+
RequestTry()
52+
: error(CASS_OK)
53+
, latency(0) {}
54+
55+
RequestTry(const Address& address, uint64_t latency)
56+
: address(address)
57+
, error(CASS_OK)
58+
, latency(latency / (1000 * 1000)) {} // To milliseconds
59+
60+
RequestTry(const Address& address, CassError error)
61+
: address(address)
62+
, error(error)
63+
, latency(0) {}
64+
65+
Address address;
66+
CassError error;
67+
uint64_t latency;
68+
};
69+
70+
typedef SmallVector<RequestTry, 2> RequestTryVec;
71+
5072
class ResponseFuture : public Future {
5173
public:
5274
typedef SharedRefPtr<ResponseFuture> Ptr;
@@ -138,6 +160,7 @@ class RequestHandler : public RefCounted<RequestHandler> {
138160

139161
RequestHandler(const Request::ConstPtr& request, const ResponseFuture::Ptr& future,
140162
Metrics* metrics = NULL);
163+
~RequestHandler();
141164

142165
void set_prepared_metadata(const PreparedMetadata::Entry::Ptr& entry);
143166

@@ -210,6 +233,8 @@ class RequestHandler : public RefCounted<RequestHandler> {
210233
ConnectionPoolManager* manager_;
211234

212235
Metrics* const metrics_;
236+
237+
RequestTryVec request_tries_;
213238
};
214239

215240
class KeyspaceChangedResponse {

src/token_map.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class TokenMap : public RefCounted<TokenMap> {
5252

5353
virtual const CopyOnWriteHostVec& get_replicas(const String& keyspace_name,
5454
const String& routing_key) const = 0;
55+
56+
virtual String dump(const String& keyspace_name) const = 0;
5557
};
5658

5759
}}} // namespace datastax::internal::core

src/token_map_impl.hpp

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434

3535
#include <algorithm>
3636
#include <assert.h>
37+
#include <iomanip>
38+
#include <ios>
3739
#include <uv.h>
3840

3941
#define CASS_NETWORK_TOPOLOGY_STRATEGY "NetworkTopologyStrategy"
@@ -143,12 +145,32 @@ class ByteOrderedPartitioner {
143145
static StringRef name() { return "ByteOrderedPartitioner"; }
144146
};
145147

148+
inline std::ostream& operator<<(std::ostream& os, const RandomPartitioner::Token& token) {
149+
os << std::setfill('0') << std::setw(16) << std::hex << token.hi << std::setfill('0')
150+
<< std::setw(16) << std::hex << token.lo;
151+
return os;
152+
}
153+
154+
inline std::ostream& operator<<(std::ostream& os, const ByteOrderedPartitioner::Token& token) {
155+
for (ByteOrderedPartitioner::Token::const_iterator it = token.begin(), end = token.end();
156+
it != end; ++it) {
157+
os << std::hex << *it;
158+
}
159+
return os;
160+
}
161+
146162
class HostSet : public DenseHashSet<Host::Ptr> {
147163
public:
148164
HostSet() {
149165
set_empty_key(Host::Ptr(new Host(Address::EMPTY_KEY)));
150166
set_deleted_key(Host::Ptr(new Host(Address::DELETED_KEY)));
151167
}
168+
169+
template <class InputIterator>
170+
HostSet(InputIterator first, InputIterator last)
171+
: DenseHashSet<Host::Ptr>(first, last, Host::Ptr(new Host(Address::EMPTY_KEY))) {
172+
set_deleted_key(Host::Ptr(new Host(Address::DELETED_KEY)));
173+
}
152174
};
153175

154176
class RackSet : public DenseHashSet<uint32_t> {
@@ -355,6 +377,17 @@ void ReplicationStrategy<Partitioner>::build_replicas(const TokenHostVec& tokens
355377
}
356378
}
357379

380+
// Adds unique replica. It returns true if the replica was added.
381+
inline bool add_replica(CopyOnWriteHostVec& hosts, const Host::Ptr& host) {
382+
for (HostVec::const_reverse_iterator it = hosts->rbegin(); it != hosts->rend(); ++it) {
383+
if ((*it)->address() == host->address()) {
384+
return false; // Already in the replica set
385+
}
386+
}
387+
hosts->push_back(host);
388+
return true;
389+
}
390+
358391
template <class Partitioner>
359392
void ReplicationStrategy<Partitioner>::build_replicas_network_topology(
360393
const TokenHostVec& tokens, const DatacenterMap& datacenters, TokenReplicasVec& result) const {
@@ -443,24 +476,27 @@ void ReplicationStrategy<Partitioner>::build_replicas_network_topology(
443476
// datacenter only then consider hosts in the same rack
444477

445478
if (rack == 0 || racks_observed_this_dc.size() == rack_count_this_dc) {
446-
++replica_count_this_dc;
447-
replicas->push_back(Host::Ptr(host));
479+
if (add_replica(replicas, Host::Ptr(host))) {
480+
++replica_count_this_dc;
481+
}
448482
} else {
449483
TokenHostQueue& skipped_endpoints_this_dc = dc_rack_info.skipped_endpoints;
450484
if (racks_observed_this_dc.count(rack) > 0) {
451485
skipped_endpoints_this_dc.push_back(curr_token_it);
452486
} else {
453-
++replica_count_this_dc;
454-
replicas->push_back(Host::Ptr(host));
455-
racks_observed_this_dc.insert(rack);
487+
if (add_replica(replicas, Host::Ptr(host))) {
488+
++replica_count_this_dc;
489+
racks_observed_this_dc.insert(rack);
490+
}
456491

457492
// Once we visited every rack in the current datacenter then starting considering
458493
// hosts we've already skipped.
459494
if (racks_observed_this_dc.size() == rack_count_this_dc) {
460495
while (!skipped_endpoints_this_dc.empty() &&
461496
replica_count_this_dc < replication_factor) {
462-
++replica_count_this_dc;
463-
replicas->push_back(Host::Ptr(skipped_endpoints_this_dc.front()->second));
497+
if (add_replica(replicas, Host::Ptr(skipped_endpoints_this_dc.front()->second))) {
498+
++replica_count_this_dc;
499+
}
464500
skipped_endpoints_this_dc.pop_front();
465501
}
466502
}
@@ -484,9 +520,10 @@ void ReplicationStrategy<Partitioner>::build_replicas_simple(const TokenHostVec&
484520
for (typename TokenHostVec::const_iterator i = tokens.begin(), end = tokens.end(); i != end;
485521
++i) {
486522
CopyOnWriteHostVec replicas(new HostVec());
523+
replicas->reserve(num_replicas);
487524
typename TokenHostVec::const_iterator token_it = i;
488525
do {
489-
replicas->push_back(Host::Ptr(token_it->second));
526+
add_replica(replicas, Host::Ptr(Host::Ptr(token_it->second)));
490527
++token_it;
491528
if (token_it == tokens.end()) {
492529
token_it = tokens.begin();
@@ -578,7 +615,11 @@ class TokenMapImpl : public TokenMap {
578615
virtual const CopyOnWriteHostVec& get_replicas(const String& keyspace_name,
579616
const String& routing_key) const;
580617

581-
// Test only
618+
virtual String dump(const String& keyspace_name) const;
619+
620+
public:
621+
// Testing only
622+
582623
bool contains(const Token& token) const {
583624
for (typename TokenHostVec::const_iterator i = tokens_.begin(), end = tokens_.end(); i != end;
584625
++i) {
@@ -587,6 +628,8 @@ class TokenMapImpl : public TokenMap {
587628
return false;
588629
}
589630

631+
const TokenReplicasVec& token_replicas(const String& keyspace_name) const;
632+
590633
private:
591634
void update_keyspace(const VersionNumber& cassandra_version, const ResultResponse* result,
592635
bool should_build_replicas);
@@ -713,6 +756,35 @@ const CopyOnWriteHostVec& TokenMapImpl<Partitioner>::get_replicas(const String&
713756
return no_replicas_dummy_;
714757
}
715758

759+
template <class Partitioner>
760+
String TokenMapImpl<Partitioner>::dump(const String& keyspace_name) const {
761+
String result;
762+
typename KeyspaceReplicaMap::const_iterator ks_it = replicas_.find(keyspace_name);
763+
const TokenReplicasVec& replicas = ks_it->second;
764+
765+
for (typename TokenReplicasVec::const_iterator it = replicas.begin(), end = replicas.end();
766+
it != end; ++it) {
767+
OStringStream ss;
768+
ss << std::setw(20) << it->first << " [ ";
769+
const CopyOnWriteHostVec& hosts = it->second;
770+
for (HostVec::const_iterator host_it = hosts->begin(), end = hosts->end(); host_it != end;
771+
++host_it) {
772+
ss << (*host_it)->address_string() << " ";
773+
}
774+
ss << "]\n";
775+
result.append(ss.str());
776+
}
777+
return result;
778+
}
779+
780+
template <class Partitioner>
781+
const typename TokenMapImpl<Partitioner>::TokenReplicasVec&
782+
TokenMapImpl<Partitioner>::token_replicas(const String& keyspace_name) const {
783+
typename KeyspaceReplicaMap::const_iterator ks_it = replicas_.find(keyspace_name);
784+
static TokenReplicasVec not_found;
785+
return ks_it != replicas_.end() ? ks_it->second : not_found;
786+
}
787+
716788
template <class Partitioner>
717789
void TokenMapImpl<Partitioner>::update_keyspace(const VersionNumber& cassandra_version,
718790
const ResultResponse* result,
@@ -773,6 +845,8 @@ void TokenMapImpl<Partitioner>::build_replicas() {
773845
const String& keyspace_name = i->first;
774846
const ReplicationStrategy<Partitioner>& strategy = i->second;
775847
strategy.build_replicas(tokens_, datacenters_, replicas_[keyspace_name]);
848+
LOG_TRACE("Replicas for keyspace '%s':\n%s", keyspace_name.c_str(),
849+
dump(keyspace_name).c_str());
776850
}
777851
}
778852

0 commit comments

Comments
 (0)