Redis and JedisPool configuration – sensible defaults?

Published on — Filed under protip, bare metal

I've been rediscovering the magic in the data access layer with Redis.

First of all, I must say that Redis is fucking brilliant — pardon my french. While it's far from being the most mature NoSQL alternative out there, if I had to put my money in any of them, Redis would most definitely be the one taking my chips.

It has the magic of a in-memory key-value store – blazing fast, even when persisting every single change to the data set to disk – and then that extra bit of collections' awesomesauce such as sets and lists – boy, do those come in handy!

As an interface for Redis, I went with Jedis. Seemed to be the most mature and active Redis binding for Java and I was pretty glad with it — except for one minor nuisance.

To use Jedis in a thread-safe and resource efficient environment, there's JedisPool — a connection pool to the likes of the connection pools you're familiar with from the JDBC world. The annoyance here is that in a JedisPool by default, connections will die of inactivity and no new ones will be spawned.

When I finally decided to take a break from feature development and address this pending/annoying issue I noticed that JedisPool is a CommonsPool backed implementation – as stated here; classic RTFM case – and CommonsPool's default configuration is pretty much a braindead pool.

Moreover, since I'm using Spring as a configuration bootstrap for this particular project, I wanted to pass a GenericObjectPool.Config instance to the JedisPool constructor.

I'm not going to focus on how dumbass that Config with its public fields and no getter/setter methods is, so I'll just rather provide a Spring (XML) config friendly version of it — it's basically just a wrapper around it — and a sensible default configuration that'll suit most of the common cases and keep your connections to Redis alive and kickin'.

public class GenericObjectPoolConfigWrapper {

  // internal vars ------------------------------

  private final GenericObjectPool.Config config;

  // constructors -------------------------------

  public GenericObjectPoolConfigWrapper() {
    this.config = new GenericObjectPool.Config();
  }

  // getters & setters --------------------------

  public GenericObjectPool.Config getConfig() {
    return config;
  }

  public int getMaxIdle() {
    return this.config.maxIdle;
  }

  public void setMaxIdle(int maxIdle) {
    this.config.maxIdle = maxIdle;
  }

  public int getMinIdle() {
    return this.config.minIdle;
  }

  public void setMinIdle(int minIdle) {
    this.config.minIdle = minIdle;
  }

  public int getMaxActive() {
    return this.config.maxActive;
  }

  public void setMaxActive(int maxActive) {
    this.config.maxActive = maxActive;
  }

  public long getMaxWait() {
    return this.config.maxWait;
  }

  public void setMaxWait(long maxWait) {
    this.config.maxWait = maxWait;
  }

  public byte getWhenExhaustedAction() {
    return this.config.whenExhaustedAction;
  }

  public void setWhenExhaustedAction(byte whenExhaustedAction) {
    this.config.whenExhaustedAction = whenExhaustedAction;
  }

  public boolean isTestOnBorrow() {
    return this.config.testOnBorrow;
  }

  public void setTestOnBorrow(boolean testOnBorrow) {
    this.config.testOnBorrow = testOnBorrow;
  }

  public boolean isTestOnReturn() {
    return this.config.testOnReturn;
  }

  public void setTestOnReturn(boolean testOnReturn) {
    this.config.testOnReturn = testOnReturn;
  }

  public boolean isTestWhileIdle() {
    return this.config.testWhileIdle;
  }

  public void setTestWhileIdle(boolean testWhileIdle) {
    this.config.testWhileIdle = testWhileIdle;
  }

  public long getTimeBetweenEvictionRunsMillis() {
    return this.config.timeBetweenEvictionRunsMillis;
  }

  public void setTimeBetweenEvictionRunsMillis(
        long timeBetweenEvictionRunsMillis) {
    this.config.timeBetweenEvictionRunsMillis =
            timeBetweenEvictionRunsMillis;
  }

  public int getNumTestsPerEvictionRun() {
    return this.config.numTestsPerEvictionRun;
  }

  public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
    this.config.numTestsPerEvictionRun = numTestsPerEvictionRun;
  }

  public long getMinEvictableIdleTimeMillis() {
    return this.config.minEvictableIdleTimeMillis;
  }

  public void setMinEvictableIdleTimeMillis(
        long minEvictableIdleTimeMillis) {
    this.config.minEvictableIdleTimeMillis =
            minEvictableIdleTimeMillis;
  }

  public long getSoftMinEvictableIdleTimeMillis() {
    return this.config.softMinEvictableIdleTimeMillis;
  }

  public void setSoftMinEvictableIdleTimeMillis(
        long softMinEvictableIdleTimeMillis) {
    this.config.softMinEvictableIdleTimeMillis =
            softMinEvictableIdleTimeMillis;
  }

  public boolean isLifo() {
    return this.config.lifo;
  }

  public void setLifo(boolean lifo) {
    this.config.lifo = lifo;
  }
}

And here's an example of it in a Spring configuration file:

<bean id="config" class="teh.package.GenericObjectPoolConfigWrapper">
  <!-- Action to take when trying to acquire a connection and all connections are taken -->
  <property name="whenExhaustedAction">
    <!-- Fail-fast behaviour, we don't like to keep the kids waiting -->
    <util:constant static-field="org.apache.commons.pool.impl.GenericObjectPool.WHEN_EXHAUSTED_FAIL" />
    <!-- Default behaviour, block the caller until a resource becomes available -->
    <!--<util:constant static-field="org.apache.commons.pool.impl.GenericObjectPool.WHEN_EXHAUSTED_BLOCK" />-->
  </property>
  <!-- Maximum active connections to Redis instance -->
  <property name="maxActive" value="10" />
  <!-- Number of connections to Redis that just sit there and do nothing -->
  <property name="maxIdle" value="5" />
  <!-- Minimum number of idle connections to Redis - these can be seen as always open and ready to serve -->
  <property name="minIdle" value="1" />
  <!-- Tests whether connection is dead when connection retrieval method is called -->
  <property name="testOnBorrow" value="true" />
  <!-- Tests whether connection is dead when returning a connection to the pool -->
  <property name="testOnReturn" value="true" />
  <!-- Tests whether connections are dead during idle periods -->
  <property name="testWhileIdle" value="true" />
  <!-- Maximum number of connections to test in each idle check -->
  <property name="numTestsPerEvictionRun" value="10" />
  <!-- Idle connection checking period -->
  <property name="timeBetweenEvictionRunsMillis" value="60000" />
  <!-- Maximum time, in milliseconds, to wait for a resource when exausted action is set to WHEN_EXAUSTED_BLOCK -->
  <property name="maxWait" value="3000" />
</bean>

And here's how you extract that configuration to the constructor of a JedisPool.

<bean id="redisConnectionPool" class="redis.clients.jedis.JedisPool"
      destroy-method="destroy">
  <constructor-arg index="0">
    <bean factory-bean="config" factory-method="getConfig" />
  </constructor-arg>
  <constructor-arg index="1" value="localhost" />
  <constructor-arg index="2" value="6379" />
</bean>