How to implement Redis Multi-Exec by using Spring-data-Redis

java, redis, spring, spring-data

Solution

The reason for the exception is probably that the Spring template implementation does not reuse the same connection for `.multi()` and `.exec()`. You can try to use `execute()` via a callback:

private RedisTemplate template = ...;

template.execute(

  new RedisCallback() {

    @Override
    public Object doInRedis(RedisConnection connection)
      throws DataAccessException {

      connection.multi();

      //do whatever you need, like deleting and repopulating some keys

      connection.expire(CHANNEL_KEY.getBytes(), EXPIRE_SECS);
      connection.exec();
      return null;
    }

  }

);

Problem

All Spring Configuration is written properly. Non-Multi-Exec Redis operations work perfectly. ``` @Autowired @Qualifier("stringRedisTemplate") StringRedisTemplate template; void test(){ template.multi(); template.boundValueOps("somevkey").increment(1); template.boundZSetOps("somezkey").add("zvalue", timestamp); template.exec(); } ``` After running above code through Junit Test, Exceptions are thrown. ``` org.springframework.data.redis.RedisSystemException: Unknown exception; nested exception is org.springframework.data.redis.RedisSystemException: Unknown jedis exception; nested exception is java.lang.NullPointerException at org.springframework.data.redis.connection.jedis.JedisUtils.convertJedisAccessException(JedisUtils.java:93) at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.translateExceptionIfPossible(JedisConnectionFactory.java:155) at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58) at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) at $Proxy66.appendUserStream(Unknown Source) at com.uniu.test.repository.StreamCacheRepositoryTest.testAppendUserStream(StreamCacheRepositoryTest.java:23) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) Caused by: org.springframework.data.redis.RedisSystemException: Unknown jedis exception; nested exception is java.lang.NullPointerException at org.springframework.data.redis.connection.jedis.JedisConnection.convertJedisAccessException(JedisConnection.java:119) at org.springframework.data.redis.connection.jedis.JedisConnection.exec(JedisConnection.java:523) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.data.redis.core.CloseSuppressingInvocationHandler.invoke(CloseSuppressingInvocationHandler.java:58) at $Proxy70.exec(Unknown Source) at org.springframework.data.redis.core.RedisTemplate$1.doInRedis(RedisTemplate.java:416) at org.springframework.data.redis.core.RedisTemplate$1.doInRedis(RedisTemplate.java:412) at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:162) at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:133) at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:121) at org.springframework.data.redis.core.RedisTemplate.exec(RedisTemplate.java:412) at com.uniu.repository.impl.StreamCacheRepositoryRedisImpl.appendUserStream(StreamCacheRepositoryRedisImpl.java:37) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155) ... 33 more Caused by: java.lang.NullPointerException at redis.clients.jedis.BinaryTransaction.exec(BinaryTransaction.java:31) at org.springframework.data.redis.connection.jedis.JedisConnection.exec(JedisConnection.java:521) ... 54 more ``` I check redis server, the above two commands have been executed and the result is correct. So the problem comes at last line of the code (template.exec()). When underlying JedisClient try to get multibulk response from EXEC, it seems to throw NullPointerException I use spring-data-redis 1.0.0.RELEASE Thanks for help.

Original source