use of org.apache.cassandra.distributed.api.IInvokableInstance in project cassandra by apache.
the class BaseAssassinatedCase method test.
@Test
public void test() throws IOException {
TokenSupplier even = TokenSupplier.evenlyDistributedTokens(3);
try (Cluster cluster = Cluster.build(3).withConfig(c -> c.with(Feature.GOSSIP, Feature.NETWORK)).withTokenSupplier(node -> even.token(node == 4 || node == 5 ? NODE_TO_REMOVE_NUM : node)).start()) {
IInvokableInstance seed = cluster.get(SEED_NUM);
IInvokableInstance nodeToRemove = cluster.get(NODE_TO_REMOVE_NUM);
IInvokableInstance peer = cluster.get(PEER_NUM);
setupCluster(cluster);
consume(cluster, nodeToRemove);
assertRingState(seed, nodeToRemove, "Normal");
// assassinate the node
peer.nodetoolResult("assassinate", getBroadcastAddressHostWithPortString(nodeToRemove)).asserts().success();
// wait until the peer sees this assassination
awaitGossipStatus(seed, nodeToRemove, "LEFT");
// Any extra checks to run after the node has been as LEFT
afterNodeStatusIsLeft(cluster, nodeToRemove);
// allow replacing nodes with the LEFT state, this should fail since the token isn't in the ring
assertThatThrownBy(() -> replaceHostAndStart(cluster, nodeToRemove, properties -> {
// since there are downed nodes its possible gossip has the downed node with an old schema, so need
// this property to allow startup
properties.set(BOOTSTRAP_SKIP_SCHEMA_CHECK, true);
// since the bootstrap should fail because the token, don't wait "too long" on schema as it doesn't
// matter for this test
properties.set(BOOTSTRAP_SCHEMA_DELAY_MS, 10);
})).hasMessage(expectedMessage(nodeToRemove));
}
}
use of org.apache.cassandra.distributed.api.IInvokableInstance in project cassandra by apache.
the class HostReplacementOfDownedClusterTest method hostReplacementOfDeadNodeAndOtherNodeStartsAfter.
/**
* Cluster stops completely, then start seed, then host replace node2; after all complete start node3 to make sure
* it comes up correctly with the new host in the ring.
*/
@Test
public void hostReplacementOfDeadNodeAndOtherNodeStartsAfter() throws IOException {
// start with 3 nodes, stop both nodes, start the seed, host replace the down node)
int numStartNodes = 3;
TokenSupplier even = TokenSupplier.evenlyDistributedTokens(numStartNodes);
try (Cluster cluster = Cluster.build(numStartNodes).withConfig(c -> c.with(Feature.GOSSIP, Feature.NETWORK)).withTokenSupplier(node -> even.token(node == (numStartNodes + 1) ? 2 : node)).start()) {
IInvokableInstance seed = cluster.get(1);
IInvokableInstance nodeToRemove = cluster.get(2);
IInvokableInstance nodeToStartAfterReplace = cluster.get(3);
InetSocketAddress addressToReplace = nodeToRemove.broadcastAddress();
setupCluster(cluster);
// collect rows/tokens to detect issues later on if the state doesn't match
SimpleQueryResult expectedState = nodeToRemove.coordinator().executeWithResult("SELECT * FROM " + KEYSPACE + ".tbl", ConsistencyLevel.ALL);
List<String> beforeCrashTokens = getTokenMetadataTokens(seed);
// now stop all nodes
stopAll(cluster);
// with all nodes down, now start the seed (should be first node)
seed.startup();
// at this point node2 should be known in gossip, but with generation/version of 0
assertGossipInfo(seed, addressToReplace, 0, -1);
// make sure node1 still has node2's tokens
List<String> currentTokens = getTokenMetadataTokens(seed);
Assertions.assertThat(currentTokens).as("Tokens no longer match after restarting").isEqualTo(beforeCrashTokens);
// now create a new node to replace the other node
IInvokableInstance replacingNode = replaceHostAndStart(cluster, nodeToRemove);
// wait till the replacing node is in the ring
awaitRingJoin(seed, replacingNode);
awaitRingJoin(replacingNode, seed);
// we see that the replaced node is properly in the ring, now lets add the other node back
nodeToStartAfterReplace.startup();
awaitRingJoin(seed, nodeToStartAfterReplace);
awaitRingJoin(replacingNode, nodeToStartAfterReplace);
// make sure all nodes are healthy
awaitRingHealthy(seed);
assertRingIs(seed, seed, replacingNode, nodeToStartAfterReplace);
assertRingIs(replacingNode, seed, replacingNode, nodeToStartAfterReplace);
logger.info("Current ring is {}", assertRingIs(nodeToStartAfterReplace, seed, replacingNode, nodeToStartAfterReplace));
validateRows(seed.coordinator(), expectedState);
validateRows(replacingNode.coordinator(), expectedState);
}
}
use of org.apache.cassandra.distributed.api.IInvokableInstance in project cassandra by apache.
the class HostReplacementOfDownedClusterTest method hostReplacementOfDeadNode.
/**
* When the full cluster crashes, make sure that we can replace a dead node after recovery. This can happen
* with DC outages (assuming single DC setup) where the recovery isn't able to recover a specific node.
*/
@Test
public void hostReplacementOfDeadNode() throws IOException {
// start with 2 nodes, stop both nodes, start the seed, host replace the down node)
TokenSupplier even = TokenSupplier.evenlyDistributedTokens(2);
try (Cluster cluster = Cluster.build(2).withConfig(c -> c.with(Feature.GOSSIP, Feature.NETWORK)).withTokenSupplier(node -> even.token(node == 3 ? 2 : node)).start()) {
IInvokableInstance seed = cluster.get(1);
IInvokableInstance nodeToRemove = cluster.get(2);
InetSocketAddress addressToReplace = nodeToRemove.broadcastAddress();
setupCluster(cluster);
// collect rows/tokens to detect issues later on if the state doesn't match
SimpleQueryResult expectedState = nodeToRemove.coordinator().executeWithResult("SELECT * FROM " + KEYSPACE + ".tbl", ConsistencyLevel.ALL);
List<String> beforeCrashTokens = getTokenMetadataTokens(seed);
// now stop all nodes
stopAll(cluster);
// with all nodes down, now start the seed (should be first node)
seed.startup();
// at this point node2 should be known in gossip, but with generation/version of 0
assertGossipInfo(seed, addressToReplace, 0, -1);
// make sure node1 still has node2's tokens
List<String> currentTokens = getTokenMetadataTokens(seed);
Assertions.assertThat(currentTokens).as("Tokens no longer match after restarting").isEqualTo(beforeCrashTokens);
// now create a new node to replace the other node
IInvokableInstance replacingNode = replaceHostAndStart(cluster, nodeToRemove);
awaitRingJoin(seed, replacingNode);
awaitRingJoin(replacingNode, seed);
assertNotInRing(seed, nodeToRemove);
logger.info("Current ring is {}", assertNotInRing(replacingNode, nodeToRemove));
validateRows(seed.coordinator(), expectedState);
validateRows(replacingNode.coordinator(), expectedState);
}
}
use of org.apache.cassandra.distributed.api.IInvokableInstance in project cassandra by apache.
the class NodeCannotJoinAsHibernatingNodeWithoutReplaceAddressTest method test.
@Test
public void test() throws IOException, InterruptedException {
TokenSupplier even = TokenSupplier.evenlyDistributedTokens(2);
try (Cluster cluster = init(Cluster.build(2).withConfig(c -> c.with(Feature.values()).set(Constants.KEY_DTEST_API_STARTUP_FAILURE_AS_SHUTDOWN, false)).withInstanceInitializer(BBHelper::install).withTokenSupplier(node -> even.token((node == 3 || node == 4) ? 2 : node)).start())) {
final IInvokableInstance toReplace = cluster.get(2);
final String toReplaceAddress = toReplace.broadcastAddress().getAddress().getHostAddress();
SharedState.cluster = cluster;
// ignore host replacement errors
cluster.setUncaughtExceptionsFilter((nodeId, cause) -> nodeId > 2);
fixDistributedSchemas(cluster);
ClusterUtils.stopUnchecked(toReplace);
try {
ClusterUtils.replaceHostAndStart(cluster, toReplace, (inst, ignore) -> ClusterUtils.updateAddress(inst, toReplaceAddress));
Assert.fail("Host replacement should exit with an error");
} catch (Exception e) {
// the instance is expected to fail, but it may not have finished shutdown yet, so wait for it to shutdown
SharedState.shutdownComplete.await(1, TimeUnit.MINUTES);
}
IInvokableInstance inst = ClusterUtils.addInstance(cluster, toReplace.config(), c -> c.set("auto_bootstrap", true));
ClusterUtils.updateAddress(inst, toReplaceAddress);
Assertions.assertThatThrownBy(() -> inst.startup()).hasMessageContaining("A node with address").hasMessageContaining("already exists, cancelling join");
}
}
use of org.apache.cassandra.distributed.api.IInvokableInstance in project cassandra by apache.
the class ReprepareNewBehaviourTest method testUseWithMultipleKeyspaces.
@Test
public void testUseWithMultipleKeyspaces() throws Throwable {
try (ICluster<IInvokableInstance> c = init(builder().withNodes(1).withConfig(config -> config.with(GOSSIP, NETWORK, NATIVE_PROTOCOL)).start())) {
try (com.datastax.driver.core.Cluster cluster = com.datastax.driver.core.Cluster.builder().addContactPoint("127.0.0.1").build();
Session session = cluster.connect()) {
c.schemaChange(withKeyspace("CREATE KEYSPACE ks1 WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};"));
c.schemaChange(withKeyspace("CREATE TABLE ks1.tbl (pk int, ck int, v int, PRIMARY KEY (pk, ck));"));
c.schemaChange(withKeyspace("CREATE KEYSPACE ks2 WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};"));
c.schemaChange(withKeyspace("CREATE TABLE ks2.tbl (pk int, ck int, v int, PRIMARY KEY (pk, ck));"));
String query = "SELECT * FROM tbl";
session.execute("USE ks1");
PreparedStatement selectKs1 = session.prepare(query);
// Insert explicitly into the ks2 version of table...
session.execute("INSERT INTO ks2.tbl (pk, ck, v) VALUES (1, 1, 1)");
// There should be nothing in the ks1 version...
ResultSet resultsKs1 = session.execute(selectKs1.bind());
Assert.assertEquals(0, resultsKs1.all().size());
session.execute("USE ks2");
// ... but after switching to use ks2, a new query prepared against tbl should return a result.
PreparedStatement selectKs2 = session.prepare(query);
Assert.assertEquals("ks2", selectKs2.getQueryKeyspace());
ResultSet resultsKs2 = session.execute(selectKs2.bind());
Assert.assertEquals(1, resultsKs2.all().size());
resultsKs1 = session.execute(selectKs1.bind());
Assert.assertEquals(0, resultsKs1.all().size());
}
}
}
Aggregations