private static boolean attemptToCreateKeyspaceOnHost(InetSocketAddress host, CassandraKeyValueServiceConfig config) throws TException { try { CassandraClient client = CassandraClientFactory.getClientInternal(host, config); KsDef ksDef = createKsDefForFresh(client, config); client.system_add_keyspace(ksDef); log.info("Created keyspace: {}", SafeArg.of("keyspace", config.getKeyspaceOrThrow())); CassandraKeyValueServices.waitForSchemaVersions(config, client, "after adding the initial empty keyspace"); return true; } catch (InvalidRequestException e) { return keyspaceAlreadyExists(host, config); } }
private boolean doesConfigReplicationFactorMatchWithCluster() { return clientPool.runWithRetry(client -> { try { CassandraVerifier.currentRfOnKeyspaceMatchesDesiredRf(client, config); return true; } catch (Exception e) { log.warn("The config and Cassandra cluster do not agree on the replication factor.", e); return false; } }); }
static KsDef checkAndSetReplicationFactor(CassandraClient client, KsDef ksDef, CassandraKeyValueServiceConfig config) throws TException { KsDef result = ksDef; Set<String> datacenters; if (Objects.equals(result.getStrategy_class(), CassandraConstants.SIMPLE_STRATEGY)) { datacenters = getDcForSimpleStrategy(client, result, config); result = setNetworkStrategyIfCheckedTopology(result, config, datacenters); } else { datacenters = sanityCheckDatacenters(client, config); } sanityCheckReplicationFactor(result, config, datacenters); return result; }
private static Set<String> getDcForSimpleStrategy(CassandraClient client, KsDef ksDef, CassandraKeyValueServiceConfig config) throws TException { checkKsDefRfEqualsOne(ksDef, config); Set<String> datacenters = sanityCheckDatacenters(client, config); checkOneDatacenter(config, datacenters); return datacenters; }
static void currentRfOnKeyspaceMatchesDesiredRf(CassandraClient client, CassandraKeyValueServiceConfig config) throws TException { KsDef ks = client.describe_keyspace(config.getKeyspaceOrThrow()); Set<String> dcs = sanityCheckDatacenters(client, config); sanityCheckReplicationFactor(ks, config, dcs); }
static Set<String> sanityCheckDatacenters(CassandraClient client, CassandraKeyValueServiceConfig config) throws TException { createSimpleRfTestKeyspaceIfNotExists(client); Multimap<String, String> datacenterToRack = HashMultimap.create(); Set<String> hosts = Sets.newHashSet(); for (TokenRange tokenRange : client.describe_ring(CassandraConstants.SIMPLE_RF_TEST_KEYSPACE)) { for (EndpointDetails details : tokenRange.getEndpoint_details()) { datacenterToRack.put(details.datacenter, details.rack); hosts.add(details.host); } } if (clusterHasExactlyOneDatacenter(datacenterToRack) && config.replicationFactor() > 1) { checkNodeTopologyIsSet(config, datacenterToRack); checkMoreRacksThanRfOrFewerHostsThanRf(config, hosts, datacenterToRack); } return datacenterToRack.keySet(); }
static KsDef createKsDefForFresh(CassandraClient client, CassandraKeyValueServiceConfig config) throws TException { KsDef ksDef = new KsDef(config.getKeyspaceOrThrow(), CassandraConstants.NETWORK_STRATEGY, ImmutableList.of()); Set<String> dcs = sanityCheckDatacenters(client, config); ksDef.setStrategy_options(Maps.asMap(dcs, ignore -> String.valueOf(config.replicationFactor()))); ksDef.setDurable_writes(true); return ksDef; }
@Test public void simpleStrategyOneDcHighRfThrows() throws TException { KsDef ksDef = new KsDef("test", CassandraConstants.SIMPLE_STRATEGY, ImmutableList.of()); ksDef.setStrategy_options(ImmutableMap.of(CassandraConstants.REPLICATION_FACTOR_OPTION, "3")); assertThatThrownBy(() -> CassandraVerifier.checkAndSetReplicationFactor(client, ksDef, config)) .isInstanceOf(IllegalStateException.class); }
private static boolean attemptToCreateIfNotExists(CassandraKeyValueServiceConfig config, InetSocketAddress host) { try { return keyspaceAlreadyExists(host, config) || attemptToCreateKeyspaceOnHost(host, config); } catch (Exception exception) { log.warn("Couldn't use host {} to create keyspace." + " It returned exception \"{}\" during the attempt." + " We will retry on other nodes, so this shouldn't be a problem unless all nodes failed." + " See the debug-level log for the stack trace.", SafeArg.of("host", CassandraLogHelper.host(host)), UnsafeArg.of("exceptionMessage", exception.toString())); log.debug("Specifically, creating the keyspace failed with the following stack trace", exception); return false; } }
@Test public void noStrategyForDcThrows() { KsDef ksDef = new KsDef("test", CassandraConstants.SIMPLE_STRATEGY, ImmutableList.of()); ksDef.setStrategy_options(ImmutableMap.of(DC_1, "1")); assertThatThrownBy(() -> CassandraVerifier.sanityCheckReplicationFactor(ksDef, config, ImmutableSet.of(DC_1, DC_2))) .isInstanceOf(IllegalStateException.class); }
@Test public void freshInstanceSetsStrategyOptions() throws TException { setTopology(createDetails(DC_1, RACK_1, HOST_1), createDetails(DC_1, RACK_1, HOST_2), createDetails(DC_1, RACK_1, HOST_3), createDetails(DC_2, RACK_1, HOST_4)); when(config.replicationFactor()).thenReturn(3); KsDef ksDef = CassandraVerifier.createKsDefForFresh(client, config); assertThat(ksDef.strategy_options).isEqualTo(ImmutableMap.of(DC_1, "3", DC_2, "3")); }
private static void createKeyspace(CassandraKeyValueServiceConfig config) throws TException { // We can't use the pool yet because it does things like setting the connection's keyspace for us if (!attemptToCreateKeyspace(config)) { throw new TException("No host tried was able to create the keyspace requested."); } }
private static boolean attemptToCreateKeyspace(CassandraKeyValueServiceConfig config) { return config.servers().stream().anyMatch(host -> attemptToCreateIfNotExists(config, host)); }
static Set<String> sanityCheckDatacenters(CassandraClient client, CassandraKeyValueServiceConfig config) throws TException { createSimpleRfTestKeyspaceIfNotExists(client); Multimap<String, String> datacenterToRack = HashMultimap.create(); Set<String> hosts = Sets.newHashSet(); for (TokenRange tokenRange : client.describe_ring(CassandraConstants.SIMPLE_RF_TEST_KEYSPACE)) { for (EndpointDetails details : tokenRange.getEndpoint_details()) { datacenterToRack.put(details.datacenter, details.rack); hosts.add(details.host); } } if (clusterHasExactlyOneDatacenter(datacenterToRack) && config.replicationFactor() > 1) { checkNodeTopologyIsSet(config, datacenterToRack); checkMoreRacksThanRfOrFewerHostsThanRf(config, hosts, datacenterToRack); } return datacenterToRack.keySet(); }
private void lowerConsistencyWhenSafe() { Set<String> dcs; Map<String, String> strategyOptions; try { dcs = clientPool.runWithRetry(client -> CassandraVerifier.sanityCheckDatacenters( client, config)); KsDef ksDef = clientPool.runWithRetry(client -> client.describe_keyspace(config.getKeyspaceOrThrow())); strategyOptions = Maps.newHashMap(ksDef.getStrategy_options()); if (dcs.size() == 1) { String dc = dcs.iterator().next(); if (strategyOptions.get(dc) != null) { int currentRf = Integer.parseInt(strategyOptions.get(dc)); if (currentRf == config.replicationFactor()) { if (currentRf == 2 && config.clusterMeetsNormalConsistencyGuarantees()) { log.info("Setting Read Consistency to ONE, as cluster has only one datacenter at RF2."); readConsistency = ConsistencyLevel.ONE; rangeLoader.setConsistencyLevel(readConsistency); } } } } } catch (TException e) { return; } }
private static Set<String> getDcForSimpleStrategy(CassandraClient client, KsDef ksDef, CassandraKeyValueServiceConfig config) throws TException { checkKsDefRfEqualsOne(ksDef, config); Set<String> datacenters = sanityCheckDatacenters(client, config); checkOneDatacenter(config, datacenters); return datacenters; }
static void currentRfOnKeyspaceMatchesDesiredRf(CassandraClient client, CassandraKeyValueServiceConfig config) throws TException { KsDef ks = client.describe_keyspace(config.getKeyspaceOrThrow()); Set<String> dcs = sanityCheckDatacenters(client, config); sanityCheckReplicationFactor(ks, config, dcs); }
@Test public void simpleStrategyOneDcOneRfSucceedsAndUpdatesKsDef() throws TException { setTopology(createDetails(DC_1, RACK_1, HOST_1)); when(config.replicationFactor()).thenReturn(1); KsDef ksDef = new KsDef("test", CassandraConstants.SIMPLE_STRATEGY, ImmutableList.of()); ImmutableMap<String, String> strategyOptions = ImmutableMap .of(CassandraConstants.REPLICATION_FACTOR_OPTION, "1"); ksDef.setStrategy_options(strategyOptions); ksDef = CassandraVerifier.checkAndSetReplicationFactor(client, ksDef, config); assertThat(ksDef.strategy_class).isEqualTo(CassandraConstants.NETWORK_STRATEGY); assertThat(ksDef.strategy_options).isEqualTo(ImmutableMap.of(DC_1, "1")); }
private static boolean attemptToCreateIfNotExists(CassandraKeyValueServiceConfig config, InetSocketAddress host) { try { return keyspaceAlreadyExists(host, config) || attemptToCreateKeyspaceOnHost(host, config); } catch (Exception exception) { log.warn("Couldn't use host {} to create keyspace." + " It returned exception \"{}\" during the attempt." + " We will retry on other nodes, so this shouldn't be a problem unless all nodes failed." + " See the debug-level log for the stack trace.", SafeArg.of("host", CassandraLogHelper.host(host)), UnsafeArg.of("exceptionMessage", exception.toString())); log.debug("Specifically, creating the keyspace failed with the following stack trace", exception); return false; } }
@Test public void differentRfThanConfigThrows() { KsDef ksDef = new KsDef("test", CassandraConstants.SIMPLE_STRATEGY, ImmutableList.of()); ksDef.setStrategy_options(ImmutableMap.of(DC_1, "1", DC_2, "2")); when(config.replicationFactor()).thenReturn(1); assertThatThrownBy(() -> CassandraVerifier.sanityCheckReplicationFactor(ksDef, config, ImmutableSortedSet.of(DC_1, DC_2))) .isInstanceOf(UnsupportedOperationException.class); }