From 1c7d99890e05201aefba6eb58d284ce0a11506a8 Mon Sep 17 00:00:00 2001 From: "Stern, Ittay (is9613)" Date: Wed, 1 Jun 2022 16:42:46 +0300 Subject: [PATCH] Allow spring.data.cassandra.config file to override default values 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 --- .../cassandra/CassandraAutoConfiguration.java | 10 +++-- .../cassandra/CassandraProperties.java | 36 +++++++++++----- .../CassandraAutoConfigurationTests.java | 41 +++++++++++++++++++ .../cassandra/override-defaults.conf | 20 +++++++++ 4 files changed, 92 insertions(+), 15 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/resources/org/springframework/boot/autoconfigure/cassandra/override-defaults.conf diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java index 20882e550f2c..a36fed6b20b4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java @@ -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(); } @@ -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(); } @@ -210,7 +211,8 @@ private void mapControlConnectionOptions(CassandraProperties properties, Cassand } private List mapContactPoints(CassandraProperties properties) { - return properties.getContactPoints().stream() + List contactPoints = properties.getContactPoints(); + return (contactPoints == null) ? null : contactPoints.stream() .map((candidate) -> formatContactPoint(candidate, properties.getPort())).collect(Collectors.toList()); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java index 17d99cf7696a..f8a5477e5da9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java @@ -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. */ @@ -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 contactPoints = new ArrayList<>(Collections.singleton("127.0.0.1:9042")); + private List contactPoints; /** * Port to use if a contact point does not specify one. @@ -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. @@ -143,6 +153,10 @@ public List getContactPoints() { return this.contactPoints; } + public void setContactPoints(List contactPoints) { + this.contactPoints = contactPoints; + } + public int getPort() { return this.port; } @@ -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(); @@ -294,7 +308,7 @@ public void setSerialConsistency(DefaultConsistencyLevel serialConsistency) { this.serialConsistency = serialConsistency; } - public int getPageSize() { + public Integer getPageSize() { return this.pageSize; } @@ -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; @@ -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 @@ -397,7 +411,7 @@ public void setType(ThrottlerType type) { this.type = type; } - public int getMaxQueueSize() { + public Integer getMaxQueueSize() { return this.maxQueueSize; } @@ -405,7 +419,7 @@ public void setMaxQueueSize(int maxQueueSize) { this.maxQueueSize = maxQueueSize; } - public int getMaxConcurrentRequests() { + public Integer getMaxConcurrentRequests() { return this.maxConcurrentRequests; } @@ -413,7 +427,7 @@ public void setMaxConcurrentRequests(int maxConcurrentRequests) { this.maxConcurrentRequests = maxConcurrentRequests; } - public int getMaxRequestsPerSecond() { + public Integer getMaxRequestsPerSecond() { return this.maxRequestsPerSecond; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationTests.java index ed05b182aa52..1975e4ff804c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationTests.java @@ -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; @@ -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; @@ -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"; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/org/springframework/boot/autoconfigure/cassandra/override-defaults.conf b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/org/springframework/boot/autoconfigure/cassandra/override-defaults.conf new file mode 100644 index 000000000000..52115731ed3d --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/org/springframework/boot/autoconfigure/cassandra/override-defaults.conf @@ -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 + } +}