diff --git a/include/cassandra.h b/include/cassandra.h
index 6faf3d268..e3aeebebb 100644
--- a/include/cassandra.h
+++ b/include/cassandra.h
@@ -2139,6 +2139,10 @@ cass_cluster_set_load_balance_round_robin(CassCluster* cluster);
* For each query, all live nodes in a primary 'local' DC are tried first,
* followed by any node from other DCs.
*
+ * Important: Pass in NULL for local_dc to automatically use the data center of the first
+ * connected control connection host as the primary data center. This approach is recommended
+ * over manually selecting the local_dc.
+ *
* Note: This is the default, and does not need to be called unless
* switching an existing from another policy or changing settings.
* Without further configuration, a default local_dc is chosen from the
@@ -2154,7 +2158,7 @@ cass_cluster_set_load_balance_round_robin(CassCluster* cluster);
* @public @memberof CassCluster
*
* @param[in] cluster
- * @param[in] local_dc The primary data center to try first
+ * @param[in] local_dc The primary data center to try first, or NULL to use the DC of the first connected node
* @param[in] used_hosts_per_remote_dc The number of hosts used in each remote
* DC if no hosts are available in the local dc (deprecated)
* @param[in] allow_remote_dcs_for_local_cl Allows remote hosts to be used if no
@@ -2173,6 +2177,10 @@ cass_cluster_set_load_balance_dc_aware(CassCluster* cluster,
* Same as cass_cluster_set_load_balance_dc_aware(), but with lengths for string
* parameters.
*
+ * Important: If local_dc is NULL or local_dc_length is 0, the data center of
+ * the first connected control connection host will be automatically used as the
+ * primary data center. This approach is recommended over manually selecting the local_dc.
+ *
* @deprecated The remote DC settings for DC-aware are not suitable for most
* scenarios that require DC failover. There is also unhandled gap between
* replication factor number of nodes failing and the full cluster failing. Only
@@ -2181,11 +2189,11 @@ cass_cluster_set_load_balance_dc_aware(CassCluster* cluster,
* @public @memberof CassCluster
*
* @param[in] cluster
- * @param[in] local_dc
- * @param[in] local_dc_length
+ * @param[in] local_dc Primary data center to try first, or NULL to use the DC of the first connected node
+ * @param[in] local_dc_length Length of local_dc string
* @param[in] used_hosts_per_remote_dc (deprecated)
* @param[in] allow_remote_dcs_for_local_cl (deprecated)
- * @return same as cass_cluster_set_load_balance_dc_aware()
+ * @return CASS_OK if successful, otherwise an error occurred
*
* @see cass_cluster_set_load_balance_dc_aware()
*/
diff --git a/src/cluster_config.cpp b/src/cluster_config.cpp
index 408e3f82e..ef8024ab2 100644
--- a/src/cluster_config.cpp
+++ b/src/cluster_config.cpp
@@ -281,8 +281,11 @@ void cass_cluster_set_load_balance_round_robin(CassCluster* cluster) {
CassError cass_cluster_set_load_balance_dc_aware(CassCluster* cluster, const char* local_dc,
unsigned used_hosts_per_remote_dc,
cass_bool_t allow_remote_dcs_for_local_cl) {
- if (local_dc == NULL) {
- return CASS_ERROR_LIB_BAD_PARAMS;
+ // Allow NULL or empty local_dc to use the DC of the first connected node
+ if (local_dc == NULL || *local_dc == '\0') {
+ return cass_cluster_set_load_balance_dc_aware_n(cluster, NULL, 0,
+ used_hosts_per_remote_dc,
+ allow_remote_dcs_for_local_cl);
}
return cass_cluster_set_load_balance_dc_aware_n(cluster, local_dc, SAFE_STRLEN(local_dc),
used_hosts_per_remote_dc,
@@ -293,11 +296,13 @@ CassError cass_cluster_set_load_balance_dc_aware_n(CassCluster* cluster, const c
size_t local_dc_length,
unsigned used_hosts_per_remote_dc,
cass_bool_t allow_remote_dcs_for_local_cl) {
- if (local_dc == NULL || local_dc_length == 0) {
- return CASS_ERROR_LIB_BAD_PARAMS;
- }
+ // If local_dc is NULL or length is 0, we use an empty string which causes the driver
+ // to use the DC of the first connected node
+ String dc_name = (local_dc != NULL && local_dc_length > 0) ?
+ String(local_dc, local_dc_length) : String();
+
cluster->config().set_load_balancing_policy(new DCAwarePolicy(
- String(local_dc, local_dc_length), used_hosts_per_remote_dc, !allow_remote_dcs_for_local_cl));
+ dc_name, used_hosts_per_remote_dc, !allow_remote_dcs_for_local_cl));
return CASS_OK;
}
diff --git a/tests/src/integration/tests/test_cluster.cpp b/tests/src/integration/tests/test_cluster.cpp
index 8ad258797..33c8a4097 100644
--- a/tests/src/integration/tests/test_cluster.cpp
+++ b/tests/src/integration/tests/test_cluster.cpp
@@ -22,16 +22,26 @@ class ClusterTests : public Integration {
};
/**
- * Set local dc to null for dc-aware lbp
+ * Set local dc to null or empty for dc-aware lbp
*
* @jira_ticket CPP-368
* @test_category configuration
- * @expected_result Error out because it is illegal to specify a null local-dc.
+ * @expected_result Success because NULL or empty local-dc now means use the DC of the first connected node.
*/
-CASSANDRA_INTEGRATION_TEST_F(ClusterTests, SetLoadBalanceDcAwareNullLocalDc) {
- test::driver::Cluster cluster;
- EXPECT_EQ(CASS_ERROR_LIB_BAD_PARAMS,
- cass_cluster_set_load_balance_dc_aware(cluster.get(), NULL, 99, cass_false));
+CASSANDRA_INTEGRATION_TEST_F(ClusterTests, SetLoadBalanceDcAwareNullOrEmptyLocalDc) {
+ // Test with NULL local_dc
+ {
+ test::driver::Cluster cluster;
+ EXPECT_EQ(CASS_OK,
+ cass_cluster_set_load_balance_dc_aware(cluster.get(), NULL, 99, cass_false));
+ }
+
+ // Test with empty string local_dc
+ {
+ test::driver::Cluster cluster;
+ EXPECT_EQ(CASS_OK,
+ cass_cluster_set_load_balance_dc_aware(cluster.get(), "", 99, cass_false));
+ }
}
/**
diff --git a/tests/src/unit/tests/test_cluster_config.cpp b/tests/src/unit/tests/test_cluster_config.cpp
new file mode 100644
index 000000000..177422b08
--- /dev/null
+++ b/tests/src/unit/tests/test_cluster_config.cpp
@@ -0,0 +1,163 @@
+/*
+ Copyright (c) DataStax, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include
+
+#include "cluster_config.hpp"
+#include "cassandra.h"
+#include "dc_aware_policy.hpp"
+#include "string.hpp"
+
+using namespace datastax;
+using namespace datastax::internal;
+using namespace datastax::internal::core;
+
+class ClusterConfigUnitTest : public testing::Test {
+public:
+ ClusterConfigUnitTest() {}
+
+ virtual void SetUp() {
+ cluster_ = cass_cluster_new();
+ }
+
+ virtual void TearDown() {
+ cass_cluster_free(cluster_);
+ }
+
+private:
+
+protected:
+ CassCluster* cluster_;
+
+ const DCAwarePolicy* build_dc_policy() {
+
+ // Disable token-aware routing in this case in order to avoid illegal instructions
+ // when creating a TokenAwarePolicy in build_load_balancing_policy()
+ cluster_->config().set_token_aware_routing(false);
+ cluster_->config().default_profile().build_load_balancing_policy();
+
+ // Verify the policy was set correctly with the right datacenter name
+ const LoadBalancingPolicy* policy =
+ cluster_->config().load_balancing_policy().get();
+ return static_cast(policy);
+ }
+};
+
+// ==================== cass_cluster_set_load_balance_dc_aware_n ====================
+TEST_F(ClusterConfigUnitTest, SetLoadBalanceDcAwareNHappyPath) {
+
+ // Test valid parameters
+ const char* valid_dc = "my_datacenter";
+ EXPECT_EQ(CASS_OK, cass_cluster_set_load_balance_dc_aware_n(
+ cluster_, valid_dc, strlen(valid_dc),
+ 2, cass_true));
+
+ const DCAwarePolicy* dc_policy = build_dc_policy();
+
+ // Should be using the partial string as local DC
+ EXPECT_EQ(dc_policy->local_dc(), valid_dc);
+ EXPECT_EQ(dc_policy->used_hosts_per_remote_dc(), 2u);
+ EXPECT_FALSE(dc_policy->skip_remote_dcs_for_local_cl());
+}
+
+TEST_F(ClusterConfigUnitTest, SetLoadBalanceDcAwareNWithNullLocalDc) {
+
+ // Test with NULL pointer (should now succeed and use empty string for DC)
+ EXPECT_EQ(CASS_OK,
+ cass_cluster_set_load_balance_dc_aware_n(
+ cluster_, NULL, 10, 2, cass_true));
+
+ const DCAwarePolicy* dc_policy = build_dc_policy();
+
+ EXPECT_EQ(dc_policy->local_dc(), String());
+ EXPECT_EQ(dc_policy->used_hosts_per_remote_dc(), 2u);
+ EXPECT_FALSE(dc_policy->skip_remote_dcs_for_local_cl());
+}
+
+TEST_F(ClusterConfigUnitTest, SetLoadBalanceDcAwareNWithZeroLengthLocalDc) {
+
+ // Test with zero length (should now succeed and use empty string for DC)
+ const char* valid_dc = "my_datacenter";
+ EXPECT_EQ(CASS_OK,
+ cass_cluster_set_load_balance_dc_aware_n(
+ cluster_, valid_dc, 0, 2, cass_true));
+
+ const DCAwarePolicy* dc_policy = build_dc_policy();
+
+ EXPECT_EQ(dc_policy->local_dc(), String());
+ EXPECT_EQ(dc_policy->used_hosts_per_remote_dc(), 2u);
+ EXPECT_FALSE(dc_policy->skip_remote_dcs_for_local_cl());
+}
+
+TEST_F(ClusterConfigUnitTest, SetLoadBalanceDcAwareNWithEmptyLocalDc) {
+
+ // Test with empty string (should now succeed)
+ const char* empty_string = "";
+ EXPECT_EQ(CASS_OK,
+ cass_cluster_set_load_balance_dc_aware_n(
+ cluster_, empty_string, strlen(empty_string), 2, cass_true));
+
+ const DCAwarePolicy* dc_policy = build_dc_policy();
+
+ EXPECT_EQ(dc_policy->local_dc(), String());
+ EXPECT_EQ(dc_policy->used_hosts_per_remote_dc(), 2u);
+ EXPECT_FALSE(dc_policy->skip_remote_dcs_for_local_cl());
+}
+
+TEST_F(ClusterConfigUnitTest, SetLoadBalanceDcAwareNWithPartialStringLocalDc) {
+
+ // Test with partial string length
+ const char* long_dc_name = "my_datacenter_with_a_long_name";
+ size_t partial_length = 5; // Should just use "my_da" as the datacenter name
+ EXPECT_EQ(CASS_OK, cass_cluster_set_load_balance_dc_aware_n(
+ cluster_, long_dc_name, partial_length,
+ 2, cass_true));
+
+ const DCAwarePolicy* dc_policy = build_dc_policy();
+
+ // Should be using the partial string as local DC
+ EXPECT_EQ(dc_policy->local_dc(), String(long_dc_name, partial_length));
+ EXPECT_EQ(dc_policy->local_dc(), String("my_da"));
+ EXPECT_EQ(dc_policy->used_hosts_per_remote_dc(), 2u);
+ EXPECT_FALSE(dc_policy->skip_remote_dcs_for_local_cl());
+}
+
+// ==================== cass_cluster_set_load_balance_dc_aware ====================
+TEST_F(ClusterConfigUnitTest, SetLoadBalanceDcAwareWithNullLocalDc) {
+ // Test with NULL to use local DC from connected node
+ EXPECT_EQ(CASS_OK, cass_cluster_set_load_balance_dc_aware(
+ cluster_, NULL, 3, cass_false));
+
+ const DCAwarePolicy* dc_policy = build_dc_policy();
+
+ // Should be using empty string as local DC (will be determined at runtime)
+ EXPECT_EQ(dc_policy->local_dc(), String());
+ EXPECT_EQ(dc_policy->used_hosts_per_remote_dc(), 3u);
+ EXPECT_TRUE(dc_policy->skip_remote_dcs_for_local_cl());
+}
+
+TEST_F(ClusterConfigUnitTest, SetLoadBalanceDcAwareWithEmptyLocalDc) {
+ // Test with empty string to use local DC from connected node
+ EXPECT_EQ(CASS_OK, cass_cluster_set_load_balance_dc_aware(
+ cluster_, "", 2, cass_true));
+
+ const DCAwarePolicy* dc_policy = build_dc_policy();
+
+ // Should be using empty string as local DC (will be determined at runtime)
+ EXPECT_EQ(dc_policy->local_dc(), String());
+ EXPECT_EQ(dc_policy->used_hosts_per_remote_dc(), 2u);
+ EXPECT_FALSE(dc_policy->skip_remote_dcs_for_local_cl());
+}
\ No newline at end of file