Skip to content

Commit b6a7b0a

Browse files
authored
Merge pull request #290 from riptano/CPP-813
CPP-813 - Detect CaaS and change consistency defaults
2 parents f933e2d + a4292a0 commit b6a7b0a

33 files changed

Lines changed: 746 additions & 187 deletions

cpp-driver/gtests/src/integration/integration.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ void Integration::connect(Cluster cluster) {
325325
std::stringstream use_keyspace_query;
326326
use_keyspace_query << "USE " << keyspace_name_;
327327
session_.execute(use_keyspace_query.str());
328+
CHECK_FAILURE;
328329
}
329330

330331
void Integration::connect() {

cpp-driver/gtests/src/integration/integration.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@
107107

108108
#define CHECK_CONTINUE(flag, message) ASSERT_TRUE(flag) << message;
109109

110-
#define CASSANDRA_KEY_VALUE_TABLE_FORMAT "CREATE TABLE %s (key %s PRIMARY KEY, value %s)"
110+
#define CASSANDRA_KEY_VALUE_TABLE_FORMAT \
111+
"CREATE TABLE IF NOT EXISTS %s (key %s PRIMARY KEY, value %s)"
111112
#define CASSANDRA_KEY_VALUE_INSERT_FORMAT "INSERT INTO %s (key, value) VALUES(%s, %s)"
112113
#define CASSANDRA_SELECT_VALUE_FORMAT "SELECT value FROM %s WHERE key=%s"
113114
#define CASSANDRA_DELETE_ROW_FORMAT "DELETE FROM %s WHERE key=%s"

cpp-driver/gtests/src/integration/tests/test_dbaas.cpp

Lines changed: 228 additions & 19 deletions
Large diffs are not rendered by default.

cpp-driver/gtests/src/unit/mockssandra.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#endif
3434

3535
using datastax::internal::bind_callback;
36+
using datastax::internal::Map;
3637
using datastax::internal::Memory;
3738
using datastax::internal::OStringStream;
3839
using datastax::internal::ScopedMutex;
@@ -1088,6 +1089,15 @@ inline int32_t encode_uuid(CassUuid uuid, String* output) {
10881089
return 16;
10891090
}
10901091

1092+
int32_t encode_string_map(const Map<String, Vector<String> >& value, String* output) {
1093+
int32_t size = encode_uint16(value.size(), output);
1094+
for (Map<String, Vector<String> >::const_iterator it = value.begin(); it != value.end(); ++it) {
1095+
size += encode_string(it->first, output);
1096+
size += encode_string_list(it->second, output);
1097+
}
1098+
return size;
1099+
}
1100+
10911101
static String encode_header(int8_t version, int8_t flags, int16_t stream, int8_t opcode,
10921102
int32_t len) {
10931103
String header;
@@ -1993,7 +2003,17 @@ int32_t ProtocolHandler::decode_frame(ClientConnection* client, const char* fram
19932003
} else {
19942004
return len - remaining;
19952005
}
1996-
state_ = BODY;
2006+
2007+
if (length_ == 0) {
2008+
decode_body(client, pos, 0);
2009+
version_ = 0;
2010+
flags_ = 0;
2011+
opcode_ = 0;
2012+
length_ = 0;
2013+
state_ = PROTOCOL_VERSION;
2014+
} else {
2015+
state_ = BODY;
2016+
}
19972017
break;
19982018
case BODY:
19992019
if (remaining >= length_) {

cpp-driver/gtests/src/unit/mockssandra.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "address.hpp"
2929
#include "event_loop.hpp"
3030
#include "list.hpp"
31+
#include "map.hpp"
3132
#include "ref_counted.hpp"
3233
#include "scoped_ptr.hpp"
3334
#include "string.hpp"
@@ -46,6 +47,7 @@
4647
using datastax::String;
4748
using datastax::internal::Atomic;
4849
using datastax::internal::List;
50+
using datastax::internal::Map;
4951
using datastax::internal::RefCounted;
5052
using datastax::internal::ScopedPtr;
5153
using datastax::internal::SharedRefPtr;
@@ -360,6 +362,8 @@ struct QueryParameters {
360362
String keyspace;
361363
};
362364

365+
int32_t encode_string_map(const Map<String, Vector<String> >& value, String* output);
366+
363367
class Type {
364368
public:
365369
static Type text();

cpp-driver/gtests/src/unit/tests/test_connection.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ TEST_F(ConnectionUnitTest, SslCancel) {
303303
}
304304

305305
TEST_F(ConnectionUnitTest, Timeout) {
306-
mockssandra::RequestHandler::Builder builder;
306+
mockssandra::SimpleRequestHandlerBuilder builder;
307307
builder.on(mockssandra::OPCODE_STARTUP).no_result(); // Don't return a response
308308
mockssandra::SimpleCluster cluster(builder.build());
309309
ASSERT_EQ(cluster.start_all(), 0);

cpp-driver/gtests/src/unit/tests/test_decoder.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ TEST_F(DecoderUnitTest, DecodeStringMultiMap) {
740740
0, 6, 80, 121, 116, 104, 111, 110, // Python
741741
0, 4, 82, 117, 98, 121 }; // Ruby
742742
TestDecoder decoder(input, 58);
743-
Map<String, Vector<String> > value;
743+
StringMultimap value;
744744

745745
// SUCCESS
746746
ASSERT_TRUE(decoder.decode_string_multimap(value));

cpp-driver/gtests/src/unit/tests/test_exec_profile.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ TEST(ExecutionProfileUnitTest, Consistency) {
4141
Config copy_config = config.new_instance();
4242
ExecutionProfile profile_lookup;
4343
ASSERT_TRUE(execution_profile(copy_config, "profile", profile_lookup));
44-
ASSERT_EQ(CASS_DEFAULT_CONSISTENCY, profile_lookup.consistency());
45-
ASSERT_EQ(CASS_DEFAULT_CONSISTENCY, copy_config.default_profile().consistency());
44+
ASSERT_EQ(CASS_CONSISTENCY_UNKNOWN, profile_lookup.consistency());
45+
ASSERT_EQ(CASS_CONSISTENCY_UNKNOWN, copy_config.default_profile().consistency());
4646
}
4747

4848
TEST(ExecutionProfileUnitTest, SerialConsistency) {

cpp-driver/gtests/src/unit/tests/test_session.cpp

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,21 @@ class SessionUnitTest : public EventLoopTest {
120120
query(session, true);
121121
}
122122

123+
bool check_consistency(const Session& session, CassConsistency expected_consistency,
124+
CassConsistency expected_profile_consistency) {
125+
Config session_config = session.config();
126+
EXPECT_EQ(expected_consistency, session_config.consistency());
127+
128+
const ExecutionProfile::Map& profiles = session_config.profiles();
129+
for (ExecutionProfile::Map::const_iterator it = profiles.begin(), end = profiles.end();
130+
it != end; ++it) {
131+
if (expected_profile_consistency != it->second.consistency()) {
132+
return false;
133+
}
134+
}
135+
return true;
136+
}
137+
123138
class HostEventFuture : public Future {
124139
public:
125140
typedef SharedRefPtr<HostEventFuture> Ptr;
@@ -241,6 +256,21 @@ class SessionUnitTest : public EventLoopTest {
241256
private:
242257
String local_dc_;
243258
};
259+
260+
class SupportedDbaasOptions : public mockssandra::Action {
261+
public:
262+
virtual void on_run(mockssandra::Request* request) const {
263+
Vector<String> product_type;
264+
product_type.push_back("DATASTAX_APOLLO");
265+
266+
StringMultimap supported;
267+
supported["PRODUCT_TYPE"] = product_type;
268+
269+
String body;
270+
mockssandra::encode_string_map(supported, &body);
271+
request->write(mockssandra::OPCODE_SUPPORTED, body);
272+
}
273+
};
244274
};
245275

246276
TEST_F(SessionUnitTest, ExecuteQueryNotConnected) {
@@ -859,3 +889,95 @@ TEST_F(SessionUnitTest, NoContactPoints) {
859889
ASSERT_TRUE(connect_future->error());
860890
EXPECT_EQ(connect_future->error()->code, CASS_ERROR_LIB_NO_HOSTS_AVAILABLE);
861891
}
892+
893+
TEST_F(SessionUnitTest, DefaultConsistency) {
894+
mockssandra::SimpleCluster cluster(simple());
895+
ASSERT_EQ(cluster.start_all(), 0);
896+
897+
Session session;
898+
{
899+
Config session_config = session.config();
900+
EXPECT_EQ(CASS_CONSISTENCY_UNKNOWN, session_config.consistency());
901+
}
902+
903+
ExecutionProfile profile;
904+
Config config;
905+
config.contact_points().push_back(Address("127.0.0.1", 9042));
906+
config.set_execution_profile("profile", &profile);
907+
connect(config, &session);
908+
909+
EXPECT_TRUE(check_consistency(session, CASS_DEFAULT_CONSISTENCY, CASS_DEFAULT_CONSISTENCY));
910+
911+
close(&session);
912+
}
913+
914+
TEST_F(SessionUnitTest, DefaultConsistencyExecutionProfileNotUpdated) {
915+
mockssandra::SimpleCluster cluster(simple());
916+
ASSERT_EQ(cluster.start_all(), 0);
917+
918+
Session session;
919+
{
920+
Config session_config = session.config();
921+
EXPECT_EQ(CASS_CONSISTENCY_UNKNOWN, session_config.consistency());
922+
}
923+
924+
ExecutionProfile profile;
925+
profile.set_consistency(CASS_CONSISTENCY_LOCAL_QUORUM);
926+
Config config;
927+
config.contact_points().push_back(Address("127.0.0.1", 9042));
928+
config.set_execution_profile("profile", &profile);
929+
connect(config, &session);
930+
931+
EXPECT_TRUE(check_consistency(session, CASS_DEFAULT_CONSISTENCY, CASS_CONSISTENCY_LOCAL_QUORUM));
932+
933+
close(&session);
934+
}
935+
936+
TEST_F(SessionUnitTest, DbaasDetectionUpdateDefaultConsistency) {
937+
mockssandra::SimpleRequestHandlerBuilder builder;
938+
builder.on(mockssandra::OPCODE_OPTIONS).execute(new SupportedDbaasOptions());
939+
mockssandra::SimpleCluster cluster(builder.build());
940+
ASSERT_EQ(cluster.start_all(), 0);
941+
942+
Session session;
943+
{
944+
Config session_config = session.config();
945+
EXPECT_EQ(CASS_CONSISTENCY_UNKNOWN, session_config.consistency());
946+
}
947+
948+
ExecutionProfile profile;
949+
Config config;
950+
config.contact_points().push_back(Address("127.0.0.1", 9042));
951+
config.set_execution_profile("profile", &profile);
952+
connect(config, &session);
953+
954+
EXPECT_TRUE(
955+
check_consistency(session, CASS_DEFAULT_DBAAS_CONSISTENCY, CASS_DEFAULT_DBAAS_CONSISTENCY));
956+
957+
close(&session);
958+
}
959+
960+
TEST_F(SessionUnitTest, DbaasDefaultConsistencyExecutionProfileNotUpdate) {
961+
mockssandra::SimpleRequestHandlerBuilder builder;
962+
builder.on(mockssandra::OPCODE_OPTIONS).execute(new SupportedDbaasOptions());
963+
mockssandra::SimpleCluster cluster(builder.build());
964+
ASSERT_EQ(cluster.start_all(), 0);
965+
966+
Session session;
967+
{
968+
Config session_config = session.config();
969+
EXPECT_EQ(CASS_CONSISTENCY_UNKNOWN, session_config.consistency());
970+
}
971+
972+
ExecutionProfile profile;
973+
profile.set_consistency(CASS_CONSISTENCY_LOCAL_ONE);
974+
Config config;
975+
config.contact_points().push_back(Address("127.0.0.1", 9042));
976+
config.set_execution_profile("profile", &profile);
977+
connect(config, &session);
978+
979+
EXPECT_TRUE(
980+
check_consistency(session, CASS_DEFAULT_DBAAS_CONSISTENCY, CASS_CONSISTENCY_LOCAL_ONE));
981+
982+
close(&session);
983+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
Copyright (c) DataStax, Inc.
3+
4+
This software can be used solely with DataStax Enterprise. Please consult the
5+
license at http://www.datastax.com/terms/datastax-dse-driver-license-terms
6+
*/
7+
8+
#include "loop_test.hpp"
9+
10+
#include "options_request.hpp"
11+
#include "request_callback.hpp"
12+
#include "supported_response.hpp"
13+
14+
using namespace datastax;
15+
using namespace datastax::internal;
16+
using namespace datastax::internal::core;
17+
18+
class SupportedResponseUnitTest : public LoopTest {
19+
public:
20+
const mockssandra::RequestHandler* simple_cluster_with_options() {
21+
mockssandra::SimpleRequestHandlerBuilder builder;
22+
builder.on(mockssandra::OPCODE_OPTIONS).execute(new SupportedOptions());
23+
return builder.build();
24+
}
25+
26+
public:
27+
static void on_connect(Connector* connector, StringMultimap* supported_options) {
28+
ASSERT_TRUE(connector->is_ok());
29+
*supported_options = connector->supported_options();
30+
}
31+
32+
private:
33+
class SupportedOptions : public mockssandra::Action {
34+
public:
35+
virtual void on_run(mockssandra::Request* request) const {
36+
Vector<String> compression;
37+
Vector<String> cql_version;
38+
Vector<String> protocol_versions;
39+
compression.push_back("snappy");
40+
compression.push_back("lz4");
41+
cql_version.push_back("3.4.5");
42+
protocol_versions.push_back("3/v3");
43+
protocol_versions.push_back("4/v4");
44+
45+
StringMultimap supported;
46+
supported["COMPRESSION"] = compression;
47+
supported["CQL_VERSION"] = cql_version;
48+
supported["PROTOCOL_VERSIONS"] = protocol_versions;
49+
50+
String body;
51+
mockssandra::encode_string_map(supported, &body);
52+
request->write(mockssandra::OPCODE_SUPPORTED, body);
53+
}
54+
};
55+
};
56+
57+
TEST_F(SupportedResponseUnitTest, Simple) {
58+
mockssandra::SimpleCluster cluster(simple_cluster_with_options());
59+
ASSERT_EQ(cluster.start_all(), 0);
60+
61+
StringMultimap supported_options;
62+
ASSERT_EQ(0, supported_options.size());
63+
Connector::Ptr connector(new Connector(Host::Ptr(new Host(Address("127.0.0.1", PORT))),
64+
PROTOCOL_VERSION,
65+
bind_callback(on_connect, &supported_options)));
66+
connector->connect(loop());
67+
uv_run(loop(), UV_RUN_DEFAULT);
68+
69+
ASSERT_EQ(3u, supported_options.size());
70+
{
71+
Vector<String> compression = supported_options.find("COMPRESSION")->second;
72+
ASSERT_EQ(2u, compression.size());
73+
EXPECT_EQ("snappy", compression[0]);
74+
EXPECT_EQ("lz4", compression[1]);
75+
}
76+
{
77+
Vector<String> cql_version = supported_options.find("CQL_VERSION")->second;
78+
ASSERT_EQ(1u, cql_version.size());
79+
EXPECT_EQ("3.4.5", cql_version[0]);
80+
}
81+
{
82+
Vector<String> protocol_versions = supported_options.find("PROTOCOL_VERSIONS")->second;
83+
ASSERT_EQ(2u, protocol_versions.size());
84+
EXPECT_EQ("3/v3", protocol_versions[0]);
85+
EXPECT_EQ("4/v4", protocol_versions[1]);
86+
}
87+
88+
{ // Non-existent key
89+
EXPECT_EQ(supported_options.end(), supported_options.find("invalid"));
90+
}
91+
}
92+
93+
TEST_F(SupportedResponseUnitTest, UppercaseKeysOnly) {
94+
class CaseInsensitiveSupportedOptions : public mockssandra::Action {
95+
public:
96+
virtual void on_run(mockssandra::Request* request) const {
97+
Vector<String> camel_key;
98+
camel_key.push_back("success");
99+
100+
StringMultimap supported;
101+
supported["CamEL_KeY"] = camel_key;
102+
103+
String body;
104+
mockssandra::encode_string_map(supported, &body);
105+
request->write(mockssandra::OPCODE_SUPPORTED, body);
106+
}
107+
};
108+
109+
mockssandra::SimpleRequestHandlerBuilder builder;
110+
builder.on(mockssandra::OPCODE_OPTIONS).execute(new CaseInsensitiveSupportedOptions());
111+
mockssandra::SimpleCluster cluster(builder.build());
112+
ASSERT_EQ(cluster.start_all(), 0);
113+
114+
StringMultimap supported_options;
115+
ASSERT_EQ(0, supported_options.size());
116+
Connector::Ptr connector(new Connector(Host::Ptr(new Host(Address("127.0.0.1", PORT))),
117+
PROTOCOL_VERSION,
118+
bind_callback(on_connect, &supported_options)));
119+
connector->connect(loop());
120+
uv_run(loop(), UV_RUN_DEFAULT);
121+
122+
ASSERT_EQ(1u, supported_options.size());
123+
{ // Uppercase
124+
Vector<String> uppercase = supported_options.find("CAMEL_KEY")->second;
125+
ASSERT_EQ(1u, uppercase.size());
126+
EXPECT_EQ("success", uppercase[0]);
127+
}
128+
{ // Exact key
129+
EXPECT_EQ(supported_options.end(), supported_options.find("CamEL_KeY"));
130+
}
131+
}

0 commit comments

Comments
 (0)