Make Jedis Cluster more likely to antirez's redis-rb-cluster
JedisClusterCommand * improvements on connection error handling ** if based on slot connection throws connection related exception, retry to random node ** if we retry with random node, but all nodes are unreachable, throw JedisConnectionException without retry ** try to release connection whether connection is broken or not * bug fix : if asking flag is on, and success this time, set asking flag to off JedisClusterConnectionHandler * have flexibility on initializing slots cache ** allow some nodes connection failure - skip ** if current node is success initializing slots cache, skip other nodes ** if current node failed to initialize slots cache, discard all discovered nodes and slots * set nodes if node does not exist in nodes ** it restricts JedisPool to replace - prevent IllegalStateException : Returned object not currently part of this pool JedisSlotBasedConnectionGuaranteedConnectionHandler * getConnection (random connection) ** check all connections by random sequence ** always return valid connection (able to ping-pong) ** throw exception if all connections are invalid * some refactoring
This commit is contained in:
@@ -3,19 +3,18 @@ package redis.clients.jedis;
|
||||
import redis.clients.jedis.exceptions.JedisAskDataException;
|
||||
import redis.clients.jedis.exceptions.JedisClusterException;
|
||||
import redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException;
|
||||
import redis.clients.jedis.exceptions.JedisConnectionException;
|
||||
import redis.clients.jedis.exceptions.JedisException;
|
||||
import redis.clients.jedis.exceptions.JedisMovedDataException;
|
||||
import redis.clients.jedis.exceptions.JedisRedirectionException;
|
||||
import redis.clients.util.JedisClusterCRC16;
|
||||
|
||||
public abstract class JedisClusterCommand<T> {
|
||||
|
||||
private boolean asking = false;
|
||||
|
||||
private JedisClusterConnectionHandler connectionHandler;
|
||||
private int commandTimeout;
|
||||
private int redirections;
|
||||
|
||||
// private boolean asking = false;
|
||||
|
||||
public JedisClusterCommand(JedisClusterConnectionHandler connectionHandler,
|
||||
int timeout, int maxRedirections) {
|
||||
this.connectionHandler = connectionHandler;
|
||||
@@ -26,40 +25,80 @@ public abstract class JedisClusterCommand<T> {
|
||||
public abstract T execute(Jedis connection);
|
||||
|
||||
public T run(String key) {
|
||||
if (key == null) {
|
||||
throw new JedisClusterException(
|
||||
"No way to dispatch this command to Redis Cluster.");
|
||||
}
|
||||
|
||||
return runWithRetries(key, this.redirections, false, false);
|
||||
}
|
||||
|
||||
private T runWithRetries(String key, int redirections,
|
||||
boolean tryRandomNode, boolean asking) {
|
||||
if (redirections <= 0) {
|
||||
throw new JedisClusterMaxRedirectionsException(
|
||||
"Too many Cluster redirections?");
|
||||
}
|
||||
|
||||
Jedis connection = null;
|
||||
try {
|
||||
|
||||
if (key == null) {
|
||||
throw new JedisClusterException(
|
||||
"No way to dispatch this command to Redis Cluster.");
|
||||
} else if (redirections == 0) {
|
||||
throw new JedisClusterMaxRedirectionsException(
|
||||
"Too many Cluster redirections?");
|
||||
if (tryRandomNode) {
|
||||
connection = connectionHandler.getConnection();
|
||||
} else {
|
||||
connection = connectionHandler
|
||||
.getConnectionFromSlot(JedisClusterCRC16.getSlot(key));
|
||||
}
|
||||
connection = connectionHandler.getConnectionFromSlot(JedisClusterCRC16
|
||||
.getSlot(key));
|
||||
|
||||
if (asking) {
|
||||
// TODO: Pipeline asking with the original command to make it
|
||||
// faster....
|
||||
connection.asking();
|
||||
|
||||
// if asking success, reset asking flag
|
||||
asking = false;
|
||||
}
|
||||
|
||||
return execute(connection);
|
||||
} catch (JedisConnectionException jce) {
|
||||
if (tryRandomNode) {
|
||||
// maybe all connection is down
|
||||
throw jce;
|
||||
}
|
||||
|
||||
releaseConnection(connection, true);
|
||||
connection = null;
|
||||
|
||||
// retry with random connection
|
||||
return runWithRetries(key, redirections--, true, asking);
|
||||
} catch (JedisRedirectionException jre) {
|
||||
return handleRedirection(jre, key);
|
||||
if (jre instanceof JedisAskDataException) {
|
||||
asking = true;
|
||||
} else if (jre instanceof JedisMovedDataException) {
|
||||
// TODO : In antirez's redis-rb-cluster implementation,
|
||||
// it rebuilds cluster's slot and node cache
|
||||
}
|
||||
|
||||
this.connectionHandler.assignSlotToNode(jre.getSlot(),
|
||||
jre.getTargetNode());
|
||||
|
||||
releaseConnection(connection, false);
|
||||
connection = null;
|
||||
|
||||
return runWithRetries(key, redirections - 1, false, asking);
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
releaseConnection(connection, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void releaseConnection(Jedis connection, boolean broken) {
|
||||
if (connection != null) {
|
||||
if (broken) {
|
||||
connectionHandler.returnBrokenConnection(connection);
|
||||
} else {
|
||||
connectionHandler.returnConnection(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private T handleRedirection(JedisRedirectionException jre, String key) {
|
||||
if (jre instanceof JedisAskDataException) {
|
||||
asking = true;
|
||||
}
|
||||
redirections--;
|
||||
this.connectionHandler.assignSlotToNode(jre.getSlot(),
|
||||
jre.getTargetNode());
|
||||
return run(key);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user