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.
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):
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.
Based on @Axexandre's comment above I used the following piece of code to do perform the operation.
import redis.clients.jedis.Jedis;
Here is some more information about the script. It took some time to understand the syntax.
if redis.call('sismember', KEYS[1], ARGV[1]) == 1
eqavalent toSISMEMBER setvar joe
For some reason if I don't have this
jedis.sadd("a", "b");
line I get an exception (see below).