Skip to content

Commit

Permalink
Allow spring.data.cassandra.config file to override default values
Browse files Browse the repository at this point in the history
Update `CassandraAutoConfiguration` so that properties in a
`spring.data.cassandra.config` file can override the default values
defined in `CassandraProperties`.

This commit changes two things:

1. Any primitive on `CassandraProperties` are replaced with object values.
   This allows distinguishing between defaults values and no-values. Then
   CassandraAutoConfiguration.mapConfig() can use whenNonNull() predicate
   to ignore those.

2. `CassandraProperties` no longer populate default values on any
   property. With that, the defaults can be applied on top of the file
   spring.data.cassandra.config; i.e. the config file have higher
   precedence than the defaults, but lower that any spring.data.cassandra.*
   property.

See gh-31238
  • Loading branch information
Stern, Ittay (is9613) authored and philwebb committed Jun 23, 2022
1 parent 7017e10 commit 1c7d998
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ private Config cassandraConfiguration(CassandraProperties properties) {

private Config applyDefaultFallback(Config config) {
ConfigFactory.invalidateCaches();
return ConfigFactory.defaultOverrides().withFallback(config).withFallback(ConfigFactory.defaultReference())
return ConfigFactory.defaultOverrides().withFallback(config)
.withFallback(mapConfig(CassandraProperties.defaults())).withFallback(ConfigFactory.defaultReference())
.resolve();
}

Expand All @@ -153,9 +154,9 @@ private Config mapConfig(CassandraProperties properties) {
mapPoolingOptions(properties, options);
mapRequestOptions(properties, options);
mapControlConnectionOptions(properties, options);
map.from(mapContactPoints(properties))
map.from(mapContactPoints(properties)).whenNonNull()
.to((contactPoints) -> options.add(DefaultDriverOption.CONTACT_POINTS, contactPoints));
map.from(properties.getLocalDatacenter()).to(
map.from(properties.getLocalDatacenter()).whenHasText().to(
(localDatacenter) -> options.add(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER, localDatacenter));
return options.build();
}
Expand Down Expand Up @@ -210,7 +211,8 @@ private void mapControlConnectionOptions(CassandraProperties properties, Cassand
}

private List<String> mapContactPoints(CassandraProperties properties) {
return properties.getContactPoints().stream()
List<String> contactPoints = properties.getContactPoints();
return (contactPoints == null) ? null : contactPoints.stream()
.map((candidate) -> formatContactPoint(candidate, properties.getPort())).collect(Collectors.toList());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@
@ConfigurationProperties(prefix = "spring.data.cassandra")
public class CassandraProperties {

static CassandraProperties defaults() {
CassandraProperties properties = new CassandraProperties();

properties.setContactPoints(new ArrayList<>(Collections.singleton("127.0.0.1:9042")));
properties.setCompression(Compression.NONE);
properties.getControlconnection().setTimeout(Duration.ofSeconds(5));

return properties;
}

/**
* Location of the configuration file to use.
*/
Expand All @@ -57,7 +67,7 @@ public class CassandraProperties {
* 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"));
private List<String> contactPoints;

/**
* Port to use if a contact point does not specify one.
Expand All @@ -83,7 +93,7 @@ public class CassandraProperties {
/**
* Compression supported by the Cassandra binary protocol.
*/
private Compression compression = Compression.NONE;
private Compression compression;

/**
* Schema action to take at startup.
Expand Down Expand Up @@ -143,6 +153,10 @@ public List<String> getContactPoints() {
return this.contactPoints;
}

public void setContactPoints(List<String> contactPoints) {
this.contactPoints = contactPoints;
}

public int getPort() {
return this.port;
}
Expand Down Expand Up @@ -266,7 +280,7 @@ public static class Request {
/**
* How many rows will be retrieved simultaneously in a single network round-trip.
*/
private int pageSize;
private Integer pageSize;

private final Throttler throttler = new Throttler();

Expand Down Expand Up @@ -294,7 +308,7 @@ public void setSerialConsistency(DefaultConsistencyLevel serialConsistency) {
this.serialConsistency = serialConsistency;
}

public int getPageSize() {
public Integer getPageSize() {
return this.pageSize;
}

Expand Down Expand Up @@ -347,7 +361,7 @@ public static class Controlconnection {
/**
* Timeout to use for control queries.
*/
private Duration timeout = Duration.ofSeconds(5);
private Duration timeout;

public Duration getTimeout() {
return this.timeout;
Expand All @@ -370,17 +384,17 @@ public static class Throttler {
* Maximum number of requests that can be enqueued when the throttling threshold
* is exceeded.
*/
private int maxQueueSize;
private Integer maxQueueSize;

/**
* Maximum number of requests that are allowed to execute in parallel.
*/
private int maxConcurrentRequests;
private Integer maxConcurrentRequests;

/**
* Maximum allowed request rate.
*/
private int maxRequestsPerSecond;
private Integer maxRequestsPerSecond;

/**
* How often the throttler attempts to dequeue requests. Set this high enough that
Expand All @@ -397,23 +411,23 @@ public void setType(ThrottlerType type) {
this.type = type;
}

public int getMaxQueueSize() {
public Integer getMaxQueueSize() {
return this.maxQueueSize;
}

public void setMaxQueueSize(int maxQueueSize) {
this.maxQueueSize = maxQueueSize;
}

public int getMaxConcurrentRequests() {
public Integer getMaxConcurrentRequests() {
return this.maxConcurrentRequests;
}

public void setMaxConcurrentRequests(int maxConcurrentRequests) {
this.maxConcurrentRequests = maxConcurrentRequests;
}

public int getMaxRequestsPerSecond() {
public Integer getMaxRequestsPerSecond() {
return this.maxRequestsPerSecond;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package org.springframework.boot.autoconfigure.cassandra;

import java.time.Duration;
import java.util.Collections;
import java.util.List;

import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.CqlSession;
Expand All @@ -28,6 +30,7 @@
import com.datastax.oss.driver.internal.core.session.throttling.ConcurrencyLimitingRequestThrottler;
import com.datastax.oss.driver.internal.core.session.throttling.PassThroughRequestThrottler;
import com.datastax.oss.driver.internal.core.session.throttling.RateLimitingRequestThrottler;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.Test;

import org.springframework.boot.autoconfigure.AutoConfigurations;
Expand Down Expand Up @@ -244,6 +247,44 @@ void driverConfigLoaderWithConfigComplementSettings() {
});
}

@Test // gh-31025
void driverConfigLoaderWithConfigOverridesDefaults() {
String configLocation = "org/springframework/boot/autoconfigure/cassandra/override-defaults.conf";
this.contextRunner.withPropertyValues("spring.data.cassandra.config=" + configLocation).run((context) -> {
assertThat(context).hasSingleBean(DriverConfigLoader.class);

SoftAssertions softly = new SoftAssertions();

softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile()
.getString(DefaultDriverOption.SESSION_NAME)).isEqualTo("advanced session");

softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile()
.getDuration(DefaultDriverOption.REQUEST_TIMEOUT)).isEqualTo(Duration.ofSeconds(2)); // default

softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile()
.getStringList(DefaultDriverOption.CONTACT_POINTS)).isEqualTo(Collections.singletonList("1.2.3.4:5678"));
softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile()
.getBoolean(DefaultDriverOption.RESOLVE_CONTACT_POINTS)).isFalse();
softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile()
.getInt(DefaultDriverOption.REQUEST_PAGE_SIZE)).isEqualTo(11);
softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile()
.getString(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER)).isEqualTo("datacenter1");

softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile()
.getInt(DefaultDriverOption.REQUEST_THROTTLER_MAX_CONCURRENT_REQUESTS)).isEqualTo(22);
softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile()
.getInt(DefaultDriverOption.REQUEST_THROTTLER_MAX_REQUESTS_PER_SECOND)).isEqualTo(33);
softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile()
.getInt(DefaultDriverOption.REQUEST_THROTTLER_MAX_QUEUE_SIZE)).isEqualTo(44);
softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile()
.getDuration(DefaultDriverOption.CONTROL_CONNECTION_TIMEOUT)).isEqualTo(Duration.ofMillis(5555));
softly.assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile()
.getString(DefaultDriverOption.PROTOCOL_COMPRESSION)).isEqualTo("SNAPPY");

softly.assertAll();
});
}

@Test
void driverConfigLoaderWithConfigCreateProfiles() {
String configLocation = "org/springframework/boot/autoconfigure/cassandra/profiles.conf";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
datastax-java-driver {
basic {
session-name = advanced session
load-balancing-policy {
local-datacenter = datacenter1
}
request.page-size = 11
contact-points = [ "1.2.3.4:5678" ]
}
advanced {
throttler {
max-concurrent-requests = 22
max-requests-per-second = 33
max-queue-size = 44
}
control-connection.timeout = 5555
protocol.compression = SNAPPY
resolve-contact-points = false
}
}

0 comments on commit 1c7d998

Please sign in to comment.