DATAREDIS-548 - Release connection after command execution in read-only transactions.

We now unbind and release the connection from the transaction resources after a Redis command is invoked. Redis read operations return always null while using RedisTemplate in a transaction so Redis read transactions are not useful.

Previously, RedisConnection's were bound as transactional resource when used in the scope of a @Transactional(readOnly = true) method but not released on transaction completion. This was, because connections are not registered with a transaction synchronizer.

Original Pull Request: #214
This commit is contained in:
Mark Paluch
2016-08-24 10:14:21 +02:00
committed by Christoph Strobl
parent b1803b6cee
commit 350c7beee3
3 changed files with 31 additions and 8 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@ import org.springframework.util.Assert;
* @author Costin Leau
* @author Christoph Strobl
* @author Thomas Darimont
* @author Mark Paluch
*/
public abstract class RedisConnectionUtils {
@@ -199,8 +200,12 @@ public abstract class RedisConnectionUtils {
return;
}
// Only release non-transactional/non-bound connections.
if (!isConnectionTransactional(conn, factory)) {
// release transactional/read-only and non-transactional/non-bound connections.
// transactional connections for read-only transactions get no synchronizer registered
if (isConnectionTransactional(conn, factory)
&& TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
unbindConnection(factory);
} else if (!isConnectionTransactional(conn, factory)) {
if (log.isDebugEnabled()) {
log.debug("Closing Redis Connection");
}

View File

@@ -73,6 +73,7 @@ import org.springframework.util.CollectionUtils;
* @author Costin Leau
* @author Christoph Strobl
* @author Anqing Shao
* @author Mark Paluch
* @param <K> the Redis key type against which the template works (usually a String)
* @param <V> the Redis value type against which the template works
* @see StringRedisTemplate
@@ -210,10 +211,7 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
// TODO: any other connection processing?
return postProcessResult(result, connToUse, existingConnection);
} finally {
if (!enableTransactionSupport) {
RedisConnectionUtils.releaseConnection(conn, factory);
}
RedisConnectionUtils.releaseConnection(conn, factory);
}
}

View File

@@ -39,6 +39,13 @@ import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
/**
* Base class with integration tests for transactional use.
*
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
*/
@RunWith(RelaxedJUnit4ClassRunner.class)
@Transactional(transactionManager = "transactionManager")
public abstract class AbstractTransactionalTestBase {
@@ -129,6 +136,19 @@ public abstract class AbstractTransactionalTestBase {
}
}
/**
* @see DATAREDIS-548
*/
@Test
@Transactional(readOnly = true)
public void valueOperationShouldWorkWithReadOnlyTransactions() {
this.valuesShouldHaveBeenPersisted = false;
for (String key : KEYS) {
template.opsForValue().get(key);
}
}
/**
* @see DATAREDIS-73
*/
@@ -146,7 +166,7 @@ public abstract class AbstractTransactionalTestBase {
*/
@Rollback(false)
@Test
public void listOperationLPushShoudBeCommittedCorrectly() {
public void listOperationLPushShouldBeCommittedCorrectly() {
this.valuesShouldHaveBeenPersisted = true;
for (String key : KEYS) {