DATACASS-502 - Add support for keyspace alteration on cluster initialization.
We now support keyspace alteration using XML configuration during after CassandraClusterFactoryBean initialization. Previously, ALTER keyspace actions resulted in IllegalStateException.
This commit is contained in:
@@ -34,13 +34,16 @@ import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.cassandra.core.cql.CassandraExceptionTranslator;
|
||||
import org.springframework.data.cassandra.core.cql.CqlTemplate;
|
||||
import org.springframework.data.cassandra.core.cql.generator.AlterKeyspaceCqlGenerator;
|
||||
import org.springframework.data.cassandra.core.cql.generator.CreateKeyspaceCqlGenerator;
|
||||
import org.springframework.data.cassandra.core.cql.generator.DropKeyspaceCqlGenerator;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.AlterKeyspaceSpecification;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.DropKeyspaceSpecification;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceActionSpecification;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -131,6 +134,7 @@ public class CassandraClusterFactoryBean
|
||||
private @Nullable LatencyTracker latencyTracker;
|
||||
|
||||
private List<CreateKeyspaceSpecification> keyspaceCreations = new ArrayList<>();
|
||||
private List<AlterKeyspaceSpecification> keyspaceAlterations = new ArrayList<>();
|
||||
private List<DropKeyspaceSpecification> keyspaceDrops = new ArrayList<>();
|
||||
private Set<KeyspaceActionSpecification> keyspaceSpecifications = new HashSet<>();
|
||||
private List<KeyspaceActions> keyspaceActions = new ArrayList<>();
|
||||
@@ -219,7 +223,12 @@ public class CassandraClusterFactoryBean
|
||||
Optional.ofNullable(latencyTracker).ifPresent(cluster::register);
|
||||
|
||||
generateSpecificationsFromFactoryBeans();
|
||||
executeSpecsAndScripts(keyspaceCreations, startupScripts, cluster);
|
||||
|
||||
List<KeyspaceActionSpecification> startup = new ArrayList<>(keyspaceCreations.size() + keyspaceAlterations.size());
|
||||
startup.addAll(keyspaceCreations);
|
||||
startup.addAll(keyspaceAlterations);
|
||||
|
||||
executeSpecsAndScripts(startup, startupScripts, cluster);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -307,6 +316,10 @@ public class CassandraClusterFactoryBean
|
||||
if (keyspaceActionSpecification instanceof DropKeyspaceSpecification) {
|
||||
keyspaceDrops.add((DropKeyspaceSpecification) keyspaceActionSpecification);
|
||||
}
|
||||
|
||||
if (keyspaceActionSpecification instanceof AlterKeyspaceSpecification) {
|
||||
keyspaceAlterations.add((AlterKeyspaceSpecification) keyspaceActionSpecification);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -332,11 +345,22 @@ public class CassandraClusterFactoryBean
|
||||
}
|
||||
}
|
||||
|
||||
private String toCql(KeyspaceActionSpecification keyspaceActionSpecification) {
|
||||
private String toCql(KeyspaceActionSpecification specification) {
|
||||
|
||||
return (keyspaceActionSpecification instanceof CreateKeyspaceSpecification
|
||||
? new CreateKeyspaceCqlGenerator((CreateKeyspaceSpecification) keyspaceActionSpecification).toCql()
|
||||
: new DropKeyspaceCqlGenerator((DropKeyspaceSpecification) keyspaceActionSpecification).toCql());
|
||||
if (specification instanceof CreateKeyspaceSpecification) {
|
||||
return new CreateKeyspaceCqlGenerator((CreateKeyspaceSpecification) specification).toCql();
|
||||
}
|
||||
|
||||
if (specification instanceof DropKeyspaceSpecification) {
|
||||
return new DropKeyspaceCqlGenerator((DropKeyspaceSpecification) specification).toCql();
|
||||
}
|
||||
|
||||
if (specification instanceof AlterKeyspaceSpecification) {
|
||||
return new AlterKeyspaceCqlGenerator((AlterKeyspaceSpecification) specification).toCql();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
"Unsupported specification type: " + ClassUtils.getQualifiedName(specification.getClass()));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -23,10 +23,12 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.cassandra.core.cql.KeyspaceIdentifier;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.AlterKeyspaceSpecification;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.DataCenterReplication;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.DefaultOption;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.DropKeyspaceSpecification;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceActionSpecification;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceOption;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceOption.ReplicationStrategy;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.Option;
|
||||
@@ -83,25 +85,77 @@ class KeyspaceActionSpecificationFactory {
|
||||
CreateKeyspaceSpecification create = CreateKeyspaceSpecification.createKeyspace(name).ifNotExists(ifNotExists)
|
||||
.with(KeyspaceOption.DURABLE_WRITES, durableWrites);
|
||||
|
||||
Map<Option, Object> replicationStrategyMap = new HashMap<>();
|
||||
replicationStrategyMap.put(new DefaultOption("class", String.class, true, false, true),
|
||||
replicationStrategy.getValue());
|
||||
Map<Option, Object> replication = getReplication();
|
||||
|
||||
if (replicationStrategy == ReplicationStrategy.SIMPLE_STRATEGY) {
|
||||
replicationStrategyMap.put(new DefaultOption("replication_factor", Long.class, true, false, false),
|
||||
replicationFactor);
|
||||
if (!replication.isEmpty()) {
|
||||
create.with(KeyspaceOption.REPLICATION, replication);
|
||||
}
|
||||
|
||||
if (replicationStrategy == ReplicationStrategy.NETWORK_TOPOLOGY_STRATEGY) {
|
||||
for (DataCenterReplication datacenter : replications) {
|
||||
replicationStrategyMap.put(new DefaultOption(datacenter.getDataCenter(), Long.class, true, false, false),
|
||||
datacenter.getReplicationFactor());
|
||||
return create;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a {@link AlterKeyspaceSpecification} for the keyspace.
|
||||
*
|
||||
* @return the {@link AlterKeyspaceSpecification}.
|
||||
* @since 2.0.1
|
||||
*/
|
||||
public KeyspaceActionSpecification alter() {
|
||||
|
||||
AlterKeyspaceSpecification alter = AlterKeyspaceSpecification.alterKeyspace(name)
|
||||
.with(KeyspaceOption.DURABLE_WRITES, durableWrites);
|
||||
|
||||
Map<Option, Object> replication = getReplication();
|
||||
|
||||
if (!replication.isEmpty()) {
|
||||
alter.with(KeyspaceOption.REPLICATION, replication);
|
||||
}
|
||||
|
||||
return alter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create replication options represented as {@link Map}.
|
||||
*
|
||||
* @return the replication options represented as {@link Map}.
|
||||
* @since 2.0.1
|
||||
*/
|
||||
protected Map<Option, Object> getReplication() {
|
||||
|
||||
Map<Option, Object> replicationStrategyMap = new HashMap<>();
|
||||
|
||||
if (hasReplicationOptions()) {
|
||||
|
||||
replicationStrategyMap.put(new DefaultOption("class", String.class, true, false, true),
|
||||
replicationStrategy.getValue());
|
||||
|
||||
if (replicationStrategy == ReplicationStrategy.SIMPLE_STRATEGY) {
|
||||
replicationStrategyMap.put(new DefaultOption("replication_factor", Long.class, true, false, false),
|
||||
replicationFactor);
|
||||
}
|
||||
|
||||
if (replicationStrategy == ReplicationStrategy.NETWORK_TOPOLOGY_STRATEGY) {
|
||||
for (DataCenterReplication datacenter : replications) {
|
||||
replicationStrategyMap.put(new DefaultOption(datacenter.getDataCenter(), Long.class, true, false, false),
|
||||
datacenter.getReplicationFactor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create.with(KeyspaceOption.REPLICATION, replicationStrategyMap);
|
||||
return replicationStrategyMap;
|
||||
}
|
||||
|
||||
return create;
|
||||
private boolean hasReplicationOptions() {
|
||||
|
||||
if (replicationStrategy == ReplicationStrategy.SIMPLE_STRATEGY && replicationFactor > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (replicationStrategy == ReplicationStrategy.NETWORK_TOPOLOGY_STRATEGY && !replications.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,8 +63,8 @@ public class KeyspaceActionSpecificationFactoryBean implements FactoryBean<Keysp
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
Assert.hasText(name, "Keyspace Name is required for a Keyspace Action");
|
||||
Assert.notNull(action, "Keyspace Action is required for a Keyspace Action");
|
||||
Assert.hasText(name, "Keyspace name is required for a keyspace action");
|
||||
Assert.notNull(action, "Keyspace action is required for a keyspace action");
|
||||
|
||||
KeyspaceActionSpecificationFactoryBuilder builder = KeyspaceActionSpecificationFactory.builder(name)
|
||||
.durableWrites(durableWrites);
|
||||
@@ -84,16 +84,20 @@ public class KeyspaceActionSpecificationFactoryBean implements FactoryBean<Keysp
|
||||
|
||||
KeyspaceActionSpecificationFactory factory = builder.build();
|
||||
|
||||
this.actions = createActions(factory);
|
||||
}
|
||||
|
||||
private KeyspaceActions createActions(KeyspaceActionSpecificationFactory factory) {
|
||||
|
||||
switch (action) {
|
||||
case NONE:
|
||||
this.actions = new KeyspaceActions();
|
||||
break;
|
||||
return new KeyspaceActions();
|
||||
case CREATE_DROP:
|
||||
this.actions = new KeyspaceActions(factory.create(ifNotExists), factory.drop(ifNotExists));
|
||||
break;
|
||||
return new KeyspaceActions(factory.create(ifNotExists), factory.drop(ifNotExists));
|
||||
case CREATE:
|
||||
this.actions = new KeyspaceActions(factory.create(ifNotExists));
|
||||
break;
|
||||
return new KeyspaceActions(factory.create(ifNotExists));
|
||||
case ALTER:
|
||||
return new KeyspaceActions(factory.alter());
|
||||
default:
|
||||
throw new IllegalStateException(String.format("KeyspaceAction %s not supported", action));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.cassandra.config;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.cassandra.core.cql.KeyspaceIdentifier;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.AlterKeyspaceSpecification;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceActionSpecification;
|
||||
import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceOption.ReplicationStrategy;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link KeyspaceActionSpecificationFactoryBean}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class KeyspaceActionSpecificationFactoryBeanUnitTests {
|
||||
|
||||
KeyspaceActionSpecificationFactoryBean bean = new KeyspaceActionSpecificationFactoryBean();
|
||||
|
||||
@Test // DATACASS-502
|
||||
public void shouldAlterKeyspace() {
|
||||
|
||||
bean.setAction(KeyspaceAction.ALTER);
|
||||
bean.setDurableWrites(true);
|
||||
bean.setName("my_keyspace");
|
||||
bean.setNetworkTopologyDataCenters(Arrays.asList("foo", "bar"));
|
||||
bean.setNetworkTopologyReplicationFactors(Arrays.asList("1", "2"));
|
||||
bean.setReplicationStrategy(ReplicationStrategy.NETWORK_TOPOLOGY_STRATEGY);
|
||||
|
||||
bean.afterPropertiesSet();
|
||||
|
||||
List<KeyspaceActionSpecification> actions = bean.getObject().getActions();
|
||||
|
||||
assertThat(actions).hasSize(1).hasAtLeastOneElementOfType(AlterKeyspaceSpecification.class);
|
||||
|
||||
AlterKeyspaceSpecification alter = (AlterKeyspaceSpecification) actions.get(0);
|
||||
|
||||
assertThat(alter.getName()).isEqualTo(KeyspaceIdentifier.of("my_keyspace"));
|
||||
assertThat(alter.getOptions()).containsKeys("durable_writes", "replication");
|
||||
}
|
||||
|
||||
@Test // DATACASS-502
|
||||
public void shouldAlterKeyspaceWithSimpleReplication() {
|
||||
|
||||
bean.setAction(KeyspaceAction.ALTER);
|
||||
bean.setDurableWrites(true);
|
||||
bean.setName("my_keyspace");
|
||||
bean.setReplicationFactor(5);
|
||||
|
||||
bean.afterPropertiesSet();
|
||||
|
||||
List<KeyspaceActionSpecification> actions = bean.getObject().getActions();
|
||||
|
||||
assertThat(actions).hasSize(1).hasAtLeastOneElementOfType(AlterKeyspaceSpecification.class);
|
||||
|
||||
AlterKeyspaceSpecification alter = (AlterKeyspaceSpecification) actions.get(0);
|
||||
|
||||
assertThat(alter.getName()).isEqualTo(KeyspaceIdentifier.of("my_keyspace"));
|
||||
assertThat(alter.getOptions()).containsKeys("durable_writes", "replication");
|
||||
}
|
||||
|
||||
@Test // DATACASS-502
|
||||
public void shouldAlterKeyspaceWithoutReplication() {
|
||||
|
||||
bean.setAction(KeyspaceAction.ALTER);
|
||||
bean.setDurableWrites(true);
|
||||
bean.setName("my_keyspace");
|
||||
bean.afterPropertiesSet();
|
||||
|
||||
List<KeyspaceActionSpecification> actions = bean.getObject().getActions();
|
||||
|
||||
assertThat(actions).hasSize(1).hasAtLeastOneElementOfType(AlterKeyspaceSpecification.class);
|
||||
|
||||
AlterKeyspaceSpecification alter = (AlterKeyspaceSpecification) actions.get(0);
|
||||
|
||||
assertThat(alter.getName()).isEqualTo(KeyspaceIdentifier.of("my_keyspace"));
|
||||
assertThat(alter.getOptions()).doesNotContainKeys("replication");
|
||||
}
|
||||
}
|
||||
@@ -20,11 +20,15 @@
|
||||
<cass:data-center name="bar" replication-factor="2" />
|
||||
</cass:replication>
|
||||
</cass:keyspace>
|
||||
<cass:keyspace action="CREATE_DROP" durable-writes="true"
|
||||
name="full2">
|
||||
<cass:replication class="SIMPLE_STRATEGY">
|
||||
<cass:data-center name="foo" replication-factor="1" />
|
||||
<cass:data-center name="bar" replication-factor="2" />
|
||||
<cass:keyspace action="CREATE_DROP" durable-writes="false"
|
||||
name="full2">
|
||||
<cass:replication class="SIMPLE_STRATEGY" replication-factor="2"/>
|
||||
</cass:keyspace>
|
||||
<cass:keyspace action="ALTER" durable-writes="true"
|
||||
name="full2">
|
||||
<cass:replication class="NETWORK_TOPOLOGY_STRATEGY">
|
||||
<cass:data-center name="foo" replication-factor="4"/>
|
||||
<cass:data-center name="bar" replication-factor="4"/>
|
||||
</cass:replication>
|
||||
</cass:keyspace>
|
||||
<cass:startup-cql><![CDATA[
|
||||
|
||||
Reference in New Issue
Block a user