Allow Lettuce connection pool for dedicated conns
DATAREDIS-201 Inject LettucePool into LettuceConnectionFactory to be used for a "dedicatedConnection". Dedicated connections are used only for blocking and tx ops when a shared connection is used. If shareNativeConnection is set to false, the dedicated conn will be used for all ops.
This commit is contained in:
@@ -36,6 +36,7 @@ import org.springframework.data.redis.RedisConnectionFailureException;
|
||||
import org.springframework.data.redis.RedisSystemException;
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.redis.connection.Pool;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisPipelineException;
|
||||
import org.springframework.data.redis.connection.RedisSubscribedConnectionException;
|
||||
@@ -47,6 +48,7 @@ import org.springframework.util.ObjectUtils;
|
||||
|
||||
import com.lambdaworks.redis.RedisAsyncConnection;
|
||||
import com.lambdaworks.redis.RedisClient;
|
||||
import com.lambdaworks.redis.RedisCommandInterruptedException;
|
||||
import com.lambdaworks.redis.RedisException;
|
||||
import com.lambdaworks.redis.SortArgs;
|
||||
import com.lambdaworks.redis.ZStoreArgs;
|
||||
@@ -65,16 +67,10 @@ import com.lambdaworks.redis.pubsub.RedisPubSubConnection;
|
||||
*/
|
||||
public class LettuceConnection implements RedisConnection {
|
||||
|
||||
private final com.lambdaworks.redis.RedisAsyncConnection<byte[], byte[]> asyncConn;
|
||||
private final com.lambdaworks.redis.RedisConnection<byte[], byte[]> con;
|
||||
|
||||
// Connection to do tx operations - not shared among threads like asyncConn
|
||||
private com.lambdaworks.redis.RedisAsyncConnection<byte[], byte[]> asyncTxConn;
|
||||
private com.lambdaworks.redis.RedisConnection<byte[], byte[]> txConn;
|
||||
|
||||
// Connection to do blocking operations - not shared among threads like asyncConn
|
||||
private com.lambdaworks.redis.RedisAsyncConnection<byte[], byte[]> asyncBlockingConn;
|
||||
private com.lambdaworks.redis.RedisConnection<byte[], byte[]> blockingConn;
|
||||
private final com.lambdaworks.redis.RedisAsyncConnection<byte[], byte[]> asyncSharedConn;
|
||||
private final com.lambdaworks.redis.RedisConnection<byte[], byte[]> sharedConn;
|
||||
private com.lambdaworks.redis.RedisAsyncConnection<byte[], byte[]> asyncDedicatedConn;
|
||||
private com.lambdaworks.redis.RedisConnection<byte[], byte[]> dedicatedConn;
|
||||
|
||||
private final RedisCodec<byte[], byte[]> codec = LettuceUtils.CODEC;
|
||||
private final long timeout;
|
||||
@@ -83,10 +79,22 @@ public class LettuceConnection implements RedisConnection {
|
||||
private boolean isClosed = false;
|
||||
private boolean isMulti = false;
|
||||
private boolean isPipelined = false;
|
||||
private boolean closeNativeConnection = true;
|
||||
private List<Command<?, ?, ?>> ppline;
|
||||
private RedisClient client;
|
||||
private volatile LettuceSubscription subscription;
|
||||
private Pool<RedisAsyncConnection<byte[], byte[]>> pool;
|
||||
/** flag indicating whether the connection needs to be dropped or not */
|
||||
private boolean broken = false;
|
||||
|
||||
public LettuceConnection(com.lambdaworks.redis.RedisAsyncConnection<byte[], byte[]> dedicatedConnection, long timeout,
|
||||
RedisClient client) {
|
||||
this(null, dedicatedConnection, timeout, client, null);
|
||||
}
|
||||
|
||||
public LettuceConnection(com.lambdaworks.redis.RedisAsyncConnection<byte[], byte[]> dedicatedConnection, long timeout,
|
||||
RedisClient client, Pool<RedisAsyncConnection<byte[], byte[]>> pool) {
|
||||
this(null, dedicatedConnection, timeout, client, pool);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new lettuce connection.
|
||||
@@ -95,9 +103,10 @@ public class LettuceConnection implements RedisConnection {
|
||||
* @param timeout the connection timeout (in milliseconds)
|
||||
* @param client The Lettuce client
|
||||
*/
|
||||
public LettuceConnection(com.lambdaworks.redis.RedisAsyncConnection<byte[], byte[]> connection, long timeout,
|
||||
public LettuceConnection(com.lambdaworks.redis.RedisAsyncConnection<byte[], byte[]> sharedConnection,
|
||||
com.lambdaworks.redis.RedisAsyncConnection<byte[], byte[]> dedicatedConnection, long timeout,
|
||||
RedisClient client) {
|
||||
this(connection, timeout, client, true);
|
||||
this(sharedConnection, dedicatedConnection, timeout, client, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,22 +118,30 @@ public class LettuceConnection implements RedisConnection {
|
||||
* @param closeNativeConnection True if native connection should be called on {@link #close()}. This should
|
||||
* be set to false if the native connection is shared.
|
||||
*/
|
||||
public LettuceConnection(com.lambdaworks.redis.RedisAsyncConnection<byte[], byte[]> connection, long timeout,
|
||||
RedisClient client, boolean closeNativeConnection) {
|
||||
Assert.notNull(connection, "a valid connection is required");
|
||||
|
||||
this.asyncConn = connection;
|
||||
public LettuceConnection(com.lambdaworks.redis.RedisAsyncConnection<byte[], byte[]> sharedConnection,
|
||||
com.lambdaworks.redis.RedisAsyncConnection<byte[], byte[]> dedicatedConnection, long timeout,
|
||||
RedisClient client, Pool<RedisAsyncConnection<byte[], byte[]>> pool) {
|
||||
Assert.notNull(dedicatedConnection, "a valid dedicated connection is required");
|
||||
this.asyncSharedConn = sharedConnection;
|
||||
this.asyncDedicatedConn = dedicatedConnection;
|
||||
this.timeout = timeout;
|
||||
this.con = new com.lambdaworks.redis.RedisConnection<byte[], byte[]>(asyncConn);
|
||||
this.sharedConn = sharedConnection != null ?
|
||||
new com.lambdaworks.redis.RedisConnection<byte[], byte[]>(asyncSharedConn) : null;
|
||||
this.dedicatedConn = new com.lambdaworks.redis.RedisConnection<byte[], byte[]>(dedicatedConnection);
|
||||
this.client = client;
|
||||
this.closeNativeConnection = closeNativeConnection;
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
protected DataAccessException convertLettuceAccessException(Exception ex) {
|
||||
if (ex instanceof RedisException) {
|
||||
if(!(ex instanceof RedisCommandInterruptedException)) {
|
||||
// Some connection issues (i.e. connection is closed) are thrown as RedisExceptions
|
||||
broken = true;
|
||||
}
|
||||
return LettuceUtils.convertRedisAccessException((RedisException) ex);
|
||||
}
|
||||
if (ex instanceof ChannelException) {
|
||||
broken = true;
|
||||
return new RedisConnectionFailureException("Redis connection failed", ex);
|
||||
}
|
||||
if (ex instanceof TimeoutException) {
|
||||
@@ -167,24 +184,19 @@ public class LettuceConnection implements RedisConnection {
|
||||
public void close() throws DataAccessException {
|
||||
isClosed = true;
|
||||
|
||||
if(asyncBlockingConn != null) {
|
||||
asyncBlockingConn.close();
|
||||
asyncBlockingConn = null;
|
||||
blockingConn = null;
|
||||
}
|
||||
|
||||
if(asyncTxConn != null) {
|
||||
asyncTxConn.close();
|
||||
asyncTxConn = null;
|
||||
txConn = null;
|
||||
}
|
||||
|
||||
if(closeNativeConnection) {
|
||||
try {
|
||||
asyncConn.close();
|
||||
} catch (RuntimeException ex) {
|
||||
throw convertLettuceAccessException(ex);
|
||||
if(pool != null) {
|
||||
if (!broken) {
|
||||
pool.returnResource(asyncDedicatedConn);
|
||||
}else {
|
||||
pool.returnBrokenResource(asyncDedicatedConn);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
asyncDedicatedConn.close();
|
||||
} catch (RuntimeException ex) {
|
||||
throw convertLettuceAccessException(ex);
|
||||
}
|
||||
|
||||
if (subscription != null) {
|
||||
@@ -712,8 +724,18 @@ public class LettuceConnection implements RedisConnection {
|
||||
|
||||
|
||||
public void select(int dbIndex) {
|
||||
throw new UnsupportedOperationException("Selecting a new database not supported due to shared connection. " +
|
||||
if(asyncSharedConn != null) {
|
||||
throw new UnsupportedOperationException("Selecting a new database not supported due to shared connection. " +
|
||||
"Use separate ConnectionFactorys to work with multiple databases");
|
||||
}
|
||||
try {
|
||||
if (isPipelined()) {
|
||||
throw new UnsupportedOperationException("Lettuce blocks for #select");
|
||||
}
|
||||
getConnection().select(dbIndex);
|
||||
} catch (Exception ex) {
|
||||
throw convertLettuceAccessException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2133,48 +2155,37 @@ public class LettuceConnection implements RedisConnection {
|
||||
if(isQueueing()) {
|
||||
return getAsyncTxConnection();
|
||||
}
|
||||
return asyncConn;
|
||||
if(asyncSharedConn != null) {
|
||||
return asyncSharedConn;
|
||||
}
|
||||
return asyncDedicatedConn;
|
||||
}
|
||||
|
||||
private com.lambdaworks.redis.RedisConnection<byte[], byte[]> getConnection() {
|
||||
if(isQueueing()) {
|
||||
return getTxConnection();
|
||||
}
|
||||
return con;
|
||||
if(sharedConn != null) {
|
||||
return sharedConn;
|
||||
}
|
||||
return dedicatedConn;
|
||||
}
|
||||
|
||||
private RedisAsyncConnection<byte[], byte[]> getAsyncBlockingConnection() {
|
||||
if(isQueueing()) {
|
||||
return getAsyncTxConnection();
|
||||
}
|
||||
if(asyncBlockingConn == null) {
|
||||
asyncBlockingConn = client.connectAsync(LettuceUtils.CODEC);
|
||||
}
|
||||
return asyncBlockingConn;
|
||||
return asyncDedicatedConn;
|
||||
}
|
||||
|
||||
private com.lambdaworks.redis.RedisConnection<byte[], byte[]> getBlockingConnection() {
|
||||
if(isQueueing()) {
|
||||
return getTxConnection();
|
||||
}
|
||||
if(blockingConn == null) {
|
||||
blockingConn = new com.lambdaworks.redis.RedisConnection<byte[], byte[]>(getAsyncBlockingConnection());
|
||||
}
|
||||
return blockingConn;
|
||||
return dedicatedConn;
|
||||
}
|
||||
|
||||
private RedisAsyncConnection<byte[], byte[]> getAsyncTxConnection() {
|
||||
if(asyncTxConn == null) {
|
||||
asyncTxConn = client.connectAsync(LettuceUtils.CODEC);
|
||||
}
|
||||
return asyncTxConn;
|
||||
return asyncDedicatedConn;
|
||||
}
|
||||
|
||||
private com.lambdaworks.redis.RedisConnection<byte[], byte[]> getTxConnection() {
|
||||
if(txConn == null) {
|
||||
txConn = new com.lambdaworks.redis.RedisConnection<byte[], byte[]>(getAsyncTxConnection());
|
||||
}
|
||||
return txConn;
|
||||
return dedicatedConn;
|
||||
|
||||
}
|
||||
|
||||
private Future<Long> asyncBitOp(BitOperation op, byte[] destination, byte[]... keys) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.RedisConnectionFailureException;
|
||||
import org.springframework.data.redis.connection.Pool;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -45,6 +46,11 @@ import com.lambdaworks.redis.RedisException;
|
||||
* therefore it is not validated by default on {@link #getConnection()}. Use
|
||||
* {@link #setValidateConnection(boolean)} to change this behavior if necessary.
|
||||
*
|
||||
* Inject a {@link Pool} to pool dedicated connections. If shareNativeConnection is
|
||||
* true, the pool will be used to select a connection for blocking and tx operations only,
|
||||
* which should not share a connection. If native connection sharing is disabled,
|
||||
* the selected connection will be used for all operations.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Jennifer Hickey
|
||||
*/
|
||||
@@ -60,6 +66,7 @@ public class LettuceConnectionFactory implements InitializingBean, DisposableBea
|
||||
private boolean validateConnection = false;
|
||||
private boolean shareNativeConnection = true;
|
||||
private RedisAsyncConnection<byte[], byte[]> connection;
|
||||
private Pool<RedisAsyncConnection<byte[], byte[]>> pool;
|
||||
private int dbIndex = 0;
|
||||
/** Synchronization monitor for the shared Connection */
|
||||
private final Object connectionMonitor = new Object();
|
||||
@@ -80,6 +87,12 @@ public class LettuceConnectionFactory implements InitializingBean, DisposableBea
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public LettuceConnectionFactory(String host, int port, Pool<RedisAsyncConnection<byte[], byte[]>> pool) {
|
||||
this.hostName = host;
|
||||
this.port = port;
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
client = new RedisClient(hostName, port);
|
||||
client.setDefaultTimeout(timeout, TimeUnit.MILLISECONDS);
|
||||
@@ -95,11 +108,7 @@ public class LettuceConnectionFactory implements InitializingBean, DisposableBea
|
||||
}
|
||||
|
||||
public RedisConnection getConnection() {
|
||||
RedisAsyncConnection<byte[], byte[]> nativeConnection = getNativeConnection();
|
||||
if (dbIndex > 0) {
|
||||
nativeConnection.select(dbIndex);
|
||||
}
|
||||
return new LettuceConnection(nativeConnection, timeout, client, !shareNativeConnection);
|
||||
return new LettuceConnection(getSharedConnection(), createLettuceConnector(false), timeout, client, pool);
|
||||
}
|
||||
|
||||
public void initConnection() {
|
||||
@@ -107,7 +116,7 @@ public class LettuceConnectionFactory implements InitializingBean, DisposableBea
|
||||
if (this.connection != null) {
|
||||
resetConnection();
|
||||
}
|
||||
this.connection = createLettuceConnector();
|
||||
this.connection = createLettuceConnector(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,7 +281,7 @@ public class LettuceConnectionFactory implements InitializingBean, DisposableBea
|
||||
this.dbIndex = index;
|
||||
}
|
||||
|
||||
protected RedisAsyncConnection<byte[], byte[]> getNativeConnection() {
|
||||
protected RedisAsyncConnection<byte[], byte[]> getSharedConnection() {
|
||||
if (shareNativeConnection) {
|
||||
synchronized (this.connectionMonitor) {
|
||||
if (this.connection == null) {
|
||||
@@ -284,13 +293,20 @@ public class LettuceConnectionFactory implements InitializingBean, DisposableBea
|
||||
return this.connection;
|
||||
}
|
||||
} else {
|
||||
return createLettuceConnector();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private RedisAsyncConnection<byte[], byte[]> createLettuceConnector() {
|
||||
protected RedisAsyncConnection<byte[], byte[]> createLettuceConnector(boolean shared) {
|
||||
if(pool != null && !shared) {
|
||||
return pool.getResource();
|
||||
}
|
||||
try {
|
||||
return client.connectAsync(LettuceUtils.CODEC);
|
||||
RedisAsyncConnection<byte[], byte[]> dedicatedConnection = client.connectAsync(LettuceUtils.CODEC);
|
||||
if(dbIndex > 0) {
|
||||
dedicatedConnection.select(dbIndex);
|
||||
}
|
||||
return dedicatedConnection;
|
||||
} catch (RedisException e) {
|
||||
throw new RedisConnectionFailureException("Unable to connect to Redis on " +
|
||||
getHostName() + ":" + getPort(), e);
|
||||
|
||||
Reference in New Issue
Block a user