Use virtual threads on Spring Data Redis if enabled
Closes gh-35942
This commit is contained in:
@@ -26,12 +26,15 @@ import org.springframework.beans.factory.ObjectProvider;
|
||||
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.ConditionalOnThreading;
|
||||
import org.springframework.boot.autoconfigure.thread.Threading;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.boot.ssl.SslOptions;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.data.redis.connection.RedisClusterConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
@@ -69,11 +72,23 @@ class JedisConnectionConfiguration extends RedisConnectionConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnThreading(Threading.PLATFORM)
|
||||
JedisConnectionFactory redisConnectionFactory(
|
||||
ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) {
|
||||
return createJedisConnectionFactory(builderCustomizers);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnThreading(Threading.VIRTUAL)
|
||||
JedisConnectionFactory redisConnectionFactoryVirtualThreads(
|
||||
ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) {
|
||||
JedisConnectionFactory factory = createJedisConnectionFactory(builderCustomizers);
|
||||
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor("redis-");
|
||||
executor.setVirtualThreads(true);
|
||||
factory.setExecutor(executor);
|
||||
return factory;
|
||||
}
|
||||
|
||||
private JedisConnectionFactory createJedisConnectionFactory(
|
||||
ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) {
|
||||
JedisClientConfiguration clientConfiguration = getJedisClientConfiguration(builderCustomizers);
|
||||
|
||||
@@ -33,13 +33,16 @@ import org.springframework.beans.factory.ObjectProvider;
|
||||
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.ConditionalOnThreading;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Lettuce.Cluster.Refresh;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Pool;
|
||||
import org.springframework.boot.autoconfigure.thread.Threading;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.boot.ssl.SslOptions;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.data.redis.connection.RedisClusterConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
@@ -83,9 +86,29 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(RedisConnectionFactory.class)
|
||||
@ConditionalOnThreading(Threading.PLATFORM)
|
||||
LettuceConnectionFactory redisConnectionFactory(
|
||||
ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
|
||||
ClientResources clientResources) {
|
||||
return createConnectionFactory(builderCustomizers, clientResources);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(RedisConnectionFactory.class)
|
||||
@ConditionalOnThreading(Threading.VIRTUAL)
|
||||
LettuceConnectionFactory redisConnectionFactoryVirtualThreads(
|
||||
ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
|
||||
ClientResources clientResources) {
|
||||
LettuceConnectionFactory factory = createConnectionFactory(builderCustomizers, clientResources);
|
||||
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor("redis-");
|
||||
executor.setVirtualThreads(true);
|
||||
factory.setExecutor(executor);
|
||||
return factory;
|
||||
}
|
||||
|
||||
private LettuceConnectionFactory createConnectionFactory(
|
||||
ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
|
||||
ClientResources clientResources) {
|
||||
LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(builderCustomizers, clientResources,
|
||||
getProperties().getLettuce().getPool());
|
||||
return createLettuceConnectionFactory(clientConfig);
|
||||
|
||||
@@ -19,14 +19,18 @@ package org.springframework.boot.autoconfigure.data.redis;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledForJreRange;
|
||||
import org.junit.jupiter.api.condition.JRE;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.testsupport.assertj.SimpleAsyncTaskExecutorAssert;
|
||||
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
@@ -270,6 +274,25 @@ class RedisAutoConfigurationJedisTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUsePlatformThreadsByDefault() {
|
||||
this.contextRunner.run((context) -> {
|
||||
JedisConnectionFactory factory = context.getBean(JedisConnectionFactory.class);
|
||||
assertThat(factory).extracting("executor").isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledForJreRange(min = JRE.JAVA_21)
|
||||
void shouldUseVirtualThreadsIfEnabled() {
|
||||
this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true").run((context) -> {
|
||||
JedisConnectionFactory factory = context.getBean(JedisConnectionFactory.class);
|
||||
SimpleAsyncTaskExecutor executor = (SimpleAsyncTaskExecutor) ReflectionTestUtils.getField(factory,
|
||||
"executor");
|
||||
SimpleAsyncTaskExecutorAssert.assertThat(executor).usesVirtualThreads();
|
||||
});
|
||||
}
|
||||
|
||||
private String getUserName(JedisConnectionFactory factory) {
|
||||
return ReflectionTestUtils.invokeMethod(factory, "getRedisUsername");
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ import io.lettuce.core.resource.DefaultClientResources;
|
||||
import io.lettuce.core.tracing.Tracing;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledForJreRange;
|
||||
import org.junit.jupiter.api.condition.JRE;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Pool;
|
||||
@@ -38,8 +40,10 @@ import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
|
||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.context.runner.ContextConsumer;
|
||||
import org.springframework.boot.testsupport.assertj.SimpleAsyncTaskExecutorAssert;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.data.redis.connection.RedisClusterConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisNode;
|
||||
@@ -582,6 +586,25 @@ class RedisAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUsePlatformThreadsByDefault() {
|
||||
this.contextRunner.run((context) -> {
|
||||
LettuceConnectionFactory factory = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(factory).extracting("executor").isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledForJreRange(min = JRE.JAVA_21)
|
||||
void shouldUseVirtualThreadsIfEnabled() {
|
||||
this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true").run((context) -> {
|
||||
LettuceConnectionFactory factory = context.getBean(LettuceConnectionFactory.class);
|
||||
SimpleAsyncTaskExecutor executor = (SimpleAsyncTaskExecutor) ReflectionTestUtils.getField(factory,
|
||||
"executor");
|
||||
SimpleAsyncTaskExecutorAssert.assertThat(executor).usesVirtualThreads();
|
||||
});
|
||||
}
|
||||
|
||||
private <T extends ClientOptions> ContextConsumer<AssertableApplicationContext> assertClientOptions(
|
||||
Class<T> expectedType, Consumer<T> options) {
|
||||
return (context) -> {
|
||||
|
||||
Reference in New Issue
Block a user