diff --git a/redisson/src/main/java/org/redisson/RedissonSet.java b/redisson/src/main/java/org/redisson/RedissonSet.java index 21f76ea0b..f986b9d3e 100644 --- a/redisson/src/main/java/org/redisson/RedissonSet.java +++ b/redisson/src/main/java/org/redisson/RedissonSet.java @@ -610,6 +610,27 @@ public class RedissonSet extends RedissonExpirable implements RSet, ScanIt return commandExecutor.writeAsync(getName(), codec, RedisCommands.SORT_TO, params.toArray()); } + @Override + public boolean tryAdd(V... values) { + return get(tryAddAsync(values)); + } + + @Override + public RFuture tryAddAsync(V... values) { + return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN, + "for i, v in ipairs(ARGV) do " + + "if redis.call('sismember', KEYS[1], v) == 1 then " + + "return 0; " + + "end; " + + "end; " + + + "for i=1, #ARGV, 5000 do " + + "redis.call('sadd', KEYS[1], unpack(ARGV, i, math.min(i+4999, #ARGV))); " + + "end; " + + "return 1; ", + Arrays.asList(getName()), encode(values).toArray()); + } + @Override public RPermitExpirableSemaphore getPermitExpirableSemaphore(V value) { String lockName = getLockByValue(value, "permitexpirablesemaphore"); diff --git a/redisson/src/main/java/org/redisson/RedissonSetCache.java b/redisson/src/main/java/org/redisson/RedissonSetCache.java index c08ba856c..9d015d02c 100644 --- a/redisson/src/main/java/org/redisson/RedissonSetCache.java +++ b/redisson/src/main/java/org/redisson/RedissonSetCache.java @@ -255,6 +255,48 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< Arrays.asList(name), System.currentTimeMillis(), timeoutDate, objectState); } + @Override + public boolean tryAdd(V... values) { + return get(tryAddAsync(values)); + } + + @Override + public RFuture tryAddAsync(V... values) { + return tryAddAsync(92233720368547758L - System.currentTimeMillis(), TimeUnit.MILLISECONDS, values); + } + + @Override + public boolean tryAdd(long ttl, TimeUnit unit, V... values) { + return get(tryAddAsync(ttl, unit, values)); + } + + @Override + public RFuture tryAddAsync(long ttl, TimeUnit unit, V... values) { + long timeoutDate = System.currentTimeMillis() + unit.toMillis(ttl); + if (ttl == 0) { + timeoutDate = 92233720368547758L - System.currentTimeMillis(); + } + + List params = new ArrayList<>(); + params.add(System.currentTimeMillis()); + params.add(timeoutDate); + params.addAll(encode(values)); + + return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN, + "for i, v in ipairs(ARGV) do " + + "local expireDateScore = redis.call('zscore', KEYS[1], v); " + + "if expireDateScore ~= false and tonumber(expireDateScore) > tonumber(ARGV[1]) then " + + "return 0; " + + "end; " + + "end; " + + + "for i=3, #ARGV, 1 do " + + "redis.call('zadd', KEYS[1], ARGV[2], ARGV[i]); " + + "end; " + + "return 1; ", + Arrays.asList(getName()), params.toArray()); + } + @Override public RFuture addAsync(V value) { return addAsync(value, 92233720368547758L - System.currentTimeMillis(), TimeUnit.MILLISECONDS); diff --git a/redisson/src/main/java/org/redisson/RedissonSetMultimapValues.java b/redisson/src/main/java/org/redisson/RedissonSetMultimapValues.java index 747de6ace..df7a161d9 100644 --- a/redisson/src/main/java/org/redisson/RedissonSetMultimapValues.java +++ b/redisson/src/main/java/org/redisson/RedissonSetMultimapValues.java @@ -78,6 +78,16 @@ public class RedissonSetMultimapValues extends RedissonExpirable implements R return null; } + @Override + public boolean tryAdd(V... values) { + return get(tryAddAsync(values)); + } + + @Override + public RFuture tryAddAsync(V... values) { + return set.tryAddAsync(values); + } + @Override public RFuture clearExpireAsync() { throw new UnsupportedOperationException("This operation is not supported for SetMultimap values Set"); diff --git a/redisson/src/main/java/org/redisson/api/RSet.java b/redisson/src/main/java/org/redisson/api/RSet.java index 27f08ee6b..39e3521a8 100644 --- a/redisson/src/main/java/org/redisson/api/RSet.java +++ b/redisson/src/main/java/org/redisson/api/RSet.java @@ -15,12 +15,12 @@ */ package org.redisson.api; +import org.redisson.api.mapreduce.RCollectionMapReduce; + import java.util.Iterator; import java.util.Set; import java.util.stream.Stream; -import org.redisson.api.mapreduce.RCollectionMapReduce; - /** * Redis based implementation of {@link java.util.Set} * @@ -246,4 +246,13 @@ public interface RSet extends Set, RExpirable, RSetAsync, RSortable readIntersection(String... names); + /** + * Tries to add elements only if none of them in set. + * + * @param values - values to add + * @return true if elements successfully added, + * otherwise false. + */ + boolean tryAdd(V... values); + } diff --git a/redisson/src/main/java/org/redisson/api/RSetAsync.java b/redisson/src/main/java/org/redisson/api/RSetAsync.java index 8d490583f..093334b04 100644 --- a/redisson/src/main/java/org/redisson/api/RSetAsync.java +++ b/redisson/src/main/java/org/redisson/api/RSetAsync.java @@ -129,5 +129,14 @@ public interface RSetAsync extends RCollectionAsync, RSortableAsync * @return values */ RFuture> readIntersectionAsync(String... names); - + + /** + * Tries to add elements only if none of them in set. + * + * @param values - values to add + * @return true if elements successfully added, + * otherwise false. + */ + RFuture tryAddAsync(V... values); + } diff --git a/redisson/src/main/java/org/redisson/api/RSetCache.java b/redisson/src/main/java/org/redisson/api/RSetCache.java index 3fbfb5982..937056abd 100644 --- a/redisson/src/main/java/org/redisson/api/RSetCache.java +++ b/redisson/src/main/java/org/redisson/api/RSetCache.java @@ -184,4 +184,25 @@ public interface RSetCache extends Set, RExpirable, RSetCacheAsync, RDe */ Set readAll(); + /** + * Tries to add elements only if none of them in set. + * + * @param values - values to add + * @return true if elements successfully added, + * otherwise false. + */ + boolean tryAdd(V... values); + + /** + * Tries to add elements only if none of them in set. + * + * @param values - values to add + * @param ttl - time to live for value. + * If 0 then stores infinitely. + * @param unit - time unit + * @return true if elements successfully added, + * otherwise false. + */ + boolean tryAdd(long ttl, TimeUnit unit, V... values); + } diff --git a/redisson/src/main/java/org/redisson/api/RSetCacheAsync.java b/redisson/src/main/java/org/redisson/api/RSetCacheAsync.java index b4108e633..f47d14694 100644 --- a/redisson/src/main/java/org/redisson/api/RSetCacheAsync.java +++ b/redisson/src/main/java/org/redisson/api/RSetCacheAsync.java @@ -57,4 +57,25 @@ public interface RSetCacheAsync extends RCollectionAsync { */ RFuture> readAllAsync(); + /** + * Tries to add elements only if none of them in set. + * + * @param values - values to add + * @return true if elements successfully added, + * otherwise false. + */ + RFuture tryAddAsync(V... values); + + /** + * Tries to add elements only if none of them in set. + * + * @param values - values to add + * @param ttl - time to live for value. + * If 0 then stores infinitely. + * @param unit - time unit + * @return true if elements successfully added, + * otherwise false. + */ + RFuture tryAddAsync(long ttl, TimeUnit unit, V... values); + } diff --git a/redisson/src/test/java/org/redisson/RedissonSetCacheTest.java b/redisson/src/test/java/org/redisson/RedissonSetCacheTest.java index f0ace657e..60791dd84 100644 --- a/redisson/src/test/java/org/redisson/RedissonSetCacheTest.java +++ b/redisson/src/test/java/org/redisson/RedissonSetCacheTest.java @@ -16,7 +16,9 @@ import java.util.concurrent.TimeUnit; import org.joor.Reflect; import org.junit.Assert; import org.junit.Test; +import org.redisson.api.RSet; import org.redisson.api.RSetCache; +import org.redisson.client.codec.IntegerCodec; import org.redisson.eviction.EvictionScheduler; public class RedissonSetCacheTest extends BaseTest { @@ -34,7 +36,31 @@ public class RedissonSetCacheTest extends BaseTest { } } - + + @Test + public void testTryAdd() { + RSetCache cache = redisson.getSetCache("list", IntegerCodec.INSTANCE); + Set names = new HashSet<>(); + int elements = 200000; + for (int i = 0; i < elements; i++) { + names.add("name" + i); + } + + boolean s = cache.tryAdd(names.toArray(new String[]{})); + assertThat(s).isTrue(); + assertThat(cache.size()).isEqualTo(elements); + + Set names2 = new HashSet<>(); + for (int i = elements+1; i < elements + 10000; i++) { + names2.add("name" + i); + } + names2.add("name10"); + + boolean r = cache.tryAdd(names2.toArray(new String[]{})); + assertThat(r).isFalse(); + assertThat(cache.size()).isEqualTo(elements); + } + @Test public void testDestroy() { RSetCache cache = redisson.getSetCache("test"); diff --git a/redisson/src/test/java/org/redisson/RedissonSetTest.java b/redisson/src/test/java/org/redisson/RedissonSetTest.java index 5d1909f1a..8dc61ea6f 100644 --- a/redisson/src/test/java/org/redisson/RedissonSetTest.java +++ b/redisson/src/test/java/org/redisson/RedissonSetTest.java @@ -42,6 +42,30 @@ public class RedissonSetTest extends BaseTest { } + @Test + public void testTryAdd() { + RSet set = redisson.getSet("list", IntegerCodec.INSTANCE); + Set names = new HashSet<>(); + int elements = 200000; + for (int i = 0; i < elements; i++) { + names.add("name" + i); + } + + boolean s = set.tryAdd(names.toArray(new String[]{})); + assertThat(s).isTrue(); + assertThat(set.size()).isEqualTo(elements); + + Set names2 = new HashSet<>(); + for (int i = elements+1; i < elements + 10000; i++) { + names2.add("name" + i); + } + names2.add("name10"); + + boolean r = set.tryAdd(names2.toArray(new String[]{})); + assertThat(r).isFalse(); + assertThat(set.size()).isEqualTo(elements); + } + @Test public void testSortOrder() { RSet list = redisson.getSet("list", IntegerCodec.INSTANCE);