initialContacts = cluster.metadata.getContactPoints();
+ Collections.shuffle(initialContacts);
+ return reconnectInternal(
+ Iterators.concat(queryPlan(), initialContacts.iterator()), false);
+ }
return reconnectInternal(queryPlan(), false);
} catch (NoHostAvailableException e) {
throw new ConnectionException(null, e.getMessage());
diff --git a/driver-core/src/main/java/com/datastax/driver/core/QueryOptions.java b/driver-core/src/main/java/com/datastax/driver/core/QueryOptions.java
index 9fcd6a437d4..137c56aa747 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/QueryOptions.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/QueryOptions.java
@@ -72,6 +72,8 @@ public class QueryOptions {
private volatile boolean schemaQueriesPaged = true;
+ private volatile boolean addOriginalContactsToReconnectionPlan = false;
+
/**
* Creates a new {@link QueryOptions} instance using the {@link #DEFAULT_CONSISTENCY_LEVEL},
* {@link #DEFAULT_SERIAL_CONSISTENCY_LEVEL} and {@link #DEFAULT_FETCH_SIZE}.
@@ -499,6 +501,26 @@ public int getMaxPendingRefreshNodeRequests() {
return maxPendingRefreshNodeRequests;
}
+ /**
+ * Whether the driver should use original contact points when reconnecting to a control node. In
+ * practice this forces driver to manually add original contact points to the end of the query
+ * plan. It is possible that it may introduce duplicates (but under differnet Host class
+ * instances) in the query plan. If this is set to false it does not mean that original contact
+ * points will be excluded.
+ *
+ * One use case of this feature is that if the original contact point is defined by hostname
+ * and its IP address changes then setting this to {@code true} allows trying reconnecting to the
+ * new IP if all connection was lost.
+ */
+ public QueryOptions setAddOriginalContactsToReconnectionPlan(boolean enabled) {
+ this.addOriginalContactsToReconnectionPlan = enabled;
+ return this;
+ }
+
+ public boolean shouldAddOriginalContactsToReconnectionPlan() {
+ return this.addOriginalContactsToReconnectionPlan;
+ }
+
@Override
public boolean equals(Object that) {
if (that == null || !(that instanceof QueryOptions)) {
diff --git a/driver-core/src/test/java/com/datastax/driver/core/CCMBridge.java b/driver-core/src/test/java/com/datastax/driver/core/CCMBridge.java
index 4c9ba61fc57..dc9b807c023 100644
--- a/driver-core/src/test/java/com/datastax/driver/core/CCMBridge.java
+++ b/driver-core/src/test/java/com/datastax/driver/core/CCMBridge.java
@@ -949,6 +949,7 @@ public static class Builder {
private static final Pattern RANDOM_PORT_PATTERN = Pattern.compile(RANDOM_PORT);
private String ipPrefix = TestUtils.IP_PREFIX;
+ private String providedClusterName = null;
int[] nodes = {1};
private int[] jmxPorts = {};
private boolean start = true;
@@ -991,6 +992,15 @@ public Builder withSniProxy() {
return this;
}
+ /**
+ * Builder takes care of naming and numbering clusters on its own. Use if you really need a
+ * specific name
+ */
+ public Builder withClusterName(String clusterName) {
+ this.providedClusterName = clusterName;
+ return this;
+ }
+
/** Enables SSL encryption. */
public Builder withSSL() {
cassandraConfiguration.put("client_encryption_options.enabled", "true");
@@ -1115,6 +1125,8 @@ public CCMBridge build() {
// be careful NOT to alter internal state (hashCode/equals) during build!
String clusterName = TestUtils.generateIdentifier("ccm_");
+ if (providedClusterName != null) clusterName = providedClusterName;
+
VersionNumber dseVersion;
VersionNumber cassandraVersion;
boolean versionConfigured = this.version != null;
diff --git a/driver-core/src/test/java/com/datastax/driver/core/HostResolutionReconnectionTest.java b/driver-core/src/test/java/com/datastax/driver/core/HostResolutionReconnectionTest.java
new file mode 100644
index 00000000000..7e0e57ce0a3
--- /dev/null
+++ b/driver-core/src/test/java/com/datastax/driver/core/HostResolutionReconnectionTest.java
@@ -0,0 +1,86 @@
+package com.datastax.driver.core;
+
+import java.net.InetSocketAddress;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.burningwave.tools.net.DefaultHostResolver;
+import org.burningwave.tools.net.HostResolutionRequestInterceptor;
+import org.burningwave.tools.net.MappedHostResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+public class HostResolutionReconnectionTest {
+
+ private static final Logger logger =
+ LoggerFactory.getLogger(HostResolutionReconnectionTest.class);
+
+ @Test(groups = "isolated")
+ public void should_reconnect_to_different_cluster() {
+ // Configure host resolution
+ Map hostAliasesA = new LinkedHashMap<>();
+ hostAliasesA.put("control.reconnect.test", "127.1.1.1");
+ HostResolutionRequestInterceptor.INSTANCE.install(
+ new MappedHostResolver(hostAliasesA), DefaultHostResolver.INSTANCE);
+
+ Cluster cluster = null;
+ Session session = null;
+ CCMBridge bridgeA = null;
+ try {
+ bridgeA =
+ CCMBridge.builder()
+ .withNodes(1)
+ .withIpPrefix("127.1.1.")
+ .withBinaryPort(9042)
+ .withClusterName("same_name")
+ .build();
+ bridgeA.start();
+
+ cluster =
+ Cluster.builder()
+ .addContactPointsWithPorts(
+ InetSocketAddress.createUnresolved("control.reconnect.test", 9042))
+ .withPort(9042)
+ .withoutAdvancedShardAwareness()
+ .withQueryOptions(new QueryOptions().setAddOriginalContactsToReconnectionPlan(true))
+ .build();
+ session = cluster.connect();
+
+ ResultSet rs = session.execute("select * from system.local");
+ Row row = rs.one();
+ String address = row.getInet("broadcast_address").toString();
+ logger.info("Queried node has broadcast_address: {}}", address);
+ System.out.flush();
+ } finally {
+ assert bridgeA != null;
+ bridgeA.close();
+ }
+
+ CCMBridge bridgeB = null;
+ // Overwrite host resolution
+ Map hostAliasesB = new LinkedHashMap<>();
+ hostAliasesB.put("control.reconnect.test", "127.2.2.1");
+ HostResolutionRequestInterceptor.INSTANCE.install(
+ new MappedHostResolver(hostAliasesB), DefaultHostResolver.INSTANCE);
+ try {
+ bridgeB =
+ CCMBridge.builder()
+ .withNodes(1)
+ .withIpPrefix("127.2.2.")
+ .withBinaryPort(9042)
+ .withClusterName("same_name")
+ .build();
+ bridgeB.start();
+ Thread.sleep(1000 * 92);
+ ResultSet rs = session.execute("select * from system.local");
+ Row row = rs.one();
+ String address = row.getInet("broadcast_address").toString();
+ logger.info("Queried node has broadcast_address: {}}", address);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } finally {
+ assert bridgeB != null;
+ bridgeB.close();
+ }
+ }
+}
diff --git a/driver-core/src/test/resources/burningwave.static.properties b/driver-core/src/test/resources/burningwave.static.properties
new file mode 100644
index 00000000000..7108b42c0fb
--- /dev/null
+++ b/driver-core/src/test/resources/burningwave.static.properties
@@ -0,0 +1,4 @@
+managed-logger.repository=autodetect
+managed-logger.repository.enabled=false
+banner.hide=true
+priority-of-this-configuration=1000
\ No newline at end of file
diff --git a/driver-core/src/test/resources/log4j.properties b/driver-core/src/test/resources/log4j.properties
index 67195750ee6..a2d3e400775 100644
--- a/driver-core/src/test/resources/log4j.properties
+++ b/driver-core/src/test/resources/log4j.properties
@@ -31,9 +31,9 @@ log4j.logger.spray.can=ERROR
#log4j.logger.org.scassandra.http.client=ERROR
# These loggers can be quite verbose
-log4j.logger.com.datastax.driver.core=INFO
-log4j.logger.com.datastax.driver.core.Cluster=ERROR
-log4j.logger.com.datastax.driver.core.policies.DCAwareRoundRobinPolicy=ERROR
+log4j.logger.com.datastax.driver.core=DEBUG
+#log4j.logger.com.datastax.driver.core.Cluster=ERROR
+#log4j.logger.com.datastax.driver.core.policies.DCAwareRoundRobinPolicy=ERROR
# Useful loggers when debugging core functionality
#log4j.logger.com.datastax.driver.core.ControlConnection=ERROR
@@ -44,7 +44,7 @@ log4j.logger.com.datastax.driver.core.policies.DCAwareRoundRobinPolicy=ERROR
# Adjust log levels for CCM tests
#log4j.logger.com.datastax.driver.core.CCMTestsSupport=DEBUG
#log4j.logger.com.datastax.driver.core.CCMCache=DEBUG
-#log4j.logger.com.datastax.driver.core.CCMBridge=DEBUG
+log4j.logger.com.datastax.driver.core.CCMBridge=DEBUG
# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender
diff --git a/pom.xml b/pom.xml
index 51d15164fde..102cb5f6894 100644
--- a/pom.xml
+++ b/pom.xml
@@ -86,6 +86,7 @@
1.1.2
1.2.13
3.0.8
+ 0.26.2
127.0.1.
unit
@@ -398,6 +399,12 @@
${groovy.version}
+
+ org.burningwave
+ tools
+ ${burningwave.tools.version}
+
+