Commit 7a64b3f7 authored by Stephane Nicoll's avatar Stephane Nicoll

Restore Cassandra port option

This commit restores the port option that was removed in an earlier
milestone. Contact points that do not define a port already are
automatically transformed to include the one configured, with a default
matching Cassandra's default port.

This makes upgrades easier in the case a cluster uses consistent ports
everywhere.

Closes gh-19672
parent a4b3d9a8
......@@ -21,6 +21,8 @@ import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.net.ssl.SSLContext;
......@@ -114,7 +116,7 @@ public class CassandraAutoConfiguration {
mapQueryOptions(properties, options);
mapSocketOptions(properties, options);
mapPoolingOptions(properties, options);
map.from(properties::getContactPoints)
map.from(mapContactPoints(properties))
.to((contactPoints) -> options.add(DefaultDriverOption.CONTACT_POINTS, contactPoints));
map.from(properties.getLocalDatacenter()).to(
(localDatacenter) -> options.add(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER, localDatacenter));
......@@ -152,6 +154,29 @@ public class CassandraAutoConfiguration {
.to((maxQueueSize) -> options.add(DefaultDriverOption.REQUEST_THROTTLER_MAX_QUEUE_SIZE, maxQueueSize));
}
private List<String> mapContactPoints(CassandraProperties properties) {
return properties.getContactPoints().stream()
.map((candidate) -> formatContactPoint(candidate, properties.getPort())).collect(Collectors.toList());
}
private String formatContactPoint(String candidate, int port) {
int i = candidate.lastIndexOf(':');
if (i == -1 || !isPort(() -> candidate.substring(i + 1))) {
return String.format("%s:%s", candidate, port);
}
return candidate;
}
private boolean isPort(Supplier<String> value) {
try {
int i = Integer.parseInt(value.get());
return i > 0 && i < 65535;
}
catch (Exception ex) {
return false;
}
}
private static class CassandraDriverOptions {
private final Map<String, String> options = new LinkedHashMap<>();
......
......@@ -51,10 +51,16 @@ public class CassandraProperties {
private String sessionName;
/**
* Cluster node addresses in the form 'host:port'.
* Cluster node addresses in the form 'host:port', or a simple 'host' to use the
* configured port.
*/
private final List<String> contactPoints = new ArrayList<>(Collections.singleton("127.0.0.1:9042"));
/**
* Port to use if a contact point does not specify one.
*/
private int port = 9042;
/**
* Datacenter that is considered "local". Contact points should be from this
* datacenter.
......@@ -147,6 +153,14 @@ public class CassandraProperties {
return this.contactPoints;
}
public int getPort() {
return this.port;
}
public void setPort(int port) {
this.port = port;
}
public String getLocalDatacenter() {
return this.localDatacenter;
}
......
......@@ -21,9 +21,6 @@ import com.datastax.oss.driver.api.core.CqlSessionBuilder;
import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
import com.datastax.oss.driver.api.core.config.DriverExecutionProfile;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigParseOptions;
import com.typesafe.config.impl.Parseable;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
......@@ -78,6 +75,38 @@ class CassandraAutoConfigurationTests {
});
}
@Test
void driverConfigLoaderWithContactPointAndNoPort() {
this.contextRunner
.withPropertyValues("spring.data.cassandra.contact-points=cluster.example.com,another.example.com:9041",
"spring.data.cassandra.local-datacenter=cassandra-eu1")
.run((context) -> {
assertThat(context).hasSingleBean(DriverConfigLoader.class);
DriverExecutionProfile configuration = context.getBean(DriverConfigLoader.class).getInitialConfig()
.getDefaultProfile();
assertThat(configuration.getStringList(DefaultDriverOption.CONTACT_POINTS))
.containsOnly("cluster.example.com:9042", "another.example.com:9041");
assertThat(configuration.getString(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER))
.isEqualTo("cassandra-eu1");
});
}
@Test
void driverConfigLoaderWithContactPointAndNoPortAndCustomPort() {
this.contextRunner
.withPropertyValues("spring.data.cassandra.contact-points=cluster.example.com:9041,another.example.com",
"spring.data.cassandra.port=9043", "spring.data.cassandra.local-datacenter=cassandra-eu1")
.run((context) -> {
assertThat(context).hasSingleBean(DriverConfigLoader.class);
DriverExecutionProfile configuration = context.getBean(DriverConfigLoader.class).getInitialConfig()
.getDefaultProfile();
assertThat(configuration.getStringList(DefaultDriverOption.CONTACT_POINTS))
.containsOnly("cluster.example.com:9041", "another.example.com:9043");
assertThat(configuration.getString(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER))
.isEqualTo("cassandra-eu1");
});
}
@Test
void driverConfigLoaderWithCustomSessionName() {
this.contextRunner.withPropertyValues("spring.data.cassandra.session-name=testcluster").run((context) -> {
......@@ -97,16 +126,6 @@ class CassandraAutoConfigurationTests {
});
}
@Test
void driverConfigLoaderApplyConsistentDefaults() {
this.contextRunner.run((context) -> {
Config defaultConfig = defaultConfig();
DriverExecutionProfile config = context.getBean(DriverConfigLoader.class).getInitialConfig()
.getDefaultProfile();
// TODO
});
}
@Test
void driverConfigLoaderCustomizePoolOptions() {
this.contextRunner.withPropertyValues("spring.data.cassandra.pool.idle-timeout=42",
......@@ -120,10 +139,6 @@ class CassandraAutoConfigurationTests {
});
}
private static Config defaultConfig() {
return Parseable.newResources("reference.conf", ConfigParseOptions.defaults()).parse().toConfig();
}
@Configuration(proxyBeanMethods = false)
static class SimpleDriverConfigLoaderBuilderCustomizerConfig {
......
......@@ -4518,6 +4518,18 @@ Generally, you provide `keyspace-name` and `contact-points` as well the local da
spring.data.cassandra.local-datacenter=datacenter1
----
If the port is the same for all your contact points you can use a shortcut and only specify the host names, as shown in the following example:
[source,properties,indent=0,configprops]
----
spring.data.cassandra.keyspace-name=mykeyspace
spring.data.cassandra.contact-points=cassandrahost1,cassandrahost2
spring.data.cassandra.local-datacenter=datacenter1
----
TIP: Those two examples are identical as the port default to `9042`.
If you need to configure the port, use `spring.data.cassandra.port`.
You can also register an arbitrary number of beans that implement `DriverConfigLoaderBuilderCustomizer` for more advanced driver customizations.
The `CqlSession` can be customized with a bean of type `CqlSessionBuilderCustomizer`.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment