SpringBoot Elasticache JedisMovedDataException: MOVED - spring-boot

Trying to use SpringBoot with SpringData with Elasticache:
application.properties:
spring.redis.host=XXXX-dev.XXXX.clusXXXcfg.XXX.cache.amazonaws.com
spring.redis.port=6379
CacheConfiguration:
#Configuration
#PropertySource("classpath:application.properties")
public class CacheConfiguration {
#Value("${spring.redis.host}")
private String redisHostName;
#Bean
public RedisTemplate<String, Company> redisTemplate() {
RedisTemplate<String, Company> template = new RedisTemplate();
template.setConnectionFactory(jedisConnectionFactory());
return template;
}
#Bean
JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory factory = new JedisConnectionFactory();
factory.setHostName(redisHostName);
factory.setUsePool(true);
return factory;
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Service call:
#Autowired
RedisTemplate<String, Company> redisTemplate;
private ValueOperations valueOperations;
#PostConstruct
private void init() {
valueOperations = redisTemplate.opsForValue();
}
#Override
public String createOtp(Company company) {
String token = UUID.randomUUID().toString();
valueOperations.set(token, company);
valueOperations.getOperations().expire(token, 5, TimeUnit.MINUTES);
return token;
}
Error:
org.springframework.data.redis.ClusterRedirectException: Redirect: slot 7228 to 10...:6379.*
redis.clients.jedis.exceptions.JedisMovedDataException: MOVED 7228 10...:6379.*
The question is - what is wrong with configuration?

You're running your Elasticache in Redis Cluster mode (only Redis Cluster responds with MOVED) but the connection factory is configured in standalone mode.
Spring Boot can auto-configure all the things you've set up manually for you. Basically, remove your CacheConfiguration class (or at least remove the majority of code):
#Configuration
public class CacheConfiguration {
#Bean
public RedisTemplate<String, Company> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Company> template = new RedisTemplate();
template.setConnectionFactory(connectionFactory);
return template;
}
}
And then configure the following properties in your application.properties file:
spring.redis.cluster.nodes=<node_host>:<port> # Comma-separated list of "host:port" pairs to bootstrap from.
Spring Boot loads application.properties by default and the Redis auto-config configures a RedisTemplate<Object, Object> bean by default. Specializing beans is a valid use-case – do not duplicate what's already provided by the auto-config, especially if you want to achieve what auto-config does.
See also:
Common application properties
Externalized Configuration

Related

Redis Cache Pool: How to validate if redis pooling configuration is working?

Folks,
I have a redis cache connection for my application. Which recently got stalled due to connections increase. So I have implimented connection pooling for my redis connection in my application. The configuration is as below:
#Configuration
public class CacheConfig {
#Value("${cache.host.name}")
private String cacheHostName;
#Value("${cache.database.number}")
private Integer database;
#Value("${cache.port.number}")
private Integer portNumber;
#Value("${cache.pool.max-total}")
private Integer maxTotal;
#Value("${cache.pool.max-idle}")
private Integer maxIdle;
#Value("${cache.pool.min-idle}")
private Integer minIdle;
#Bean
JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration redisConfig =
new RedisStandaloneConfiguration(cacheHostName, portNumber);
redisConfig.setDatabase(database);
JedisConnectionFactory redisConnectionFactory =
new JedisConnectionFactory(redisConfig, jedisClientConfiguration());
return redisConnectionFactory;
}
#Bean
JedisClientConfiguration jedisClientConfiguration() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); // GenericObjectPoolConfig
jedisPoolConfig.setMaxTotal(maxTotal);
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
return JedisClientConfiguration.builder().usePooling().poolConfig(jedisPoolConfig).build();
}
#Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory());
return template;
}
}
But I'm not able to validate if my configuration is actually in effect. Any suggestion to validate this is appreciated!

Multi redis connection issue

