Add support for external configuration for Cassandra
Closes gh-24065
This commit is contained in:
parent
7eccf4a92d
commit
9b0cdac97a
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2021 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.
|
||||
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.cassandra;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Duration;
|
||||
import java.util.LinkedHashMap;
|
||||
|
@ -52,6 +53,7 @@ import org.springframework.context.annotation.Bean;
|
|||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Cassandra.
|
||||
|
@ -116,6 +118,28 @@ public class CassandraAutoConfiguration {
|
|||
}
|
||||
|
||||
private Config cassandraConfiguration(CassandraProperties properties) {
|
||||
Config config = mapConfig(properties);
|
||||
Resource configFile = properties.getConfig();
|
||||
return (configFile != null) ? applyDefaultFallback(config.withFallback(loadConfig(configFile)))
|
||||
: applyDefaultFallback(config);
|
||||
}
|
||||
|
||||
private Config applyDefaultFallback(Config config) {
|
||||
ConfigFactory.invalidateCaches();
|
||||
return ConfigFactory.defaultOverrides().withFallback(config).withFallback(ConfigFactory.defaultReference())
|
||||
.resolve();
|
||||
}
|
||||
|
||||
private Config loadConfig(Resource config) {
|
||||
try {
|
||||
return ConfigFactory.parseURL(config.getURL());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Failed to load cassandra configuration from " + config, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Config mapConfig(CassandraProperties properties) {
|
||||
CassandraDriverOptions options = new CassandraDriverOptions();
|
||||
PropertyMapper map = PropertyMapper.get();
|
||||
map.from(properties.getSessionName()).whenHasText()
|
||||
|
@ -133,9 +157,7 @@ public class CassandraAutoConfiguration {
|
|||
.to((contactPoints) -> options.add(DefaultDriverOption.CONTACT_POINTS, contactPoints));
|
||||
map.from(properties.getLocalDatacenter()).to(
|
||||
(localDatacenter) -> options.add(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER, localDatacenter));
|
||||
ConfigFactory.invalidateCaches();
|
||||
return ConfigFactory.defaultOverrides().withFallback(options.build())
|
||||
.withFallback(ConfigFactory.defaultReference()).resolve();
|
||||
return options.build();
|
||||
}
|
||||
|
||||
private void mapConnectionOptions(CassandraProperties properties, CassandraDriverOptions options) {
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.util.List;
|
|||
import com.datastax.oss.driver.api.core.DefaultConsistencyLevel;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* Configuration properties for Cassandra.
|
||||
|
@ -37,6 +38,11 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||
@ConfigurationProperties(prefix = "spring.data.cassandra")
|
||||
public class CassandraProperties {
|
||||
|
||||
/**
|
||||
* Location of the configuration file to use.
|
||||
*/
|
||||
private Resource config;
|
||||
|
||||
/**
|
||||
* Keyspace name to use.
|
||||
*/
|
||||
|
@ -109,6 +115,14 @@ public class CassandraProperties {
|
|||
*/
|
||||
private final Controlconnection controlconnection = new Controlconnection();
|
||||
|
||||
public Resource getConfig() {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
public void setConfig(Resource config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public String getKeyspaceName() {
|
||||
return this.keyspaceName;
|
||||
}
|
||||
|
@ -206,13 +220,13 @@ public class CassandraProperties {
|
|||
/**
|
||||
* Timeout to use when establishing driver connections.
|
||||
*/
|
||||
private Duration connectTimeout = Duration.ofSeconds(5);
|
||||
private Duration connectTimeout;
|
||||
|
||||
/**
|
||||
* Timeout to use for internal queries that run as part of the initialization
|
||||
* process, just after a connection is opened.
|
||||
*/
|
||||
private Duration initQueryTimeout = Duration.ofSeconds(5);
|
||||
private Duration initQueryTimeout;
|
||||
|
||||
public Duration getConnectTimeout() {
|
||||
return this.connectTimeout;
|
||||
|
@ -237,7 +251,7 @@ public class CassandraProperties {
|
|||
/**
|
||||
* How long the driver waits for a request to complete.
|
||||
*/
|
||||
private Duration timeout = Duration.ofSeconds(2);
|
||||
private Duration timeout;
|
||||
|
||||
/**
|
||||
* Queries consistency level.
|
||||
|
@ -252,7 +266,7 @@ public class CassandraProperties {
|
|||
/**
|
||||
* How many rows will be retrieved simultaneously in a single network roundtrip.
|
||||
*/
|
||||
private int pageSize = 5000;
|
||||
private int pageSize;
|
||||
|
||||
private final Throttler throttler = new Throttler();
|
||||
|
||||
|
@ -302,13 +316,13 @@ public class CassandraProperties {
|
|||
/**
|
||||
* Idle timeout before an idle connection is removed.
|
||||
*/
|
||||
private Duration idleTimeout = Duration.ofSeconds(5);
|
||||
private Duration idleTimeout;
|
||||
|
||||
/**
|
||||
* Heartbeat interval after which a message is sent on an idle connection to make
|
||||
* sure it's still alive.
|
||||
*/
|
||||
private Duration heartbeatInterval = Duration.ofSeconds(30);
|
||||
private Duration heartbeatInterval;
|
||||
|
||||
public Duration getIdleTimeout() {
|
||||
return this.idleTimeout;
|
||||
|
@ -350,7 +364,7 @@ public class CassandraProperties {
|
|||
/**
|
||||
* Request throttling type.
|
||||
*/
|
||||
private ThrottlerType type = ThrottlerType.NONE;
|
||||
private ThrottlerType type;
|
||||
|
||||
/**
|
||||
* Maximum number of requests that can be enqueued when the throttling threshold
|
||||
|
|
|
@ -511,6 +511,14 @@
|
|||
"name": "spring.data.cassandra.compression",
|
||||
"defaultValue": "none"
|
||||
},
|
||||
{
|
||||
"name": "spring.data.cassandra.connection.connection-timeout",
|
||||
"defaultValue": "5s"
|
||||
},
|
||||
{
|
||||
"name": "spring.data.cassandra.connection.init-query-timeout",
|
||||
"defaultValue": "5s"
|
||||
},
|
||||
{
|
||||
"name": "spring.data.cassandra.contact-points",
|
||||
"defaultValue": [
|
||||
|
@ -565,6 +573,14 @@
|
|||
"description": "Type of Cassandra repositories to enable.",
|
||||
"defaultValue": "auto"
|
||||
},
|
||||
{
|
||||
"name": "spring.data.cassandra.request.page-size",
|
||||
"defaultValue": 5000
|
||||
},
|
||||
{
|
||||
"name": "spring.data.cassandra.request.timeout",
|
||||
"defaultValue": "2s"
|
||||
},
|
||||
{
|
||||
"name": "spring.data.cassandra.request.throttler.type",
|
||||
"defaultValue": "none"
|
||||
|
@ -577,6 +593,14 @@
|
|||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.data.cassandra.pool.heartbeat-interval",
|
||||
"defaultValue": "30s"
|
||||
},
|
||||
{
|
||||
"name": "spring.data.cassandra.pool.idle-timeout",
|
||||
"defaultValue": "5s"
|
||||
},
|
||||
{
|
||||
"name": "spring.data.couchbase.consistency",
|
||||
"type": "org.springframework.data.couchbase.core.query.Consistency",
|
||||
|
|
|
@ -16,10 +16,13 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.cassandra;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlIdentifier;
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
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.DriverConfig;
|
||||
import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
|
||||
import com.datastax.oss.driver.api.core.config.DriverExecutionProfile;
|
||||
import com.datastax.oss.driver.internal.core.session.throttling.ConcurrencyLimitingRequestThrottler;
|
||||
|
@ -228,6 +231,31 @@ class CassandraAutoConfigurationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void driverConfigLoaderWithConfigComplementSettings() {
|
||||
String configLocation = "org/springframework/boot/autoconfigure/cassandra/simple.conf";
|
||||
this.contextRunner.withPropertyValues("spring.data.cassandra.session-name=testcluster",
|
||||
"spring.data.cassandra.config=" + configLocation).run((context) -> {
|
||||
assertThat(context).hasSingleBean(DriverConfigLoader.class);
|
||||
assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile()
|
||||
.getString(DefaultDriverOption.SESSION_NAME)).isEqualTo("testcluster");
|
||||
assertThat(context.getBean(DriverConfigLoader.class).getInitialConfig().getDefaultProfile()
|
||||
.getDuration(DefaultDriverOption.REQUEST_TIMEOUT)).isEqualTo(Duration.ofMillis(500));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void driverConfigLoaderWithConfigCreateProfiles() {
|
||||
String configLocation = "org/springframework/boot/autoconfigure/cassandra/profiles.conf";
|
||||
this.contextRunner.withPropertyValues("spring.data.cassandra.config=" + configLocation).run((context) -> {
|
||||
assertThat(context).hasSingleBean(DriverConfigLoader.class);
|
||||
DriverConfig driverConfig = context.getBean(DriverConfigLoader.class).getInitialConfig();
|
||||
assertThat(driverConfig.getProfiles()).containsOnlyKeys("default", "first", "second");
|
||||
assertThat(driverConfig.getProfile("first").getDuration(DefaultDriverOption.REQUEST_TIMEOUT))
|
||||
.isEqualTo(Duration.ofMillis(100));
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class SimpleDriverConfigLoaderBuilderCustomizerConfig {
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.cassandra;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import com.datastax.oss.driver.api.core.config.OptionsMap;
|
||||
import com.datastax.oss.driver.api.core.config.TypedDriverOption;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -26,27 +28,34 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
* Tests for {@link CassandraProperties}.
|
||||
*
|
||||
* @author Chris Bono
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class CassandraPropertiesTests {
|
||||
|
||||
/**
|
||||
* To let a configuration file override values, {@link CassandraProperties} can't have
|
||||
* any default hardcoded. This test makes sure that the default that we moved to
|
||||
* manual meta-data are accurate.
|
||||
*/
|
||||
@Test
|
||||
void defaultValuesAreConsistent() {
|
||||
CassandraProperties properties = new CassandraProperties();
|
||||
void defaultValuesInManualMetadataAreConsistent() {
|
||||
OptionsMap driverDefaults = OptionsMap.driverDefaults();
|
||||
assertThat(properties.getConnection().getConnectTimeout())
|
||||
.isEqualTo(driverDefaults.get(TypedDriverOption.CONNECTION_CONNECT_TIMEOUT));
|
||||
assertThat(properties.getConnection().getInitQueryTimeout())
|
||||
.isEqualTo(driverDefaults.get(TypedDriverOption.CONNECTION_INIT_QUERY_TIMEOUT));
|
||||
assertThat(properties.getRequest().getTimeout())
|
||||
.isEqualTo(driverDefaults.get(TypedDriverOption.REQUEST_TIMEOUT));
|
||||
assertThat(properties.getRequest().getPageSize())
|
||||
.isEqualTo(driverDefaults.get(TypedDriverOption.REQUEST_PAGE_SIZE));
|
||||
assertThat(properties.getRequest().getThrottler().getType().type())
|
||||
.isEqualTo(driverDefaults.get(TypedDriverOption.REQUEST_THROTTLER_CLASS));
|
||||
assertThat(properties.getPool().getHeartbeatInterval())
|
||||
.isEqualTo(driverDefaults.get(TypedDriverOption.HEARTBEAT_INTERVAL));
|
||||
assertThat(properties.getPool().getIdleTimeout())
|
||||
.isEqualTo(driverDefaults.get(TypedDriverOption.HEARTBEAT_TIMEOUT));
|
||||
// spring.data.cassandra.connection.connection-timeout
|
||||
assertThat(driverDefaults.get(TypedDriverOption.CONNECTION_CONNECT_TIMEOUT)).isEqualTo(Duration.ofSeconds(5));
|
||||
// spring.data.cassandra.connection.init-query-timeout
|
||||
assertThat(driverDefaults.get(TypedDriverOption.CONNECTION_INIT_QUERY_TIMEOUT))
|
||||
.isEqualTo(Duration.ofSeconds(5));
|
||||
// spring.data.cassandra.request.timeout
|
||||
assertThat(driverDefaults.get(TypedDriverOption.REQUEST_TIMEOUT)).isEqualTo(Duration.ofSeconds(2));
|
||||
// spring.data.cassandra.request.page-size
|
||||
assertThat(driverDefaults.get(TypedDriverOption.REQUEST_PAGE_SIZE)).isEqualTo(5000);
|
||||
// spring.data.cassandra.request.throttler.type
|
||||
assertThat(driverDefaults.get(TypedDriverOption.REQUEST_THROTTLER_CLASS))
|
||||
.isEqualTo("PassThroughRequestThrottler"); // "none"
|
||||
// spring.data.cassandra.pool.heartbeat-interval
|
||||
assertThat(driverDefaults.get(TypedDriverOption.HEARTBEAT_INTERVAL)).isEqualTo(Duration.ofSeconds(30));
|
||||
// spring.data.cassandra.pool.idle-timeout
|
||||
assertThat(driverDefaults.get(TypedDriverOption.HEARTBEAT_TIMEOUT)).isEqualTo(Duration.ofSeconds(5));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
datastax-java-driver {
|
||||
profiles {
|
||||
first {
|
||||
basic.request.timeout = 100 milliseconds
|
||||
basic.request.consistency = ONE
|
||||
}
|
||||
second {
|
||||
basic.request.timeout = 5 seconds
|
||||
basic.request.consistency = QUORUM
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
datastax-java-driver {
|
||||
basic {
|
||||
session-name = Test session
|
||||
request.timeout = 500 milliseconds
|
||||
}
|
||||
}
|
|
@ -4554,7 +4554,9 @@ If you need to configure the port, use `spring.data.cassandra.port`.
|
|||
====
|
||||
The Cassandra driver has its own configuration infrastructure that loads an `application.conf` at the root of the classpath.
|
||||
|
||||
Spring Boot does not look for such a file and rather provides a number of configuration properties via the `spring.data.cassandra.*` namespace.
|
||||
Spring Boot does not look for such a file by default but can load one using `spring.data.cassandra.config`.
|
||||
If a property is both present in `+spring.data.cassandra.*+` and the configuration file, the value in `+spring.data.cassandra.*+` takes precedence.
|
||||
|
||||
For more advanced driver customizations, you can register an arbitrary number of beans that implement `DriverConfigLoaderBuilderCustomizer`.
|
||||
The `CqlSession` can be customized with a bean of type `CqlSessionBuilderCustomizer`.
|
||||
====
|
||||
|
|
Loading…
Reference in New Issue