feat: Add support for connecting to Redis clusters (#24924)

This PR adds support to Appsmith server to be able to connect to
ElastiCache Redis with cluster mode turned on. When the
`APPSMITH_REDIS_URL` is set to `redis://...`, the current default
behaviour is preserved. But if it is set to `redis-cluster://...`, then
we setup a pooled connection with cluster mod enabled.
This commit is contained in:
Shrikant Sharat Kandula 2023-06-30 17:31:47 +05:30 committed by GitHub
parent 67567e1ee7
commit 6c7ec746e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 53 additions and 2 deletions

View File

@ -224,6 +224,14 @@
<scope>test</scope>
</dependency>
<!-- Needed for establishing pooled connections to ElastiCache Redis with cluster mode enabled. -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite-engine</artifactId>
@ -455,4 +463,4 @@
</dependencies>
</dependencyManagement>
</project>
</project>

View File

@ -8,10 +8,17 @@ import com.fasterxml.jackson.databind.json.JsonMapper;
import io.lettuce.core.resource.ClientResources;
import io.micrometer.observation.ObservationRegistry;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.connection.lettuce.observability.MicrometerTracingAdapter;
import org.springframework.data.redis.core.ReactiveRedisOperations;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
@ -28,9 +35,11 @@ import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession;
import java.net.URI;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@Configuration
@Slf4j
@ -38,6 +47,9 @@ import java.util.Map;
@EnableRedisWebSession(maxInactiveIntervalInSeconds = 2592000)
public class RedisConfig {
@Value("${appsmith.redis.url:}")
private String redisURL;
/**
* This is the topic to which we will publish & subscribe to. We can have multiple topics based on the messages
* that we wish to broadcast. Starting with a single one for now.
@ -49,6 +61,31 @@ public class RedisConfig {
return new ChannelTopic("appsmith:queue");
}
@Bean
@Primary
public ReactiveRedisConnectionFactory reactiveRedisConnectionFactory() {
final URI redisUri = URI.create(redisURL);
final String scheme = redisUri.getScheme();
switch (scheme) {
case "redis" -> {
return new LettuceConnectionFactory(redisUri.getHost(), redisUri.getPort());
}
case "redis-cluster" -> {
// For ElastiCache Redis with cluster mode enabled, with the configuration endpoint.
final LettuceClientConfiguration config = LettucePoolingClientConfiguration
.builder()
.build();
final RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration();
clusterConfig.addClusterNode(new RedisNode(redisUri.getHost(), redisUri.getPort()));
return new LettuceConnectionFactory(clusterConfig, config);
}
default -> throw new InvalidRedisURIException("Invalid redis scheme: " + scheme);
}
}
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new JSONSessionRedisSerializer();
@ -179,4 +216,10 @@ public class RedisConfig {
}
}
private static class InvalidRedisURIException extends RuntimeException {
public InvalidRedisURIException(String message) {
super(message);
}
}
}

View File

@ -54,7 +54,7 @@ sentry.debug=false
sentry.environment=${APPSMITH_SERVER_SENTRY_ENVIRONMENT:}
# Redis Properties
spring.data.redis.url=${APPSMITH_REDIS_URL}
appsmith.redis.url=${APPSMITH_REDIS_URL}
# Mail Properties
# Email defaults to false, because, when true and the other SMTP properties are not set, Spring will try to use a