REDIS TRANSACTION IN JAVA

Engin Demircioğlu
turkcell
Published in
9 min readDec 4, 2021

--

We had developed an application to demonstrate how Redis can be used in a Java application. We had also studied cache capabilities of Redis in Java. You can check the article here. Now it is time to look through one of the advanced topics in Redis.

Note: The screenshots were taken from IntelliJ IDEA version 2021.2.1, TablePlus version 4.5.0, Docker Desktop version 3.6.0 and Terminal version 2.10.

TRANSACTION SUPPORT IN REDIS

Redis is mostly used for purposes like session cache and in-memory object store. In some cases, stored objects can be used in different threads or services at the same time. As an example we can think of an online shopping application in which Redis is used to save the shopping chart. When a user added objects to his/her chart, the application should update the stock count of these objects. At the same time, another user also can add the same objects. This scenario forces the application to check the stock counts of the objects. So the application should save the chart and the stock counts considering other users or scenarios. Redis transaction support can be used to meet these validity needs in multi-user applications. We will study transaction support of Redis both in redis-cli and a Java application.

Redis transaction commands
Redis transaction commands

Redis transaction processes are managed by five commands which can be seen in the picture at left side.

We will firstly see the usage of these commands in command line interface of Redis (redis-cli). We had installed Redis by using docker application. You can check other download options in official website below.
https://redis.io/download

How to open Redis Cli from Docker Application
How to open redis-cli from docker application

We can open redis-cli application from docker by using the link that can be seen in the picture at left side. Alternatively, you can open it by running the command “redis-cli” in terminal applications or command prompt.

Redis command line application interface

A terminal window will be opened and we can switch to redis-cli by using the command “redis-cli”. As you will see in the picture at left side, we connected to redis-cli in host “127.0.0.1” and port “6379”.

In order to start a transaction process in Redis we firstly run the command “MULTI”. After this command Redis will queue all the commands that we apply. We will lastly need to run the command “EXEC” to commit the transaction. The command “DISCARD” will end the transaction by rollbacking all the previous commands. The command “WATCH” is used to prevent committing the current transaction if some keys are changed outside the current transaction. The command “UNWATCHED” is used to cancel the watching process on the related key.

Running transaction commands

As you will see at the terminal screenshot at the left side, we started a transaction with the command “MULTI”. The answer “OK” means that a transaction is started and next commands will be queued. We want to store a key called “sample_key” with a value of 100. After running the second command (SET sample_key 100) we saw that there is no object stored in Redis database with a key named “sample_key”. We can see that Redis queued this command because the transaction has not been committed yet. Then we continue to update the key by incrementing it by firstly 10 and then by 1. We used the commands “INCRBY” and “INCR” to update the key value. Then we run the command “EXEC” to commit the transaction. You can see that Redis gave the output of queued commands in sequence. When we check the Redis database in host “127.0.0.1” and port “6379”, we can see that there is an object stored with a key named “sample_key” and value of “111”.

For another example, we stored a key called “sample_key2” with a value of “100”. Then we started a new transaction with the command “MULTI”. We incremented the key by “1” twice and then we run the command “DISCARD” to rollback the process. If we check the Redis database, we will see that the key value is still “100” because the transaction is rollbacked.

Sample objects stored in Redis
Watching keys

We opened two terminal applications and switched to redis-cli in both terminals as seen in picture at left side. We started to watch the key “sample_key” by running the command “WATCH”. The value of the key was “111” in the previous part. We started a transaction in “Terminal 1” and updated the key by running the command “INCRBY sample_key 100” to update the value to “211". At this point, we run the command “INCR sample_key” at the “Terminal 2” and we see that Redis gave an output “(integer) 112” which means the object value is stored in Redis database as “112”. After this update process we switch to “Terminal 1” and commit the transaction with command “EXEC”. But this time the result is “(nil)” which means the object could not be stored to database with value “211”. Because the key was being watched during the transaction process in “Terminal 1" and the key value is changed outside the transaction in “Terminal 2”.

REDIS IN JAVA

We had studied Redis in a Java application in the previous article which you can check from the link below.

https://medium.com/@engindemircioglu/a-simple-redis-implementation-in-java-d1000610f8ad

Now, we will continue developing this application for transactional purposes by using Redis transaction commands. We edited the project application class (RedisDemoApplication.java) to reach to the “run” method which is being called when the application is started. We will override this method to run Redis transaction commands.

New method “saveNumbersUsingTransaction” is added to try Redis transaction commands
The method “saveNumbersUsingTransaction” content in RedisDemoApplication

