Spring Redis template with Jedis connection factory , Redis standalone configuration and multi-threading

Swinky picture Swinky · Jul 30, 2018 · Viewed 7.3k times · Source

I am using Spring Redis template in a multi-threaded environment. One thread saves the data into Redis and other one (the scheduler) fetches the data from it. JedisConnectionFactory is used in redis template. Following is the code snippet for obtaining redis connection:

JedisConnectionFactory jedisConnectionFactory() {

    JedisConnectionFactory jedisConnectionFactory = null;

    try {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(hostName,
                port);
        jedisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration);
    } catch (RedisConnectionFailureException e) {
        LOGGER.error("Connection break with redis " + e.getMessage());
    }

    return jedisConnectionFactory;
}

/**
 * Redis template.
 *
 * @return the redis template
 */
@Bean
public RedisTemplate<String, Object> redisTemplate() {
    final RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
    template.setConnectionFactory(jedisConnectionFactory());
    template.setValueSerializer(new GenericToStringSerializer<Object>(Object.class));
    template.setEnableTransactionSupport(true);
    return template;
}

The instance of redis template is obtained using constructor auto-wiring as follows:

@Autowired
public A(RedisTemplate<String, Object> redisTemplate) {
    this.redisTemplate = redisTemplate;
}

I am getting an exception while fetching data from Redis using "findAll()" method of redis template:

org.springframework.data.redis.RedisConnectionFailureException: java.net.SocketException: Connection reset by peer: socket write error; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Connection reset by peer: socket write error

Below are my findings:

  1. connection reset by peer exception occurs when TCP socket is to be "closing" and your code to not have yet been notified. (the thread for findAll has not been notified for the closed connection).
  2. Redis template is thread safe (only if connection pool is used) and handles connection management on its own. It may happen that when thread saved data into redis and closed the connection and during that only, the fetch operation occured and demanded data. In that case, the server might have issued RST command but fetch operation might have not obtained it.
  3. Jedis pool config can be used; but there are depreciated methods in this that can later cause upgradation issues.

Please suggest best method to handle multi-threading with "JedisConnectionFactory", "RedisStandAloneConfiguration" and "RedisTemplate".

Answer

Swinky picture Swinky · Aug 2, 2018

The cause of this problem was :

Redis Template is thread safe but only when it uses connection pooling; If connection pool is not used then the simultaneous connection calls result in RST signal from either (server / client) side and thus 'connection reset by peer' exception is thrown. SO, if we need to use Redis template then create a connection pool and set 'maxIdle' and 'maxTotal' for the pool config. Also, make sure that the system should not goes down (sleep) in any case.

Code that worked correctly:

@Bean
JedisConnectionFactory jedisConnectionFactory() {

    JedisConnectionFactory jedisConnectionFactory = null;

    try {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(hostName,
                port);
        jedisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration);
        jedisConnectionFactory.getPoolConfig().setMaxTotal(50);
        jedisConnectionFactory.getPoolConfig().setMaxIdle(50);
    } catch (RedisConnectionFailureException e) {
        e.getMessage();
    }

    return jedisConnectionFactory;
}


@Bean
public RedisTemplate<String, Object> redisTemplate() {
    final RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
    template.setConnectionFactory(jedisConnectionFactory());
    template.setValueSerializer(new GenericToStringSerializer<Object>(Object.class));
    template.setEnableTransactionSupport(true);
    return template;
}