DATAREDIS-315 - Add initial support for redis cluster (jedis/lettuce).
Cluster support is based on the very same building blocks as non clustered communication. RedisClusterConnection and extension to RedisConnection handles the communication with the Redis Cluster and translates errors into the Spring DAO exception hierarchy. Redirects for to a specific keys to the corresponding slot serving node are handled by the driver libraries, higher level functions like collecting information accross nodes, or sending commands to all nodes in the cluster that are covered by RedisClusterConnection utilizing a ClusterCommandExecutor distributing commands accross the cluster and collecting results. RedisTemplate provides access to cluster specific operations via the ClusterOperations interface that can be obtained via RedisTemplate.opsForCluster(). This allows to execute commands explicitly on a single node within the cluster while retaining de-/serialization features configured for the template. Original pull request: #158.
This commit is contained in:
committed by
Mark Paluch
parent
a6ab1b53b3
commit
c5047f40c6
@@ -52,30 +52,34 @@ public class RedisTestProfileValueSource implements ProfileValueSource {
|
||||
|
||||
Version version = fallbackVersion;
|
||||
|
||||
Jedis jedis = new Jedis(SettingsUtils.getHost(), SettingsUtils.getPort(), 100);
|
||||
Jedis jedis = null;
|
||||
try {
|
||||
|
||||
jedis.connect();
|
||||
jedis = new Jedis(SettingsUtils.getHost(), SettingsUtils.getPort(), 100);
|
||||
// jedis.connect();
|
||||
String info = jedis.info();
|
||||
String versionString = (String) JedisConverters.stringToProps().convert(info).get("redis_version");
|
||||
|
||||
version = RedisVersionUtils.parseVersion(versionString);
|
||||
} finally {
|
||||
|
||||
try {
|
||||
// force socket to be closed
|
||||
jedis.getClient().quit();
|
||||
jedis.getClient().getSocket().close();
|
||||
if (jedis != null) {
|
||||
try {
|
||||
// need to wait a bit
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
// just ignore it
|
||||
// force socket to be closed
|
||||
jedis.getClient().quit();
|
||||
jedis.getClient().getSocket().close();
|
||||
try {
|
||||
// need to wait a bit
|
||||
Thread.sleep(5);
|
||||
} catch (InterruptedException e) {
|
||||
// just ignore it
|
||||
Thread.interrupted();
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
// ignore as well
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
// ignore as well
|
||||
jedis.close();
|
||||
}
|
||||
jedis.close();
|
||||
|
||||
}
|
||||
return version;
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.hamcrest.core.IsCollectionContaining.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.redis.test.util.MockitoUtils.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hamcrest.core.IsInstanceOf;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.redis.ClusterRedirectException;
|
||||
import org.springframework.data.redis.PassThroughExceptionTranslationStrategy;
|
||||
import org.springframework.data.redis.TooManyClusterRedirectionsException;
|
||||
import org.springframework.data.redis.connection.ClusterCommandExecutor.ClusterCommandCallback;
|
||||
import org.springframework.data.redis.connection.ClusterCommandExecutor.MultiKeyClusterCommandCallback;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode.LinkState;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode.SlotRange;
|
||||
import org.springframework.data.redis.connection.RedisNode.NodeType;
|
||||
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ClusterCommandExecutorUnitTests {
|
||||
|
||||
static final String CLUSTER_NODE_1_HOST = "127.0.0.1";
|
||||
static final String CLUSTER_NODE_2_HOST = "127.0.0.1";
|
||||
static final String CLUSTER_NODE_3_HOST = "127.0.0.1";
|
||||
|
||||
static final int CLUSTER_NODE_1_PORT = 7379;
|
||||
static final int CLUSTER_NODE_2_PORT = 7380;
|
||||
static final int CLUSTER_NODE_3_PORT = 7381;
|
||||
|
||||
static final RedisClusterNode CLUSTER_NODE_1 = RedisClusterNode.newRedisClusterNode()
|
||||
.listeningAt(CLUSTER_NODE_1_HOST, CLUSTER_NODE_1_PORT).serving(new SlotRange(0, 5460))
|
||||
.withId("ef570f86c7b1a953846668debc177a3a16733420").promotedAs(NodeType.MASTER).linkState(LinkState.CONNECTED)
|
||||
.build();
|
||||
static final RedisClusterNode CLUSTER_NODE_2 = RedisClusterNode.newRedisClusterNode()
|
||||
.listeningAt(CLUSTER_NODE_2_HOST, CLUSTER_NODE_2_PORT).serving(new SlotRange(5461, 10922))
|
||||
.withId("0f2ee5df45d18c50aca07228cc18b1da96fd5e84").promotedAs(NodeType.MASTER).linkState(LinkState.CONNECTED)
|
||||
.build();
|
||||
static final RedisClusterNode CLUSTER_NODE_3 = RedisClusterNode.newRedisClusterNode()
|
||||
.listeningAt(CLUSTER_NODE_3_HOST, CLUSTER_NODE_3_PORT).serving(new SlotRange(10923, 16383))
|
||||
.withId("3b9b8192a874fa8f1f09dbc0ee20afab5738eee7").promotedAs(NodeType.MASTER).linkState(LinkState.CONNECTED)
|
||||
.build();
|
||||
|
||||
static final RedisClusterNode UNKNOWN_CLUSTER_NODE = new RedisClusterNode("8.8.8.8", 7379, null);
|
||||
|
||||
private ClusterCommandExecutor executor;
|
||||
|
||||
private static final ConnectionCommandCallback<String> COMMAND_CALLBACK = new ConnectionCommandCallback<String>() {
|
||||
|
||||
@Override
|
||||
public String doInCluster(Connection connection) {
|
||||
return connection.theWheelWeavesAsTheWheelWills();
|
||||
}
|
||||
};
|
||||
|
||||
private static final Converter<Exception, DataAccessException> exceptionConverter = new Converter<Exception, DataAccessException>() {
|
||||
|
||||
@Override
|
||||
public DataAccessException convert(Exception source) {
|
||||
|
||||
if (source instanceof MovedException) {
|
||||
return new ClusterRedirectException(1000, ((MovedException) source).host, ((MovedException) source).port,
|
||||
source);
|
||||
}
|
||||
|
||||
return new InvalidDataAccessApiUsageException(source.getMessage(), source);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private static final MultiKeyConnectionCommandCallback<String> MULTIKEY_CALLBACK = new MultiKeyConnectionCommandCallback<String>() {
|
||||
|
||||
@Override
|
||||
public String doInCluster(Connection connection, byte[] key) {
|
||||
return connection.bloodAndAshes(key);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@Mock Connection con1;
|
||||
@Mock Connection con2;
|
||||
@Mock Connection con3;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
this.executor = new ClusterCommandExecutor(new MockClusterNodeProvider(), new MockClusterResourceProvider(),
|
||||
new PassThroughExceptionTranslationStrategy(exceptionConverter));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
this.executor.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void executeCommandOnSingleNodeShouldBeExecutedCorrectly() {
|
||||
|
||||
executor.executeCommandOnSingleNode(COMMAND_CALLBACK, CLUSTER_NODE_2);
|
||||
|
||||
verify(con2, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void executeCommandOnSingleNodeShouldThrowExceptionWhenNodeIsNull() {
|
||||
executor.executeCommandOnSingleNode(COMMAND_CALLBACK, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void executeCommandOnSingleNodeShouldThrowExceptionWhenCommandCallbackIsNull() {
|
||||
executor.executeCommandOnSingleNode(null, CLUSTER_NODE_1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void executeCommandOnSingleNodeShouldThrowExceptionWhenNodeIsUnknown() {
|
||||
executor.executeCommandOnSingleNode(COMMAND_CALLBACK, UNKNOWN_CLUSTER_NODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void executeCommandAsyncOnNodesShouldExecuteCommandOnGivenNodes() {
|
||||
|
||||
ClusterCommandExecutor executor = new ClusterCommandExecutor(new MockClusterNodeProvider(),
|
||||
new MockClusterResourceProvider(), new PassThroughExceptionTranslationStrategy(exceptionConverter),
|
||||
new ConcurrentTaskExecutor(new SyncTaskExecutor()));
|
||||
|
||||
executor.executeCommandAsyncOnNodes(COMMAND_CALLBACK, Arrays.asList(CLUSTER_NODE_1, CLUSTER_NODE_2));
|
||||
|
||||
verify(con1, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
verify(con2, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
verify(con3, never()).theWheelWeavesAsTheWheelWills();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void executeCommandOnAllNodesShouldExecuteCommandOnEveryKnwonClusterNode() {
|
||||
|
||||
ClusterCommandExecutor executor = new ClusterCommandExecutor(new MockClusterNodeProvider(),
|
||||
new MockClusterResourceProvider(), new PassThroughExceptionTranslationStrategy(exceptionConverter),
|
||||
new ConcurrentTaskExecutor(new SyncTaskExecutor()));
|
||||
|
||||
executor.executeCommandOnAllNodes(COMMAND_CALLBACK);
|
||||
|
||||
verify(con1, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
verify(con2, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
verify(con3, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void executeCommandAsyncOnNodesShouldCompleteAndCollectErrorsOfAllNodes() {
|
||||
|
||||
when(con1.theWheelWeavesAsTheWheelWills()).thenReturn("rand");
|
||||
when(con2.theWheelWeavesAsTheWheelWills()).thenThrow(new IllegalStateException("(error) mat lost the dagger..."));
|
||||
when(con3.theWheelWeavesAsTheWheelWills()).thenReturn("perrin");
|
||||
|
||||
try {
|
||||
executor.executeCommandOnAllNodes(COMMAND_CALLBACK);
|
||||
} catch (ClusterCommandExecutionFailureException e) {
|
||||
|
||||
assertThat(e.getCauses().size(), is(1));
|
||||
assertThat(e.getCauses().iterator().next(), IsInstanceOf.instanceOf(DataAccessException.class));
|
||||
}
|
||||
|
||||
verify(con1, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
verify(con2, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
verify(con3, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void executeCommandAsyncOnNodesShouldCollectResultsCorrectly() {
|
||||
|
||||
when(con1.theWheelWeavesAsTheWheelWills()).thenReturn("rand");
|
||||
when(con2.theWheelWeavesAsTheWheelWills()).thenReturn("mat");
|
||||
when(con3.theWheelWeavesAsTheWheelWills()).thenReturn("perrin");
|
||||
|
||||
Map<RedisClusterNode, String> result = executor.executeCommandOnAllNodes(COMMAND_CALLBACK);
|
||||
|
||||
assertThat(result.keySet(), hasItems(CLUSTER_NODE_1, CLUSTER_NODE_2, CLUSTER_NODE_3));
|
||||
assertThat(result.values(), hasItems("rand", "mat", "perrin"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void executeMultikeyCommandShouldRunCommandAcrossCluster() {
|
||||
|
||||
// key-1 and key-9 map both to node1
|
||||
ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
|
||||
when(con1.bloodAndAshes(captor.capture())).thenReturn("rand");
|
||||
|
||||
when(con2.bloodAndAshes(any(byte[].class))).thenReturn("mat");
|
||||
when(con3.bloodAndAshes(any(byte[].class))).thenReturn("perrin");
|
||||
|
||||
Map<RedisClusterNode, String> result = executor.executeMuliKeyCommand(
|
||||
MULTIKEY_CALLBACK,
|
||||
new HashSet<byte[]>(Arrays.asList("key-1".getBytes(), "key-2".getBytes(), "key-3".getBytes(),
|
||||
"key-9".getBytes())));
|
||||
|
||||
assertThat(result.keySet(), hasItems(CLUSTER_NODE_1, CLUSTER_NODE_2, CLUSTER_NODE_3));
|
||||
assertThat(result.values(), hasItems("rand", "mat", "perrin"));
|
||||
|
||||
// check that 2 keys have been routed to node1
|
||||
assertThat(captor.getAllValues().size(), is(2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void executeCommandOnSingleNodeAndFollowRedirect() {
|
||||
|
||||
when(con1.theWheelWeavesAsTheWheelWills()).thenThrow(new MovedException(CLUSTER_NODE_3_HOST, CLUSTER_NODE_3_PORT));
|
||||
|
||||
executor.executeCommandOnSingleNode(COMMAND_CALLBACK, CLUSTER_NODE_1);
|
||||
|
||||
verify(con1, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
verify(con3, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
verify(con2, never()).theWheelWeavesAsTheWheelWills();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void executeCommandOnSingleNodeAndFollowRedirectButStopsAfterMaxRedirects() {
|
||||
|
||||
when(con1.theWheelWeavesAsTheWheelWills()).thenThrow(new MovedException(CLUSTER_NODE_3_HOST, CLUSTER_NODE_3_PORT));
|
||||
when(con3.theWheelWeavesAsTheWheelWills()).thenThrow(new MovedException(CLUSTER_NODE_2_HOST, CLUSTER_NODE_2_PORT));
|
||||
when(con2.theWheelWeavesAsTheWheelWills()).thenThrow(new MovedException(CLUSTER_NODE_1_HOST, CLUSTER_NODE_1_PORT));
|
||||
|
||||
try {
|
||||
executor.setMaxRedirects(4);
|
||||
executor.executeCommandOnSingleNode(COMMAND_CALLBACK, CLUSTER_NODE_1);
|
||||
} catch (Exception e) {
|
||||
assertThat(e, IsInstanceOf.instanceOf(TooManyClusterRedirectionsException.class));
|
||||
}
|
||||
|
||||
verify(con1, times(2)).theWheelWeavesAsTheWheelWills();
|
||||
verify(con3, times(2)).theWheelWeavesAsTheWheelWills();
|
||||
verify(con2, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void executeCommandOnArbitraryNodeShouldPickARandomNode() {
|
||||
|
||||
executor.executeCommandOnArbitraryNode(COMMAND_CALLBACK);
|
||||
|
||||
verifyInvocationsAcross("theWheelWeavesAsTheWheelWills", times(1), con1, con2, con3);
|
||||
}
|
||||
|
||||
class MockClusterNodeProvider implements ClusterTopologyProvider {
|
||||
|
||||
@Override
|
||||
public ClusterTopology getTopology() {
|
||||
return new ClusterTopology(new LinkedHashSet<RedisClusterNode>(Arrays.asList(CLUSTER_NODE_1, CLUSTER_NODE_2,
|
||||
CLUSTER_NODE_3)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MockClusterResourceProvider implements ClusterNodeResourceProvider {
|
||||
|
||||
@Override
|
||||
public Connection getResourceForSpecificNode(RedisClusterNode node) {
|
||||
|
||||
if (CLUSTER_NODE_1.equals(node)) {
|
||||
return con1;
|
||||
}
|
||||
if (CLUSTER_NODE_2.equals(node)) {
|
||||
return con2;
|
||||
}
|
||||
if (CLUSTER_NODE_3.equals(node)) {
|
||||
return con3;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void returnResourceForSpecificNode(RedisClusterNode node, Object resource) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static interface ConnectionCommandCallback<S> extends ClusterCommandCallback<Connection, S> {
|
||||
|
||||
}
|
||||
|
||||
static interface MultiKeyConnectionCommandCallback<S> extends MultiKeyClusterCommandCallback<Connection, S> {
|
||||
|
||||
}
|
||||
|
||||
static interface Connection {
|
||||
|
||||
String theWheelWeavesAsTheWheelWills();
|
||||
|
||||
String bloodAndAshes(byte[] key);
|
||||
}
|
||||
|
||||
static class MovedException extends RuntimeException {
|
||||
|
||||
String host;
|
||||
int port;
|
||||
|
||||
public MovedException(String host, int port) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,896 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
public interface ClusterConnectionTests {
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void shouldAllowSettingAndGettingValues();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void delShouldRemoveSingleKeyCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void delShouldRemoveMultipleKeysCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void delShouldRemoveMultipleKeysOnSameSlotCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void typeShouldReadKeyTypeCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void keysShouldReturnAllKeys();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void keysShouldReturnAllKeysForSpecificNode();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void randomKeyShouldReturnCorrectlyWhenKeysAvailable();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void randomKeyShouldReturnNullWhenNoKeysAvailable();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void rename();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void renameSameKeysOnSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void renameNXWhenTargetKeyDoesNotExist();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void renameNXWhenTargetKeyDoesExist();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void renameNXWhenOnSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void expireShouldBeSetCorreclty();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void pExpireShouldBeSetCorreclty();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void expireAtShouldBeSetCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void pExpireAtShouldBeSetCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void persistShoudRemoveTTL();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void moveShouldNotBeSupported();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void dbSizeShouldReturnCummulatedDbSize();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void dbSizeForSpecificNodeShouldGetNodeDbSize();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void ttlShouldReturnMinusTwoWhenKeyDoesNotExist();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void ttlShouldReturnMinusOneWhenKeyDoesNotHaveExpirationSet();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void ttlShouldReturnValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void pTtlShouldReturnMinusTwoWhenKeyDoesNotExist();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void pTtlShouldReturnMinusOneWhenKeyDoesNotHaveExpirationSet();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void pTtlShouldReturValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sortShouldReturnValuesCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sortAndStoreShouldAddSortedValuesValuesCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sortAndStoreShouldReturnZeroWhenListDoesNotExist();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void dumpAndRestoreShouldWorkCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void getShouldReturnValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void getSetShouldWorkCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void mGetShouldReturnCorrectlyWhenKeysMapToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void mGetShouldReturnCorrectlyWhenKeysDoNotMapToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void setShouldSetValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void setNxShouldSetValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void setNxShouldNotSetValueWhenAlreadyExistsInDBCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void setExShouldSetValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void pSetExShouldSetValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void mSetShouldWorkWhenKeysMapToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void mSetShouldWorkWhenKeysDoNotMapToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void mSetNXShouldReturnTrueIfAllKeysSet();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void mSetNXShouldReturnFalseIfNotAllKeysSet();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void mSetNXShouldWorkForOnSameSlotKeys();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void incrShouldIncreaseValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void incrByShouldIncreaseValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void incrByFloatShouldIncreaseValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void decrShouldDecreaseValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void decrByShouldDecreaseValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void appendShouldAddValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void getRangeShouldReturnValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void setRangeShouldWorkCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void getBitShouldWorkCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void setBitShouldWorkCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void bitCountShouldWorkCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void bitCountWithRangeShouldWorkCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void bitOpShouldThrowExceptionWhenKeysDoNotMapToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void strLenShouldWorkCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void rPushShoultAddValuesCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void lPushShoultAddValuesCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void rPushNXShoultNotAddValuesWhenKeyDoesNotExist();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void lPushNXShoultNotAddValuesWhenKeyDoesNotExist();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void lLenShouldCountValuesCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void lRangeShouldGetValuesCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void lTrimShouldTrimListCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void lIndexShouldGetElementAtIndexCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void lInsertShouldAddElementAtPositionCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void lSetShouldSetElementAtPositionCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void lRemShouldRemoveElementAtPositionCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void lPopShouldReturnElementCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void rPopShouldReturnElementCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void blPopShouldPopElementCorectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void blPopShouldPopElementCorectlyWhenKeyOnSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void brPopShouldPopElementCorectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void brPopShouldPopElementCorectlyWhenKeyOnSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void rPopLPushShouldWorkWhenDoNotMapToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
public void rPopLPushShouldWorkWhenKeysOnSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void bRPopLPushShouldWork();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void bRPopLPushShouldWorkOnSameSlotKeys();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sAddShouldAddValueToSetCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sRemShouldRemoveValueFromSetCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sPopShouldPopValueFromSetCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sMoveShouldWorkWhenKeysMapToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sMoveShouldWorkWhenKeysDoNotMapToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sCardShouldCountValuesInSetCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sIsMemberShouldReturnTrueIfValueIsMemberOfSet();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sIsMemberShouldReturnFalseIfValueIsMemberOfSet();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sInterShouldWorkForKeysMappingToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sInterShouldWorkForKeysNotMappingToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sInterStoreShouldWorkForKeysMappingToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sInterStoreShouldWorkForKeysNotMappingToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sUnionShouldWorkForKeysMappingToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sUnionShouldWorkForKeysNotMappingToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sUnionStoreShouldWorkForKeysMappingToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sUnionStoreShouldWorkForKeysNotMappingToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sDiffShouldWorkWhenKeysMapToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sDiffShouldWorkWhenKeysNotMapToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sDiffStoreShouldWorkWhenKeysMapToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sDiffStoreShouldWorkWhenKeysNotMapToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sMembersShouldReturnValuesContainedInSetCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sRandMamberShouldReturnValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sRandMamberWithCountShouldReturnValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void sscanShouldRetrieveAllValuesInSetCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zAddShouldAddValueWithScoreCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRemShouldRemoveValueWithScoreCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zIncrByShouldIncScoreForValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRankShouldReturnPositionForValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRankShouldReturnReversePositionForValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRangeShouldReturnValuesCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRangeWithScoresShouldReturnValuesAndScoreCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRangeByScoreShouldReturnValuesCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRangeByScoreWithScoresShouldReturnValuesAndScoreCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRangeByScoreShouldReturnValuesCorrectlyWhenGivenOffsetAndScore();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRangeByScoreWithScoresShouldReturnValuesCorrectlyWhenGivenOffsetAndScore();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRevRangeShouldReturnValuesCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRevRangeWithScoresShouldReturnValuesAndScoreCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRevRangeByScoreShouldReturnValuesCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRevRangeByScoreWithScoresShouldReturnValuesAndScoreCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRevRangeByScoreShouldReturnValuesCorrectlyWhenGivenOffsetAndScore();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRevRangeByScoreWithScoresShouldReturnValuesCorrectlyWhenGivenOffsetAndScore();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zCountShouldCountValuesInRange();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zCardShouldReturnTotalNumberOfValues();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zScoreShouldRetrieveScoreForValue();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRemRangeShouldRemoveValues();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRemRangeByScoreShouldRemoveValues();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zUnionStoreShouldWorkForSameSlotKeys();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zUnionStoreShouldThrowExceptionWhenKeysDoNotMapToSameSlots();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zInterStoreShouldWorkForSameSlotKeys();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zInterStoreShouldThrowExceptionWhenKeysDoNotMapToSameSlots();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void hSetShouldSetValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void hSetNXShouldSetValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void hSetNXShouldNotSetValueWhenAlreadyExists();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void hGetShouldRetrieveValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void hMGetShouldRetrieveValueCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void hMSetShouldAddValuesCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void hIncrByShouldIncreaseFieldCorretly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void hIncrByFloatShouldIncreaseFieldCorretly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void hExistsShouldReturnPresenceOfFieldCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void hDelShouldRemoveFieldsCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void hLenShouldRetrieveSizeCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void hKeysShouldRetrieveKeysCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void hValsShouldRetrieveValuesCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void hGetAllShouldRetrieveEntriesCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void multiShouldThrowException();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void execShouldThrowException();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void discardShouldThrowException();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void watchShouldThrowException();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void unwatchShouldThrowException();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void selectShouldAllowSelectionOfDBIndexZero();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void selectShouldThrowExceptionWhenSelectingNonZeroDbIndex();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void echoShouldReturnInputCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void pingShouldRetrunPongForExistingNode();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void pingShouldRetrunPong();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void pingShouldThrowExceptionWhenNodeNotKnownToCluster();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void flushDbShouldFlushAllClusterNodes();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void flushDbOnSingleNodeShouldFlushOnlyGivenNodesDb();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void zRangeByLexShouldReturnResultCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void infoShouldCollectionInfoFromAllClusterNodes();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void clientListShouldGetInfosForAllClients();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void getClusterNodeForKeyShouldReturnNodeCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void countKeysShouldReturnNumberOfKeysInSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
|
||||
void pfAddShouldAddValuesCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void pfCountShouldAllowCountingOnSingleKey();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void pfCountShouldAllowCountingOnSameSlotKeys();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void pfCountShouldThrowErrorCountingOnDifferentSlotKeys();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void pfMergeShouldWorkWhenAllKeysMapToSameSlot();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
public void pfMergeShouldThrowErrorOnDifferentSlotKeys();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void infoShouldCollectInfoForSpecificNode();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void infoShouldCollectInfoForSpecificNodeAndSection();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void getConfigShouldLoadCumulatedConfiguration();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void getConfigShouldLoadConfigurationOfSpecificNode();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void clusterGetSlavesShouldReturnSlaveCorrectly();
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
void clusterGetMasterSlaveMapShouldListMastersAndSlavesCorrectly();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.redis.test.util.RedisClusterRule;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import redis.clients.jedis.HostAndPort;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisCluster;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class ClusterSlotHashUtilsTests {
|
||||
|
||||
public static @ClassRule RedisClusterRule requiresCluster = new RedisClusterRule();
|
||||
|
||||
@Test
|
||||
public void localCalculationShoudMatchServers() throws IOException {
|
||||
|
||||
JedisCluster cluster = null;
|
||||
try {
|
||||
cluster = new JedisCluster(Collections.singleton(new HostAndPort("127.0.0.1", 7379)));
|
||||
|
||||
JedisPool pool = cluster.getClusterNodes().values().iterator().next();
|
||||
Jedis jedis = pool.getResource();
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
|
||||
String key = randomString();
|
||||
int slot = ClusterSlotHashUtil.calculateSlot(key);
|
||||
Long serverSlot = jedis.clusterKeySlot(key);
|
||||
|
||||
assertEquals(
|
||||
String.format("Expected slot for key '%s' to be %s but server calculated %s.", key, slot, serverSlot),
|
||||
serverSlot.intValue(), slot);
|
||||
|
||||
}
|
||||
pool.returnResource(jedis);
|
||||
} finally {
|
||||
if (cluster != null) {
|
||||
cluster.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void localCalculationShoudMatchServersForPrefixedKeys() throws IOException {
|
||||
|
||||
JedisCluster cluster = null;
|
||||
try {
|
||||
cluster = new JedisCluster(Collections.singleton(new HostAndPort("127.0.0.1", 7379)));
|
||||
|
||||
JedisPool pool = cluster.getClusterNodes().values().iterator().next();
|
||||
Jedis jedis = pool.getResource();
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
|
||||
String slotPrefix = "{" + randomString() + "}";
|
||||
|
||||
String key1 = slotPrefix + "." + randomString();
|
||||
String key2 = slotPrefix + "." + randomString();
|
||||
|
||||
int slot1 = ClusterSlotHashUtil.calculateSlot(key1);
|
||||
int slot2 = ClusterSlotHashUtil.calculateSlot(key2);
|
||||
|
||||
assertEquals(String.format("Expected slot for prefixed keys '%s' and '%s' to be %s but was %s.", key1, key2,
|
||||
slot1, slot2), slot1, slot2);
|
||||
|
||||
Long serverSlot1 = jedis.clusterKeySlot(key1);
|
||||
Long serverSlot2 = jedis.clusterKeySlot(key2);
|
||||
|
||||
assertEquals(
|
||||
String.format("Expected slot for key '%s' to be %s but server calculated %s.", key1, slot1, serverSlot1),
|
||||
serverSlot1.intValue(), slot1);
|
||||
assertEquals(
|
||||
String.format("Expected slot for key '%s' to be %s but server calculated %s.", key2, slot2, serverSlot2),
|
||||
serverSlot1.intValue(), slot1);
|
||||
|
||||
}
|
||||
pool.returnResource(jedis);
|
||||
} finally {
|
||||
if (cluster != null) {
|
||||
cluster.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random string using ascii chars {@code ' ' (space)} to {@code 'z'}. Explicitly skipping { and }.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String randomString() {
|
||||
|
||||
int leftLimit = 32; // letter ' ' (space)
|
||||
int rightLimit = 122; // letter 'z' (tilde)
|
||||
int targetStringLength = 0;
|
||||
while (targetStringLength == 0) {
|
||||
targetStringLength = new Random().nextInt(100);
|
||||
}
|
||||
|
||||
StringBuilder buffer = new StringBuilder(targetStringLength);
|
||||
for (int i = 0; i < targetStringLength; i++) {
|
||||
int randomLimitedInt = leftLimit + (int) (new Random().nextFloat() * (rightLimit - leftLimit));
|
||||
buffer.append((char) randomLimitedInt);
|
||||
}
|
||||
|
||||
String result = buffer.toString();
|
||||
if (StringUtils.hasText(result)) {
|
||||
return result;
|
||||
}
|
||||
return randomString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisNode.NodeType;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public abstract class ClusterTestVariables {
|
||||
|
||||
public static final String KEY_1 = "key1";
|
||||
public static final String KEY_2 = "key2";
|
||||
public static final String KEY_3 = "key3";
|
||||
|
||||
public static final String VALUE_1 = "value1";
|
||||
public static final String VALUE_2 = "value2";
|
||||
public static final String VALUE_3 = "value3";
|
||||
|
||||
public static final String SAME_SLOT_KEY_1 = "key2660";
|
||||
public static final String SAME_SLOT_KEY_2 = "key7112";
|
||||
public static final String SAME_SLOT_KEY_3 = "key8885";
|
||||
|
||||
public static final String CLUSTER_HOST = "127.0.0.1";
|
||||
public static final int MASTER_NODE_1_PORT = 7379;
|
||||
public static final int MASTER_NODE_2_PORT = 7380;
|
||||
public static final int MASTER_NODE_3_PORT = 7381;
|
||||
public static final int SLAVEOF_NODE_1_PORT = 7382;
|
||||
|
||||
public static final String MASTER_NODE_1_ID = "ef570f86c7b1a953846668debc177a3a16733420";
|
||||
public static final String MASTER_NODE_2_ID = "0f2ee5df45d18c50aca07228cc18b1da96fd5e84";
|
||||
public static final String MASTER_NODE_3_ID = "3b9b8192a874fa8f1f09dbc0ee20afab5738eee7";
|
||||
public static final String SLAVEOF_NODE_1_ID = "b8b5ee73b1d1997abff694b3fe8b2397d2138b6d";
|
||||
|
||||
public static final RedisClusterNode CLUSTER_NODE_1 = RedisClusterNode.newRedisClusterNode()
|
||||
.listeningAt(CLUSTER_HOST, MASTER_NODE_1_PORT).withId(MASTER_NODE_1_ID).promotedAs(NodeType.MASTER).build();
|
||||
public static final RedisClusterNode CLUSTER_NODE_2 = RedisClusterNode.newRedisClusterNode()
|
||||
.listeningAt(CLUSTER_HOST, MASTER_NODE_2_PORT).withId(MASTER_NODE_2_ID).promotedAs(NodeType.MASTER).build();
|
||||
public static final RedisClusterNode CLUSTER_NODE_3 = RedisClusterNode.newRedisClusterNode()
|
||||
.listeningAt(CLUSTER_HOST, MASTER_NODE_3_PORT).withId(MASTER_NODE_3_ID).promotedAs(NodeType.MASTER).build();
|
||||
public static final RedisClusterNode SLAVE_OF_NODE_1 = RedisClusterNode.newRedisClusterNode()
|
||||
.listeningAt(CLUSTER_HOST, SLAVEOF_NODE_1_PORT).withId(SLAVEOF_NODE_1_ID).promotedAs(NodeType.SLAVE).build();
|
||||
|
||||
public static final RedisClusterNode UNKNOWN_CLUSTER_NODE = new RedisClusterNode("8.8.8.8", 6379);
|
||||
|
||||
private ClusterTestVariables() {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.hamcrest.core.IsCollectionContaining.*;
|
||||
import static org.hamcrest.core.IsNull.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.mock.env.MockPropertySource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class RedisClusterConfigurationUnitTests {
|
||||
|
||||
static final String HOST_AND_PORT_1 = "127.0.0.1:123";
|
||||
static final String HOST_AND_PORT_2 = "localhost:456";
|
||||
static final String HOST_AND_PORT_3 = "localhost:789";
|
||||
static final String HOST_AND_NO_PORT = "localhost";
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void shouldCreateRedisClusterConfigurationCorrectly() {
|
||||
|
||||
RedisClusterConfiguration config = new RedisClusterConfiguration(Collections.singleton(HOST_AND_PORT_1));
|
||||
|
||||
assertThat(config.getClusterNodes().size(), is(1));
|
||||
assertThat(config.getClusterNodes(), hasItems(new RedisNode("127.0.0.1", 123)));
|
||||
assertThat(config.getClusterTimeout(), nullValue());
|
||||
assertThat(config.getMaxRedirects(), nullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void shouldCreateRedisClusterConfigurationCorrectlyGivenMultipleHostAndPortStrings() {
|
||||
|
||||
RedisClusterConfiguration config = new RedisClusterConfiguration(new HashSet<String>(Arrays.asList(HOST_AND_PORT_1,
|
||||
HOST_AND_PORT_2, HOST_AND_PORT_3)));
|
||||
|
||||
assertThat(config.getClusterNodes().size(), is(3));
|
||||
assertThat(config.getClusterNodes(),
|
||||
hasItems(new RedisNode("127.0.0.1", 123), new RedisNode("localhost", 456), new RedisNode("localhost", 789)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldThrowExecptionOnInvalidHostAndPortString() {
|
||||
new RedisClusterConfiguration(Collections.singleton(HOST_AND_NO_PORT));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldThrowExceptionWhenListOfHostAndPortIsNull() {
|
||||
new RedisClusterConfiguration(Collections.<String> singleton(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotFailWhenListOfHostAndPortIsEmpty() {
|
||||
|
||||
RedisClusterConfiguration config = new RedisClusterConfiguration(Collections.<String> emptySet());
|
||||
|
||||
assertThat(config.getClusterNodes().size(), is(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldThrowExceptionGivenNullPropertySource() {
|
||||
new RedisClusterConfiguration((PropertySource<?>) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotFailWhenGivenPropertySourceNotContainingRelevantProperties() {
|
||||
|
||||
RedisClusterConfiguration config = new RedisClusterConfiguration(new MockPropertySource());
|
||||
|
||||
assertThat(config.getMaxRedirects(), nullValue());
|
||||
assertThat(config.getClusterTimeout(), nullValue());
|
||||
assertThat(config.getClusterNodes().size(), is(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void shouldBeCreatedCorrecltyGivenValidPropertySourceWithSingleHostPort() {
|
||||
|
||||
MockPropertySource propertySource = new MockPropertySource();
|
||||
propertySource.setProperty("spring.redis.cluster.timeout", "10");
|
||||
propertySource.setProperty("spring.redis.cluster.nodes", HOST_AND_PORT_1);
|
||||
propertySource.setProperty("spring.redis.cluster.max-redirects", "5");
|
||||
|
||||
RedisClusterConfiguration config = new RedisClusterConfiguration(propertySource);
|
||||
|
||||
assertThat(config.getMaxRedirects(), is(5));
|
||||
assertThat(config.getClusterTimeout(), is(10L));
|
||||
assertThat(config.getClusterNodes(), hasItems(new RedisNode("127.0.0.1", 123)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void shouldBeCreatedCorrecltyGivenValidPropertySourceWithMultipleHostPort() {
|
||||
|
||||
MockPropertySource propertySource = new MockPropertySource();
|
||||
propertySource.setProperty("spring.redis.cluster.timeout", "10");
|
||||
propertySource.setProperty("spring.redis.cluster.nodes",
|
||||
StringUtils.collectionToCommaDelimitedString(Arrays.asList(HOST_AND_PORT_1, HOST_AND_PORT_2, HOST_AND_PORT_3)));
|
||||
propertySource.setProperty("spring.redis.cluster.max-redirects", "5");
|
||||
|
||||
RedisClusterConfiguration config = new RedisClusterConfiguration(propertySource);
|
||||
|
||||
assertThat(config.getMaxRedirects(), is(5));
|
||||
assertThat(config.getClusterTimeout(), is(10L));
|
||||
assertThat(config.getClusterNodes(),
|
||||
hasItems(new RedisNode("127.0.0.1", 123), new RedisNode("localhost", 456), new RedisNode("localhost", 789)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -881,5 +881,15 @@ public class RedisConnectionUnitTests {
|
||||
public Set<Tuple> zRevRangeByScoreWithScores(byte[] key, Range range) {
|
||||
return delegate.zRevRangeByScoreWithScores(key, range);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(byte[] key, RedisNode target, int dbIndex, MigrateOption option) {
|
||||
delegate.migrate(key, target, dbIndex, option);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(byte[] key, RedisNode target, int dbIndex, MigrateOption option, long timeout) {
|
||||
delegate.migrate(key, target, dbIndex, option, timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.convert;
|
||||
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.hamcrest.core.IsCollectionContaining.*;
|
||||
import static org.hamcrest.core.IsNull.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode.Flag;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode.LinkState;
|
||||
import org.springframework.data.redis.connection.RedisNode.NodeType;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConverters;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class ConvertersUnitTests {
|
||||
|
||||
private static final String CLUSTER_NODES_RESPONSE = "" //
|
||||
+ "ef570f86c7b1a953846668debc177a3a16733420 127.0.0.1:6379 myself,master - 0 0 1 connected 0-5460"
|
||||
+ System.getProperty("line.separator")
|
||||
+ "0f2ee5df45d18c50aca07228cc18b1da96fd5e84 127.0.0.1:6380 master - 0 1427718161587 2 connected 5461-10922"
|
||||
+ System.getProperty("line.separator")
|
||||
+ "3b9b8192a874fa8f1f09dbc0ee20afab5738eee7 127.0.0.1:6381 master - 0 1427718161587 3 connected 10923-16383"
|
||||
+ System.getProperty("line.separator")
|
||||
+ "8cad73f63eb996fedba89f041636f17d88cda075 127.0.0.1:7369 slave ef570f86c7b1a953846668debc177a3a16733420 0 1427718161587 1 connected";
|
||||
|
||||
private static final String CLUSTER_NODE_WITH_SINGLE_SLOT_RESPONSE = "ef570f86c7b1a953846668debc177a3a16733420 127.0.0.1:6379 myself,master - 0 0 1 connected 3456";
|
||||
|
||||
private static final String CLUSTER_NODE_WITH_FAIL_FLAG_AND_DISCONNECTED_LINK_STATE = "b8b5ee73b1d1997abff694b3fe8b2397d2138b6d 127.0.0.1:7382 master,fail - 1450160048933 1450160048832 38 disconnected";
|
||||
|
||||
private static final String CLUSTER_NODE_IMPORTING_SLOT = "ef570f86c7b1a953846668debc177a3a16733420 127.0.0.1:6379 myself,master - 0 0 1 connected [5461-<-0f2ee5df45d18c50aca07228cc18b1da96fd5e84]";
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void toSetOfRedisClusterNodesShouldConvertSingleStringNodesResponseCorrectly() {
|
||||
|
||||
Iterator<RedisClusterNode> nodes = JedisConverters.toSetOfRedisClusterNodes(CLUSTER_NODES_RESPONSE).iterator();
|
||||
|
||||
RedisClusterNode node = nodes.next(); // 127.0.0.1:6379
|
||||
assertThat(node.getId(), is("ef570f86c7b1a953846668debc177a3a16733420"));
|
||||
assertThat(node.getHost(), is("127.0.0.1"));
|
||||
assertThat(node.getPort(), is(6379));
|
||||
assertThat(node.getType(), is(NodeType.MASTER));
|
||||
assertThat(node.getSlotRange().contains(0), is(true));
|
||||
assertThat(node.getSlotRange().contains(5460), is(true));
|
||||
assertThat(node.getFlags(), hasItems(Flag.MASTER, Flag.MYSELF));
|
||||
assertThat(node.getLinkState(), is(LinkState.CONNECTED));
|
||||
|
||||
node = nodes.next(); // 127.0.0.1:6380
|
||||
assertThat(node.getId(), is("0f2ee5df45d18c50aca07228cc18b1da96fd5e84"));
|
||||
assertThat(node.getHost(), is("127.0.0.1"));
|
||||
assertThat(node.getPort(), is(6380));
|
||||
assertThat(node.getType(), is(NodeType.MASTER));
|
||||
assertThat(node.getSlotRange().contains(5461), is(true));
|
||||
assertThat(node.getSlotRange().contains(10922), is(true));
|
||||
assertThat(node.getFlags(), hasItems(Flag.MASTER));
|
||||
assertThat(node.getLinkState(), is(LinkState.CONNECTED));
|
||||
|
||||
node = nodes.next(); // 127.0.0.1:638
|
||||
assertThat(node.getId(), is("3b9b8192a874fa8f1f09dbc0ee20afab5738eee7"));
|
||||
assertThat(node.getHost(), is("127.0.0.1"));
|
||||
assertThat(node.getPort(), is(6381));
|
||||
assertThat(node.getType(), is(NodeType.MASTER));
|
||||
assertThat(node.getSlotRange().contains(10923), is(true));
|
||||
assertThat(node.getSlotRange().contains(16383), is(true));
|
||||
assertThat(node.getFlags(), hasItems(Flag.MASTER));
|
||||
assertThat(node.getLinkState(), is(LinkState.CONNECTED));
|
||||
|
||||
node = nodes.next(); // 127.0.0.1:7369
|
||||
assertThat(node.getId(), is("8cad73f63eb996fedba89f041636f17d88cda075"));
|
||||
assertThat(node.getHost(), is("127.0.0.1"));
|
||||
assertThat(node.getPort(), is(7369));
|
||||
assertThat(node.getType(), is(NodeType.SLAVE));
|
||||
assertThat(node.getMasterId(), is("ef570f86c7b1a953846668debc177a3a16733420"));
|
||||
assertThat(node.getSlotRange(), notNullValue());
|
||||
assertThat(node.getFlags(), hasItems(Flag.SLAVE));
|
||||
assertThat(node.getLinkState(), is(LinkState.CONNECTED));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void toSetOfRedisClusterNodesShouldConvertNodesWihtSingleSlotCorrectly() {
|
||||
|
||||
Iterator<RedisClusterNode> nodes = JedisConverters.toSetOfRedisClusterNodes(CLUSTER_NODE_WITH_SINGLE_SLOT_RESPONSE)
|
||||
.iterator();
|
||||
|
||||
RedisClusterNode node = nodes.next(); // 127.0.0.1:6379
|
||||
assertThat(node.getId(), is("ef570f86c7b1a953846668debc177a3a16733420"));
|
||||
assertThat(node.getHost(), is("127.0.0.1"));
|
||||
assertThat(node.getPort(), is(6379));
|
||||
assertThat(node.getType(), is(NodeType.MASTER));
|
||||
assertThat(node.getSlotRange().contains(3456), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void toSetOfRedisClusterNodesShouldParseLinkStateAndDisconnectedCorrectly() {
|
||||
|
||||
Iterator<RedisClusterNode> nodes = JedisConverters.toSetOfRedisClusterNodes(
|
||||
CLUSTER_NODE_WITH_FAIL_FLAG_AND_DISCONNECTED_LINK_STATE).iterator();
|
||||
|
||||
RedisClusterNode node = nodes.next();
|
||||
assertThat(node.getId(), is("b8b5ee73b1d1997abff694b3fe8b2397d2138b6d"));
|
||||
assertThat(node.getHost(), is("127.0.0.1"));
|
||||
assertThat(node.getPort(), is(7382));
|
||||
assertThat(node.getType(), is(NodeType.MASTER));
|
||||
assertThat(node.getFlags(), hasItems(Flag.MASTER, Flag.FAIL));
|
||||
assertThat(node.getLinkState(), is(LinkState.DISCONNECTED));
|
||||
assertThat(node.getSlotRange().getSlots().size(), is(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void toSetOfRedisClusterNodesShouldIgnoreImportingSlot() {
|
||||
|
||||
Iterator<RedisClusterNode> nodes = JedisConverters.toSetOfRedisClusterNodes(CLUSTER_NODE_IMPORTING_SLOT).iterator();
|
||||
|
||||
RedisClusterNode node = nodes.next();
|
||||
assertThat(node.getId(), is("ef570f86c7b1a953846668debc177a3a16733420"));
|
||||
assertThat(node.getHost(), is("127.0.0.1"));
|
||||
assertThat(node.getPort(), is(6379));
|
||||
assertThat(node.getType(), is(NodeType.MASTER));
|
||||
assertThat(node.getFlags(), hasItem(Flag.MASTER));
|
||||
assertThat(node.getLinkState(), is(LinkState.CONNECTED));
|
||||
assertThat(node.getSlotRange().getSlots().size(), is(0));
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.jedis;
|
||||
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.redis.connection.ClusterTestVariables.*;
|
||||
import static org.springframework.data.redis.test.util.MockitoUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.redis.ClusterStateFailureExeption;
|
||||
import org.springframework.data.redis.connection.ClusterInfo;
|
||||
import org.springframework.data.redis.connection.RedisClusterCommands.AddSlots;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode;
|
||||
import org.springframework.data.redis.connection.jedis.JedisClusterConnection.JedisClusterTopologyProvider;
|
||||
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisCluster;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.exceptions.JedisConnectionException;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class JedisClusterConnectionUnitTests {
|
||||
|
||||
private static final String CLUSTER_NODES_RESPONSE = "" //
|
||||
+ MASTER_NODE_1_ID + " " + CLUSTER_HOST + ":" + MASTER_NODE_1_PORT
|
||||
+ " myself,master - 0 0 1 connected 0-5460"
|
||||
+ System.getProperty("line.separator") + MASTER_NODE_2_ID + " " + CLUSTER_HOST + ":"
|
||||
+ MASTER_NODE_2_PORT
|
||||
+ " master - 0 1427718161587 2 connected 5461-10922" + System.getProperty("line.separator")
|
||||
+ MASTER_NODE_2_ID
|
||||
+ " " + CLUSTER_HOST + ":" + MASTER_NODE_3_PORT + " master - 0 1427718161587 3 connected 10923-16383";
|
||||
|
||||
static final String CLUSTER_INFO_RESPONSE = "cluster_state:ok" + System.getProperty("line.separator")
|
||||
+ "cluster_slots_assigned:16384" + System.getProperty("line.separator") + "cluster_slots_ok:16384"
|
||||
+ System.getProperty("line.separator") + "cluster_slots_pfail:0" + System.getProperty("line.separator")
|
||||
+ "cluster_slots_fail:0" + System.getProperty("line.separator") + "cluster_known_nodes:4"
|
||||
+ System.getProperty("line.separator") + "cluster_size:3" + System.getProperty("line.separator")
|
||||
+ "cluster_current_epoch:30" + System.getProperty("line.separator") + "cluster_my_epoch:2"
|
||||
+ System.getProperty("line.separator") + "cluster_stats_messages_sent:2560260"
|
||||
+ System.getProperty("line.separator") + "cluster_stats_messages_received:2560086";
|
||||
|
||||
JedisClusterConnection connection;
|
||||
|
||||
@Mock JedisCluster clusterMock;
|
||||
|
||||
@Mock JedisPool node1PoolMock;
|
||||
@Mock JedisPool node2PoolMock;
|
||||
@Mock JedisPool node3PoolMock;
|
||||
|
||||
@Mock Jedis con1Mock;
|
||||
@Mock Jedis con2Mock;
|
||||
@Mock Jedis con3Mock;
|
||||
|
||||
public @Rule ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
Map<String, JedisPool> nodes = new LinkedHashMap<String, JedisPool>(3);
|
||||
nodes.put(CLUSTER_HOST + ":" + MASTER_NODE_1_PORT, node1PoolMock);
|
||||
nodes.put(CLUSTER_HOST + ":" + MASTER_NODE_2_PORT, node2PoolMock);
|
||||
nodes.put(CLUSTER_HOST + ":" + MASTER_NODE_3_PORT, node3PoolMock);
|
||||
|
||||
when(clusterMock.getClusterNodes()).thenReturn(nodes);
|
||||
when(node1PoolMock.getResource()).thenReturn(con1Mock);
|
||||
when(node2PoolMock.getResource()).thenReturn(con2Mock);
|
||||
when(node3PoolMock.getResource()).thenReturn(con3Mock);
|
||||
|
||||
when(con1Mock.clusterNodes()).thenReturn(CLUSTER_NODES_RESPONSE);
|
||||
|
||||
connection = new JedisClusterConnection(clusterMock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void thowsExceptionWhenClusterCommandExecturorIsNull() {
|
||||
|
||||
expectedException.expect(IllegalArgumentException.class);
|
||||
|
||||
new JedisClusterConnection(clusterMock, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterMeetShouldSendCommandsToExistingNodesCorrectly() {
|
||||
|
||||
connection.clusterMeet(UNKNOWN_CLUSTER_NODE);
|
||||
|
||||
verify(con1Mock, times(1)).clusterMeet(UNKNOWN_CLUSTER_NODE.getHost(), UNKNOWN_CLUSTER_NODE.getPort());
|
||||
verify(con2Mock, times(1)).clusterMeet(UNKNOWN_CLUSTER_NODE.getHost(), UNKNOWN_CLUSTER_NODE.getPort());
|
||||
verify(con2Mock, times(1)).clusterMeet(UNKNOWN_CLUSTER_NODE.getHost(), UNKNOWN_CLUSTER_NODE.getPort());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterMeetShouldThrowExceptionWhenNodeIsNull() {
|
||||
|
||||
expectedException.expect(IllegalArgumentException.class);
|
||||
|
||||
connection.clusterMeet(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterForgetShouldSendCommandsToRemainingNodesCorrectly() {
|
||||
|
||||
connection.clusterForget(CLUSTER_NODE_2);
|
||||
|
||||
verify(con1Mock, times(1)).clusterForget(CLUSTER_NODE_2.getId());
|
||||
verifyZeroInteractions(con2Mock);
|
||||
verify(con3Mock, times(1)).clusterForget(CLUSTER_NODE_2.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterReplicateShouldSendCommandsCorrectly() {
|
||||
|
||||
connection.clusterReplicate(CLUSTER_NODE_1, CLUSTER_NODE_2);
|
||||
|
||||
verify(con2Mock, times(1)).clusterReplicate(CLUSTER_NODE_1.getId());
|
||||
verifyZeroInteractions(con1Mock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void closeShouldNotCloseUnderlyingClusterPool() throws IOException {
|
||||
|
||||
connection.close();
|
||||
|
||||
verify(clusterMock, never()).close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void isClosedShouldReturnConnectionStateCorrectly() {
|
||||
|
||||
assertThat(connection.isClosed(), is(false));
|
||||
|
||||
connection.close();
|
||||
|
||||
assertThat(connection.isClosed(), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterInfoShouldBeReturnedCorrectly() {
|
||||
|
||||
when(con1Mock.clusterInfo()).thenReturn(CLUSTER_INFO_RESPONSE);
|
||||
when(con2Mock.clusterInfo()).thenReturn(CLUSTER_INFO_RESPONSE);
|
||||
when(con3Mock.clusterInfo()).thenReturn(CLUSTER_INFO_RESPONSE);
|
||||
|
||||
ClusterInfo p = connection.clusterGetClusterInfo();
|
||||
assertThat(p.getSlotsAssigned(), is(16384L));
|
||||
|
||||
verifyInvocationsAcross("clusterInfo", times(1), con1Mock, con2Mock, con3Mock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterSetSlotImportingShouldBeExecutedCorrectly() {
|
||||
|
||||
connection.clusterSetSlot(CLUSTER_NODE_1, 100, AddSlots.IMPORTING);
|
||||
|
||||
verify(con1Mock, times(1)).clusterSetSlotImporting(eq(100), eq(CLUSTER_NODE_1.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterSetSlotMigratingShouldBeExecutedCorrectly() {
|
||||
|
||||
connection.clusterSetSlot(CLUSTER_NODE_1, 100, AddSlots.MIGRATING);
|
||||
|
||||
verify(con1Mock, times(1)).clusterSetSlotMigrating(eq(100), eq(CLUSTER_NODE_1.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterSetSlotStableShouldBeExecutedCorrectly() {
|
||||
|
||||
connection.clusterSetSlot(CLUSTER_NODE_1, 100, AddSlots.STABLE);
|
||||
|
||||
verify(con1Mock, times(1)).clusterSetSlotStable(eq(100));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterSetSlotNodeShouldBeExecutedCorrectly() {
|
||||
|
||||
connection.clusterSetSlot(CLUSTER_NODE_1, 100, AddSlots.NODE);
|
||||
|
||||
verify(con1Mock, times(1)).clusterSetSlotNode(eq(100), eq(CLUSTER_NODE_1.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterSetSlotShouldBeExecutedOnTargetNodeWhenNodeIdNotSet() {
|
||||
|
||||
connection.clusterSetSlot(new RedisClusterNode(CLUSTER_HOST, MASTER_NODE_2_PORT), 100, AddSlots.IMPORTING);
|
||||
|
||||
verify(con2Mock, times(1)).clusterSetSlotImporting(eq(100), eq(CLUSTER_NODE_2.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void clusterSetSlotShouldThrowExceptionWhenModeIsNull() {
|
||||
connection.clusterSetSlot(CLUSTER_NODE_1, 100, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterDeleteSlotsShouldBeExecutedCorrectly() {
|
||||
|
||||
int[] slots = new int[] { 9000, 10000 };
|
||||
connection.clusterDeleteSlots(CLUSTER_NODE_2, slots);
|
||||
|
||||
verify(con2Mock, times(1)).clusterDelSlots((int[]) anyVararg());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void clusterDeleteSlotShouldThrowExceptionWhenNodeIsNull() {
|
||||
connection.clusterDeleteSlots(null, new int[] { 1 });
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void timeShouldBeExecutedOnArbitraryNode() {
|
||||
|
||||
List<String> values = Arrays.asList("1449655759", "92217");
|
||||
when(con1Mock.time()).thenReturn(values);
|
||||
when(con2Mock.time()).thenReturn(values);
|
||||
when(con3Mock.time()).thenReturn(values);
|
||||
|
||||
connection.time();
|
||||
|
||||
verifyInvocationsAcross("time", times(1), con1Mock, con2Mock, con3Mock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void timeShouldBeExecutedOnSingleNode() {
|
||||
|
||||
when(con2Mock.time()).thenReturn(Arrays.asList("1449655759", "92217"));
|
||||
|
||||
connection.time(CLUSTER_NODE_2);
|
||||
|
||||
verify(con2Mock, times(1)).time();
|
||||
verifyZeroInteractions(con1Mock, con3Mock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void resetConfigStatsShouldBeExecutedOnAllNodes() {
|
||||
|
||||
connection.resetConfigStats();
|
||||
|
||||
verify(con1Mock, times(1)).configResetStat();
|
||||
verify(con2Mock, times(1)).configResetStat();
|
||||
verify(con3Mock, times(1)).configResetStat();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void resetConfigStatsShouldBeExecutedOnSingleNodeCorrectly() {
|
||||
|
||||
connection.resetConfigStats(CLUSTER_NODE_2);
|
||||
|
||||
verify(con2Mock, times(1)).configResetStat();
|
||||
verify(con1Mock, never()).configResetStat();
|
||||
verify(con3Mock, never()).configResetStat();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterTopologyProviderShouldCollectErrorsWhenLoadingNodes() {
|
||||
|
||||
expectedException.expect(ClusterStateFailureExeption.class);
|
||||
expectedException.expectMessage("127.0.0.1:7379 failed: o.O");
|
||||
expectedException.expectMessage("127.0.0.1:7380 failed: o.1");
|
||||
expectedException.expectMessage("127.0.0.1:7381 failed: o.2");
|
||||
|
||||
when(con1Mock.clusterNodes()).thenThrow(new JedisConnectionException("o.O"));
|
||||
when(con2Mock.clusterNodes()).thenThrow(new JedisConnectionException("o.1"));
|
||||
when(con3Mock.clusterNodes()).thenThrow(new JedisConnectionException("o.2"));
|
||||
|
||||
new JedisClusterTopologyProvider(clusterMock).getTopology();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2015 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.
|
||||
@@ -17,10 +17,16 @@ package org.springframework.data.redis.connection.jedis;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Matchers;
|
||||
import org.springframework.data.redis.connection.RedisClusterConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import redis.clients.jedis.JedisCluster;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
|
||||
/**
|
||||
@@ -33,6 +39,9 @@ public class JedisConnectionFactoryUnitTests {
|
||||
private static final RedisSentinelConfiguration SINGLE_SENTINEL_CONFIG = new RedisSentinelConfiguration().master(
|
||||
"mymaster").sentinel("127.0.0.1", 26379);
|
||||
|
||||
private static final RedisClusterConfiguration CLUSTER_CONFIG = new RedisClusterConfiguration().clusterNode(
|
||||
"127.0.0.1", 6379).clusterNode("127.0.0.1", 6380);
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-324
|
||||
*/
|
||||
@@ -52,13 +61,43 @@ public class JedisConnectionFactoryUnitTests {
|
||||
@Test
|
||||
public void shouldInitJedisPoolWhenNoSentinelConfigPresent() {
|
||||
|
||||
connectionFactory = initSpyedConnectionFactory(null, new JedisPoolConfig());
|
||||
connectionFactory = initSpyedConnectionFactory((RedisSentinelConfiguration) null, new JedisPoolConfig());
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
verify(connectionFactory, times(1)).createRedisPool();
|
||||
verify(connectionFactory, never()).createRedisSentinelPool(Matchers.any(RedisSentinelConfiguration.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void shouldInitConnectionCorrectlyWhenClusterConfigPresent() {
|
||||
|
||||
connectionFactory = initSpyedConnectionFactory(CLUSTER_CONFIG, new JedisPoolConfig());
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
verify(connectionFactory, times(1)).createCluster(Matchers.eq(CLUSTER_CONFIG),
|
||||
Matchers.any(GenericObjectPoolConfig.class));
|
||||
verify(connectionFactory, never()).createRedisPool();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IOException
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void shouldClostClusterCorrectlyOnFactoryDestruction() throws IOException {
|
||||
|
||||
JedisCluster clusterMock = mock(JedisCluster.class);
|
||||
JedisConnectionFactory factory = new JedisConnectionFactory();
|
||||
ReflectionTestUtils.setField(factory, "cluster", clusterMock);
|
||||
|
||||
factory.destroy();
|
||||
|
||||
verify(clusterMock, times(1)).close();
|
||||
}
|
||||
|
||||
private JedisConnectionFactory initSpyedConnectionFactory(RedisSentinelConfiguration sentinelConfig,
|
||||
JedisPoolConfig poolConfig) {
|
||||
|
||||
@@ -68,4 +107,14 @@ public class JedisConnectionFactoryUnitTests {
|
||||
doReturn(null).when(factorySpy).createRedisPool();
|
||||
return factorySpy;
|
||||
}
|
||||
|
||||
private JedisConnectionFactory initSpyedConnectionFactory(RedisClusterConfiguration clusterConfig,
|
||||
JedisPoolConfig poolConfig) {
|
||||
|
||||
JedisConnectionFactory factorySpy = spy(new JedisConnectionFactory(clusterConfig));
|
||||
doReturn(null).when(factorySpy).createCluster(Matchers.any(RedisClusterConfiguration.class),
|
||||
Matchers.any(GenericObjectPoolConfig.class));
|
||||
doReturn(null).when(factorySpy).createRedisPool();
|
||||
return factorySpy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +115,6 @@ public class JedisConvertersUnitTests {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-378
|
||||
*/
|
||||
@Test
|
||||
public void boundaryToBytesForZRangeByLexShouldReturnDefaultValueWhenBoundaryIsNull() {
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.jedis;
|
||||
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.hamcrest.core.IsInstanceOf.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.ClusterRedirectException;
|
||||
import org.springframework.data.redis.TooManyClusterRedirectionsException;
|
||||
|
||||
import redis.clients.jedis.HostAndPort;
|
||||
import redis.clients.jedis.exceptions.JedisAskDataException;
|
||||
import redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException;
|
||||
import redis.clients.jedis.exceptions.JedisMovedDataException;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class JedisExceptionConverterUnitTests {
|
||||
|
||||
JedisExceptionConverter converter;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
converter = new JedisExceptionConverter();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void shouldConvertMovedDataException() {
|
||||
|
||||
DataAccessException converted = converter.convert(new JedisMovedDataException("MOVED 3999 127.0.0.1:6381",
|
||||
new HostAndPort("127.0.0.1", 6381), 3999));
|
||||
|
||||
assertThat(converted, instanceOf(ClusterRedirectException.class));
|
||||
assertThat(((ClusterRedirectException) converted).getSlot(), is(3999));
|
||||
assertThat(((ClusterRedirectException) converted).getTargetHost(), is("127.0.0.1"));
|
||||
assertThat(((ClusterRedirectException) converted).getTargetPort(), is(6381));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void shouldConvertAskDataException() {
|
||||
|
||||
DataAccessException converted = converter.convert(new JedisAskDataException("ASK 3999 127.0.0.1:6381",
|
||||
new HostAndPort("127.0.0.1", 6381), 3999));
|
||||
|
||||
assertThat(converted, instanceOf(ClusterRedirectException.class));
|
||||
assertThat(((ClusterRedirectException) converted).getSlot(), is(3999));
|
||||
assertThat(((ClusterRedirectException) converted).getTargetHost(), is("127.0.0.1"));
|
||||
assertThat(((ClusterRedirectException) converted).getTargetPort(), is(6381));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void shouldConvertMaxRedirectException() {
|
||||
|
||||
DataAccessException converted = converter
|
||||
.convert(new JedisClusterMaxRedirectionsException("Too many redirections?"));
|
||||
|
||||
assertThat(converted, instanceOf(TooManyClusterRedirectionsException.class));
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import static org.hamcrest.core.AnyOf.*;
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.hamcrest.core.IsNull.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.redis.connection.ClusterTestVariables.*;
|
||||
import static org.springframework.data.redis.test.util.MockitoUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.redis.connection.ClusterCommandExecutor;
|
||||
import org.springframework.data.redis.connection.ClusterNodeResourceProvider;
|
||||
import org.springframework.data.redis.connection.RedisClusterCommands.AddSlots;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode;
|
||||
|
||||
import com.lambdaworks.redis.RedisAsyncConnection;
|
||||
import com.lambdaworks.redis.RedisConnection;
|
||||
import com.lambdaworks.redis.RedisURI;
|
||||
import com.lambdaworks.redis.cluster.RedisClusterClient;
|
||||
import com.lambdaworks.redis.cluster.models.partitions.Partitions;
|
||||
import com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode.NodeFlag;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LettuceClusterConnectionUnitTests {
|
||||
|
||||
static final byte[] KEY_1_BYTES = KEY_1.getBytes();
|
||||
|
||||
static final byte[] VALUE_1_BYTES = VALUE_1.getBytes();
|
||||
|
||||
static final byte[] KEY_2_BYTES = KEY_2.getBytes();
|
||||
|
||||
static final byte[] KEY_3_BYTES = KEY_3.getBytes();
|
||||
|
||||
@Mock RedisClusterClient clusterMock;
|
||||
|
||||
@Mock ClusterNodeResourceProvider resourceProvider;
|
||||
@Mock RedisAsyncConnection<byte[], byte[]> dedicatedConnectionMock;
|
||||
@Mock RedisConnection<byte[], byte[]> clusterConnection1Mock;
|
||||
@Mock RedisConnection<byte[], byte[]> clusterConnection2Mock;
|
||||
@Mock RedisConnection<byte[], byte[]> clusterConnection3Mock;
|
||||
|
||||
LettuceClusterConnection connection;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
Partitions partitions = new Partitions();
|
||||
|
||||
com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode partition1 = new com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode();
|
||||
partition1.setNodeId(CLUSTER_NODE_1.getId());
|
||||
partition1.setConnected(true);
|
||||
partition1.setFlags(Collections.singleton(NodeFlag.MASTER));
|
||||
partition1.setUri(RedisURI.create("redis://" + CLUSTER_HOST + ":" + MASTER_NODE_1_PORT));
|
||||
|
||||
com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode partition2 = new com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode();
|
||||
partition2.setNodeId(CLUSTER_NODE_2.getId());
|
||||
partition2.setConnected(true);
|
||||
partition2.setFlags(Collections.singleton(NodeFlag.MASTER));
|
||||
partition2.setUri(RedisURI.create("redis://" + CLUSTER_HOST + ":" + MASTER_NODE_2_PORT));
|
||||
|
||||
com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode partition3 = new com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode();
|
||||
partition3.setNodeId(CLUSTER_NODE_3.getId());
|
||||
partition3.setConnected(true);
|
||||
partition3.setFlags(Collections.singleton(NodeFlag.MASTER));
|
||||
partition3.setUri(RedisURI.create("redis://" + CLUSTER_HOST + ":" + MASTER_NODE_3_PORT));
|
||||
|
||||
partitions.addPartition(partition1);
|
||||
partitions.addPartition(partition2);
|
||||
partitions.addPartition(partition3);
|
||||
|
||||
when(resourceProvider.getResourceForSpecificNode(CLUSTER_NODE_1)).thenReturn(clusterConnection1Mock);
|
||||
when(resourceProvider.getResourceForSpecificNode(CLUSTER_NODE_2)).thenReturn(clusterConnection2Mock);
|
||||
when(resourceProvider.getResourceForSpecificNode(CLUSTER_NODE_3)).thenReturn(clusterConnection3Mock);
|
||||
|
||||
when(clusterMock.getPartitions()).thenReturn(partitions);
|
||||
|
||||
ClusterCommandExecutor executor = new ClusterCommandExecutor(
|
||||
new LettuceClusterConnection.LettuceClusterTopologyProvider(clusterMock), resourceProvider,
|
||||
LettuceClusterConnection.exceptionConverter);
|
||||
|
||||
connection = new LettuceClusterConnection(clusterMock, executor) {
|
||||
|
||||
@Override
|
||||
protected RedisAsyncConnection<byte[], byte[]> getAsyncDedicatedConnection() {
|
||||
return dedicatedConnectionMock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RedisClusterNode> clusterGetClusterNodes() {
|
||||
return Arrays.asList(CLUSTER_NODE_1, CLUSTER_NODE_2, CLUSTER_NODE_3);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void thowsExceptionWhenClusterCommandExecturorIsNull() {
|
||||
new LettuceClusterConnection(clusterMock, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterMeetShouldSendCommandsToExistingNodesCorrectly() {
|
||||
|
||||
connection.clusterMeet(UNKNOWN_CLUSTER_NODE);
|
||||
|
||||
verify(clusterConnection1Mock, times(1))
|
||||
.clusterMeet(UNKNOWN_CLUSTER_NODE.getHost(), UNKNOWN_CLUSTER_NODE.getPort());
|
||||
verify(clusterConnection2Mock, times(1))
|
||||
.clusterMeet(UNKNOWN_CLUSTER_NODE.getHost(), UNKNOWN_CLUSTER_NODE.getPort());
|
||||
verify(clusterConnection3Mock, times(1))
|
||||
.clusterMeet(UNKNOWN_CLUSTER_NODE.getHost(), UNKNOWN_CLUSTER_NODE.getPort());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void clusterMeetShouldThrowExceptionWhenNodeIsNull() {
|
||||
connection.clusterMeet(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterForgetShouldSendCommandsToRemainingNodesCorrectly() {
|
||||
|
||||
connection.clusterForget(CLUSTER_NODE_2);
|
||||
|
||||
verify(clusterConnection1Mock, times(1)).clusterForget(CLUSTER_NODE_2.getId());
|
||||
verifyZeroInteractions(clusterConnection2Mock);
|
||||
verify(clusterConnection3Mock, times(1)).clusterForget(CLUSTER_NODE_2.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterReplicateShouldSendCommandsCorrectly() {
|
||||
|
||||
connection.clusterReplicate(CLUSTER_NODE_1, CLUSTER_NODE_2);
|
||||
|
||||
verify(clusterConnection2Mock, times(1)).clusterReplicate(CLUSTER_NODE_1.getId());
|
||||
verifyZeroInteractions(clusterConnection1Mock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void closeShouldNotCloseUnderlyingClusterPool() throws IOException {
|
||||
|
||||
connection.close();
|
||||
|
||||
verify(clusterMock, never()).shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void isClosedShouldReturnConnectionStateCorrectly() {
|
||||
|
||||
assertThat(connection.isClosed(), is(false));
|
||||
|
||||
connection.close();
|
||||
|
||||
assertThat(connection.isClosed(), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void keysShouldBeRunOnAllClusterNodes() {
|
||||
|
||||
when(clusterConnection1Mock.keys(any(byte[].class))).thenReturn(Collections.<byte[]> emptyList());
|
||||
when(clusterConnection2Mock.keys(any(byte[].class))).thenReturn(Collections.<byte[]> emptyList());
|
||||
when(clusterConnection3Mock.keys(any(byte[].class))).thenReturn(Collections.<byte[]> emptyList());
|
||||
|
||||
byte[] pattern = LettuceConverters.toBytes("*");
|
||||
|
||||
connection.keys(pattern);
|
||||
|
||||
verify(clusterConnection1Mock, times(1)).keys(pattern);
|
||||
verify(clusterConnection2Mock, times(1)).keys(pattern);
|
||||
verify(clusterConnection3Mock, times(1)).keys(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void keysShouldOnlyBeRunOnDedicatedNodeWhenPinned() {
|
||||
|
||||
when(clusterConnection2Mock.keys(any(byte[].class))).thenReturn(Collections.<byte[]> emptyList());
|
||||
|
||||
byte[] pattern = LettuceConverters.toBytes("*");
|
||||
|
||||
connection.keys(CLUSTER_NODE_2, pattern);
|
||||
|
||||
verify(clusterConnection1Mock, never()).keys(pattern);
|
||||
verify(clusterConnection2Mock, times(1)).keys(pattern);
|
||||
verify(clusterConnection3Mock, never()).keys(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void randomKeyShouldReturnAnyKeyFromRandomNode() {
|
||||
|
||||
when(clusterConnection1Mock.randomkey()).thenReturn(KEY_1_BYTES);
|
||||
when(clusterConnection2Mock.randomkey()).thenReturn(KEY_2_BYTES);
|
||||
when(clusterConnection3Mock.randomkey()).thenReturn(KEY_3_BYTES);
|
||||
|
||||
assertThat(connection.randomKey(), anyOf(is(KEY_1_BYTES), is(KEY_2_BYTES), is(KEY_3_BYTES)));
|
||||
verifyInvocationsAcross("randomkey", times(1), clusterConnection1Mock, clusterConnection2Mock,
|
||||
clusterConnection3Mock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void randomKeyShouldReturnKeyWhenAvailableOnAnyNode() {
|
||||
|
||||
when(clusterConnection3Mock.randomkey()).thenReturn(KEY_3_BYTES);
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
assertThat(connection.randomKey(), is(KEY_3_BYTES));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void randomKeyShouldReturnNullWhenNoKeysPresentOnAllNodes() {
|
||||
|
||||
when(clusterConnection1Mock.randomkey()).thenReturn(null);
|
||||
when(clusterConnection2Mock.randomkey()).thenReturn(null);
|
||||
when(clusterConnection3Mock.randomkey()).thenReturn(null);
|
||||
|
||||
assertThat(connection.randomKey(), nullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterSetSlotImportingShouldBeExecutedCorrectly() {
|
||||
|
||||
connection.clusterSetSlot(CLUSTER_NODE_1, 100, AddSlots.IMPORTING);
|
||||
|
||||
verify(clusterConnection1Mock, times(1)).clusterSetSlotImporting(eq(100), eq(CLUSTER_NODE_1.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterSetSlotMigratingShouldBeExecutedCorrectly() {
|
||||
|
||||
connection.clusterSetSlot(CLUSTER_NODE_1, 100, AddSlots.MIGRATING);
|
||||
|
||||
verify(clusterConnection1Mock, times(1)).clusterSetSlotMigrating(eq(100), eq(CLUSTER_NODE_1.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
@Ignore("Stable not available for lettuce")
|
||||
public void clusterSetSlotStableShouldBeExecutedCorrectly() {
|
||||
|
||||
connection.clusterSetSlot(CLUSTER_NODE_1, 100, AddSlots.STABLE);
|
||||
|
||||
// verify(clusterConnection1Mock, times(1)).clusterSetSlotStable(eq(100));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterSetSlotNodeShouldBeExecutedCorrectly() {
|
||||
|
||||
connection.clusterSetSlot(CLUSTER_NODE_1, 100, AddSlots.NODE);
|
||||
|
||||
verify(clusterConnection1Mock, times(1)).clusterSetSlotNode(eq(100), eq(CLUSTER_NODE_1.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterSetSlotShouldBeExecutedOnTargetNodeWhenNodeIdNotSet() {
|
||||
|
||||
connection.clusterSetSlot(new RedisClusterNode(CLUSTER_HOST, MASTER_NODE_2_PORT), 100, AddSlots.IMPORTING);
|
||||
|
||||
verify(clusterConnection2Mock, times(1)).clusterSetSlotImporting(eq(100), eq(CLUSTER_NODE_2.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void clusterSetSlotShouldThrowExceptionWhenModeIsNull() {
|
||||
connection.clusterSetSlot(CLUSTER_NODE_1, 100, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void clusterDeleteSlotsShouldBeExecutedCorrectly() {
|
||||
|
||||
int[] slots = new int[] { 9000, 10000 };
|
||||
connection.clusterDeleteSlots(CLUSTER_NODE_2, slots);
|
||||
|
||||
verify(clusterConnection2Mock, times(1)).clusterDelSlots((int[]) anyVararg());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void clusterDeleteSlotShouldThrowExceptionWhenNodeIsNull() {
|
||||
connection.clusterDeleteSlots(null, new int[] { 1 });
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void timeShouldBeExecutedOnArbitraryNode() {
|
||||
|
||||
List<byte[]> values = Arrays.asList("1449655759".getBytes(), "92217".getBytes());
|
||||
when(clusterConnection1Mock.time()).thenReturn(values);
|
||||
when(clusterConnection2Mock.time()).thenReturn(values);
|
||||
when(clusterConnection3Mock.time()).thenReturn(values);
|
||||
|
||||
connection.time();
|
||||
|
||||
verifyInvocationsAcross("time", times(1), clusterConnection1Mock, clusterConnection2Mock, clusterConnection3Mock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void timeShouldBeExecutedOnSingleNode() {
|
||||
|
||||
when(clusterConnection2Mock.time()).thenReturn(Arrays.asList("1449655759".getBytes(), "92217".getBytes()));
|
||||
|
||||
connection.time(CLUSTER_NODE_2);
|
||||
|
||||
verify(clusterConnection2Mock, times(1)).time();
|
||||
verifyZeroInteractions(clusterConnection1Mock, clusterConnection3Mock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void resetConfigStatsShouldBeExecutedOnAllNodes() {
|
||||
|
||||
connection.resetConfigStats();
|
||||
|
||||
verify(clusterConnection1Mock, times(1)).configResetstat();
|
||||
verify(clusterConnection2Mock, times(1)).configResetstat();
|
||||
verify(clusterConnection3Mock, times(1)).configResetstat();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void resetConfigStatsShouldBeExecutedOnSingleNodeCorrectly() {
|
||||
|
||||
connection.resetConfigStats(CLUSTER_NODE_2);
|
||||
|
||||
verify(clusterConnection2Mock, times(1)).configResetstat();
|
||||
verify(clusterConnection1Mock, never()).configResetstat();
|
||||
verify(clusterConnection1Mock, never()).configResetstat();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import static org.hamcrest.core.IsInstanceOf.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.util.ReflectionTestUtils.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.redis.connection.RedisClusterConfiguration;
|
||||
|
||||
import com.lambdaworks.redis.cluster.RedisClusterClient;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class LettuceConnectionFactoryUnitTests {
|
||||
|
||||
private static final RedisClusterConfiguration CLUSTER_CONFIG = new RedisClusterConfiguration().clusterNode(
|
||||
"127.0.0.1", 6379).clusterNode("127.0.0.1", 6380);
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void shouldInitClientCorrectlyWhenClusterConfigPresent() {
|
||||
|
||||
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(CLUSTER_CONFIG);
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
assertThat(getField(connectionFactory, "client"), instanceOf(RedisClusterClient.class));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2015 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.
|
||||
@@ -15,14 +15,28 @@
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.hamcrest.core.IsCollectionContaining.*;
|
||||
import static org.hamcrest.core.IsEqual.*;
|
||||
import static org.hamcrest.core.IsNull.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.redis.connection.ClusterTestVariables.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode.Flag;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode.LinkState;
|
||||
import org.springframework.data.redis.core.types.RedisClientInfo;
|
||||
|
||||
import com.lambdaworks.redis.RedisURI;
|
||||
import com.lambdaworks.redis.cluster.models.partitions.Partitions;
|
||||
import com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode.NodeFlag;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@@ -61,4 +75,40 @@ public class LettuceConvertersUnitTests {
|
||||
assertThat(LettuceConverters.toListOfRedisClientInformation(sb.toString()).size(), equalTo(2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void partitionsToClusterNodesShouldReturnEmptyCollectionWhenPartionsDoesNotContainElements() {
|
||||
assertThat(LettuceConverters.partitionsToClusterNodes(new Partitions()), notNullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void partitionsToClusterNodesShouldConvertPartitionCorrctly() {
|
||||
|
||||
Partitions partitions = new Partitions();
|
||||
|
||||
com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode partition = new com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode();
|
||||
partition.setNodeId(CLUSTER_NODE_1.getId());
|
||||
partition.setConnected(true);
|
||||
partition.setFlags(new HashSet<NodeFlag>(Arrays.asList(NodeFlag.MASTER, NodeFlag.MYSELF)));
|
||||
partition.setUri(RedisURI.create("redis://" + CLUSTER_HOST + ":" + MASTER_NODE_1_PORT));
|
||||
partition.setSlots(Arrays.<Integer> asList(1, 2, 3, 4, 5));
|
||||
|
||||
partitions.addPartition(partition);
|
||||
|
||||
List<RedisClusterNode> nodes = LettuceConverters.partitionsToClusterNodes(partitions);
|
||||
assertThat(nodes.size(), is(1));
|
||||
|
||||
RedisClusterNode node = nodes.get(0);
|
||||
assertThat(node.getHost(), is(CLUSTER_HOST));
|
||||
assertThat(node.getPort(), is(MASTER_NODE_1_PORT));
|
||||
assertThat(node.getFlags(), hasItems(Flag.MASTER, Flag.MYSELF));
|
||||
assertThat(node.getId(), is(CLUSTER_NODE_1.getId()));
|
||||
assertThat(node.getLinkState(), is(LinkState.CONNECTED));
|
||||
assertThat(node.getSlotRange().getSlots(), hasItems(1, 2, 3, 4, 5));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.core;
|
||||
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.hamcrest.core.IsCollectionContaining.*;
|
||||
import static org.hamcrest.core.IsNull.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.connection.RedisClusterCommands.AddSlots;
|
||||
import org.springframework.data.redis.connection.RedisClusterConnection;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode.SlotRange;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisServerCommands.MigrateOption;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DefaultClusterOperationsUnitTests {
|
||||
|
||||
static final RedisClusterNode NODE_1 = RedisClusterNode.newRedisClusterNode().listeningAt("127.0.0.1", 6379)
|
||||
.withId("d1861060fe6a534d42d8a19aeb36600e18785e04").build();
|
||||
|
||||
static final RedisClusterNode NODE_2 = RedisClusterNode.newRedisClusterNode().listeningAt("127.0.0.1", 6380)
|
||||
.withId("0f2ee5df45d18c50aca07228cc18b1da96fd5e84").build();
|
||||
|
||||
@Mock RedisConnectionFactory connectionFactory;
|
||||
@Mock RedisClusterConnection connection;
|
||||
|
||||
RedisSerializer<String> serializer;
|
||||
|
||||
DefaultClusterOperations<String, String> clusterOps;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
when(connectionFactory.getConnection()).thenReturn(connection);
|
||||
when(connectionFactory.getClusterConnection()).thenReturn(connection);
|
||||
|
||||
serializer = new StringRedisSerializer();
|
||||
|
||||
RedisTemplate<String, String> template = new RedisTemplate<String, String>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
template.setValueSerializer(serializer);
|
||||
template.setKeySerializer(serializer);
|
||||
template.afterPropertiesSet();
|
||||
|
||||
this.clusterOps = new DefaultClusterOperations<String, String>(template);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void keysShouldDelegateToConnectionCorrectly() {
|
||||
|
||||
Set<byte[]> keys = new HashSet<byte[]>(Arrays.asList(serializer.serialize("key-1"), serializer.serialize("key-2")));
|
||||
when(connection.keys(any(RedisClusterNode.class), any(byte[].class))).thenReturn(keys);
|
||||
|
||||
assertThat(clusterOps.keys(NODE_1, "*"), hasItems("key-1", "key-2"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void keysShouldThrowExceptionWhenNodeIsNull() {
|
||||
clusterOps.keys(null, "*");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void keysShouldReturnEmptySetWhenNoKeysAvailable() {
|
||||
|
||||
when(connection.keys(any(RedisClusterNode.class), any(byte[].class))).thenReturn(null);
|
||||
|
||||
assertThat(clusterOps.keys(NODE_1, "*"), notNullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void randomKeyShouldDelegateToConnection() {
|
||||
|
||||
when(connection.randomKey(any(RedisClusterNode.class))).thenReturn(serializer.serialize("key-1"));
|
||||
|
||||
assertThat(clusterOps.randomKey(NODE_1), is("key-1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void randomKeyShouldThrowExceptionWhenNodeIsNull() {
|
||||
clusterOps.randomKey(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void randomKeyShouldReturnNullWhenNoKeyAvailable() {
|
||||
|
||||
when(connection.randomKey(any(RedisClusterNode.class))).thenReturn(null);
|
||||
|
||||
assertThat(clusterOps.randomKey(NODE_1), nullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void pingShouldDelegateToConnection() {
|
||||
|
||||
when(connection.ping(any(RedisClusterNode.class))).thenReturn("PONG");
|
||||
|
||||
assertThat(clusterOps.ping(NODE_1), is("PONG"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void pingShouldThrowExceptionWhenNodeIsNull() {
|
||||
clusterOps.ping(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void addSlotsShouldDelegateToConnection() {
|
||||
|
||||
clusterOps.addSlots(NODE_1, 1, 2, 3);
|
||||
|
||||
verify(connection, times(1)).clusterAddSlots(eq(NODE_1), Mockito.<int[]> anyVararg());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void addSlotsShouldThrowExceptionWhenNodeIsNull() {
|
||||
clusterOps.addSlots(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void addSlotsWithRangeShouldDelegateToConnection() {
|
||||
|
||||
clusterOps.addSlots(NODE_1, new SlotRange(1, 3));
|
||||
|
||||
verify(connection, times(1)).clusterAddSlots(eq(NODE_1), Mockito.<int[]> anyVararg());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void addSlotsWithRangeShouldThrowExceptionWhenRangeIsNull() {
|
||||
clusterOps.addSlots(NODE_1, (SlotRange) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void bgSaveShouldDelegateToConnection() {
|
||||
|
||||
clusterOps.bgSave(NODE_1);
|
||||
|
||||
verify(connection, times(1)).bgSave(eq(NODE_1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void bgSaveShouldThrowExceptionWhenNodeIsNull() {
|
||||
clusterOps.bgSave(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void meetShouldDelegateToConnection() {
|
||||
|
||||
clusterOps.meet(NODE_1);
|
||||
|
||||
verify(connection, times(1)).clusterMeet(eq(NODE_1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void meetShouldThrowExceptionWhenNodeIsNull() {
|
||||
clusterOps.meet(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void forgetShouldDelegateToConnection() {
|
||||
|
||||
clusterOps.forget(NODE_1);
|
||||
|
||||
verify(connection, times(1)).clusterForget(eq(NODE_1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void forgetShouldThrowExceptionWhenNodeIsNull() {
|
||||
clusterOps.forget(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void flushDbShouldDelegateToConnection() {
|
||||
|
||||
clusterOps.flushDb(NODE_1);
|
||||
|
||||
verify(connection, times(1)).flushDb(eq(NODE_1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void flushDbShouldThrowExceptionWhenNodeIsNull() {
|
||||
clusterOps.flushDb(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void getSlavesShouldDelegateToConnection() {
|
||||
|
||||
clusterOps.getSlaves(NODE_1);
|
||||
|
||||
verify(connection, times(1)).clusterGetSlaves(eq(NODE_1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void getSlavesShouldThrowExceptionWhenNodeIsNull() {
|
||||
clusterOps.getSlaves(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void saveShouldDelegateToConnection() {
|
||||
|
||||
clusterOps.save(NODE_1);
|
||||
|
||||
verify(connection, times(1)).save(eq(NODE_1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void saveShouldThrowExceptionWhenNodeIsNull() {
|
||||
clusterOps.save(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void shutdownShouldDelegateToConnection() {
|
||||
|
||||
clusterOps.shutdown(NODE_1);
|
||||
|
||||
verify(connection, times(1)).shutdown(eq(NODE_1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shutdownShouldThrowExceptionWhenNodeIsNull() {
|
||||
clusterOps.shutdown(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void executeShouldDelegateToConnection() {
|
||||
|
||||
final byte[] key = serializer.serialize("foo");
|
||||
clusterOps.execute(new RedisClusterCallback<String>() {
|
||||
|
||||
@Override
|
||||
public String doInRedis(RedisClusterConnection connection) throws DataAccessException {
|
||||
return serializer.deserialize(connection.get(key));
|
||||
}
|
||||
});
|
||||
|
||||
verify(connection, times(1)).get(eq(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void executeShouldThrowExceptionWhenCallbackIsNull() {
|
||||
clusterOps.execute(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
*/
|
||||
@Test
|
||||
public void reshardShouldExecuteCommandsCorrectly() {
|
||||
|
||||
byte[] key = "foo".getBytes();
|
||||
when(connection.clusterGetKeysInSlot(eq(100), anyInt())).thenReturn(Collections.singletonList(key));
|
||||
clusterOps.reshard(NODE_1, 100, NODE_2);
|
||||
|
||||
verify(connection, times(1)).clusterSetSlot(eq(NODE_2), eq(100), eq(AddSlots.IMPORTING));
|
||||
verify(connection, times(1)).clusterSetSlot(eq(NODE_1), eq(100), eq(AddSlots.MIGRATING));
|
||||
verify(connection, times(1)).clusterGetKeysInSlot(eq(100), anyInt());
|
||||
verify(connection, times(1)).migrate(any(byte[].class), eq(NODE_1), eq(0), eq(MigrateOption.COPY));
|
||||
verify(connection, times(1)).clusterSetSlot(eq(NODE_2), eq(100), eq(AddSlots.NODE));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2015 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.
|
||||
@@ -23,10 +23,12 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.springframework.data.redis.ConnectionFactoryTracker;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
@@ -43,6 +45,12 @@ public class MultithreadedRedisTemplateTests {
|
||||
|
||||
public MultithreadedRedisTemplateTests(RedisConnectionFactory factory) {
|
||||
this.factory = factory;
|
||||
ConnectionFactoryTracker.add(factory);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void cleanUp() {
|
||||
ConnectionFactoryTracker.cleanUp();
|
||||
}
|
||||
|
||||
@Parameters
|
||||
@@ -57,7 +65,7 @@ public class MultithreadedRedisTemplateTests {
|
||||
lettuce.afterPropertiesSet();
|
||||
|
||||
SrpConnectionFactory srp = new SrpConnectionFactory();
|
||||
srp.setPort(6479);
|
||||
srp.setPort(6379);
|
||||
srp.afterPropertiesSet();
|
||||
|
||||
return Arrays.asList(new Object[][] { { jedis }, { lettuce }, { srp } });
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.core;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.redis.LongObjectFactory;
|
||||
import org.springframework.data.redis.ObjectFactory;
|
||||
import org.springframework.data.redis.Person;
|
||||
import org.springframework.data.redis.PersonObjectFactory;
|
||||
import org.springframework.data.redis.RawObjectFactory;
|
||||
import org.springframework.data.redis.StringObjectFactory;
|
||||
import org.springframework.data.redis.connection.RedisClusterConfiguration;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.serializer.GenericToStringSerializer;
|
||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.OxmSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.data.redis.test.util.RedisClusterRule;
|
||||
import org.springframework.oxm.xstream.XStreamMarshaller;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class RedisClusterTemplateTests<K, V> extends RedisTemplateTests<K, V> {
|
||||
|
||||
static final List<String> CLUSTER_NODES = Arrays.asList("127.0.0.1:7379", "127.0.0.1:7380", "127.0.0.1:7381");
|
||||
|
||||
public RedisClusterTemplateTests(RedisTemplate<K, V> redisTemplate, ObjectFactory<K> keyFactory,
|
||||
ObjectFactory<V> valueFactory) {
|
||||
super(redisTemplate, keyFactory, valueFactory);
|
||||
}
|
||||
|
||||
public static @ClassRule RedisClusterRule clusterAvaialbale = new RedisClusterRule();
|
||||
|
||||
@Test(expected = InvalidDataAccessApiUsageException.class)
|
||||
@Ignore("Pipeline not supported in cluster mode")
|
||||
public void testExecutePipelinedNonNullRedisCallback() {
|
||||
super.testExecutePipelinedNonNullRedisCallback();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Pipeline not supported in cluster mode")
|
||||
public void testExecutePipelinedTx() {
|
||||
super.testExecutePipelinedTx();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Watch only supported on same connection...")
|
||||
public void testWatch() {
|
||||
super.testWatch();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Watch only supported on same connection...")
|
||||
public void testUnwatch() {
|
||||
super.testUnwatch();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("EXEC only supported on same connection...")
|
||||
public void testExec() {
|
||||
super.testExec();
|
||||
}
|
||||
|
||||
@Test(expected = InvalidDataAccessApiUsageException.class)
|
||||
@Ignore("Pipeline not supported in cluster mode")
|
||||
public void testExecutePipelinedNonNullSessionCallback() {
|
||||
super.testExecutePipelinedNonNullSessionCallback();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("PubSub not supported in cluster mode")
|
||||
public void testConvertAndSend() {
|
||||
super.testConvertAndSend();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Watch only supported on same connection...")
|
||||
public void testExecConversionDisabled() {
|
||||
super.testExecConversionDisabled();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Discard only supported on same connection...")
|
||||
public void testDiscard() {
|
||||
super.testDiscard();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Pipleline not supported in cluster mode")
|
||||
public void testExecutePipelined() {
|
||||
super.testExecutePipelined();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Watch only supported on same connection...")
|
||||
public void testWatchMultipleKeys() {
|
||||
super.testWatchMultipleKeys();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("This one fails when using GET options on numbers")
|
||||
public void testSortBulkMapper() {
|
||||
super.testSortBulkMapper();
|
||||
}
|
||||
|
||||
@Parameters
|
||||
public static Collection<Object[]> testParams() {
|
||||
|
||||
ObjectFactory<String> stringFactory = new StringObjectFactory();
|
||||
ObjectFactory<Long> longFactory = new LongObjectFactory();
|
||||
ObjectFactory<byte[]> rawFactory = new RawObjectFactory();
|
||||
ObjectFactory<Person> personFactory = new PersonObjectFactory();
|
||||
|
||||
// XStream serializer
|
||||
XStreamMarshaller xstream = new XStreamMarshaller();
|
||||
try {
|
||||
xstream.afterPropertiesSet();
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Cannot init XStream", ex);
|
||||
}
|
||||
|
||||
OxmSerializer serializer = new OxmSerializer(xstream, xstream);
|
||||
Jackson2JsonRedisSerializer<Person> jackson2JsonSerializer = new Jackson2JsonRedisSerializer<Person>(Person.class);
|
||||
|
||||
// JEDIS
|
||||
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(new RedisClusterConfiguration(
|
||||
CLUSTER_NODES));
|
||||
|
||||
jedisConnectionFactory.afterPropertiesSet();
|
||||
|
||||
RedisTemplate<String, String> jedisStringTemplate = new RedisTemplate<String, String>();
|
||||
jedisStringTemplate.setDefaultSerializer(new StringRedisSerializer());
|
||||
jedisStringTemplate.setConnectionFactory(jedisConnectionFactory);
|
||||
jedisStringTemplate.afterPropertiesSet();
|
||||
|
||||
RedisTemplate<String, Long> jedisLongTemplate = new RedisTemplate<String, Long>();
|
||||
jedisLongTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
jedisLongTemplate.setValueSerializer(new GenericToStringSerializer<Long>(Long.class));
|
||||
jedisLongTemplate.setConnectionFactory(jedisConnectionFactory);
|
||||
jedisLongTemplate.afterPropertiesSet();
|
||||
|
||||
RedisTemplate<byte[], byte[]> jedisRawTemplate = new RedisTemplate<byte[], byte[]>();
|
||||
jedisRawTemplate.setEnableDefaultSerializer(false);
|
||||
jedisRawTemplate.setConnectionFactory(jedisConnectionFactory);
|
||||
jedisRawTemplate.afterPropertiesSet();
|
||||
|
||||
RedisTemplate<String, Person> jedisPersonTemplate = new RedisTemplate<String, Person>();
|
||||
jedisPersonTemplate.setConnectionFactory(jedisConnectionFactory);
|
||||
jedisPersonTemplate.afterPropertiesSet();
|
||||
|
||||
RedisTemplate<String, String> jedisXstreamStringTemplate = new RedisTemplate<String, String>();
|
||||
jedisXstreamStringTemplate.setConnectionFactory(jedisConnectionFactory);
|
||||
jedisXstreamStringTemplate.setDefaultSerializer(serializer);
|
||||
jedisXstreamStringTemplate.afterPropertiesSet();
|
||||
|
||||
RedisTemplate<String, Person> jedisJackson2JsonPersonTemplate = new RedisTemplate<String, Person>();
|
||||
jedisJackson2JsonPersonTemplate.setConnectionFactory(jedisConnectionFactory);
|
||||
jedisJackson2JsonPersonTemplate.setValueSerializer(jackson2JsonSerializer);
|
||||
jedisJackson2JsonPersonTemplate.afterPropertiesSet();
|
||||
|
||||
// LETTUCE
|
||||
|
||||
LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(new RedisClusterConfiguration(
|
||||
CLUSTER_NODES));
|
||||
|
||||
lettuceConnectionFactory.afterPropertiesSet();
|
||||
|
||||
RedisTemplate<String, String> lettuceStringTemplate = new RedisTemplate<String, String>();
|
||||
lettuceStringTemplate.setDefaultSerializer(new StringRedisSerializer());
|
||||
lettuceStringTemplate.setConnectionFactory(lettuceConnectionFactory);
|
||||
lettuceStringTemplate.afterPropertiesSet();
|
||||
|
||||
RedisTemplate<String, Long> lettuceLongTemplate = new RedisTemplate<String, Long>();
|
||||
lettuceLongTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
lettuceLongTemplate.setValueSerializer(new GenericToStringSerializer<Long>(Long.class));
|
||||
lettuceLongTemplate.setConnectionFactory(lettuceConnectionFactory);
|
||||
lettuceLongTemplate.afterPropertiesSet();
|
||||
|
||||
RedisTemplate<byte[], byte[]> lettuceRawTemplate = new RedisTemplate<byte[], byte[]>();
|
||||
lettuceRawTemplate.setEnableDefaultSerializer(false);
|
||||
lettuceRawTemplate.setConnectionFactory(lettuceConnectionFactory);
|
||||
lettuceRawTemplate.afterPropertiesSet();
|
||||
|
||||
RedisTemplate<String, Person> lettucePersonTemplate = new RedisTemplate<String, Person>();
|
||||
lettucePersonTemplate.setConnectionFactory(lettuceConnectionFactory);
|
||||
lettucePersonTemplate.afterPropertiesSet();
|
||||
|
||||
RedisTemplate<String, String> lettuceXstreamStringTemplate = new RedisTemplate<String, String>();
|
||||
lettuceXstreamStringTemplate.setConnectionFactory(lettuceConnectionFactory);
|
||||
lettuceXstreamStringTemplate.setDefaultSerializer(serializer);
|
||||
lettuceXstreamStringTemplate.afterPropertiesSet();
|
||||
|
||||
RedisTemplate<String, Person> lettuceJackson2JsonPersonTemplate = new RedisTemplate<String, Person>();
|
||||
lettuceJackson2JsonPersonTemplate.setConnectionFactory(lettuceConnectionFactory);
|
||||
lettuceJackson2JsonPersonTemplate.setValueSerializer(jackson2JsonSerializer);
|
||||
lettuceJackson2JsonPersonTemplate.afterPropertiesSet();
|
||||
|
||||
return Arrays.asList(new Object[][] { //
|
||||
|
||||
// JEDIS
|
||||
{ jedisStringTemplate, stringFactory, stringFactory }, //
|
||||
{ jedisLongTemplate, stringFactory, longFactory }, //
|
||||
{ jedisRawTemplate, rawFactory, rawFactory }, //
|
||||
{ jedisPersonTemplate, stringFactory, personFactory }, //
|
||||
{ jedisXstreamStringTemplate, stringFactory, stringFactory }, //
|
||||
{ jedisJackson2JsonPersonTemplate, stringFactory, personFactory }, //
|
||||
|
||||
// LETTUCE
|
||||
{ lettuceStringTemplate, stringFactory, stringFactory }, //
|
||||
{ lettuceLongTemplate, stringFactory, longFactory }, //
|
||||
{ lettuceRawTemplate, rawFactory, rawFactory }, //
|
||||
{ lettucePersonTemplate, stringFactory, personFactory }, //
|
||||
{ lettuceXstreamStringTemplate, stringFactory, stringFactory }, //
|
||||
{ lettuceJackson2JsonPersonTemplate, stringFactory, personFactory } //
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-2015 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.
|
||||
@@ -35,6 +35,7 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.hamcrest.core.IsNot;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
@@ -42,6 +43,7 @@ import org.junit.runners.Parameterized.Parameters;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.redis.ConnectionFactoryTracker;
|
||||
import org.springframework.data.redis.ObjectFactory;
|
||||
import org.springframework.data.redis.RedisTestProfileValueSource;
|
||||
import org.springframework.data.redis.SettingsUtils;
|
||||
@@ -68,17 +70,19 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
@RunWith(Parameterized.class)
|
||||
public class RedisTemplateTests<K, V> {
|
||||
|
||||
@Autowired private RedisTemplate<K, V> redisTemplate;
|
||||
@Autowired protected RedisTemplate<K, V> redisTemplate;
|
||||
|
||||
private ObjectFactory<K> keyFactory;
|
||||
protected ObjectFactory<K> keyFactory;
|
||||
|
||||
private ObjectFactory<V> valueFactory;
|
||||
protected ObjectFactory<V> valueFactory;
|
||||
|
||||
public RedisTemplateTests(RedisTemplate<K, V> redisTemplate, ObjectFactory<K> keyFactory,
|
||||
ObjectFactory<V> valueFactory) {
|
||||
this.redisTemplate = redisTemplate;
|
||||
this.keyFactory = keyFactory;
|
||||
this.valueFactory = valueFactory;
|
||||
|
||||
ConnectionFactoryTracker.add(redisTemplate.getConnectionFactory());
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -91,6 +95,11 @@ public class RedisTemplateTests<K, V> {
|
||||
});
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void cleanUp() {
|
||||
ConnectionFactoryTracker.cleanUp();
|
||||
}
|
||||
|
||||
@Parameters
|
||||
public static Collection<Object[]> testParams() {
|
||||
return AbstractOperationsTestParams.testParams();
|
||||
@@ -445,16 +454,16 @@ public class RedisTemplateTests<K, V> {
|
||||
|
||||
@Test
|
||||
public void testExpireMillisNotSupported() throws Exception {
|
||||
|
||||
assumeTrue(RedisTestProfileValueSource.matches("runLongTests", "true"));
|
||||
assumeTrue(redisTemplate.getConnectionFactory() instanceof JredisConnectionFactory);
|
||||
|
||||
final K key1 = keyFactory.instance();
|
||||
V value1 = valueFactory.instance();
|
||||
|
||||
assumeTrue(key1 instanceof String && value1 instanceof String);
|
||||
// JRedis does not support pExpire
|
||||
JredisConnectionFactory factory = new JredisConnectionFactory();
|
||||
factory.setHostName(SettingsUtils.getHost());
|
||||
factory.setPort(SettingsUtils.getPort());
|
||||
factory.afterPropertiesSet();
|
||||
final StringRedisTemplate template2 = new StringRedisTemplate(factory);
|
||||
|
||||
final StringRedisTemplate template2 = new StringRedisTemplate(redisTemplate.getConnectionFactory());
|
||||
template2.boundValueOps((String) key1).set((String) value1);
|
||||
template2.expire((String) key1, 10, TimeUnit.MILLISECONDS);
|
||||
Thread.sleep(15);
|
||||
@@ -489,15 +498,15 @@ public class RedisTemplateTests<K, V> {
|
||||
|
||||
@Test
|
||||
public void testGetExpireMillisNotSupported() {
|
||||
|
||||
assumeTrue(redisTemplate.getConnectionFactory() instanceof JedisConnectionFactory);
|
||||
|
||||
final K key1 = keyFactory.instance();
|
||||
V value1 = valueFactory.instance();
|
||||
|
||||
assumeTrue(key1 instanceof String && value1 instanceof String);
|
||||
// Jedis does not support pTtl
|
||||
JedisConnectionFactory factory = new JedisConnectionFactory();
|
||||
factory.setHostName(SettingsUtils.getHost());
|
||||
factory.setPort(SettingsUtils.getPort());
|
||||
factory.afterPropertiesSet();
|
||||
final StringRedisTemplate template2 = new StringRedisTemplate(factory);
|
||||
|
||||
final StringRedisTemplate template2 = new StringRedisTemplate(redisTemplate.getConnectionFactory());
|
||||
template2.boundValueOps((String) key1).set((String) value1);
|
||||
template2.expire((String) key1, 5, TimeUnit.SECONDS);
|
||||
long expire = template2.getExpire((String) key1, TimeUnit.MILLISECONDS);
|
||||
@@ -520,16 +529,16 @@ public class RedisTemplateTests<K, V> {
|
||||
|
||||
@Test
|
||||
public void testExpireAtMillisNotSupported() {
|
||||
|
||||
assumeTrue(RedisTestProfileValueSource.matches("runLongTests", "true"));
|
||||
assumeTrue(redisTemplate.getConnectionFactory() instanceof JedisConnectionFactory);
|
||||
|
||||
final K key1 = keyFactory.instance();
|
||||
V value1 = valueFactory.instance();
|
||||
|
||||
assumeTrue(key1 instanceof String && value1 instanceof String);
|
||||
// Jedis does not support pExpireAt
|
||||
JedisConnectionFactory factory = new JedisConnectionFactory();
|
||||
factory.setHostName(SettingsUtils.getHost());
|
||||
factory.setPort(SettingsUtils.getPort());
|
||||
factory.afterPropertiesSet();
|
||||
final StringRedisTemplate template2 = new StringRedisTemplate(factory);
|
||||
|
||||
final StringRedisTemplate template2 = new StringRedisTemplate(redisTemplate.getConnectionFactory());
|
||||
template2.boundValueOps((String) key1).set((String) value1);
|
||||
template2.expireAt((String) key1, new Date(System.currentTimeMillis() + 5l));
|
||||
// Just ensure this works as expected, pExpireAt just adds some precision over expireAt
|
||||
|
||||
@@ -17,7 +17,7 @@ package org.springframework.data.redis.test.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.internal.AssumptionViolatedException;
|
||||
import org.junit.AssumptionViolatedException;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
@@ -50,36 +50,36 @@ public class MinimumRedisVersionRule implements TestRule {
|
||||
|
||||
private static synchronized Version readServerVersion() {
|
||||
|
||||
Jedis jedis = new Jedis(SettingsUtils.getHost(), SettingsUtils.getPort());
|
||||
|
||||
Version version = Version.UNKNOWN;
|
||||
|
||||
Jedis jedis = null;
|
||||
try {
|
||||
|
||||
jedis.connect();
|
||||
jedis = new Jedis(SettingsUtils.getHost(), SettingsUtils.getPort());
|
||||
String info = jedis.info();
|
||||
String versionString = (String) JedisConverters.stringToProps().convert(info).get("redis_version");
|
||||
|
||||
version = RedisVersionUtils.parseVersion(versionString);
|
||||
} finally {
|
||||
try {
|
||||
if (jedis != null) {
|
||||
try {
|
||||
|
||||
jedis.disconnect();
|
||||
if (jedis.getClient().getSocket().isConnected()) {
|
||||
// force socket to be closed
|
||||
jedis.getClient().getSocket().close();
|
||||
try {
|
||||
// need to wait a bit
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
// just ignore it
|
||||
jedis.disconnect();
|
||||
if (jedis.getClient().getSocket().isConnected()) {
|
||||
// force socket to be closed
|
||||
jedis.getClient().getSocket().close();
|
||||
try {
|
||||
// need to wait a bit
|
||||
Thread.sleep(5);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.interrupted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e1) {
|
||||
// ignore as well
|
||||
} catch (IOException e1) {
|
||||
// ignore as well
|
||||
}
|
||||
jedis.close();
|
||||
}
|
||||
jedis.close();
|
||||
}
|
||||
|
||||
return version;
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.test.util;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.mockito.internal.invocation.InvocationMatcher;
|
||||
import org.mockito.internal.verification.api.VerificationData;
|
||||
import org.mockito.invocation.Invocation;
|
||||
import org.mockito.verification.VerificationMode;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public class MockitoUtils {
|
||||
|
||||
/**
|
||||
* Verifies a given method is called a total number of times across all given mocks.
|
||||
*
|
||||
* @param method
|
||||
* @param mode
|
||||
* @param mocks
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "serial" })
|
||||
public static void verifyInvocationsAcross(final String method, final VerificationMode mode, Object... mocks) {
|
||||
|
||||
mode.verify(new VerificationDataImpl(getInvocations(method, mocks), new InvocationMatcher(null, Collections
|
||||
.<Matcher> singletonList(org.mockito.internal.matchers.Any.ANY)) {
|
||||
|
||||
@Override
|
||||
public boolean matches(Invocation actual) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s for method: %s", mode, method);
|
||||
}
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
private static List<Invocation> getInvocations(String method, Object... mocks) {
|
||||
|
||||
List<Invocation> invocations = new ArrayList<Invocation>();
|
||||
for (Object mock : mocks) {
|
||||
|
||||
if (StringUtils.hasText(method)) {
|
||||
|
||||
for (Invocation invocation : mockingDetails(mock).getInvocations()) {
|
||||
if (invocation.getMethod().getName().equals(method)) {
|
||||
invocations.add(invocation);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
invocations.addAll(mockingDetails(mock).getInvocations());
|
||||
}
|
||||
}
|
||||
return invocations;
|
||||
}
|
||||
|
||||
static class VerificationDataImpl implements VerificationData {
|
||||
|
||||
private final List<Invocation> invocations;
|
||||
private final InvocationMatcher wanted;
|
||||
|
||||
public VerificationDataImpl(List<Invocation> invocations, InvocationMatcher wanted) {
|
||||
this.invocations = invocations;
|
||||
this.wanted = wanted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Invocation> getAllInvocations() {
|
||||
return invocations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvocationMatcher getWanted() {
|
||||
return wanted;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.redis.test.util;
|
||||
|
||||
import org.junit.internal.AssumptionViolatedException;
|
||||
import org.junit.AssumptionViolatedException;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.test.util;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
|
||||
import org.junit.Assume;
|
||||
import org.junit.rules.ExternalResource;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.springframework.data.redis.connection.RedisClusterConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisNode;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConverters;
|
||||
|
||||
import redis.clients.jedis.Jedis;
|
||||
|
||||
/**
|
||||
* Simple {@link TestRule} implementation that check Redis is running in cluster mode.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
*/
|
||||
public class RedisClusterRule extends ExternalResource {
|
||||
|
||||
private RedisClusterConfiguration clusterConfig;
|
||||
private String mode;
|
||||
|
||||
/**
|
||||
* Create and init {@link RedisClusterRule} with default configuration ({@code host=127.0.0.1 port=7379}).
|
||||
*/
|
||||
public RedisClusterRule() {
|
||||
this(new RedisClusterConfiguration().clusterNode("127.0.0.1", 7379));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and init {@link RedisClientRule} with given configuration.
|
||||
*
|
||||
* @param config
|
||||
*/
|
||||
public RedisClusterRule(RedisClusterConfiguration config) {
|
||||
this.clusterConfig = config;
|
||||
init();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.junit.rules.ExternalResource#before()
|
||||
*/
|
||||
@Override
|
||||
protected void before() throws Throwable {
|
||||
Assume.assumeThat(mode, is("cluster"));
|
||||
}
|
||||
|
||||
private void init() {
|
||||
|
||||
if (clusterConfig == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (RedisNode node : clusterConfig.getClusterNodes()) {
|
||||
|
||||
Jedis jedis = null;
|
||||
try {
|
||||
|
||||
jedis = new Jedis(node.getHost(), node.getPort());
|
||||
mode = JedisConverters.toProperties(jedis.info()).getProperty("redis_mode");
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
// ignore and move on
|
||||
} finally {
|
||||
jedis.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.redis.test.util;
|
||||
|
||||
import org.junit.internal.AssumptionViolatedException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.AssumptionViolatedException;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
@@ -39,6 +42,8 @@ public class RedisSentinelRule implements TestRule {
|
||||
private RedisSentinelConfiguration sentinelConfig;
|
||||
private SentinelsAvailable requiredSentinels;
|
||||
|
||||
private Map<Object, Boolean> cache = new HashMap<Object, Boolean>();
|
||||
|
||||
protected RedisSentinelRule(RedisSentinelConfiguration config) {
|
||||
this.sentinelConfig = config;
|
||||
}
|
||||
@@ -128,7 +133,12 @@ public class RedisSentinelRule implements TestRule {
|
||||
|
||||
int failed = 0;
|
||||
for (RedisNode node : sentinelConfig.getSentinels()) {
|
||||
if (!isAvailable(node)) {
|
||||
|
||||
if (cache.isEmpty() || !cache.containsKey(node.asString())) {
|
||||
cache.put(node.asString(), isAvailable(node));
|
||||
}
|
||||
|
||||
if (!cache.get(node.asString())) {
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
@@ -159,7 +169,6 @@ public class RedisSentinelRule implements TestRule {
|
||||
try {
|
||||
|
||||
jedis = new Jedis(node.getHost(), node.getPort());
|
||||
jedis.connect();
|
||||
jedis.ping();
|
||||
|
||||
return true;
|
||||
@@ -173,7 +182,7 @@ public class RedisSentinelRule implements TestRule {
|
||||
jedis.disconnect();
|
||||
if (jedis.getClient().getSocket().isConnected()) {
|
||||
jedis.getClient().getSocket().close();
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(5);
|
||||
}
|
||||
|
||||
jedis.close();
|
||||
|
||||
Reference in New Issue
Block a user