Consider intermediate command interface in ConnectionSplittingInterceptor.

We now consider requests to command API objects such as RedisConnection.keyCommands() in ConnectionSplittingInterceptor to identify the correct command and route it accordingly.

Closes: #2886
Original Pull Request: #2887
This commit is contained in:
Mark Paluch
2024-04-02 15:08:37 +02:00
committed by Christoph Strobl
parent 6f28b530b0
commit eed4c2af40
3 changed files with 90 additions and 5 deletions

View File

@@ -32,6 +32,7 @@ import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.test.annotation.Rollback;
@@ -132,6 +133,18 @@ public abstract class AbstractTransactionalTestBase {
}
}
@Rollback(false)
@Test // GH-2886
public void shouldReturnReadOnlyCommandResultInTransaction() {
RedisTemplate<String, String> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setEnableTransactionSupport(true);
template.afterPropertiesSet();
assertThat(template.hasKey("foo")).isFalse();
}
@Test // DATAREDIS-548
@Transactional(readOnly = true)
public void valueOperationShouldWorkWithReadOnlyTransactions() {

View File

@@ -24,6 +24,7 @@ import org.mockito.Mockito;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisKeyCommands;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
@@ -201,6 +202,45 @@ class RedisConnectionUtilsUnitTests {
assertThat(TransactionSynchronizationManager.hasResource(factoryMock)).isFalse();
}
@Test // GH-2886
void connectionProxyShouldInvokeReadOnlyMethods() {
TransactionTemplate template = new TransactionTemplate(new DummyTransactionManager());
byte[] anyBytes = new byte[] { 1, 2, 3 };
when(connectionMock2.exists(anyBytes)).thenReturn(true);
template.executeWithoutResult(status -> {
RedisConnection connection = RedisConnectionUtils.getConnection(factoryMock, true);
assertThat(connection.exists(anyBytes)).isEqualTo(true);
});
}
@Test // GH-2886
void connectionProxyShouldConsiderCommandInterfaces() {
TransactionTemplate template = new TransactionTemplate(new DummyTransactionManager());
byte[] anyBytes = new byte[] { 1, 2, 3 };
RedisKeyCommands commandsMock = mock(RedisKeyCommands.class);
when(connectionMock1.keyCommands()).thenReturn(commandsMock);
when(connectionMock2.keyCommands()).thenReturn(commandsMock);
when(commandsMock.exists(anyBytes)).thenReturn(true);
when(commandsMock.del(anyBytes)).thenReturn(42L);
template.executeWithoutResult(status -> {
RedisConnection connection = RedisConnectionUtils.getConnection(factoryMock, true);
assertThat(connection.keyCommands().exists(anyBytes)).isEqualTo(true);
assertThat(connection.keyCommands().del(anyBytes)).isEqualTo(42L);
});
}
static class DummyTransactionManager extends AbstractPlatformTransactionManager {
@Override