We added a new method named “saveNumbersUsingTransaction”. In this method, we will generate randomly 5 numbers between 0 and 5 and we will save these numbers to Redis database with a key named “numbers”. But if one of the numbers is 5 then we will throw an exception and we will not save the result to Redis. This is a sample scenario to try Redis transaction and rollback processes.

We also added the service method “saveNumbersUsingTransaction” to “RedisService” class in where all transaction commands will be run.

The method “saveNumbersUsingTransaction” definition in RedisService interface
The method “saveNumbersUsingTransaction” content in RedisServiceImpl

We added “RedisTemplate” bean to “RedisServiceImpl” class by using autowiring property of Spring in order to reach to all of the commands of Redis. In order to execute operations in the same session, we started a “session callback” thread by calling the “execute” method of “RedisTemplate“ bean. This method allows transactions to take place through the use of Redis transaction commands. As you will see from the overrided method content, transaction process will start with calling “multi” command of the parameter “operations”. Then we loop the number list and check if the number is 5 or not. If the number is 5 then we throw an exception typed “IllegalArgumentException” and we call the “discard” method to rollback the session in the catch block. If the number is not 5 then we update the key value with the number and we queue the saving command of Redis. The key value will begin with the text of “Saved numbers” and then the numbers with underscore character between them will be added to the key value respectively. If there is no number 5 in the list then the command “exec” will be called to commit the transaction. This command will save the key and the value to Redis database.

After running the application, we will see the output log as seen below. If there is a number 5 in the list, the application will throw an exception and the key will not be saved to Redis database.

An exception occurred because the third number is 5
There is no key saved to Redis database

If the parameter list does not contain the number 5, the application will output the Redis process count and the key will be saved to Redis database as seen below.

Numbers are processed respectively
There is a key named “numbers” saved to Redis database

SPRING TRANSACTION SUPPORT FOR REDIS

Another way of using Redis transaction commands is getting support of Spring transaction module. There is a feature in “RedisTemplate” bean which makes it possible to use Spring transaction methods in order to commit and rollback Redis transaction commands automatically. We will set this feature and add an annotation to “RedisConfig” class as seen below.

Modifications for Spring transaction support.

We set the value of the property “EnableTransactionSupport” to “true” and we added the annotation “EnableTransactionManagement” to the configuration class “RedisConfig”.

We have to modify our application to enable Spring transaction support. We will add needed dependencies to our “pom.xml” file. We will need an embedded datasource implementation to use Spring transaction management. We preferred “h2” database but any of the supported databases can also be used.

Jdbc and datasource dependencies

We added a method named “saveNumbersUsingTransactionWithAnnotation” to try Redis transaction commands with Spring transaction support.

New method “saveNumbersUsingTransactionWithAnnotation” is added to try Spring transaction support

In this method, we will generate randomly 5 numbers between 0 and 5 and we will save these numbers to Redis database with a key named “numbersAsText”. But if one of the numbers is 5 then we will throw an exception and we will not save the result to Redis.

We also added the service method “saveNumbersUsingTransactionWithAnnotation” to “RedisService” class in where all transaction commands will be run.

The method “saveNumbersUsingTransactionWithAnnotation” definition in RedisService interface
The method “saveNumbersUsingTransactionWithAnnotation” content in RedisServiceImpl

The key value will begin with the text of “Saved numbers” and then the text equivalents of numbers with underscore character between them will be added to the key value respectively. As you will see above, we added “Transactional” annotation to the method. This will enable Spring transaction when we call the service method. At the previous method, we used “exec” and “discard” commands to commit and rollback Redis transaction but in this method these processes will be handled by Spring automatically. So we did not use these Redis commands in the method content as seen above.

After running the application, we will see the output log as seen below. If there is a number 5 in the list, the application will throw an exception and the key will not be saved to Redis database.

An exception occurred because the fourth number is 5
There is no key named “numbersAsText” saved to Redis database

If the number 5 is not in the list, the key will be saved to Redis database as seen below.

Numbers are processed respectively
There is a key named “numbersAsText” saved to Redis database

Spring transaction is committed when the application exits the method without any exception. It is also rollbacked when an exception occurred in the method call. In order to check the rollback mechanism we will modify the transaction annotation definition. We will update the property “noRollBackFor” for “IllegalArgumentException” class. This modification will not rollback the transaction even if the list contains the number 5.

noRollbackFor modification

After running the application, we will see the output log as seen below.

An exception occurred because the second number is 5
Eventhough there was an exception, the number 4 was saved to Redis database with key “numbersAsText”

As a result, we can use Redis transaction capabilities in different ways and Spring framework makes it easy for us to integrate Redis to our applications.

You can download the application from the github address below.

https://github.com/dengin/redisdemo.git

--

--