Why Redis Increment Returns Null When Using @Transactional with Transaction Support in Spring Boot
The production failure where a customer‑service event creation stopped each morning was traced to RedisTemplate’s increment returning null because @Transactional combined with enabled Redis transaction support caused the command to be queued in a MULTI/EXEC block, which is fixed by using separate non‑transactional and transactional StringRedisTemplate beans.
The article describes a production issue where creating a customer‑service event fails every morning until the microservice is restarted. The failure is caused by the Redis increment operation returning null , which leads to a cascade of errors.
Initial investigation shows that the code uses return redisTemplate.opsForValue().increment("count", 1); and that the increment call returns null only in the early‑morning runs. The team first hypothesized a connection‑leak, but other Redis‑using features worked fine, so this was ruled out.
The second hypothesis focused on Redis transactions. Official Redis documentation states that increment returns null when executed inside a pipeline or a transaction. The service implementation is annotated with @Transactional , which may cause the Redis command to be treated as part of a transaction.
Experiments were performed:
When Redis transaction support is disabled, the increment call returns the expected numeric value, regardless of the @Transactional annotation.
When Redis transaction support is enabled ( setEnableTransactionSupport(true) ) and the method is marked with @Transactional , the increment call consistently returns null , reproducing the production symptom.
Root cause analysis revealed that enabling Redis transaction support makes RedisTemplate bind a connection. Inside a Spring @Transactional method, the bound connection treats the increment as part of a Redis MULTI/EXEC block, queuing the command and returning null until the transaction is committed.
Two remediation strategies are proposed:
Disable transaction support after each Redis transaction. This approach works but still fails if a Redis command is issued inside a @Transactional method while the transaction is active.
Use two separate StringRedisTemplate beans: one with transaction support for explicit Redis transaction scenarios, and another without transaction support for ordinary Redis commands. The configuration example: \@Bean\npublic StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {\n StringRedisTemplate template = new StringRedisTemplate(factory);\n template.setEnableTransactionSupport(false);\n return template;\n}\n\@Bean\npublic StringRedisTemplate stringRedisTemplateTransaction(RedisConnectionFactory factory) {\n StringRedisTemplate template = new StringRedisTemplate(factory);\n template.setEnableTransactionSupport(true);\n return template;\n} Service classes can then inject the appropriate bean and avoid the null result.
After applying the second solution, the Redis increment returns the correct count value even inside @Transactional methods, and the original production issue is resolved.
Java Tech Enthusiast
Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.