Skip to content

Commit 275bade

Browse files
authored
Add MockResolverIT#replace_cluster_test() (#335)
* Remove ccm rule from MockResolverIT should_connect_with_mocked_hostname() will use CcmBridge.Builder builder instead. * Add `MockResolverIT#replace_cluster_test()` Adds another method that runs scenario in which we replace three node cluster with the completely new three node cluster. This method runs 20 times called by `run_replace_test_20_times()` test method. * Add flags to CcmBridge.start methods Adds "--wait-other-notice", "--wait-for-binary-proto" if missing. * Make defaultResolverFactory settable only once before use
1 parent ef77cd6 commit 275bade

File tree

3 files changed

+193
-27
lines changed

3 files changed

+193
-27
lines changed

core/src/main/java/com/datastax/oss/driver/internal/core/resolver/ResolverProvider.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
*/
99
public class ResolverProvider {
1010

11+
private static boolean alreadyInUse = false;
12+
private static boolean alreadySet = false;
1113
private static AbstractResolverFactory defaultResolverFactoryImpl = new DefaultResolverFactory();
1214

1315
/**
@@ -16,7 +18,8 @@ public class ResolverProvider {
1618
* @param clazz Class that is requesting the {@link Resolver}.
1719
* @return new {@link Resolver}.
1820
*/
19-
public static Resolver getResolver(Class<?> clazz) {
21+
public static synchronized Resolver getResolver(Class<?> clazz) {
22+
alreadyInUse = true;
2023
return defaultResolverFactoryImpl.getResolver(clazz);
2124
}
2225

@@ -26,7 +29,20 @@ public static Resolver getResolver(Class<?> clazz) {
2629
*
2730
* @param resolverFactoryImpl new {@link Resolver} factory.
2831
*/
29-
public static void setDefaultResolverFactory(AbstractResolverFactory resolverFactoryImpl) {
32+
public static synchronized void setDefaultResolverFactory(
33+
AbstractResolverFactory resolverFactoryImpl) {
34+
if (alreadyInUse) {
35+
throw new IllegalStateException(
36+
"Cannot change default resolver factory: ResolverProvider has already returned "
37+
+ "an instance of a Resolver to use. Default resolver factory needs to be set up before first use by any "
38+
+ "class.");
39+
}
40+
if (alreadySet) {
41+
throw new IllegalStateException(
42+
"Cannot change default resolver factory: this method has already been called. "
43+
+ "You can set default resolver factory only once.");
44+
}
45+
alreadySet = true;
3046
defaultResolverFactoryImpl = resolverFactoryImpl;
3147
}
3248
}

integration-tests/src/test/java/com/datastax/oss/driver/core/resolver/MockResolverIT.java

Lines changed: 170 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import static org.assertj.core.api.Assertions.assertThat;
2727
import static org.junit.Assert.assertTrue;
28+
import static org.junit.Assert.fail;
2829

2930
import com.datastax.oss.driver.api.core.CqlSession;
3031
import com.datastax.oss.driver.api.core.CqlSessionBuilder;
@@ -34,7 +35,6 @@
3435
import com.datastax.oss.driver.api.core.cql.Row;
3536
import com.datastax.oss.driver.api.core.metadata.Node;
3637
import com.datastax.oss.driver.api.testinfra.ccm.CcmBridge;
37-
import com.datastax.oss.driver.api.testinfra.ccm.CustomCcmRule;
3838
import com.datastax.oss.driver.categories.IsolatedTests;
3939
import com.datastax.oss.driver.internal.core.config.typesafe.DefaultProgrammaticDriverConfigLoaderBuilder;
4040
import com.datastax.oss.driver.internal.core.resolver.ResolverProvider;
@@ -45,10 +45,11 @@
4545
import java.net.UnknownHostException;
4646
import java.util.Collection;
4747
import java.util.Collections;
48+
import java.util.Iterator;
4849
import java.util.List;
4950
import java.util.Set;
5051
import java.util.stream.Collectors;
51-
import org.junit.ClassRule;
52+
import org.junit.BeforeClass;
5253
import org.junit.Test;
5354
import org.junit.experimental.categories.Category;
5455
import org.slf4j.Logger;
@@ -58,52 +59,199 @@
5859
public class MockResolverIT {
5960

6061
private static final Logger LOG = LoggerFactory.getLogger(MockResolverIT.class);
62+
private static final MockResolverFactory RESOLVER_FACTORY = new MockResolverFactory();
6163

62-
@ClassRule
63-
public static final CustomCcmRule CCM_RULE = CustomCcmRule.builder().withNodes(1).build();
64+
private static final int CLUSTER_WAIT_SECONDS =
65+
60; // Maximal wait time for cluster nodes to get up
66+
67+
@BeforeClass
68+
public static void setUpResolver() {
69+
ResolverProvider.setDefaultResolverFactory(RESOLVER_FACTORY);
70+
}
6471

6572
@Test
6673
public void should_connect_with_mocked_hostname() {
67-
CcmBridge ccmBridge = CCM_RULE.getCcmBridge();
74+
CcmBridge.Builder ccmBridgeBuilder = CcmBridge.builder().withNodes(1).withIpPrefix("127.0.1.");
75+
try (CcmBridge ccmBridge = ccmBridgeBuilder.build()) {
76+
RESOLVER_FACTORY.updateResponse(
77+
"test.cluster.fake",
78+
new ValidResponse(new InetAddress[] {getNodeInetAddress(ccmBridge, 1)}));
79+
ccmBridge.create();
80+
ccmBridge.start();
6881

69-
MockResolverFactory resolverFactory = new MockResolverFactory();
70-
resolverFactory.updateResponse(
71-
"node-1.cluster.fake",
72-
new ValidResponse(new InetAddress[] {getNodeInetAddress(ccmBridge, 1)}));
73-
ResolverProvider.setDefaultResolverFactory(resolverFactory);
82+
DriverConfigLoader loader =
83+
new DefaultProgrammaticDriverConfigLoaderBuilder()
84+
.withBoolean(TypedDriverOption.RESOLVE_CONTACT_POINTS.getRawOption(), false)
85+
.withBoolean(TypedDriverOption.RECONNECT_ON_INIT.getRawOption(), true)
86+
.withStringList(
87+
TypedDriverOption.CONTACT_POINTS.getRawOption(),
88+
Collections.singletonList("test.cluster.fake:9042"))
89+
.build();
7490

91+
CqlSessionBuilder builder = new CqlSessionBuilder().withConfigLoader(loader);
92+
try (CqlSession session = builder.build()) {
93+
ResultSet rs = session.execute("SELECT * FROM system.local");
94+
List<Row> rows = rs.all();
95+
assertThat(rows).hasSize(1);
96+
LOG.trace("system.local contents: {}", rows.get(0).getFormattedContents());
97+
Collection<Node> nodes = session.getMetadata().getNodes().values();
98+
for (Node node : nodes) {
99+
LOG.trace("Found metadata node: {}", node);
100+
}
101+
Set<Node> filteredNodes;
102+
filteredNodes =
103+
nodes.stream()
104+
.filter(x -> x.toString().contains("test.cluster.fake"))
105+
.collect(Collectors.toSet());
106+
assertThat(filteredNodes).hasSize(1);
107+
InetSocketAddress address =
108+
(InetSocketAddress) filteredNodes.iterator().next().getEndPoint().resolve();
109+
assertTrue(address.isUnresolved());
110+
}
111+
}
112+
}
113+
114+
@Test
115+
public void replace_cluster_test() {
116+
final int numberOfNodes = 3;
75117
DriverConfigLoader loader =
76118
new DefaultProgrammaticDriverConfigLoaderBuilder()
77119
.withBoolean(TypedDriverOption.RESOLVE_CONTACT_POINTS.getRawOption(), false)
78120
.withBoolean(TypedDriverOption.RECONNECT_ON_INIT.getRawOption(), true)
79121
.withStringList(
80122
TypedDriverOption.CONTACT_POINTS.getRawOption(),
81-
Collections.singletonList("node-1.cluster.fake:9042"))
123+
Collections.singletonList("test.cluster.fake:9042"))
82124
.build();
83125

84126
CqlSessionBuilder builder = new CqlSessionBuilder().withConfigLoader(loader);
85-
try (CqlSession session = builder.build()) {
127+
CqlSession session;
128+
129+
try (CcmBridge ccmBridge =
130+
CcmBridge.builder().withNodes(numberOfNodes).withIpPrefix("127.0.1.").build()) {
131+
RESOLVER_FACTORY.updateResponse(
132+
"test.cluster.fake",
133+
new ValidResponse(
134+
new InetAddress[] {
135+
getNodeInetAddress(ccmBridge, 1),
136+
getNodeInetAddress(ccmBridge, 2),
137+
getNodeInetAddress(ccmBridge, 3)
138+
}));
139+
ccmBridge.create();
140+
ccmBridge.start();
141+
session = builder.build();
142+
boolean allNodesUp = false;
143+
int nodesUp = 0;
144+
for (int i = 0; i < CLUSTER_WAIT_SECONDS; i++) {
145+
try {
146+
Collection<Node> nodes = session.getMetadata().getNodes().values();
147+
nodesUp = 0;
148+
for (Node node : nodes) {
149+
if (node.getUpSinceMillis() > 0) {
150+
nodesUp++;
151+
}
152+
}
153+
if (nodesUp == numberOfNodes) {
154+
allNodesUp = true;
155+
break;
156+
}
157+
Thread.sleep(1000);
158+
} catch (InterruptedException e) {
159+
break;
160+
}
161+
}
162+
if (!allNodesUp) {
163+
LOG.error(
164+
"Driver sees only {} nodes UP instead of {} after waiting {}s",
165+
nodesUp,
166+
numberOfNodes,
167+
CLUSTER_WAIT_SECONDS);
168+
}
86169
ResultSet rs = session.execute("SELECT * FROM system.local");
87-
List<Row> rows = rs.all();
88-
assertThat(rows).hasSize(1);
89-
LOG.trace("system.local contents: {}", rows.get(0).getFormattedContents());
170+
assertThat(rs).isNotNull();
171+
Row row = rs.one();
172+
assertThat(row).isNotNull();
90173
Collection<Node> nodes = session.getMetadata().getNodes().values();
91-
for (Node node : nodes) {
92-
LOG.trace("Found metadata node: {}", node);
174+
assertThat(nodes).hasSize(numberOfNodes);
175+
Iterator<Node> iterator = nodes.iterator();
176+
while (iterator.hasNext()) {
177+
LOG.trace("Metadata node: " + iterator.next().toString());
93178
}
94179
Set<Node> filteredNodes;
95180
filteredNodes =
96181
nodes.stream()
97-
.filter(x -> x.toString().contains("node-1.cluster.fake"))
182+
.filter(x -> x.toString().contains("test.cluster.fake"))
98183
.collect(Collectors.toSet());
99184
assertThat(filteredNodes).hasSize(1);
100-
InetSocketAddress address =
101-
(InetSocketAddress) filteredNodes.iterator().next().getEndPoint().resolve();
102-
assertTrue(address.isUnresolved());
185+
}
186+
try (CcmBridge ccmBridge =
187+
CcmBridge.builder().withNodes(numberOfNodes).withIpPrefix("127.0.1.").build()) {
188+
ccmBridge.create();
189+
ccmBridge.start();
190+
boolean allNodesUp = false;
191+
int nodesUp = 0;
192+
for (int i = 0; i < CLUSTER_WAIT_SECONDS; i++) {
193+
try {
194+
Collection<Node> nodes = session.getMetadata().getNodes().values();
195+
nodesUp = 0;
196+
for (Node node : nodes) {
197+
if (node.getUpSinceMillis() > 0) {
198+
nodesUp++;
199+
}
200+
}
201+
if (nodesUp == numberOfNodes) {
202+
allNodesUp = true;
203+
break;
204+
}
205+
Thread.sleep(1000);
206+
} catch (InterruptedException e) {
207+
break;
208+
}
209+
}
210+
if (!allNodesUp) {
211+
LOG.error(
212+
"Driver sees only {} nodes UP instead of {} after waiting {}s",
213+
nodesUp,
214+
numberOfNodes,
215+
CLUSTER_WAIT_SECONDS);
216+
}
217+
ResultSet rs = session.execute("SELECT * FROM system.local");
218+
assertThat(rs).isNotNull();
219+
Row row = rs.one();
220+
assertThat(row).isNotNull();
221+
222+
Collection<Node> nodes = session.getMetadata().getNodes().values();
223+
assertThat(nodes).hasSize(numberOfNodes);
224+
Iterator<Node> iterator = nodes.iterator();
225+
while (iterator.hasNext()) {
226+
LOG.trace("Metadata node: " + iterator.next().toString());
227+
}
228+
Set<Node> filteredNodes;
229+
filteredNodes =
230+
nodes.stream()
231+
.filter(x -> x.toString().contains("test.cluster.fake"))
232+
.collect(Collectors.toSet());
233+
if (filteredNodes.size() == 0) {
234+
LOG.error(
235+
"No metadata node with \"test.cluster.fake\" substring. The unresolved endpoint socket was likely "
236+
+ "replaced with resolved one.");
237+
} else if (filteredNodes.size() > 1) {
238+
fail(
239+
"Somehow there is more than 1 node in metadata with unresolved hostname. This should not ever happen.");
240+
}
241+
}
242+
session.close();
243+
}
244+
245+
@SuppressWarnings("unused")
246+
public void run_replace_test_20_times() {
247+
for (int i = 1; i <= 20; i++) {
248+
LOG.info(
249+
"Running ({}/20}) {}", i, MockResolverIT.class.toString() + "#replace_cluster_test()");
250+
replace_cluster_test();
103251
}
104252
}
105253

106-
private InetAddress getNodeInetAddress(CcmBridge ccmBridge, int nodeid) {
254+
private static InetAddress getNodeInetAddress(CcmBridge ccmBridge, int nodeid) {
107255
try {
108256
return InetAddress.getByName(ccmBridge.getNodeIpAddress(nodeid));
109257
} catch (UnknownHostException e) {

test-infra/src/main/java/com/datastax/oss/driver/api/testinfra/ccm/CcmBridge.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ public void reloadCore(int node, String keyspace, String table, boolean reindex)
373373
public void start() {
374374
if (started.compareAndSet(false, true)) {
375375
try {
376-
execute("start", jvmArgs, "--wait-for-binary-proto");
376+
execute("start", jvmArgs, "--wait-for-binary-proto", "--wait-other-notice");
377377
} catch (RuntimeException re) {
378378
// if something went wrong starting CCM, see if we can also dump the error
379379
executeCheckLogError();
@@ -407,9 +407,11 @@ public void start(int n) {
407407
"node" + n,
408408
"start",
409409
"--jvm_arg=-Dcassandra.allow_new_old_config_keys=true",
410-
"--jvm_arg=-Dcassandra.allow_duplicate_config_keys=false");
410+
"--jvm_arg=-Dcassandra.allow_duplicate_config_keys=false",
411+
"--wait-other-notice",
412+
"--wait-for-binary-proto");
411413
} else {
412-
execute("node" + n, "start");
414+
execute("node" + n, "start", "--wait-other-notice", "--wait-for-binary-proto");
413415
}
414416
}
415417

0 commit comments

Comments
 (0)