From 0d41596a426e6d12f94262188af502644d8ff776 Mon Sep 17 00:00:00 2001 From: Rodolpho Couto Date: Wed, 29 Apr 2020 00:19:27 -0300 Subject: [PATCH 1/2] Add additional properties to configure R2DBC pool See gh-21219 --- .../ConnectionFactoryConfigurations.java | 7 ++- .../autoconfigure/r2dbc/R2dbcProperties.java | 55 +++++++++++++++++++ ...itional-spring-configuration-metadata.json | 4 ++ .../r2dbc/R2dbcAutoConfigurationTests.java | 24 +++++++- 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/ConnectionFactoryConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/ConnectionFactoryConfigurations.java index d918470ba6..e1c5165d4a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/ConnectionFactoryConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/ConnectionFactoryConfigurations.java @@ -43,6 +43,7 @@ import org.springframework.util.StringUtils; * * @author Mark Paluch * @author Stephane Nicoll + * @author Rodolpho S. Couto */ abstract class ConnectionFactoryConfigurations { @@ -69,7 +70,11 @@ abstract class ConnectionFactoryConfigurations { customizers.orderedStream().collect(Collectors.toList())); R2dbcProperties.Pool pool = properties.getPool(); ConnectionPoolConfiguration.Builder builder = ConnectionPoolConfiguration.builder(connectionFactory) - .maxSize(pool.getMaxSize()).initialSize(pool.getInitialSize()).maxIdleTime(pool.getMaxIdleTime()); + .initialSize(pool.getInitialSize()).maxSize(pool.getMaxSize()).maxIdleTime(pool.getMaxIdleTime()) + .maxLifeTime(pool.getMaxLifeTime()).maxAcquireTime(pool.getMaxAcquireTime()) + .maxCreateConnectionTime(pool.getMaxCreateConnectionTime()) + .validationDepth(pool.getValidationDepth()); + if (StringUtils.hasText(pool.getValidationQuery())) { builder.validationQuery(pool.getValidationQuery()); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcProperties.java index 7792e67392..5f61fdd969 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcProperties.java @@ -21,6 +21,8 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; +import io.r2dbc.spi.ValidationDepth; + import org.springframework.boot.context.properties.ConfigurationProperties; /** @@ -29,6 +31,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * @author Mark Paluch * @author Andreas Killaitis * @author Stephane Nicoll + * @author Rodolpho S. Couto * @since 2.3.0 */ @ConfigurationProperties(prefix = "spring.r2dbc") @@ -138,6 +141,21 @@ public class R2dbcProperties { */ private Duration maxIdleTime = Duration.ofMinutes(30); + /** + * Max lifetime. + */ + private Duration maxLifeTime = Duration.ofMinutes(0L); + + /** + * Max acquire time. + */ + private Duration maxAcquireTime = Duration.ofMinutes(0L); + + /** + * Max create connection time. + */ + private Duration maxCreateConnectionTime = Duration.ofMinutes(0L); + /** * Initial connection pool size. */ @@ -153,6 +171,11 @@ public class R2dbcProperties { */ private String validationQuery; + /** + * Validation depth. + */ + private ValidationDepth validationDepth = ValidationDepth.LOCAL; + public Duration getMaxIdleTime() { return this.maxIdleTime; } @@ -161,6 +184,30 @@ public class R2dbcProperties { this.maxIdleTime = maxIdleTime; } + public Duration getMaxLifeTime() { + return this.maxLifeTime; + } + + public void setMaxLifeTime(Duration maxLifeTime) { + this.maxLifeTime = maxLifeTime; + } + + public Duration getMaxAcquireTime() { + return this.maxAcquireTime; + } + + public void setMaxAcquireTime(Duration maxAcquireTime) { + this.maxAcquireTime = maxAcquireTime; + } + + public Duration getMaxCreateConnectionTime() { + return this.maxCreateConnectionTime; + } + + public void setMaxCreateConnectionTime(Duration maxCreateConnectionTime) { + this.maxCreateConnectionTime = maxCreateConnectionTime; + } + public int getInitialSize() { return this.initialSize; } @@ -185,6 +232,14 @@ public class R2dbcProperties { this.validationQuery = validationQuery; } + public ValidationDepth getValidationDepth() { + return this.validationDepth; + } + + public void setValidationDepth(ValidationDepth validationDepth) { + this.validationDepth = validationDepth; + } + } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 7e215490bd..48872b475b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1468,6 +1468,10 @@ "type": "java.lang.Boolean", "description": "Whether pooling is enabled. Enabled automatically if \"r2dbc-pool\" is on the classpath." }, + { + "name": "spring.r2dbc.pool.validationDepth", + "defaultValue": "local" + }, { "name": "spring.rabbitmq.cache.connection.mode", "defaultValue": "channel" diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcAutoConfigurationTests.java index 5e8a3f5998..bb82c30a85 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcAutoConfigurationTests.java @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.r2dbc; import java.net.URL; import java.net.URLClassLoader; +import java.time.Duration; import java.util.UUID; import java.util.function.Function; @@ -28,6 +29,7 @@ import io.r2dbc.pool.ConnectionPool; import io.r2dbc.pool.PoolMetrics; import io.r2dbc.spi.ConnectionFactory; import io.r2dbc.spi.Option; +import io.r2dbc.spi.ValidationDepth; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanCreationException; @@ -61,11 +63,27 @@ class R2dbcAutoConfigurationTests { @Test void configureWithUrlAndPoolPropertiesApplyProperties() { - this.contextRunner.withPropertyValues("spring.r2dbc.url:r2dbc:h2:mem:///" + randomDatabaseName(), - "spring.r2dbc.pool.max-size=15").run((context) -> { - assertThat(context).hasSingleBean(ConnectionFactory.class).hasSingleBean(ConnectionPool.class); + this.contextRunner + .withPropertyValues("spring.r2dbc.url:r2dbc:h2:mem:///" + randomDatabaseName(), + "spring.r2dbc.pool.initial-size=5", "spring.r2dbc.pool.max-size=15", + "spring.r2dbc.pool.max-idle-time=1ms", "spring.r2dbc.pool.max-life-time=2s", + "spring.r2dbc.pool.max-acquire-time=3m", "spring.r2dbc.pool.max-create-connection-time=4h", + "spring.r2dbc.pool.validation-query=SELECT 1", "spring.r2dbc.pool.validation-depth=remote") + .run((context) -> { + assertThat(context).hasSingleBean(ConnectionFactory.class).hasSingleBean(ConnectionPool.class) + .hasSingleBean(R2dbcProperties.class); PoolMetrics poolMetrics = context.getBean(ConnectionPool.class).getMetrics().get(); assertThat(poolMetrics.getMaxAllocatedSize()).isEqualTo(15); + + R2dbcProperties properties = context.getBean(R2dbcProperties.class); + assertThat(properties.getPool().getInitialSize()).isEqualTo(5); + assertThat(properties.getPool().getMaxSize()).isEqualTo(15); + assertThat(properties.getPool().getMaxIdleTime()).isEqualTo(Duration.ofMillis(1)); + assertThat(properties.getPool().getMaxLifeTime()).isEqualTo(Duration.ofSeconds(2)); + assertThat(properties.getPool().getMaxAcquireTime()).isEqualTo(Duration.ofMinutes(3)); + assertThat(properties.getPool().getMaxCreateConnectionTime()).isEqualTo(Duration.ofHours(4)); + assertThat(properties.getPool().getValidationQuery()).isEqualTo("SELECT 1"); + assertThat(properties.getPool().getValidationDepth()).isEqualTo(ValidationDepth.REMOTE); }); } From c8b67becceff76fa551b1d9ab72f5384a4483a17 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 14 Jul 2020 16:48:43 +0200 Subject: [PATCH 2/2] Polish "Add additional properties to configure R2DBC pool" See gh-21219 --- .../ConnectionFactoryConfigurations.java | 20 ++++++----- .../autoconfigure/r2dbc/R2dbcProperties.java | 14 ++++---- ...itional-spring-configuration-metadata.json | 2 +- .../r2dbc/R2dbcAutoConfigurationTests.java | 33 +++++++++---------- 4 files changed, 35 insertions(+), 34 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/ConnectionFactoryConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/ConnectionFactoryConfigurations.java index e1c5165d4a..3f80c5f81a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/ConnectionFactoryConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/ConnectionFactoryConfigurations.java @@ -29,6 +29,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; +import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; @@ -69,15 +70,16 @@ abstract class ConnectionFactoryConfigurations { ConnectionFactory connectionFactory = createConnectionFactory(properties, resourceLoader.getClassLoader(), customizers.orderedStream().collect(Collectors.toList())); R2dbcProperties.Pool pool = properties.getPool(); - ConnectionPoolConfiguration.Builder builder = ConnectionPoolConfiguration.builder(connectionFactory) - .initialSize(pool.getInitialSize()).maxSize(pool.getMaxSize()).maxIdleTime(pool.getMaxIdleTime()) - .maxLifeTime(pool.getMaxLifeTime()).maxAcquireTime(pool.getMaxAcquireTime()) - .maxCreateConnectionTime(pool.getMaxCreateConnectionTime()) - .validationDepth(pool.getValidationDepth()); - - if (StringUtils.hasText(pool.getValidationQuery())) { - builder.validationQuery(pool.getValidationQuery()); - } + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); + ConnectionPoolConfiguration.Builder builder = ConnectionPoolConfiguration.builder(connectionFactory); + map.from(pool.getMaxIdleTime()).to(builder::maxIdleTime); + map.from(pool.getMaxLifeTime()).to(builder::maxLifeTime); + map.from(pool.getMaxAcquireTime()).to(builder::maxAcquireTime); + map.from(pool.getMaxCreateConnectionTime()).to(builder::maxCreateConnectionTime); + map.from(pool.getInitialSize()).to(builder::initialSize); + map.from(pool.getMaxSize()).to(builder::maxSize); + map.from(pool.getValidationQuery()).whenHasText().to(builder::validationQuery); + map.from(pool.getValidationDepth()).to(builder::validationDepth); return new ConnectionPool(builder.build()); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcProperties.java index 5f61fdd969..dfbcc9b7a7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcProperties.java @@ -142,19 +142,21 @@ public class R2dbcProperties { private Duration maxIdleTime = Duration.ofMinutes(30); /** - * Max lifetime. + * Maximum lifetime of a connection in the pool. By default, connections have an + * infinite lifetime. */ - private Duration maxLifeTime = Duration.ofMinutes(0L); + private Duration maxLifeTime; /** - * Max acquire time. + * Maximum time to acquire a connection from the pool. By default, wait + * indefinitely. */ - private Duration maxAcquireTime = Duration.ofMinutes(0L); + private Duration maxAcquireTime; /** - * Max create connection time. + * Maximum time to wait to create a new connection. By default, wait indefinitely. */ - private Duration maxCreateConnectionTime = Duration.ofMinutes(0L); + private Duration maxCreateConnectionTime; /** * Initial connection pool size. diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 48872b475b..194db53153 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1469,7 +1469,7 @@ "description": "Whether pooling is enabled. Enabled automatically if \"r2dbc-pool\" is on the classpath." }, { - "name": "spring.r2dbc.pool.validationDepth", + "name": "spring.r2dbc.pool.validation-depth", "defaultValue": "local" }, { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcAutoConfigurationTests.java index bb82c30a85..f8734fde94 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcAutoConfigurationTests.java @@ -29,7 +29,6 @@ import io.r2dbc.pool.ConnectionPool; import io.r2dbc.pool.PoolMetrics; import io.r2dbc.spi.ConnectionFactory; import io.r2dbc.spi.Option; -import io.r2dbc.spi.ValidationDepth; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanCreationException; @@ -63,27 +62,25 @@ class R2dbcAutoConfigurationTests { @Test void configureWithUrlAndPoolPropertiesApplyProperties() { - this.contextRunner - .withPropertyValues("spring.r2dbc.url:r2dbc:h2:mem:///" + randomDatabaseName(), - "spring.r2dbc.pool.initial-size=5", "spring.r2dbc.pool.max-size=15", - "spring.r2dbc.pool.max-idle-time=1ms", "spring.r2dbc.pool.max-life-time=2s", - "spring.r2dbc.pool.max-acquire-time=3m", "spring.r2dbc.pool.max-create-connection-time=4h", - "spring.r2dbc.pool.validation-query=SELECT 1", "spring.r2dbc.pool.validation-depth=remote") - .run((context) -> { + this.contextRunner.withPropertyValues("spring.r2dbc.url:r2dbc:h2:mem:///" + randomDatabaseName(), + "spring.r2dbc.pool.max-size=15", "spring.r2dbc.pool.max-acquire-time=3m").run((context) -> { assertThat(context).hasSingleBean(ConnectionFactory.class).hasSingleBean(ConnectionPool.class) .hasSingleBean(R2dbcProperties.class); - PoolMetrics poolMetrics = context.getBean(ConnectionPool.class).getMetrics().get(); + ConnectionPool connectionPool = context.getBean(ConnectionPool.class); + PoolMetrics poolMetrics = connectionPool.getMetrics().get(); assertThat(poolMetrics.getMaxAllocatedSize()).isEqualTo(15); + assertThat(connectionPool).hasFieldOrPropertyWithValue("maxAcquireTime", Duration.ofMinutes(3)); + }); + } - R2dbcProperties properties = context.getBean(R2dbcProperties.class); - assertThat(properties.getPool().getInitialSize()).isEqualTo(5); - assertThat(properties.getPool().getMaxSize()).isEqualTo(15); - assertThat(properties.getPool().getMaxIdleTime()).isEqualTo(Duration.ofMillis(1)); - assertThat(properties.getPool().getMaxLifeTime()).isEqualTo(Duration.ofSeconds(2)); - assertThat(properties.getPool().getMaxAcquireTime()).isEqualTo(Duration.ofMinutes(3)); - assertThat(properties.getPool().getMaxCreateConnectionTime()).isEqualTo(Duration.ofHours(4)); - assertThat(properties.getPool().getValidationQuery()).isEqualTo("SELECT 1"); - assertThat(properties.getPool().getValidationDepth()).isEqualTo(ValidationDepth.REMOTE); + @Test + void configureWithUrlAndDefaultDoNotOverrideDefaultTimeouts() { + this.contextRunner.withPropertyValues("spring.r2dbc.url:r2dbc:h2:mem:///" + randomDatabaseName()) + .run((context) -> { + assertThat(context).hasSingleBean(ConnectionFactory.class).hasSingleBean(ConnectionPool.class) + .hasSingleBean(R2dbcProperties.class); + ConnectionPool connectionPool = context.getBean(ConnectionPool.class); + assertThat(connectionPool).hasFieldOrPropertyWithValue("maxAcquireTime", Duration.ZERO); }); }