0

I'm trying to do a multi-get on my redis data store which is distributed across multiple shards. However the keys I want to do this on do not belong to the same shard so I can't use redis' inbuilt multi-get.

Instead I'm trying to use futures to achieve this. But after checking the lookup times it almost seems like these cache calls are being made serially.

The request/sec on the server is about 1.5k with an average of 10 ms response time. Literature I've read told me that my threadpool size should be requests/sec * response time. Since I'm spawning 3 threads this becomes 1500 * 0.010 * 3 = 45. I've tried using threadpool sizes of 50,100,300. But this hasn't helped either.

I'm using Jedis as a client. I thought it could be an issue with exceeding Jedis' max total/idle connection limit. But even after increasing this from 8 to 24 I see no difference in lookup times.

I understand that some overhead will be there since there will be context switches and the overhead of spawning new threads.

Can anyone help me figure out where I'm missing out. Let me know if you need more info.

                for(String recordKey : pidArr) {
                //Adding futures. Max 3
                if(count >= 3) {
                    break;
                }
                count++;
                Callable<String> a = new FeedCacheCaller(recordKey);

                Future<String> future = feedThreadPool.submit(a);
                futureList.add(future);
            }

            //Getting the data from the futures

            for(Future<String> foo : futureList) {
                try {
                    String data = foo.get();
                    logger.debug(data);
                    feedDataList.add(parseInfo(data));

                } catch (Exception e) {
                    logger.error("somethings going wrong in retrieval",e);
                }
            }

Here's the Callable class

public class FeedCacheCaller implements Callable {

String pid = null;
FeedCache feedCache;
public FeedCacheCaller(String pid) {
    this.pid = pid;
    this.feedCache =  new FeedCache();
}
@Override
public String call() throws Exception {
    return feedCache.get(pid);
}

}

Edit 1:

Here's the Jedis side code.

    public class FeedCache {

    private ShardedJedisPool feedClient = RedisPool.getPool("feed");

    public String get(String key) {
    ShardedJedis client = null;
    String value = null;
    try {
        client = feedClient.getResource();
        byte[] valueByteArray = client.get(key.getBytes(Constants.CHARSET));
        if (valueByteArray != null) {
            value = new String(CacheUtils.decompress(valueByteArray),
                               Constants.CHARSET);
        }
    } catch (JedisConnectionException e) {
        if (client != null) {
            feedClient.returnBrokenResource(client);
            client = null;
        }
        logger.error(e.getMessage());
    } finally {
        if (client != null) {
            feedClient.returnResource(client);
        }
    }

    return value;
}

}

Here is the code that initializes the ShardedJedisPool

public class RedisPool {

private static final Logger logger = LoggerFactory.getLogger(
        RedisPool.class);

private static ConcurrentHashMap<String, ShardedJedisPool> redisPools = new ConcurrentHashMap<String, ShardedJedisPool>();

public static void initializePool(String poolName) {
    List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
    ArrayList<String> servers = new ArrayList<String>(Arrays.asList(
            Constants.config.getStringArray(
                poolName + "_redis_servers")));
    for (int i = 0; i < servers.size(); i++) {
        JedisShardInfo shardInfo = new JedisShardInfo(servers.get(i).split(":")[0], Integer.parseInt(servers.get(i).split(":")[1]));
        shards.add(shardInfo);
    }
    redisPools.putIfAbsent(poolName,
            new ShardedJedisPool(new GenericObjectPoolConfig(), shards));
}

public static ShardedJedisPool getPool(String poolName) {
    if (!redisPools.containsKey(poolName)) {
        synchronized (RedisPool.class) {
            if (!redisPools.containsKey(poolName)) {
                initializePool(poolName);
            }
        }
    }
    return redisPools.get(poolName);
}

public static void shutdown(String poolName) {
    ShardedJedisPool pool = getPool(poolName);
    pool.destroy();
    redisPools.remove(poolName);
}

public static void main(String args[]) {
    initializePool("vizidtoud");
}

}

Ole Gooner
  • 567
  • 2
  • 10
  • 25
  • seems like FeedCache works synchronous. Does new instance guarantee async work, like standalone connection to db? – Dmitry Aug 17 '15 at 15:08
  • So there are 24 connections established to the DB for every shard. The FeedCache tries to pick one resource from the pool if available. So yes. – Ole Gooner Aug 17 '15 at 15:16
  • There's no code how you use Jedis. Please be aware, that Jedis connections are not thread-safe. You should use pooled resources to stay on safe. Sharded Jedis uses only one connection per shard. FYI: Jedis sharding is deprecated, rather use Redis Cluster. – mp911de Aug 17 '15 at 21:05
  • Sharded Jedis by default establishes 8 connections per shard. I have increased this to 24. I'll share the Jedis code as well. – Ole Gooner Aug 18 '15 at 05:02
  • You're using already `ShardedJedisPool `? I assumed `ShardedJedis` or `BinaryShardedJedis`. – mp911de Aug 18 '15 at 05:21
  • yes ShardedJedis pool. – Ole Gooner Aug 18 '15 at 07:12
  • Please share code for Jedis part to diagnose issue. – Jungtaek Lim Aug 19 '15 at 04:02
  • Posted the necessary info. – Ole Gooner Aug 19 '15 at 08:39
  • @JungtaekLim. Sorry for the tag. But do you see any issues with my implementation? – Ole Gooner Aug 20 '15 at 05:11
  • @OleGooner I'll take a look when I'm on a laptop. Thanks. – Jungtaek Lim Aug 20 '15 at 05:43
  • 1. RedisPool.initializePool() uses default configuration of GenericObjectPoolConfig. Did you modify it? 2. Could you add some logs to FeedCacheCaller.call() to ensure ThreadPool works correctly? Or could you please share initialization part of feedThreadPool? – Jungtaek Lim Aug 20 '15 at 11:50
  • I had changed this to GenericObjectPoolConfig a = new GenericObjectPoolConfig(). a.setMaxIdle = 24 and a.setMaxTotal = 24. And I passed a to the constructor. When I check the redis instances by doing a client list and check for that ip, I can see that ~20 connections are made. private static final ExecutorService feedThreadPool = Executors.newFixedThreadPool(100); – Ole Gooner Aug 21 '15 at 05:58

0 Answers0