@@ -189,6 +189,32 @@ class ClusterUnitTest : public EventLoopTest {
189189 OutagePlan* outage_plan_;
190190 };
191191
192+ class RecoverClusterListener : public UpDownListener {
193+ public:
194+ typedef SharedRefPtr<RecoverClusterListener> Ptr;
195+
196+ RecoverClusterListener (const Future::Ptr& close_future,
197+ const Future::Ptr& up_future,
198+ const Future::Ptr& recover_future)
199+ : UpDownListener(close_future, up_future, Future::Ptr())
200+ , recover_future_(recover_future) { }
201+
202+ const HostVec& connected_hosts () const {
203+ return connected_hosts_;
204+ }
205+
206+ virtual void on_reconnect (Cluster* cluster) {
207+ connected_hosts_.push_back (cluster->connected_host ());
208+ if (connected_hosts_.size () > 1 && recover_future_) {
209+ recover_future_->set ();
210+ }
211+ }
212+
213+ private:
214+ HostVec connected_hosts_;
215+ Future::Ptr recover_future_;
216+ };
217+
192218 static void on_connection_connected (ClusterConnector* connector, Future* future) {
193219 if (connector->is_ok ()) {
194220 future->set ();
@@ -208,7 +234,7 @@ class ClusterUnitTest : public EventLoopTest {
208234 break ;
209235 case ClusterConnector::CLUSTER_ERROR_NO_HOSTS_AVAILABLE:
210236 future->set_error (CASS_ERROR_LIB_NO_HOSTS_AVAILABLE,
211- " Unable to connect to any contact points " );
237+ connector-> error_message () );
212238 break ;
213239 case ClusterConnector::CLUSTER_CANCELED:
214240 future->set_error (CASS_ERROR_LIB_UNABLE_TO_CONNECT,
@@ -668,3 +694,88 @@ TEST_F(ClusterUnitTest, InvalidSsl) {
668694 ASSERT_TRUE (connect_future->error ());
669695 EXPECT_EQ (CASS_ERROR_SSL_INVALID_PEER_CERT, connect_future->error ()->code );
670696}
697+
698+ TEST_F (ClusterUnitTest, DCAwareRecoverOnRemoteHost) {
699+ mockssandra::SimpleCluster cluster (simple (), 1 , 1 ); // 2 DCs with a single node each
700+ ASSERT_EQ (cluster.start_all (), 0 );
701+
702+ Address local_address (" 127.0.0.1" , 9042 );
703+ Address remote_address (" 127.0.0.2" , 9042 );
704+
705+ ContactPointList contact_points;
706+ contact_points.push_back (local_address.to_string ());
707+
708+ Future::Ptr close_future (Memory::allocate<Future>());
709+ Future::Ptr connect_future (Memory::allocate<Future>());
710+ ClusterConnector::Ptr connector (Memory::allocate<ClusterConnector>(contact_points,
711+ PROTOCOL_VERSION,
712+ bind_callback (on_connection_reconnect, connect_future.get ())));
713+
714+ Future::Ptr up_future (Memory::allocate<Future>());
715+ Future::Ptr recover_future (Memory::allocate<Future>());
716+ RecoverClusterListener::Ptr listener (
717+ Memory::allocate<RecoverClusterListener>(close_future, up_future, recover_future));
718+
719+ ClusterSettings settings;
720+ settings.load_balancing_policy .reset (Memory::allocate<DCAwarePolicy>(" dc1" , 1 , false )); // Allow connection to a single remote host
721+ settings.load_balancing_policies .clear ();
722+ settings.load_balancing_policies .push_back (settings.load_balancing_policy );
723+ settings.reconnect_timeout_ms = 1 ; // Reconnect immediately
724+ settings.control_connection_settings .connection_settings .connect_timeout_ms = 200 ; // Give enough time for the connection to complete
725+
726+ connector
727+ ->with_settings (settings)
728+ ->with_listener (listener.get ())
729+ ->connect (event_loop ());
730+
731+ ASSERT_TRUE (connect_future->wait_for (WAIT_FOR_TIME));
732+ EXPECT_FALSE (connect_future->error ());
733+
734+ // Notify every host as down
735+ connect_future->cluster ()->notify_down (local_address);
736+ connect_future->cluster ()->notify_down (remote_address);
737+
738+ // Notify the remote host as up
739+ connect_future->cluster ()->notify_up (remote_address);
740+
741+ // Verify that the remote host was marked as up
742+ ASSERT_TRUE (up_future->wait_for (WAIT_FOR_TIME));
743+ EXPECT_EQ (remote_address, listener->address ());
744+
745+ cluster.stop (1 ); // Stop local node to verify that remote host is tried for reconnection.
746+ ASSERT_TRUE (recover_future->wait_for (WAIT_FOR_TIME));
747+
748+ connect_future->cluster ()->close ();
749+ ASSERT_TRUE (close_future->wait_for (WAIT_FOR_TIME));
750+
751+ ASSERT_EQ (listener->connected_hosts ().size (), 2u );
752+ EXPECT_EQ (listener->connected_hosts ()[0 ]->address (), Address (" 127.0.0.1" , PORT));
753+ EXPECT_EQ (listener->connected_hosts ()[1 ]->address (), Address (" 127.0.0.2" , PORT)); // Connected to remote host.
754+ }
755+
756+ TEST_F (ClusterUnitTest, InvalidDC) {
757+ mockssandra::SimpleCluster cluster (simple ());
758+ ASSERT_EQ (cluster.start_all (), 0 );
759+
760+ ContactPointList contact_points;
761+ contact_points.push_back (" 127.0.0.1" );
762+
763+ Future::Ptr connect_future (Memory::allocate<Future>());
764+ ClusterConnector::Ptr connector (Memory::allocate<ClusterConnector>(contact_points,
765+ PROTOCOL_VERSION,
766+ bind_callback (on_connection_connected, connect_future.get ())));
767+
768+ ClusterSettings settings;
769+ settings.load_balancing_policy .reset (Memory::allocate<DCAwarePolicy>(" invalid_dc" , 0 , false )); // Invalid DC and not using remote hosts
770+ settings.load_balancing_policies .clear ();
771+ settings.load_balancing_policies .push_back (settings.load_balancing_policy );
772+
773+ connector
774+ ->with_settings (settings)
775+ ->connect (event_loop ());
776+
777+ ASSERT_TRUE (connect_future->wait_for (WAIT_FOR_TIME));
778+ ASSERT_TRUE (connect_future->error ());
779+ EXPECT_EQ (CASS_ERROR_LIB_NO_HOSTS_AVAILABLE, connect_future->error ()->code );
780+ EXPECT_TRUE (connect_future->error ()->message .find (" Check to see if the configured local datacenter is valid" ) != String::npos);
781+ }
0 commit comments