I am trying to connect my project with two Redis connection but I got
Consider defining a bean named 'redisTemplate' in your configuration.
I don't know if my code is correct but this is my code config, my spring boot version 2.4.2 :
#Configuration
public class RedisConfiguration {
#Autowired
private EditorialRedisPropertyConfiguration editorialRedisConfiguration;
#Autowired
private ProductRedisPropertyConfiguration productRedisConfiguration;
#Bean(name = "editorialRedisTemplate")
public RedisTemplate<String, ?> editorialRedisTemplate(
#Qualifier(value = "redisEditorialConnectionFactory") RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, ?> template = new RedisTemplate<>();
template.setConnectionFactory(redisEditorialConnectionFactory());
return template;
}
#Bean(name = "productRedisTemplate")
public RedisTemplate<String, ?> productRedisTemplate(
#Qualifier(value = "redisProductConnectionFactory") RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, ?> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
#SuppressWarnings("deprecation")
private Jackson2JsonRedisSerializer<Object> initJackson2JsonRedisSerializer() {
. ..
}
#Bean(name = "redisEditorialConnectionFactory")
#Primary
public LettuceConnectionFactory redisEditorialConnectionFactory() {
...
}
#Bean(name = "redisProductConnectionFactory")
#Primary
public LettuceConnectionFactory redisProductConnectionFactory() {
.....
}
}
with application config :
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration
Not quite sure about your application code but this can be fixed by simply creating another bean, given you don't use that you're free to create it from any redis config.
#Bean(name = "productRedisTemplate")
public RedisTemplate<String, ?> redisTemplate(
#Qualifier(value = "redisProductConnectionFactory") RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, ?> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}

springboot multiple module, use #EnableCaching can not be effective in imported module

[enter image description here][1]There were tow module, module B include some persist code(entity dao service), module A include controller and import module B, the cachemanager config bean was in moudle A, when i run the application that i find #Cacheable annotation on service wasn't work, someone who can help me
code as follow
#Configuration
public class RedisConfig extends CachingConfigurerSupport {
#Resource
private LettuceConnectionFactory lettuceConnectionFactory;
#Bean
#SuppressWarnings("unchecked")
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
RedisSerializer<Object> jsonString = new FastJsonRedisSerializer<>(Object.class);
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(jsonString);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setHashValueSerializer(jsonString);
return redisTemplate;
}
/**
* CacheManager
*
* #return
*/
#Bean
public CacheManager cacheManager(LettuceConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5));
RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
RedisCacheManager cacheManager = RedisCacheManager
.builder(RedisCacheWriter.lockingRedisCacheWriter(factory))
.cacheDefaults(redisCacheConfiguration)
.disableCachingNullValues()))
.transactionAware().build();
return cacheManager;
}
}
```java
what should i do
[1]: https://i.stack.imgur.com/d2NNN.png

How to capture Redis connection failure on Spring Boot Redis Session implementation?

I have implemented Redis session management using LettuceConnectionFactory on my Spring Boot java application. I couldn't figure out a way to capture Redis connection failure. Is there a way to capture the connection failure?
Spring Boot version: 2.2.6
Code:
#Configuration
#PropertySource("classpath:application.properties")
#EnableRedisHttpSession
public class HttpSessionConfig extends AbstractHttpSessionApplicationInitializer {
Logger logger = LoggerFactory.getLogger(HttpSessionConfig.class);
#Value("${spring.redis.host}")
private String redisHostName;
#Value("${spring.redis.port}")
private int redisPort;
#Value("${spring.redis.password}")
private String redisPassword;
private #Value("${spring.redis.custom.command.timeout}")
Duration redisCommandTimeout;
private #Value("${spring.redis.timeout}")
Duration socketTimeout;
#Bean
LettuceConnectionFactory lettuceConnectionFactory() {
final SocketOptions socketOptions = SocketOptions.builder().connectTimeout(socketTimeout).build();
final ClientOptions clientOptions = ClientOptions.builder()
.socketOptions(socketOptions)
.build();
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.commandTimeout(redisCommandTimeout)
.clientOptions(clientOptions)
.readFrom(ReadFrom.SLAVE_PREFERRED)
.build();
RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration(redisHostName,
redisPort);
final LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(serverConfig,
clientConfig);
return lettuceConnectionFactory;
}
#Bean
public RedisTemplate<Object, Object> sessionRedisTemplate() {
final RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(lettuceConnectionFactory());
return template;
}
#Bean
public ConfigureRedisAction configureRedisAction() {
return ConfigureRedisAction.NO_OP;
}
}

Error using Spring AOP with Dynamic Proxy with JMS Template on Weblogic 12.2.1

I'm getting an error when I try to use in the same project Spring Template JMS to messaging to ActiveMQ and Spring AOP to auditing and handle exceptions on Weblogic 12.2.1. Error occurs on server startup.
If I config aspect to use CGLIB, I got exceptions from Weblogic, and I prefer, if it's possible to maintain using of Dynamic Proxy. Does anyone already had this problem or have any idea what could cause it?
My aspect config class:
#Configuration
#EnableAspectJAutoProxy
#lombok.extern.slf4j.Slf4j
public class AspectConfig {
#Bean
public LoggingErrorAspect loggingErrorAspect(){
return new LoggingErrorAspect();
}
}
Message Listener config class:
#Configuration
#EnableJms
#lombok.extern.slf4j.Slf4j
public class MessagingListenerConfig {
#Autowired
ConnectionFactory connectionFactory;
#Bean
public JmsListenerContainerFactory<?> jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setConcurrency("1-1");
return factory;
}
}
Stack:
weblogic.application.ModuleException:
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean
named
'org.springframework.jms.config.internalJmsListenerEndpointRegistry'
is expected to be of type
'org.springframework.jms.config.JmsListenerEndpointRegistry' but was
actually of type 'com.sun.proxy.$Proxy213'
You don't show where you are injecting the JmsListenerEndpointRegistry (or RabbitTemplate) but you have to inject by interface when you proxy with JDK proxies.
Why are you advising the registry - what are you trying to achieve with that? It doesn't provide many interfaces so it can't be referenced once proxied, except by DisposableBean and SmartLifecycle.
The RabbitTemplate can be proxied but you need to inject RabbitOperations, not RabbitTemplate.
I fix it the problem. I had to change the broker to AMQP RabbitMQ, and I needed to use interface instead of classes in all my config. After it, startup load works and aspect too.
Here is Aspect Config and Messaging Config.
AspectConfig:
#Configuration
#EnableAspectJAutoProxy
#lombok.extern.slf4j.Slf4j
public class AspectConfig {
#Bean
public LoggingErrorAspect loggingErrorAspect(){
return new LoggingErrorAspect();
}
}
MessagingConfig:
#Configuration
#PropertySources({ #PropertySource("classpath:/config/messaging.properties") })
#ComponentScan("br.com.aegea.scab.notification")
#lombok.extern.slf4j.Slf4j
public class MessagingConfig {
public static final String ERROR_QUEUE = "ERROR_QUEUE";
public static final String EMAIL_QUEUE = "EMAIL_QUEUE";
#Autowired
private Environment environment;
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(environment.getProperty("spring.rabbitMQ.host"));
connectionFactory.setUsername(environment.getProperty("spring.rabbitMQ.user"));
connectionFactory.setPassword(environment.getProperty("spring.rabbitMQ.password"));
return connectionFactory;
}
#Bean
public MessageConverter jsonMessageConverter() {
return new JsonMessageConverter();
}
#Bean
public RabbitOperations emailRabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(EMAIL_QUEUE);
template.setMessageConverter(jsonMessageConverter());
return template;
}
#Bean
public MessageListenerContainer listenerContainer() {
SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory());
listenerContainer.setQueueNames(EMAIL_QUEUE);
listenerContainer.setMessageConverter(jsonMessageConverter());
listenerContainer.setMessageListener(messageReceiver());
listenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
return listenerContainer;
}
#Bean
public MessageListener messageReceiver() {
return new MessageReceiver();
}
}

Resources