|
19 | 19 | import java.time.Duration;
|
20 | 20 | import java.util.Arrays;
|
21 | 21 | import java.util.EnumSet;
|
| 22 | +import java.util.Iterator; |
22 | 23 | import java.util.List;
|
23 | 24 | import java.util.Set;
|
24 | 25 | import java.util.function.Consumer;
|
25 | 26 | import java.util.stream.Collectors;
|
| 27 | +import java.util.stream.Stream; |
26 | 28 |
|
27 | 29 | import io.lettuce.core.ClientOptions;
|
| 30 | +import io.lettuce.core.ReadFrom; |
| 31 | +import io.lettuce.core.ReadFrom.Nodes; |
| 32 | +import io.lettuce.core.RedisURI; |
28 | 33 | import io.lettuce.core.cluster.ClusterClientOptions;
|
29 | 34 | import io.lettuce.core.cluster.ClusterTopologyRefreshOptions.RefreshTrigger;
|
| 35 | +import io.lettuce.core.models.role.RedisNodeDescription; |
30 | 36 | import io.lettuce.core.resource.DefaultClientResources;
|
31 | 37 | import io.lettuce.core.tracing.Tracing;
|
32 | 38 | import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
33 | 39 | import org.junit.jupiter.api.Test;
|
34 | 40 | import org.junit.jupiter.api.condition.EnabledForJreRange;
|
35 | 41 | import org.junit.jupiter.api.condition.JRE;
|
| 42 | +import org.junit.jupiter.params.ParameterizedTest; |
| 43 | +import org.junit.jupiter.params.provider.Arguments; |
| 44 | +import org.junit.jupiter.params.provider.MethodSource; |
36 | 45 |
|
37 | 46 | import org.springframework.boot.autoconfigure.AutoConfigurations;
|
| 47 | +import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Lettuce; |
38 | 48 | import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Pool;
|
39 | 49 | import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
|
40 | 50 | import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
|
62 | 72 |
|
63 | 73 | import static org.assertj.core.api.Assertions.assertThat;
|
64 | 74 | import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
| 75 | +import static org.mockito.BDDMockito.given; |
65 | 76 | import static org.mockito.Mockito.mock;
|
66 | 77 |
|
67 | 78 | /**
|
@@ -112,6 +123,57 @@ void testOverrideRedisConfiguration() {
|
112 | 123 | });
|
113 | 124 | }
|
114 | 125 |
|
| 126 | + @ParameterizedTest |
| 127 | + @MethodSource |
| 128 | + void shouldConfigureLettuceReadFromProperty(Lettuce.ReadFrom.Type when, io.lettuce.core.ReadFrom expected) { |
| 129 | + this.contextRunner.withPropertyValues("spring.data.redis.lettuce.read-from.type:" + when).run((context) -> { |
| 130 | + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); |
| 131 | + assertThat(cf.getClientConfiguration().getReadFrom()).hasValue(expected); |
| 132 | + }); |
| 133 | + } |
| 134 | + |
| 135 | + @Test |
| 136 | + void shouldConfigureLettuceReadFromPropertyRegexType() { |
| 137 | + RedisNodeDescription node1 = mock(RedisNodeDescription.class); |
| 138 | + given(node1.getUri()).willReturn(new RedisURI("127.0.0.1", 6379, Duration.ZERO)); |
| 139 | + RedisNodeDescription node2 = mock(RedisNodeDescription.class); |
| 140 | + given(node2.getUri()).willReturn(new RedisURI("192.12.128.0", 6379, Duration.ZERO)); |
| 141 | + RedisNodeDescription node3 = mock(RedisNodeDescription.class); |
| 142 | + given(node3.getUri()).willReturn(new RedisURI("192.168.255.255", 6379, Duration.ZERO)); |
| 143 | + this.contextRunner |
| 144 | + .withPropertyValues("spring.data.redis.lettuce.read-from.type:" + Lettuce.ReadFrom.Type.REGEX, |
| 145 | + "spring.data.redis.lettuce.read-from.pattern:192.*") |
| 146 | + .run((context) -> { |
| 147 | + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); |
| 148 | + assertThat(cf.getClientConfiguration().getReadFrom()).isNotEmpty(); |
| 149 | + ReadFrom readFrom = cf.getClientConfiguration().getReadFrom().get(); |
| 150 | + List<RedisNodeDescription> selected = readFrom.select(new RedisNodes(node1, node2, node3)); |
| 151 | + assertThat(selected).hasSize(2); |
| 152 | + assertThat(selected).contains(node2, node3); |
| 153 | + }); |
| 154 | + } |
| 155 | + |
| 156 | + @Test |
| 157 | + void shouldConfigureLettuceReadFromPropertySubnetType() { |
| 158 | + RedisNodeDescription node1 = mock(RedisNodeDescription.class); |
| 159 | + given(node1.getUri()).willReturn(new RedisURI("127.0.0.1", 6379, Duration.ZERO)); |
| 160 | + RedisNodeDescription node2 = mock(RedisNodeDescription.class); |
| 161 | + given(node2.getUri()).willReturn(new RedisURI("192.12.128.0", 6379, Duration.ZERO)); |
| 162 | + RedisNodeDescription node3 = mock(RedisNodeDescription.class); |
| 163 | + given(node3.getUri()).willReturn(new RedisURI("192.168.255.255", 6379, Duration.ZERO)); |
| 164 | + this.contextRunner |
| 165 | + .withPropertyValues("spring.data.redis.lettuce.read-from.type:" + Lettuce.ReadFrom.Type.SUBNET, |
| 166 | + "spring.data.redis.lettuce.read-from.cidr-notations:192.12.128.0/32,192.168.255.255/32") |
| 167 | + .run((context) -> { |
| 168 | + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); |
| 169 | + assertThat(cf.getClientConfiguration().getReadFrom()).isNotEmpty(); |
| 170 | + ReadFrom readFrom = cf.getClientConfiguration().getReadFrom().get(); |
| 171 | + List<RedisNodeDescription> selected = readFrom.select(new RedisNodes(node1, node2, node3)); |
| 172 | + assertThat(selected).hasSize(2); |
| 173 | + assertThat(selected).contains(node2, node3); |
| 174 | + }); |
| 175 | + } |
| 176 | + |
115 | 177 | @Test
|
116 | 178 | void testCustomizeClientResources() {
|
117 | 179 | Tracing tracing = mock(Tracing.class);
|
@@ -632,6 +694,36 @@ private String getUserName(LettuceConnectionFactory factory) {
|
632 | 694 | return ReflectionTestUtils.invokeMethod(factory, "getRedisUsername");
|
633 | 695 | }
|
634 | 696 |
|
| 697 | + static Stream<Arguments> shouldConfigureLettuceReadFromProperty() { |
| 698 | + return Stream.of(Arguments.of(Lettuce.ReadFrom.Type.ANY, ReadFrom.ANY), |
| 699 | + Arguments.of(Lettuce.ReadFrom.Type.ANY_REPLICA, ReadFrom.ANY_REPLICA), |
| 700 | + Arguments.of(Lettuce.ReadFrom.Type.LOWEST_LATENCY, ReadFrom.LOWEST_LATENCY), |
| 701 | + Arguments.of(Lettuce.ReadFrom.Type.REPLICA, ReadFrom.REPLICA), |
| 702 | + Arguments.of(Lettuce.ReadFrom.Type.REPLICA_PREFERRED, ReadFrom.REPLICA_PREFERRED), |
| 703 | + Arguments.of(Lettuce.ReadFrom.Type.UPSTREAM, ReadFrom.UPSTREAM), |
| 704 | + Arguments.of(Lettuce.ReadFrom.Type.UPSTREAM_PREFERRED, ReadFrom.UPSTREAM_PREFERRED)); |
| 705 | + } |
| 706 | + |
| 707 | + private static final class RedisNodes implements Nodes { |
| 708 | + |
| 709 | + private final List<RedisNodeDescription> descriptions; |
| 710 | + |
| 711 | + RedisNodes(RedisNodeDescription... descriptions) { |
| 712 | + this.descriptions = List.of(descriptions); |
| 713 | + } |
| 714 | + |
| 715 | + @Override |
| 716 | + public List<RedisNodeDescription> getNodes() { |
| 717 | + return this.descriptions; |
| 718 | + } |
| 719 | + |
| 720 | + @Override |
| 721 | + public Iterator<RedisNodeDescription> iterator() { |
| 722 | + return this.descriptions.iterator(); |
| 723 | + } |
| 724 | + |
| 725 | + } |
| 726 | + |
635 | 727 | @Configuration(proxyBeanMethods = false)
|
636 | 728 | static class CustomConfiguration {
|
637 | 729 |
|
|
0 commit comments