Support for pipeline and transactions in JedisCluster for Redis 3.x Cluster

4,375 views
Skip to first unread message

Dmitry Dragan

unread,
Jun 26, 2015, 11:56:22 AM6/26/15
to jedis...@googlegroups.com
Hi guys, 

First all, thank you! You are doing really great stuff!

I`m curious is there are plans to add pipeline and transactions functionality to JedisCluster in near future?

neha narayan

unread,
Jun 28, 2015, 11:33:13 AM6/28/15
to jedis...@googlegroups.com
One related query: Can Jedis class with pipeline support be used with one Redis Cluster node?

This is what I am thinking:
- Using hashtags, put all the related keys in one Redis Cluster node
- Discover the redis cluster node for the specific key
- Then use Jedis with pipeline to send all the requests.

Will this work?

Thanks,
-Neha

Dmitry Dragan

unread,
Jul 1, 2015, 2:47:30 PM7/1/15
to jedis...@googlegroups.com
Hi Neha,

I think I have fond solution. 
We can use JedisCluster for processing simple operations to Redis Cluster (including splitting data by hash tags).
As far as we control distribution of keys on the same instance, for pipeline and transaction operations we can use SharedJedis.
By shardedJedis.getShard(key)we can get Jedis connection to appropriate instance.

It seems like it works, but I hope that someone from Jedis team could warn about pitfalls of this approach.

Simple snippet example: 

Set<HostAndPort> hosts = new HashSet<>();
List<JedisShardInfo> list = new ArrayList<>();
for (int i = 1; i < 7; i++) {
    hosts
.add(new HostAndPort("localhost", Integer.parseInt("3000" + i)));
    list
.add(new JedisShardInfo("localhost", Integer.parseInt("3000" + i)));
}

try (JedisCluster jedisCluster = new JedisCluster(hosts);
     
ShardedJedis shardedJedis = new ShardedJedis(list)) {
   
jedisCluster.hset("wmap", "20","4");
   

   
Jedis jedis = shardedJedis.getShard("wmap");
   
Transaction t = jedis.multi();
    t
.hset("wmap","20","2");
    t
.exec();
   
Pipeline p = jedis.pipelined();
    p
.hset("wmap", "2", "5");
   
Response<String> r = p.hget("wmap","2");
    p
.sync();
   
String v2 = r.get();
    jedis
.close();
}



Marcos NiLs

unread,
Jul 1, 2015, 2:52:16 PM7/1/15
to jedis...@googlegroups.com
Dmitry / Neha, 

To implement multi-key / pipeline commands in JedisCluster is not easy as a lot things need to be considered when using it. 

I encourage you to read the following thread about it https://github.com/xetorthio/jedis/issues/990 and give your opinions about what you think. 

We're working on that and we're trying to come up with the best approach to implement it.

Best, 

Marcos.

Dmitry Dragan

unread,
Jul 24, 2015, 3:19:48 AM7/24/15
to Jedis, dmitro...@gmail.com
My solution was wrong as far as ShardedJedis use for sharding MURMUR algorithm which has nothing similar with slots which are based on CRC16.

Better one is reuse current JedisCluster:
1) Get nodeMap <String,JedisPool> where key is host. 
2) Take any host from it for getting information about slot distribution.
3) Store slot distribution as TreeMap
4) Get slot by key, get host by slot, get JedisPool by host, get Jedis from JedisPool
protected void initJedisNodeMap() {
   
try {
        nodeMap
= jedisCluster.getClusterNodes();
       
String anyHost = nodeMap.keySet().iterator().next();
        slotHostMap
= getSlotHostMap(anyHost);
   
}catch (JedisClusterException e){
       
logger.error(e.getMessage());
   
}
}

protected Jedis getJedis(String key) {
   
int slot = JedisClusterCRC16.getSlot(key);
   
Map.Entry<Long, String> entry = slotHostMap.lowerEntry(Long.valueOf(slot));
   
return nodeMap.get(entry.getValue()).getResource();
}

public static TreeMap<Long, String> getSlotHostMap(String anyHostAndPortStr) {
   
TreeMap<Long, String> tree = new TreeMap<>();
   
String parts[] = anyHostAndPortStr.split(":");
   
HostAndPort anyHostAndPort = new HostAndPort(parts[0], Integer.parseInt(parts[1]));
   
try (Jedis jedis = new Jedis(anyHostAndPort.getHost(), anyHostAndPort.getPort())) {
       
List<Object> list = jedis.clusterSlots();
       
for (Object object : list) {
           
List<Object> list1 = (List<Object>) object;
           
List<Object> master = (List<Object>) list1.get(2);
           
String hostAndPort = new String((byte[]) master.get(0)) + ":" + master.get(1);
            tree
.put((Long) list1.get(0), hostAndPort);
            tree
.put((Long) list1.get(1), hostAndPort);
       
}
   
}
   
return tree;
}

It works)
Hope it helps anyone while there is no better solution from Jedis. 

임정택

unread,
Jul 30, 2015, 6:17:41 PM7/30/15
to Jedis, dmitro...@gmail.com
It is completely up to users cause your approach doesn't care about redirection while in transaction / pipeline.
In user's view it never redirects before he/she is trying to migrate the slot, so he/she can say it is safe.

But Jedis cannot start with that precondition. Slot can be migrated at any time.
That's why we're struggling to provide better solution about this issue.

Best,
Jungtaek Lim (HeartSaVioR)
 

2015년 7월 24일 금요일 오후 4시 19분 48초 UTC+9, Dmitry Dragan 님의 말:

Dmitry Dragan

unread,
Jul 31, 2015, 1:39:07 AM7/31/15
to Jedis, dmitro...@gmail.com, kab...@gmail.com
Hi Jungtaek Lim,

Thank for your answer!
For now, in case of any redirection we can re-init  nodeMap and slotMap, and try one more time.

пятница, 31 июля 2015 г., 1:17:41 UTC+3 пользователь 임정택 написал:
Reply all
Reply to author
Forward
0 new messages