DATAREDIS-467 - Fix cluster multi key commands.
We moved away from returning raw map types and now return dedicated objects for command executions in cluster environment. This allows to maintain node information and collecting results from each and every callback. MGET now returns values according to the key position. Additionally we now prefix info commands in the cluster with the nodes host:port. Original pull request: #174.
This commit is contained in:
committed by
Mark Paluch
parent
6f26a5a073
commit
02742e3b49
@@ -25,7 +25,6 @@ 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;
|
||||
@@ -43,6 +42,7 @@ 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.MulitNodeResult;
|
||||
import org.springframework.data.redis.connection.ClusterCommandExecutor.MultiKeyClusterCommandCallback;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode.LinkState;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode.SlotRange;
|
||||
@@ -77,8 +77,7 @@ public class ClusterCommandExecutorUnitTests {
|
||||
.withId("3b9b8192a874fa8f1f09dbc0ee20afab5738eee7").promotedAs(NodeType.MASTER).linkState(LinkState.CONNECTED)
|
||||
.build();
|
||||
static final RedisClusterNode CLUSTER_NODE_2_LOOKUP = RedisClusterNode.newRedisClusterNode()
|
||||
.withId("0f2ee5df45d18c50aca07228cc18b1da96fd5e84")
|
||||
.build();
|
||||
.withId("0f2ee5df45d18c50aca07228cc18b1da96fd5e84").build();
|
||||
|
||||
static final RedisClusterNode UNKNOWN_CLUSTER_NODE = new RedisClusterNode("8.8.8.8", 7379, null);
|
||||
|
||||
@@ -149,7 +148,8 @@ public class ClusterCommandExecutorUnitTests {
|
||||
@Test
|
||||
public void executeCommandOnSingleNodeByHostAndPortShouldBeExecutedCorrectly() {
|
||||
|
||||
executor.executeCommandOnSingleNode(COMMAND_CALLBACK, new RedisClusterNode(CLUSTER_NODE_2_HOST, CLUSTER_NODE_2_PORT));
|
||||
executor.executeCommandOnSingleNode(COMMAND_CALLBACK,
|
||||
new RedisClusterNode(CLUSTER_NODE_2_HOST, CLUSTER_NODE_2_PORT));
|
||||
|
||||
verify(con2, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
}
|
||||
@@ -216,8 +216,9 @@ public class ClusterCommandExecutorUnitTests {
|
||||
new MockClusterResourceProvider(), new PassThroughExceptionTranslationStrategy(exceptionConverter),
|
||||
new ConcurrentTaskExecutor(new SyncTaskExecutor()));
|
||||
|
||||
executor.executeCommandAsyncOnNodes(COMMAND_CALLBACK, Arrays.asList(new RedisClusterNode(CLUSTER_NODE_1_HOST, CLUSTER_NODE_1_PORT),
|
||||
new RedisClusterNode(CLUSTER_NODE_2_HOST, CLUSTER_NODE_2_PORT)));
|
||||
executor.executeCommandAsyncOnNodes(COMMAND_CALLBACK,
|
||||
Arrays.asList(new RedisClusterNode(CLUSTER_NODE_1_HOST, CLUSTER_NODE_1_PORT),
|
||||
new RedisClusterNode(CLUSTER_NODE_2_HOST, CLUSTER_NODE_2_PORT)));
|
||||
|
||||
verify(con1, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
verify(con2, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
@@ -234,8 +235,8 @@ public class ClusterCommandExecutorUnitTests {
|
||||
new MockClusterResourceProvider(), new PassThroughExceptionTranslationStrategy(exceptionConverter),
|
||||
new ConcurrentTaskExecutor(new SyncTaskExecutor()));
|
||||
|
||||
executor.executeCommandAsyncOnNodes(COMMAND_CALLBACK, Arrays.asList(new RedisClusterNode(CLUSTER_NODE_1.id),
|
||||
CLUSTER_NODE_2_LOOKUP));
|
||||
executor.executeCommandAsyncOnNodes(COMMAND_CALLBACK,
|
||||
Arrays.asList(new RedisClusterNode(CLUSTER_NODE_1.id), CLUSTER_NODE_2_LOOKUP));
|
||||
|
||||
verify(con1, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
verify(con2, times(1)).theWheelWeavesAsTheWheelWills();
|
||||
@@ -252,8 +253,8 @@ public class ClusterCommandExecutorUnitTests {
|
||||
new MockClusterResourceProvider(), new PassThroughExceptionTranslationStrategy(exceptionConverter),
|
||||
new ConcurrentTaskExecutor(new SyncTaskExecutor()));
|
||||
|
||||
executor.executeCommandAsyncOnNodes(COMMAND_CALLBACK, Arrays.asList(
|
||||
new RedisClusterNode("unknown"), CLUSTER_NODE_2_LOOKUP));
|
||||
executor.executeCommandAsyncOnNodes(COMMAND_CALLBACK,
|
||||
Arrays.asList(new RedisClusterNode("unknown"), CLUSTER_NODE_2_LOOKUP));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,32 +307,29 @@ public class ClusterCommandExecutorUnitTests {
|
||||
when(con2.theWheelWeavesAsTheWheelWills()).thenReturn("mat");
|
||||
when(con3.theWheelWeavesAsTheWheelWills()).thenReturn("perrin");
|
||||
|
||||
Map<RedisClusterNode, String> result = executor.executeCommandOnAllNodes(COMMAND_CALLBACK);
|
||||
MulitNodeResult<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"));
|
||||
assertThat(result.resultsAsList(), hasItems("rand", "mat", "perrin"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-315
|
||||
* @see DATAREDIS-467
|
||||
*/
|
||||
@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(con1.bloodAndAshes(captor.capture())).thenReturn("rand").thenReturn("egwene");
|
||||
|
||||
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())));
|
||||
MulitNodeResult<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"));
|
||||
assertThat(result.resultsAsList(), hasItems("rand", "mat", "perrin", "egwene"));
|
||||
|
||||
// check that 2 keys have been routed to node1
|
||||
assertThat(captor.getAllValues().size(), is(2));
|
||||
@@ -389,8 +387,8 @@ public class ClusterCommandExecutorUnitTests {
|
||||
|
||||
@Override
|
||||
public ClusterTopology getTopology() {
|
||||
return new ClusterTopology(new LinkedHashSet<RedisClusterNode>(Arrays.asList(CLUSTER_NODE_1, CLUSTER_NODE_2,
|
||||
CLUSTER_NODE_3)));
|
||||
return new ClusterTopology(
|
||||
new LinkedHashSet<RedisClusterNode>(Arrays.asList(CLUSTER_NODE_1, CLUSTER_NODE_2, CLUSTER_NODE_3)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.redis.connection.jedis;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.collection.IsIterableContainingInOrder.*;
|
||||
import static org.hamcrest.number.IsCloseTo.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.redis.connection.ClusterTestVariables.*;
|
||||
@@ -536,7 +537,7 @@ public class JedisClusterConnectionTests implements ClusterConnectionTests {
|
||||
nativeConnection.set(SAME_SLOT_KEY_2_BYTES, VALUE_2_BYTES);
|
||||
|
||||
assertThat(clusterConnection.mGet(SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES),
|
||||
hasItems(VALUE_1_BYTES, VALUE_2_BYTES));
|
||||
contains(VALUE_1_BYTES, VALUE_2_BYTES));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -548,7 +549,7 @@ public class JedisClusterConnectionTests implements ClusterConnectionTests {
|
||||
nativeConnection.set(KEY_1_BYTES, VALUE_1_BYTES);
|
||||
nativeConnection.set(KEY_2_BYTES, VALUE_2_BYTES);
|
||||
|
||||
assertThat(clusterConnection.mGet(KEY_1_BYTES, KEY_2_BYTES), hasItems(VALUE_1_BYTES, VALUE_2_BYTES));
|
||||
assertThat(clusterConnection.mGet(KEY_1_BYTES, KEY_2_BYTES), contains(VALUE_1_BYTES, VALUE_2_BYTES));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2088,29 +2089,23 @@ public class JedisClusterConnectionTests implements ClusterConnectionTests {
|
||||
|
||||
assertThat(values,
|
||||
hasItems(JedisConverters.toBytes("a"), JedisConverters.toBytes("b"), JedisConverters.toBytes("c")));
|
||||
assertThat(
|
||||
values,
|
||||
not(hasItems(JedisConverters.toBytes("d"), JedisConverters.toBytes("e"), JedisConverters.toBytes("f"),
|
||||
JedisConverters.toBytes("g"))));
|
||||
assertThat(values, not(hasItems(JedisConverters.toBytes("d"), JedisConverters.toBytes("e"),
|
||||
JedisConverters.toBytes("f"), JedisConverters.toBytes("g"))));
|
||||
|
||||
values = clusterConnection.zRangeByLex(KEY_1_BYTES, Range.range().lt("c"));
|
||||
assertThat(values, hasItems(JedisConverters.toBytes("a"), JedisConverters.toBytes("b")));
|
||||
assertThat(values, not(hasItem(JedisConverters.toBytes("c"))));
|
||||
|
||||
values = clusterConnection.zRangeByLex(KEY_1_BYTES, Range.range().gte("aaa").lt("g"));
|
||||
assertThat(
|
||||
values,
|
||||
hasItems(JedisConverters.toBytes("b"), JedisConverters.toBytes("c"), JedisConverters.toBytes("d"),
|
||||
JedisConverters.toBytes("e"), JedisConverters.toBytes("f")));
|
||||
assertThat(values, hasItems(JedisConverters.toBytes("b"), JedisConverters.toBytes("c"),
|
||||
JedisConverters.toBytes("d"), JedisConverters.toBytes("e"), JedisConverters.toBytes("f")));
|
||||
assertThat(values, not(hasItems(JedisConverters.toBytes("a"), JedisConverters.toBytes("g"))));
|
||||
|
||||
values = clusterConnection.zRangeByLex(KEY_1_BYTES, Range.range().gte("e"));
|
||||
assertThat(values,
|
||||
hasItems(JedisConverters.toBytes("e"), JedisConverters.toBytes("f"), JedisConverters.toBytes("g")));
|
||||
assertThat(
|
||||
values,
|
||||
not(hasItems(JedisConverters.toBytes("a"), JedisConverters.toBytes("b"), JedisConverters.toBytes("c"),
|
||||
JedisConverters.toBytes("d"))));
|
||||
assertThat(values, not(hasItems(JedisConverters.toBytes("a"), JedisConverters.toBytes("b"),
|
||||
JedisConverters.toBytes("c"), JedisConverters.toBytes("d"))));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2118,7 +2113,7 @@ public class JedisClusterConnectionTests implements ClusterConnectionTests {
|
||||
*/
|
||||
@Test
|
||||
public void infoShouldCollectionInfoFromAllClusterNodes() {
|
||||
assertThat(clusterConnection.info().size(), is(3));
|
||||
assertThat(Double.valueOf(clusterConnection.info().size()), closeTo(245d, 35d));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2288,8 +2283,8 @@ public class JedisClusterConnectionTests implements ClusterConnectionTests {
|
||||
@Test
|
||||
public void clusterGetSlavesShouldReturnSlaveCorrectly() {
|
||||
|
||||
Set<RedisClusterNode> slaves = clusterConnection.clusterGetSlaves(new RedisClusterNode(CLUSTER_HOST,
|
||||
MASTER_NODE_1_PORT));
|
||||
Set<RedisClusterNode> slaves = clusterConnection
|
||||
.clusterGetSlaves(new RedisClusterNode(CLUSTER_HOST, MASTER_NODE_1_PORT));
|
||||
|
||||
assertThat(slaves.size(), is(1));
|
||||
assertThat(slaves, hasItem(new RedisClusterNode(CLUSTER_HOST, SLAVEOF_NODE_1_PORT)));
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.collection.IsIterableContainingInOrder.*;
|
||||
import static org.hamcrest.number.IsCloseTo.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.redis.connection.ClusterTestVariables.*;
|
||||
@@ -529,7 +530,7 @@ public class LettuceClusterConnectionTests implements ClusterConnectionTests {
|
||||
nativeConnection.set(SAME_SLOT_KEY_2, VALUE_2);
|
||||
|
||||
assertThat(clusterConnection.mGet(SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES),
|
||||
hasItems(VALUE_1_BYTES, VALUE_2_BYTES));
|
||||
contains(VALUE_1_BYTES, VALUE_2_BYTES));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -541,7 +542,7 @@ public class LettuceClusterConnectionTests implements ClusterConnectionTests {
|
||||
nativeConnection.set(KEY_1, VALUE_1);
|
||||
nativeConnection.set(KEY_2, VALUE_2);
|
||||
|
||||
assertThat(clusterConnection.mGet(KEY_1_BYTES, KEY_2_BYTES), hasItems(VALUE_1_BYTES, VALUE_2_BYTES));
|
||||
assertThat(clusterConnection.mGet(KEY_1_BYTES, KEY_2_BYTES), contains(VALUE_1_BYTES, VALUE_2_BYTES));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2103,7 +2104,7 @@ public class LettuceClusterConnectionTests implements ClusterConnectionTests {
|
||||
*/
|
||||
@Test
|
||||
public void infoShouldCollectionInfoFromAllClusterNodes() {
|
||||
assertThat(clusterConnection.info().size(), is(3));
|
||||
assertThat(Double.valueOf(clusterConnection.info().size()), closeTo(245d, 35d));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user