Redis exception with transactions with Jedis client

exceptionjedisredis

In order to avoid duplicates in my redis channel I'm checking if the message is already there by keeping an index in Redis set. Following is my implementation. However, it is giving an exception.

redis.clients.jedis.exceptions.JedisDataException: Please close pipeline or multi block before calling this method.
    at redis.clients.jedis.Response.get(Response.java:23)

Here is the implementation.

          Jedis jedis = pool.getResource();

          String id = message.getId();
          Transaction transaction = jedis.multi();
          redis.clients.jedis.Response<java.lang.Boolean> response = transaction.sismember(ID_SET_REDIS_KEY, id);
          if (response != null && !response.get().booleanValue()) {
                //add it to the 
                transaction.sadd(ID_SET_REDIS_KEY, id);
                transaction.publish(redisChannelName, message);
            }
            transaction.exec();
            pool.returnResource(jedis);

I need to do the get inside the transaction because there are multiple publishers who may publish the exact same message.

Best Answer

You can't have the result of your get before you end the transaction.

If you are using Redis > 2.6.X, what you can do is use a Lua Script to create a function with you logic. See Redis Lua

This is exactly what I did to guarantee concurrency in my project.

Edit: Including a more complete example

You should create something like a PUBLISHNX script (not tested):

local shouldPublish = redis.call('SISMEMBER', KEYS[1], ARGV[1])

if shouldPublish == 0
    redis.call('SADD', KEYS[1], ARGV[1])
    redis.call('PUBLISH', ARGV[2], ARGV[3])
end

And you pass all the arguments necessary, channel, messageId, message, controlKey.

PS. Wei Li is right, you could accomplish the same result using WATCH and a loop for retrying in case of concurrency, but I still prefer using a Lua script.