xetorthio/jedis — pull request #517 by HeartSaVioR (2014-01-27 04:54:42+00:00)
Title: Fix Pool (and JedisSentinelPool) 's race condition

This PR is a splitted version of #499.
(with remove some lines from diff which are annoying about space vs tab, some refactoring on JedisSentinelPoolTest)

In short
----
* Fix Pool instance to thread-safe while initPool() is calling multiple time.
* assertOpen() could be failed before applying
* Fix JedisSentinelPool to change master information after initPool
* Fix JedisSentinelPoolTest to remove resource leak
* It could be throw JedisConnectionError - cannot get resource before applying

Details about race conditions
----

Current master resolves JedisSentinelPoolTest's timing issue by taking additional sleep.
But I think it is caused by race conditions, could reproduce outside of Jedis.

* JedisSentinelPool.initPool()

````
private void initPool(HostAndPort master) {
if (!master.equals(currentHostMaster)) {
currentHostMaster = master;
log.info("Created JedisPool to master at " + master);
initPool(poolConfig, new JedisFactory(master.getHost(), master.getPort(),
timeout, password, database));
}
}
````

There is race condition between changing master information and initPool().
User could know master is changed by getting master information, but pool is not changed yet.
We can synchronize this method, but I think changing sequence (pool re-init and change master info) would be OK.
(If we have more time to talk, we need to talk about JedisSentinelPool's preferred state when initPool() throws Exception.)

* Pool.initPool()

````
public void initPool(final GenericObjectPoolConfig poolConfig,
PooledObjectFactory factory) {

if (this.internalPool != null) {
try {
closeInternalPool();
} catch (Exception e) {
}
}

this.internalPool = new GenericObjectPool(factory, poolConfig);
}
````

There is race condition between closeInternalPool and renew internalPool.
Using JedisSentinelPool means we're in multi-thread.
(JedisSentinelPool makes a thread subscribing Sentinel's channel)
It means user can request borrowing or returning Jedis instance to JedisSentinelPool while internal pool is closed but not yet to renew internal pool.