This is an automated email from the ASF dual-hosted git repository. jensdeppe pushed a commit to branch support/1.15 in repository https://gitbox.apache.org/repos/asf/geode.git
commit 0331eeeb0895167680e3d3f876bf05b457fec5aa Author: Kris10 <kod...@vmware.com> AuthorDate: Thu Jan 27 12:40:53 2022 -0800 GEODE-9830: SINTERSTORE Command Support (#7302) (cherry picked from commit 32e32af84a2727b3589d5940546ccc655a40e69d) --- geode-for-redis/README.md | 1 + .../set/SInterStoreNativeRedisAcceptanceTest.java} | 20 ++- .../server/AbstractHitsMissesIntegrationTest.java | 12 +- .../set/AbstractSInterIntegrationTest.java | 140 +---------------- ...ava => AbstractSInterStoreIntegrationTest.java} | 127 +++++++-------- .../set/AbstractSUnionStoreIntegrationTest.java | 2 +- .../executor/set/SInterStoreIntegrationTest.java} | 18 +-- .../redis/internal/commands/RedisCommandType.java | 4 +- .../commands/executor/set/SDiffExecutor.java | 17 +- .../commands/executor/set/SDiffStoreExecutor.java | 14 +- .../commands/executor/set/SInterExecutor.java | 16 +- .../commands/executor/set/SInterStoreExecutor.java | 13 +- .../commands/executor/set/SUnionExecutor.java | 16 +- .../commands/executor/set/SUnionStoreExecutor.java | 13 +- .../commands/executor/set/SetOpExecutor.java | 173 +++------------------ .../apache/geode/redis/internal/data/RedisSet.java | 51 +++--- 16 files changed, 196 insertions(+), 441 deletions(-) diff --git a/geode-for-redis/README.md b/geode-for-redis/README.md index 592b129..183d148 100644 --- a/geode-for-redis/README.md +++ b/geode-for-redis/README.md @@ -203,6 +203,7 @@ Geode for Redis implements a subset of the full Redis command set. - SDIFF - SDIFFSTORE - SINTER +- SINTERSTORE - SISMEMBER - SET - SETEX diff --git a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SDiffExecutor.java b/geode-for-redis/src/acceptanceTest/java/org/apache/geode/redis/internal/commands/executor/set/SInterStoreNativeRedisAcceptanceTest.java similarity index 69% copy from geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SDiffExecutor.java copy to geode-for-redis/src/acceptanceTest/java/org/apache/geode/redis/internal/commands/executor/set/SInterStoreNativeRedisAcceptanceTest.java index 1021b86..b19232a 100755 --- a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SDiffExecutor.java +++ b/geode-for-redis/src/acceptanceTest/java/org/apache/geode/redis/internal/commands/executor/set/SInterStoreNativeRedisAcceptanceTest.java @@ -14,18 +14,24 @@ */ package org.apache.geode.redis.internal.commands.executor.set; -import java.util.Set; -public class SDiffExecutor extends SetOpExecutor { +import org.junit.ClassRule; + +import org.apache.geode.redis.NativeRedisClusterTestRule; + +public class SInterStoreNativeRedisAcceptanceTest extends AbstractSInterStoreIntegrationTest { + + @ClassRule + public static NativeRedisClusterTestRule redis = new NativeRedisClusterTestRule(); @Override - protected boolean doSetOp(Set<byte[]> resultSet, Set<byte[]> nextSet) { - resultSet.removeAll(nextSet); - return resultSet.isEmpty(); + public int getPort() { + return redis.getExposedPorts().get(0); } @Override - protected boolean isStorage() { - return false; + public void flushAll() { + redis.flushAll(); } + } diff --git a/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/server/AbstractHitsMissesIntegrationTest.java b/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/server/AbstractHitsMissesIntegrationTest.java index 23735d9..0933ed6 100644 --- a/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/server/AbstractHitsMissesIntegrationTest.java +++ b/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/server/AbstractHitsMissesIntegrationTest.java @@ -395,6 +395,12 @@ public abstract class AbstractHitsMissesIntegrationTest implements RedisIntegrat } @Test + public void testSinterstore() { + runMultiKeyCommandAndAssertNoStatUpdates(SET_KEY, + (k1, k2) -> jedis.sinterstore(HASHTAG + "dest", k1, k2)); + } + + @Test public void testSismember() { runCommandAndAssertHitsAndMisses(SET_KEY, k -> jedis.sismember(k, "member")); } @@ -565,12 +571,6 @@ public abstract class AbstractHitsMissesIntegrationTest implements RedisIntegrat runCommandAndAssertHitsAndMisses(SET_KEY, k -> jedis.sscan(k, "0")); } - @Test - public void testSinterstore() { - runMultiKeyCommandAndAssertNoStatUpdates(SET_KEY, - (k1, k2) -> jedis.sinterstore(HASHTAG + "dest", k1, k2)); - } - /************* Helper Methods *************/ private void runCommandAndAssertHitsAndMisses(String key, Consumer<String> command) { Map<String, String> info = RedisTestHelper.getInfo(jedis); diff --git a/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSInterIntegrationTest.java b/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSInterIntegrationTest.java index 115317d..34b927d 100755 --- a/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSInterIntegrationTest.java +++ b/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSInterIntegrationTest.java @@ -17,13 +17,12 @@ package org.apache.geode.redis.internal.commands.executor.set; import static org.apache.geode.redis.RedisCommandArgumentsTestHelper.assertAtLeastNArgs; import static org.apache.geode.redis.internal.RedisConstants.ERROR_WRONG_SLOT; import static org.apache.geode.redis.internal.RedisConstants.ERROR_WRONG_TYPE; +import static org.apache.geode.test.dunit.rules.RedisClusterStartupRule.BIND_ADDRESS; +import static org.apache.geode.test.dunit.rules.RedisClusterStartupRule.REDIS_CLIENT_TIMEOUT; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static redis.clients.jedis.Protocol.Command.SINTER; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; @@ -32,23 +31,19 @@ import org.junit.Before; import org.junit.Test; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisCluster; -import redis.clients.jedis.Protocol; import org.apache.geode.redis.ConcurrentLoopingThreads; import org.apache.geode.redis.RedisIntegrationTest; -import org.apache.geode.test.awaitility.GeodeAwaitility; public abstract class AbstractSInterIntegrationTest implements RedisIntegrationTest { private JedisCluster jedis; - private static final int REDIS_CLIENT_TIMEOUT = - Math.toIntExact(GeodeAwaitility.getTimeout().toMillis()); private static final String SET1 = "{tag1}set1"; private static final String SET2 = "{tag1}set2"; private static final String SET3 = "{tag1}set3"; @Before public void setUp() { - jedis = new JedisCluster(new HostAndPort("localhost", getPort()), REDIS_CLIENT_TIMEOUT); + jedis = new JedisCluster(new HostAndPort(BIND_ADDRESS, getPort()), REDIS_CLIENT_TIMEOUT); } @After @@ -63,11 +58,6 @@ public abstract class AbstractSInterIntegrationTest implements RedisIntegrationT } @Test - public void sinterstoreErrors_givenTooFewArguments() { - assertAtLeastNArgs(jedis, Protocol.Command.SINTERSTORE, 2); - } - - @Test public void sinter_withSetsFromDifferentSlots_returnsCrossSlotError() { String setKeyDifferentSlot = "{tag2}set2"; jedis.sadd(SET1, "member1"); @@ -201,128 +191,4 @@ public abstract class AbstractSInterIntegrationTest implements RedisIntegrationT jedis.srem(SET3, newValues); }); } - - @Test - public void testSInterStore() { - String[] firstSet = new String[] {"pear", "apple", "plum", "orange", "peach"}; - String[] secondSet = new String[] {"apple", "microsoft", "linux", "peach"}; - String[] thirdSet = new String[] {"luigi", "bowser", "peach", "mario"}; - jedis.sadd("{tag1}set1", firstSet); - jedis.sadd("{tag1}set2", secondSet); - jedis.sadd("{tag1}set3", thirdSet); - - Long resultSize = - jedis.sinterstore("{tag1}result", "{tag1}set1", "{tag1}set2", "{tag1}set3"); - Set<String> resultSet = jedis.smembers("{tag1}result"); - - String[] expected = new String[] {"peach"}; - assertThat(resultSize).isEqualTo(expected.length); - assertThat(resultSet).containsExactlyInAnyOrder(expected); - - Long otherResultSize = jedis.sinterstore("{tag1}set1", "{tag1}set1", "{tag1}set2"); - Set<String> otherResultSet = jedis.smembers("{tag1}set1"); - String[] otherExpected = new String[] {"apple", "peach"}; - assertThat(otherResultSize).isEqualTo(otherExpected.length); - assertThat(otherResultSet).containsExactlyInAnyOrder(otherExpected); - - Long emptySetSize = - jedis.sinterstore("{tag1}newEmpty", "{tag1}nonexistent", "{tag1}set2", "{tag1}set3"); - Set<String> emptyResultSet = jedis.smembers("{tag1}newEmpty"); - assertThat(emptySetSize).isEqualTo(0L); - assertThat(emptyResultSet).isEmpty(); - - emptySetSize = - jedis.sinterstore("{tag1}set1", "{tag1}nonexistent", "{tag1}set2", "{tag1}set3"); - emptyResultSet = jedis.smembers("{tag1}set1"); - assertThat(emptySetSize).isEqualTo(0L); - assertThat(emptyResultSet).isEmpty(); - - Long copySetSize = jedis.sinterstore("{tag1}copySet", "{tag1}set2", "{tag1}newEmpty"); - Set<String> copyResultSet = jedis.smembers("{tag1}copySet"); - assertThat(copySetSize).isEqualTo(0); - assertThat(copyResultSet).isEmpty(); - } - - @Test - public void testSInterStore_withNonExistentKeys() { - String[] firstSet = new String[] {"pear", "apple", "plum", "orange", "peach"}; - jedis.sadd("{tag1}set1", firstSet); - - Long resultSize = - jedis.sinterstore("{tag1}set1", "{tag1}nonExistent1", "{tag1}nonExistent2"); - assertThat(resultSize).isEqualTo(0); - assertThat(jedis.exists("{tag1}set1")).isFalse(); - } - - @Test - public void testSInterStore_withNonExistentKeys_andNonSetTarget() { - jedis.set("string1", "stringValue"); - - Long resultSize = - jedis.sinterstore("{tag1}string1", "{tag1}nonExistent1", "{tag1}nonExistent2"); - assertThat(resultSize).isEqualTo(0); - assertThat(jedis.exists("{tag1}set1")).isFalse(); - } - - @Test - public void testSInterStore_withNonSetKey() { - String[] firstSet = new String[] {"pear", "apple", "plum", "orange", "peach"}; - jedis.sadd("{tag1}set1", firstSet); - jedis.set("{tag1}string1", "value1"); - - assertThatThrownBy(() -> jedis.sinterstore("{tag1}set1", "{tag1}string1")) - .hasMessage("WRONGTYPE Operation against a key holding the wrong kind of value"); - assertThat(jedis.exists("{tag1}set1")).isTrue(); - } - - @Test - public void testConcurrentSInterStore() throws InterruptedException { - int ENTRIES = 100; - int SUBSET_SIZE = 100; - - Set<String> masterSet = new HashSet<>(); - for (int i = 0; i < ENTRIES; i++) { - masterSet.add("master-" + i); - } - - List<Set<String>> otherSets = new ArrayList<>(); - for (int i = 0; i < ENTRIES; i++) { - Set<String> oneSet = new HashSet<>(); - for (int j = 0; j < SUBSET_SIZE; j++) { - oneSet.add("set-" + i + "-" + j); - } - otherSets.add(oneSet); - } - - jedis.sadd("master", masterSet.toArray(new String[] {})); - - for (int i = 0; i < ENTRIES; i++) { - jedis.sadd("set-" + i, otherSets.get(i).toArray(new String[] {})); - jedis.sadd("set-" + i, masterSet.toArray(new String[] {})); - } - - Runnable runnable1 = () -> { - for (int i = 0; i < ENTRIES; i++) { - jedis.sinterstore("master", "master", "set-" + i); - Thread.yield(); - } - }; - - Runnable runnable2 = () -> { - for (int i = 0; i < ENTRIES; i++) { - jedis.sinterstore("master", "master", "set-" + i); - Thread.yield(); - } - }; - - Thread thread1 = new Thread(runnable1); - Thread thread2 = new Thread(runnable2); - - thread1.start(); - thread2.start(); - thread1.join(); - thread2.join(); - - assertThat(jedis.smembers("master").toArray()).containsExactlyInAnyOrder(masterSet.toArray()); - } } diff --git a/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSUnionStoreIntegrationTest.java b/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSInterStoreIntegrationTest.java similarity index 57% copy from geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSUnionStoreIntegrationTest.java copy to geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSInterStoreIntegrationTest.java index b6526c1..305a063 100644 --- a/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSUnionStoreIntegrationTest.java +++ b/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSInterStoreIntegrationTest.java @@ -14,6 +14,7 @@ */ package org.apache.geode.redis.internal.commands.executor.set; + import static org.apache.geode.redis.RedisCommandArgumentsTestHelper.assertAtLeastNArgs; import static org.apache.geode.redis.internal.RedisConstants.ERROR_WRONG_SLOT; import static org.apache.geode.redis.internal.RedisConstants.ERROR_WRONG_TYPE; @@ -21,7 +22,7 @@ import static org.apache.geode.test.dunit.rules.RedisClusterStartupRule.BIND_ADD import static org.apache.geode.test.dunit.rules.RedisClusterStartupRule.REDIS_CLIENT_TIMEOUT; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static redis.clients.jedis.Protocol.Command.SUNIONSTORE; +import static redis.clients.jedis.Protocol.Command.SINTERSTORE; import java.util.concurrent.atomic.AtomicLong; @@ -35,7 +36,7 @@ import redis.clients.jedis.Protocol; import org.apache.geode.redis.ConcurrentLoopingThreads; import org.apache.geode.redis.RedisIntegrationTest; -public abstract class AbstractSUnionStoreIntegrationTest implements RedisIntegrationTest { +public abstract class AbstractSInterStoreIntegrationTest implements RedisIntegrationTest { private JedisCluster jedis; private static final String DESTINATION_KEY = "{tag1}destinationKey"; private static final String[] DESTINATION_MEMBERS = @@ -57,159 +58,155 @@ public abstract class AbstractSUnionStoreIntegrationTest implements RedisIntegra } @Test - public void sunionstore_givenTooFewArguments_returnsError() { - assertAtLeastNArgs(jedis, Protocol.Command.SUNION, 1); + public void sinterstore_givenTooFewArguments_returnsError() { + assertAtLeastNArgs(jedis, Protocol.Command.SINTERSTORE, 2); } @Test - public void sunionstore_withExistentSet_returnsUnionSize_storesUnion() { + public void sinterstore_withExistentSet_returnsInterSize_storesInter() { jedis.sadd(SET_KEY_1, SET_MEMBERS_1); - assertThat(jedis.sunionstore(DESTINATION_KEY, SET_KEY_1)).isEqualTo(SET_MEMBERS_1.length); + assertThat(jedis.sinterstore(DESTINATION_KEY, SET_KEY_1)).isEqualTo(SET_MEMBERS_1.length); assertThat(jedis.smembers(DESTINATION_KEY)).containsExactlyInAnyOrder(SET_MEMBERS_1); } @Test - public void sunionstore_withNonExistentSet_returnsZero_destKeyDoesNotExist() { - assertThat(jedis.sunionstore(DESTINATION_KEY, NON_EXISTENT_SET)).isEqualTo(0); + public void sinterstore_withNonExistentSet_returnsZero_doesNotCreateSetAtDestination() { + assertThat(jedis.sinterstore(DESTINATION_KEY, NON_EXISTENT_SET)).isEqualTo(0); assertThat(jedis.exists(DESTINATION_KEY)).isFalse(); } @Test - public void sunionstore_withOneExistentAndOneNonExistentSet_returnsUnionSize_storesUnion() { + public void sinterstore_withExistentFirstSetAndNonExistentSecondSet_returnsZero_doesNotCreateSetAtDestination() { jedis.sadd(SET_KEY_1, SET_MEMBERS_1); - assertThat(jedis.sunionstore(DESTINATION_KEY, SET_KEY_1, NON_EXISTENT_SET)) - .isEqualTo(SET_MEMBERS_1.length); - assertThat(jedis.smembers(DESTINATION_KEY)).containsExactlyInAnyOrder(SET_MEMBERS_1); + assertThat(jedis.sinterstore(DESTINATION_KEY, SET_KEY_1, NON_EXISTENT_SET)) + .isEqualTo(0); + assertThat(jedis.exists(DESTINATION_KEY)).isFalse(); } @Test - public void sunionstore_withOneNonExistentAndOneExistentSet_returnsUnionSize_storesUnion() { + public void sinterstore_withNonExistentFirstSetAndExistentSecondSetSet_returnsZero_doesNotCreateSetAtDestination() { jedis.sadd(SET_KEY_1, SET_MEMBERS_1); - assertThat(jedis.sunionstore(DESTINATION_KEY, NON_EXISTENT_SET, SET_KEY_1)) - .isEqualTo(SET_MEMBERS_1.length); - assertThat(jedis.smembers(DESTINATION_KEY)).containsExactlyInAnyOrder(SET_MEMBERS_1); + assertThat(jedis.sinterstore(DESTINATION_KEY, NON_EXISTENT_SET, SET_KEY_1)) + .isEqualTo(0); + assertThat(jedis.exists(DESTINATION_KEY)).isFalse(); } @Test - public void sunionstore_withNonOverlappingSets_returnsUnionSize_storesUnion() { + public void sinterstore_withNoIntersectingMembers_returnsZero_doesNotCreateSetAtDestination() { String[] secondSetMembers = new String[] {"apple", "microsoft", "linux", "peach"}; jedis.sadd(SET_KEY_1, SET_MEMBERS_1); jedis.sadd(SET_KEY_2, secondSetMembers); - String[] result = - {"one", "two", "three", "four", "five", "apple", "microsoft", "linux", "peach"}; - assertThat(jedis.sunionstore(DESTINATION_KEY, SET_KEY_1, SET_KEY_2)).isEqualTo(result.length); - assertThat(jedis.smembers(DESTINATION_KEY)).containsExactlyInAnyOrder(result); - + assertThat(jedis.sinterstore(DESTINATION_KEY, SET_KEY_1, SET_KEY_2)).isEqualTo(0); + assertThat(jedis.exists(DESTINATION_KEY)).isFalse(); } @Test - public void sunionstore_withSomeSharedValues_returnsUnionSize_storesUnion() { + public void sinterstore_withSomeIntersectingMembers_returnsInterSize_storesInter() { String[] secondSetMembers = new String[] {"one", "two", "linux", "peach"}; jedis.sadd(SET_KEY_1, SET_MEMBERS_1); jedis.sadd(SET_KEY_2, secondSetMembers); - String[] result = {"one", "two", "three", "four", "five", "linux", "peach"}; - assertThat(jedis.sunionstore(DESTINATION_KEY, SET_KEY_1, SET_KEY_2)).isEqualTo(result.length); + String[] result = {"one", "two"}; + assertThat(jedis.sinterstore(DESTINATION_KEY, SET_KEY_1, SET_KEY_2)).isEqualTo(result.length); assertThat(jedis.smembers(DESTINATION_KEY)).containsExactlyInAnyOrder(result); } @Test - public void sunionstore_withAllSharedValues_returnsUnionSize_storesUnion() { + public void sinterstore_withAllIntersectingMembers_returnsInterSize_storesInter() { jedis.sadd(SET_KEY_1, SET_MEMBERS_1); jedis.sadd(SET_KEY_2, SET_MEMBERS_1); - assertThat(jedis.sunionstore(DESTINATION_KEY, SET_KEY_1, SET_KEY_2)) + assertThat(jedis.sinterstore(DESTINATION_KEY, SET_KEY_1, SET_KEY_2)) .isEqualTo(SET_MEMBERS_1.length); assertThat(jedis.smembers(DESTINATION_KEY)).containsExactlyInAnyOrder(SET_MEMBERS_1); } @Test - public void sunionstore_withMultipleNonExistentSets_returnsZero_destKeyDoesNotExist() { - assertThat(jedis.sunionstore(DESTINATION_KEY, NON_EXISTENT_SET, "{tag1}nonExistentSet2")) + public void sinterstore_withMultipleNonExistentSets_returnsZero_doesNotCreateSetAtDestination() { + assertThat(jedis.sinterstore(DESTINATION_KEY, NON_EXISTENT_SET, "{tag1}nonExistentSet2")) .isEqualTo(0); assertThat(jedis.exists(DESTINATION_KEY)).isFalse(); } @Test - public void sunionstore_withExistentSet_returnsUnionSize_destKeyOverwrittenWithUnion() { + public void sinterstore_withExistentSet_returnsInterSize_destKeyOverwrittenWithInter() { jedis.sadd(DESTINATION_KEY, DESTINATION_MEMBERS); jedis.sadd(SET_KEY_1, SET_MEMBERS_1); - assertThat(jedis.sunionstore(DESTINATION_KEY, SET_KEY_1)).isEqualTo(SET_MEMBERS_1.length); + assertThat(jedis.sinterstore(DESTINATION_KEY, SET_KEY_1)).isEqualTo(SET_MEMBERS_1.length); assertThat(jedis.smembers(DESTINATION_KEY)).containsExactlyInAnyOrder(SET_MEMBERS_1); } @Test - public void sunionstore_withNonExistentSet_returnsZero_destKeyIsDeleted() { + public void sinterstore_withNonExistentSet_returnsZero_destKeyIsDeleted() { jedis.sadd(DESTINATION_KEY, DESTINATION_MEMBERS); - assertThat(jedis.sunionstore(DESTINATION_KEY, NON_EXISTENT_SET)).isEqualTo(0); + assertThat(jedis.sinterstore(DESTINATION_KEY, NON_EXISTENT_SET)).isEqualTo(0); assertThat(jedis.exists(DESTINATION_KEY)).isFalse(); } @Test - public void sunionstore_withNonSetDestKey_withExistentSet_returnsUnionSize_destKeyOverwrittenWithUnion() { + public void sinterstore_withNonSetDestKey_withExistentSet_returnsInterSize_destKeyOverwrittenWithInter() { String stringKey = "{tag1}ding"; jedis.set(stringKey, "dong"); jedis.sadd(SET_KEY_1, SET_MEMBERS_1); - assertThat(jedis.sunionstore(stringKey, SET_KEY_1)).isEqualTo(SET_MEMBERS_1.length); + assertThat(jedis.sinterstore(stringKey, SET_KEY_1)).isEqualTo(SET_MEMBERS_1.length); assertThat(jedis.smembers(stringKey)).containsExactlyInAnyOrder(SET_MEMBERS_1); } @Test - public void sunionstore_withNonSetDestKey_withNonExistentSet_returnsZero_destKeyIsDeleted() { + public void sinterstore_withNonSetDestKey_withNonExistentSet_returnsZero_destKeyIsDeleted() { String stringKey = "{tag1}ding"; jedis.set(stringKey, "dong"); - assertThat(jedis.sunionstore(stringKey, NON_EXISTENT_SET)).isEqualTo(0); + assertThat(jedis.sinterstore(stringKey, NON_EXISTENT_SET)).isEqualTo(0); assertThat(jedis.exists(DESTINATION_KEY)).isFalse(); } @Test - public void sunionstore_withNonExistentDest_withNonSetKeyAsFirstKey_returnsWrongTypeError() { + public void sinterstore_withNonSetKeyAsFirstKey_returnsWrongTypeError() { String stringKey = "{tag1}ding"; jedis.set(stringKey, "dong"); jedis.sadd(SET_KEY_1, SET_MEMBERS_1); jedis.sadd(SET_KEY_2, "doorbell"); - assertThatThrownBy(() -> jedis.sunionstore(DESTINATION_KEY, stringKey, SET_KEY_1, SET_KEY_2)) + assertThatThrownBy(() -> jedis.sinterstore(DESTINATION_KEY, stringKey, SET_KEY_1, SET_KEY_2)) .hasMessageContaining(ERROR_WRONG_TYPE); } @Test - public void sunionstore_withExistentDest_withNonSetKeyAsThirdKey_returnsWrongTypeError() { + public void sinterstore_withNonSetKeyAsThirdKey_returnsWrongTypeError() { String stringKey = "{tag1}ding"; jedis.set(stringKey, "dong"); jedis.sadd(DESTINATION_KEY, DESTINATION_MEMBERS); jedis.sadd(SET_KEY_1, SET_MEMBERS_1); jedis.sadd(SET_KEY_2, "doorbell"); - assertThatThrownBy(() -> jedis.sunionstore(DESTINATION_KEY, SET_KEY_1, SET_KEY_2, stringKey)) + assertThatThrownBy(() -> jedis.sinterstore(DESTINATION_KEY, SET_KEY_1, SET_KEY_2, stringKey)) .hasMessageContaining(ERROR_WRONG_TYPE); } @Test - public void sunionstore_withNonExistentDest_withNonSetKeyAsThirdKeyAndNonExistentSetAsFirstKey_returnsWrongTypeError() { + public void sinterstore_withNonSetKeyAsThirdKeyAndNonExistentSetAsFirstKey_returnsWrongTypeError() { String stringKey = "{tag1}ding"; jedis.set(stringKey, "dong"); jedis.sadd(SET_KEY_1, SET_MEMBERS_1); jedis.sadd(SET_KEY_2, "doorbell"); assertThatThrownBy( - () -> jedis.sunionstore(DESTINATION_KEY, NON_EXISTENT_SET, SET_KEY_1, stringKey)) + () -> jedis.sinterstore(DESTINATION_KEY, NON_EXISTENT_SET, SET_KEY_1, stringKey)) .hasMessageContaining(ERROR_WRONG_TYPE); } @Test - public void sunionstore_withSetsFromDifferentSlots_returnsCrossSlotError() { + public void sinterstore_withSetsFromDifferentSlots_returnsCrossSlotError() { String setKeyDifferentSlot = "{tag2}setKey2"; String[] secondSetMembers = new String[] {"one", "two", "linux", "peach"}; jedis.sadd(SET_KEY_1, SET_MEMBERS_1); jedis.sadd(setKeyDifferentSlot, secondSetMembers); - assertThatThrownBy(() -> jedis.sendCommand(DESTINATION_KEY, SUNIONSTORE, DESTINATION_KEY, - SET_KEY_1, setKeyDifferentSlot)) - .hasMessage("CROSSSLOT " + ERROR_WRONG_SLOT); + assertThatThrownBy(() -> jedis.sendCommand(DESTINATION_KEY, SINTERSTORE, DESTINATION_KEY, + SET_KEY_1, setKeyDifferentSlot)).hasMessage("CROSSSLOT " + ERROR_WRONG_SLOT); } @Test @@ -218,27 +215,23 @@ public abstract class AbstractSUnionStoreIntegrationTest implements RedisIntegra jedis.sadd(SET_KEY_1, SET_MEMBERS_1); jedis.sadd(SET_KEY_2, secondSetMembers); - String[] unionMembers = {"one", "two", "three", "four", "five", "linux", "peach"}; - final AtomicLong sunionstoreResultReference = new AtomicLong(0); + String[] result = {"one", "two"}; + final AtomicLong sinterSizeReference = new AtomicLong(0); new ConcurrentLoopingThreads(1000, i -> jedis.srem(SET_KEY_2, secondSetMembers), - i -> sunionstoreResultReference - .set(jedis.sunionstore(DESTINATION_KEY, SET_KEY_1, SET_KEY_2))) - .runWithAction(() -> { - // Check sunionstore return size of union - assertThat(sunionstoreResultReference).satisfiesAnyOf( - sunionstoreSize -> assertThat(sunionstoreSize.get()) - .isEqualTo(SET_MEMBERS_1.length), - sunionstoreSize -> assertThat(sunionstoreSize.get()) - .isEqualTo(unionMembers.length)); - // Checks if values were stored in destination key - assertThat(DESTINATION_KEY).satisfiesAnyOf( - key -> assertThat(jedis.smembers(DESTINATION_KEY)) - .containsExactlyInAnyOrder(SET_MEMBERS_1), - key -> assertThat(jedis.smembers(DESTINATION_KEY)) - .containsExactlyInAnyOrder(unionMembers)); - jedis.sadd(SET_KEY_2, secondSetMembers); - jedis.del(DESTINATION_KEY); - }); + i -> sinterSizeReference.set(jedis.sinterstore(DESTINATION_KEY, SET_KEY_1, SET_KEY_2))) + .runWithAction(() -> { + // Checks sinterstore return size of inter + assertThat(sinterSizeReference).satisfiesAnyOf( + sinterSize -> assertThat(sinterSize.get()).isEqualTo(0), + sinterSize -> assertThat(sinterSize.get()).isEqualTo(result.length)); + // Checks if values were stored in destination key + assertThat(DESTINATION_KEY).satisfiesAnyOf( + key -> assertThat(jedis.exists(key)).isFalse(), + key -> assertThat(jedis.smembers(DESTINATION_KEY)) + .containsExactlyInAnyOrder(result)); + jedis.sadd(SET_KEY_2, secondSetMembers); + jedis.del(DESTINATION_KEY); + }); } } diff --git a/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSUnionStoreIntegrationTest.java b/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSUnionStoreIntegrationTest.java index b6526c1..5a71f4c 100644 --- a/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSUnionStoreIntegrationTest.java +++ b/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSUnionStoreIntegrationTest.java @@ -58,7 +58,7 @@ public abstract class AbstractSUnionStoreIntegrationTest implements RedisIntegra @Test public void sunionstore_givenTooFewArguments_returnsError() { - assertAtLeastNArgs(jedis, Protocol.Command.SUNION, 1); + assertAtLeastNArgs(jedis, Protocol.Command.SUNIONSTORE, 2); } @Test diff --git a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SDiffExecutor.java b/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/SInterStoreIntegrationTest.java similarity index 73% copy from geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SDiffExecutor.java copy to geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/SInterStoreIntegrationTest.java index 1021b86..dd78b7d 100755 --- a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SDiffExecutor.java +++ b/geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/SInterStoreIntegrationTest.java @@ -14,18 +14,18 @@ */ package org.apache.geode.redis.internal.commands.executor.set; -import java.util.Set; +import org.junit.ClassRule; -public class SDiffExecutor extends SetOpExecutor { +import org.apache.geode.redis.GeodeRedisServerRule; - @Override - protected boolean doSetOp(Set<byte[]> resultSet, Set<byte[]> nextSet) { - resultSet.removeAll(nextSet); - return resultSet.isEmpty(); - } +public class SInterStoreIntegrationTest extends AbstractSInterStoreIntegrationTest { + + @ClassRule + public static GeodeRedisServerRule server = new GeodeRedisServerRule(); @Override - protected boolean isStorage() { - return false; + public int getPort() { + return server.getPort(); } + } diff --git a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/RedisCommandType.java b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/RedisCommandType.java index 7887281..c11155b 100755 --- a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/RedisCommandType.java +++ b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/RedisCommandType.java @@ -246,6 +246,8 @@ public enum RedisCommandType { new Parameter().min(3).lastKey(-1).flags(WRITE, DENYOOM)), SINTER(new SInterExecutor(), SUPPORTED, new Parameter().min(2).lastKey(-1).flags(READONLY, SORT_FOR_SCRIPT)), + SINTERSTORE(new SInterStoreExecutor(), SUPPORTED, + new Parameter().min(3).lastKey(-1).flags(WRITE, DENYOOM)), SISMEMBER(new SIsMemberExecutor(), SUPPORTED, new Parameter().exact(3).flags(READONLY, FAST)), SMEMBERS(new SMembersExecutor(), SUPPORTED, new Parameter().exact(2).flags(READONLY, SORT_FOR_SCRIPT)), @@ -353,8 +355,6 @@ public enum RedisCommandType { /**************** Sets *****************/ - SINTERSTORE(new SInterStoreExecutor(), UNSUPPORTED, - new Parameter().min(3).lastKey(-1).flags(WRITE, DENYOOM)), SPOP(new SPopExecutor(), UNSUPPORTED, new Parameter().min(2).max(3, ERROR_SYNTAX).flags(WRITE, RANDOM, FAST)), SSCAN(new SScanExecutor(), UNSUPPORTED, new Parameter().min(3).flags(READONLY, RANDOM), diff --git a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SDiffExecutor.java b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SDiffExecutor.java index 1021b86..e8ba9ef 100755 --- a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SDiffExecutor.java +++ b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SDiffExecutor.java @@ -14,18 +14,19 @@ */ package org.apache.geode.redis.internal.commands.executor.set; +import static org.apache.geode.redis.internal.data.RedisSet.sdiff; + +import java.util.List; import java.util.Set; -public class SDiffExecutor extends SetOpExecutor { +import org.apache.geode.redis.internal.data.RedisKey; +import org.apache.geode.redis.internal.services.RegionProvider; - @Override - protected boolean doSetOp(Set<byte[]> resultSet, Set<byte[]> nextSet) { - resultSet.removeAll(nextSet); - return resultSet.isEmpty(); - } +public class SDiffExecutor extends SetOpArrayResult { @Override - protected boolean isStorage() { - return false; + protected Set<byte[]> getResult(RegionProvider regionProvider, List<RedisKey> setKeys) { + return sdiff(regionProvider, setKeys, true); } + } diff --git a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SDiffStoreExecutor.java b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SDiffStoreExecutor.java index 644a802..fff9088 100755 --- a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SDiffStoreExecutor.java +++ b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SDiffStoreExecutor.java @@ -14,10 +14,18 @@ */ package org.apache.geode.redis.internal.commands.executor.set; -public class SDiffStoreExecutor extends SDiffExecutor { +import static org.apache.geode.redis.internal.data.RedisSet.sdiffstore; + +import java.util.List; + +import org.apache.geode.redis.internal.data.RedisKey; +import org.apache.geode.redis.internal.services.RegionProvider; + +public class SDiffStoreExecutor extends SetOpIntegerResult { @Override - protected boolean isStorage() { - return true; + protected int getResult(RegionProvider regionProvider, List<RedisKey> setKeys, RedisKey destKey) { + return sdiffstore(regionProvider, setKeys, destKey); } + } diff --git a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SInterExecutor.java b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SInterExecutor.java index 1f83344..d40cb6a 100755 --- a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SInterExecutor.java +++ b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SInterExecutor.java @@ -14,18 +14,18 @@ */ package org.apache.geode.redis.internal.commands.executor.set; +import static org.apache.geode.redis.internal.data.RedisSet.sinter; + +import java.util.List; import java.util.Set; -public class SInterExecutor extends SetOpExecutor { +import org.apache.geode.redis.internal.data.RedisKey; +import org.apache.geode.redis.internal.services.RegionProvider; - @Override - protected boolean isStorage() { - return false; - } +public class SInterExecutor extends SetOpArrayResult { @Override - protected boolean doSetOp(Set<byte[]> resultSet, Set<byte[]> nextSet) { - resultSet.retainAll(nextSet); - return resultSet.isEmpty(); + protected Set<byte[]> getResult(RegionProvider regionProvider, List<RedisKey> setKeys) { + return sinter(regionProvider, setKeys, true); } } diff --git a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SInterStoreExecutor.java b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SInterStoreExecutor.java index de1fabb..bc6dc1a 100755 --- a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SInterStoreExecutor.java +++ b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SInterStoreExecutor.java @@ -15,11 +15,18 @@ package org.apache.geode.redis.internal.commands.executor.set; +import static org.apache.geode.redis.internal.data.RedisSet.sinterstore; -public class SInterStoreExecutor extends SInterExecutor { +import java.util.List; + +import org.apache.geode.redis.internal.data.RedisKey; +import org.apache.geode.redis.internal.services.RegionProvider; + +public class SInterStoreExecutor extends SetOpIntegerResult { @Override - protected boolean isStorage() { - return true; + protected int getResult(RegionProvider regionProvider, List<RedisKey> setKeys, RedisKey destKey) { + return sinterstore(regionProvider, setKeys, destKey); } + } diff --git a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SUnionExecutor.java b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SUnionExecutor.java index 5089417..ed88ed9 100755 --- a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SUnionExecutor.java +++ b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SUnionExecutor.java @@ -14,18 +14,18 @@ */ package org.apache.geode.redis.internal.commands.executor.set; +import static org.apache.geode.redis.internal.data.RedisSet.sunion; + +import java.util.List; import java.util.Set; -public class SUnionExecutor extends SetOpExecutor { +import org.apache.geode.redis.internal.data.RedisKey; +import org.apache.geode.redis.internal.services.RegionProvider; - @Override - protected boolean isStorage() { - return false; - } +public class SUnionExecutor extends SetOpArrayResult { @Override - protected boolean doSetOp(Set<byte[]> resultSet, Set<byte[]> nextSet) { - resultSet.addAll(nextSet); - return false; + protected Set<byte[]> getResult(RegionProvider regionProvider, List<RedisKey> setKeys) { + return sunion(regionProvider, setKeys, true); } } diff --git a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SUnionStoreExecutor.java b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SUnionStoreExecutor.java index 9a956fe..2d5860b 100755 --- a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SUnionStoreExecutor.java +++ b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SUnionStoreExecutor.java @@ -14,10 +14,17 @@ */ package org.apache.geode.redis.internal.commands.executor.set; -public class SUnionStoreExecutor extends SUnionExecutor { +import static org.apache.geode.redis.internal.data.RedisSet.sunionstore; + +import java.util.List; + +import org.apache.geode.redis.internal.data.RedisKey; +import org.apache.geode.redis.internal.services.RegionProvider; + +public class SUnionStoreExecutor extends SetOpIntegerResult { @Override - protected boolean isStorage() { - return true; + protected int getResult(RegionProvider regionProvider, List<RedisKey> setKeys, RedisKey destKey) { + return sunionstore(regionProvider, setKeys, destKey); } } diff --git a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SetOpExecutor.java b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SetOpExecutor.java index 9f6fce0..a769de8 100755 --- a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SetOpExecutor.java +++ b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/commands/executor/set/SetOpExecutor.java @@ -14,24 +14,15 @@ */ package org.apache.geode.redis.internal.commands.executor.set; -import static java.util.Collections.emptySet; -import static org.apache.geode.redis.internal.data.RedisSet.sdiff; -import static org.apache.geode.redis.internal.data.RedisSet.sdiffstore; -import static org.apache.geode.redis.internal.data.RedisSet.sinter; -import static org.apache.geode.redis.internal.data.RedisSet.sunion; -import static org.apache.geode.redis.internal.data.RedisSet.sunionstore; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.apache.geode.redis.internal.commands.Command; -import org.apache.geode.redis.internal.commands.RedisCommandType; import org.apache.geode.redis.internal.commands.executor.CommandExecutor; import org.apache.geode.redis.internal.commands.executor.RedisResponse; import org.apache.geode.redis.internal.data.RedisKey; -import org.apache.geode.redis.internal.data.RedisSet; -import org.apache.geode.redis.internal.data.RedisSet.MemberSet; import org.apache.geode.redis.internal.netty.ExecutionHandlerContext; import org.apache.geode.redis.internal.services.RegionProvider; @@ -39,160 +30,36 @@ public abstract class SetOpExecutor implements CommandExecutor { @Override public RedisResponse executeCommand(Command command, ExecutionHandlerContext context) { - int setsStartIndex = 1; - - if (isStorage()) { - setsStartIndex++; - } - - List<RedisKey> commandElements = command.getProcessedCommandKeys(); - List<RedisKey> setKeys = commandElements.subList(setsStartIndex, commandElements.size()); RegionProvider regionProvider = context.getRegionProvider(); + List<RedisKey> commandElements = command.getProcessedCommandKeys(); + List<RedisKey> setKeys = commandElements.subList(1, commandElements.size()); - /* - * SINTERSTORE currently use the else part of the code for their implementation. - * TODO: Once the above commands have been implemented remove the if else - * Refactor so the implementation is in the executor. After delete doActualSetOperation, - * doStoreSetOp, doStoreSetOpWhileLocked, computeStoreSetOp, fetchSets - */ - List<RedisKey> keysToLock = new ArrayList<>(setKeys); - if (isStorage()) { - keysToLock.add(command.getKey()); - } - if (command.isOfType(RedisCommandType.SDIFF) || command.isOfType(RedisCommandType.SDIFFSTORE)) { - if (isStorage()) { - RedisKey destinationKey = command.getKey(); - int resultSize = context.lockedExecute(destinationKey, keysToLock, - () -> sdiffstore(regionProvider, destinationKey, setKeys)); - return RedisResponse.integer(resultSize); - } - - Set<byte[]> resultSet = context.lockedExecute(setKeys.get(0), keysToLock, - () -> sdiff(regionProvider, setKeys)); - return RedisResponse.array(resultSet, true); - - } else if (command.isOfType(RedisCommandType.SINTER)) { - Set<byte[]> resultSet = context.lockedExecute(setKeys.get(0), keysToLock, - () -> sinter(regionProvider, setKeys)); - return RedisResponse.array(resultSet, true); - - } else if (command.isOfType(RedisCommandType.SUNION) - || command.isOfType(RedisCommandType.SUNIONSTORE)) { - if (isStorage()) { - RedisKey destinationKey = command.getKey(); - int resultSize = context.lockedExecute(destinationKey, keysToLock, - () -> sunionstore(regionProvider, destinationKey, setKeys)); - return RedisResponse.integer(resultSize); - } - - Set<byte[]> resultSet = context.lockedExecute(setKeys.get(0), keysToLock, - () -> sunion(regionProvider, setKeys)); - return RedisResponse.array(resultSet, true); - } - - return doActualSetOperation(command, context, setKeys); + return context.lockedExecute(setKeys.get(0), new ArrayList<>(setKeys), + () -> performCommand(regionProvider, setKeys)); } - private RedisResponse doActualSetOperation(Command command, ExecutionHandlerContext context, - List<RedisKey> setKeys) { - if (isStorage()) { - RedisKey destination = command.getKey(); - int storeCount = doStoreSetOp(command.getCommandType(), context, destination, setKeys); - return RedisResponse.integer(storeCount); - } - - Set<byte[]> resultSet = null; - for (RedisKey key : setKeys) { - Set<byte[]> keySet = context.setLockedExecute(key, true, - set -> new MemberSet(set.smembers())); - if (resultSet == null) { - resultSet = keySet; - } else if (doSetOp(resultSet, keySet)) { - break; - } - } - - return RedisResponse.array(resultSet, true); - } + protected abstract RedisResponse performCommand(RegionProvider regionProvider, + List<RedisKey> setKeys); +} - protected int doStoreSetOp(RedisCommandType setOp, ExecutionHandlerContext context, - RedisKey destination, - List<RedisKey> setKeys) { - List<MemberSet> nonDestinationSets = fetchSets(context, setKeys, destination); - return context.lockedExecute(destination, - () -> doStoreSetOpWhileLocked(setOp, context, destination, nonDestinationSets)); +// Used for set operations without store that return an array +abstract class SetOpArrayResult extends SetOpExecutor { + protected RedisResponse performCommand(RegionProvider regionProvider, List<RedisKey> setKeys) { + return RedisResponse.array(getResult(regionProvider, setKeys), true); } - private int doStoreSetOpWhileLocked(RedisCommandType setOp, ExecutionHandlerContext context, - RedisKey destination, - List<MemberSet> nonDestinationSets) { - Set<byte[]> result = - computeStoreSetOp(setOp, nonDestinationSets, context, destination); - if (result.isEmpty()) { - context.getRegion().remove(destination); - return 0; - } else { - context.getRegion().put(destination, new RedisSet(result)); - return result.size(); - } - } + protected abstract Set<byte[]> getResult(RegionProvider regionProvider, List<RedisKey> setKeys); +} - private Set<byte[]> computeStoreSetOp(RedisCommandType setOp, List<MemberSet> nonDestinationSets, - ExecutionHandlerContext context, RedisKey destination) { - MemberSet result = null; - if (nonDestinationSets.isEmpty()) { - return emptySet(); - } - for (MemberSet set : nonDestinationSets) { - if (set == null) { - RedisSet redisSet = context.getRedisSet(destination, false); - set = new MemberSet(redisSet.smembers()); - } - if (result == null) { - result = set; - } else { - switch (setOp) { - case SUNIONSTORE: - result.addAll(set); - break; - case SINTERSTORE: - result.retainAll(set); - break; - default: - throw new IllegalStateException( - "expected a set store command but found: " + setOp); - } - } - } - return result; - } - /** - * Gets the set data for the given keys, excluding the destination if it was in setKeys. - * The result will have an element for each corresponding key and a null element if - * the corresponding key is the destination. - * This is all done outside the striped executor to prevent a deadlock. - */ - private List<MemberSet> fetchSets(ExecutionHandlerContext context, List<RedisKey> setKeys, - RedisKey destination) { - List<MemberSet> result = new ArrayList<>(setKeys.size()); - for (RedisKey key : setKeys) { - if (key.equals(destination)) { - result.add(null); - } else { - result.add(context.setLockedExecute(key, false, - set -> new MemberSet(set.smembers()))); - } - } - return result; +// Used for set operations with store that return an integer +abstract class SetOpIntegerResult extends SetOpExecutor { + protected RedisResponse performCommand(RegionProvider regionProvider, List<RedisKey> setKeys) { + RedisKey destKey = setKeys.remove(0); + return RedisResponse.integer(getResult(regionProvider, setKeys, destKey)); } - /** - * @return true if no further calls of doSetOp are needed - */ - protected abstract boolean doSetOp(Set<byte[]> resultSet, Set<byte[]> nextSet); - - protected abstract boolean isStorage(); - + protected abstract int getResult(RegionProvider regionProvider, List<RedisKey> setKeys, + RedisKey destKey); } diff --git a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/data/RedisSet.java b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/data/RedisSet.java index f9a2e1a..04654da 100644 --- a/geode-for-redis/src/main/java/org/apache/geode/redis/internal/data/RedisSet.java +++ b/geode-for-redis/src/main/java/org/apache/geode/redis/internal/data/RedisSet.java @@ -92,17 +92,7 @@ public class RedisSet extends AbstractRedisData { return 1; } - public static Set<byte[]> sunion(RegionProvider regionProvider, List<RedisKey> keys) { - return calculateUnion(regionProvider, keys, true); - } - - public static int sunionstore(RegionProvider regionProvider, RedisKey destinationKey, - List<RedisKey> keys) { - MemberSet union = calculateUnion(regionProvider, keys, false); - return setOpStoreResult(regionProvider, destinationKey, union); - } - - private static MemberSet calculateUnion(RegionProvider regionProvider, List<RedisKey> keys, + public static MemberSet sunion(RegionProvider regionProvider, List<RedisKey> keys, boolean updateStats) { MemberSet union = new MemberSet(); for (RedisKey key : keys) { @@ -112,17 +102,13 @@ public class RedisSet extends AbstractRedisData { return union; } - public static Set<byte[]> sdiff(RegionProvider regionProvider, List<RedisKey> keys) { - return calculateDiff(regionProvider, keys, true); - } - - public static int sdiffstore(RegionProvider regionProvider, RedisKey destinationKey, - List<RedisKey> keys) { - MemberSet diff = calculateDiff(regionProvider, keys, false); - return setOpStoreResult(regionProvider, destinationKey, diff); + public static int sunionstore(RegionProvider regionProvider, List<RedisKey> keys, + RedisKey destinationKey) { + MemberSet union = sunion(regionProvider, keys, false); + return setOpStoreResult(regionProvider, destinationKey, union); } - private static MemberSet calculateDiff(RegionProvider regionProvider, List<RedisKey> keys, + public static MemberSet sdiff(RegionProvider regionProvider, List<RedisKey> keys, boolean updateStats) { RedisSet firstSet = regionProvider.getTypedRedisData(REDIS_SET, keys.get(0), updateStats); MemberSet diff = new MemberSet(firstSet.members); @@ -136,14 +122,21 @@ public class RedisSet extends AbstractRedisData { return diff; } - public static Set<byte[]> sinter(RegionProvider regionProvider, List<RedisKey> keys) { - List<RedisSet> sets = createRedisSetList(keys, regionProvider); + public static int sdiffstore(RegionProvider regionProvider, + List<RedisKey> keys, RedisKey destinationKey) { + MemberSet diff = sdiff(regionProvider, keys, false); + return setOpStoreResult(regionProvider, destinationKey, diff); + } + + public static MemberSet sinter(RegionProvider regionProvider, List<RedisKey> keys, + boolean updateStats) { + List<RedisSet> sets = createRedisSetList(keys, regionProvider, updateStats); final RedisSet smallestSet = findSmallest(sets); + MemberSet result = new MemberSet(smallestSet.scard()); if (smallestSet.scard() == 0) { - return Collections.emptySet(); + return result; } - MemberSet result = new MemberSet(smallestSet.scard()); for (byte[] member : smallestSet.members) { boolean addToSet = true; for (RedisSet otherSet : sets) { @@ -175,15 +168,21 @@ public class RedisSet extends AbstractRedisData { } private static List<RedisSet> createRedisSetList(List<RedisKey> keys, - RegionProvider regionProvider) { + RegionProvider regionProvider, boolean updateStats) { List<RedisSet> sets = new ArrayList<>(keys.size()); for (RedisKey key : keys) { - RedisSet redisSet = regionProvider.getTypedRedisData(REDIS_SET, key, true); + RedisSet redisSet = regionProvider.getTypedRedisData(REDIS_SET, key, updateStats); sets.add(redisSet); } return sets; } + public static int sinterstore(RegionProvider regionProvider, + List<RedisKey> keys, RedisKey destinationKey) { + MemberSet inter = sinter(regionProvider, keys, false); + return setOpStoreResult(regionProvider, destinationKey, inter); + } + @VisibleForTesting static int setOpStoreResult(RegionProvider regionProvider, RedisKey destinationKey, MemberSet result) {