From 87984aa71f2bd985dc6f4a120c2f79a91fffd9ec Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Wed, 8 Nov 2023 10:09:10 +0300 Subject: [PATCH 01/23] refactoring --- .../java/org/redisson/command/CommandAsyncExecutor.java | 2 ++ .../java/org/redisson/command/CommandAsyncService.java | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/redisson/src/main/java/org/redisson/command/CommandAsyncExecutor.java b/redisson/src/main/java/org/redisson/command/CommandAsyncExecutor.java index 49d584c8c..7332d826a 100644 --- a/redisson/src/main/java/org/redisson/command/CommandAsyncExecutor.java +++ b/redisson/src/main/java/org/redisson/command/CommandAsyncExecutor.java @@ -140,6 +140,8 @@ public interface CommandAsyncExecutor { RFuture evalWriteBatchedAsync(Codec codec, RedisCommand command, String script, List keys, SlotCallback callback); + RFuture evalReadBatchedAsync(Codec codec, RedisCommand command, String script, List keys, SlotCallback callback); + boolean isEvalShaROSupported(); void setEvalShaROSupported(boolean value); diff --git a/redisson/src/main/java/org/redisson/command/CommandAsyncService.java b/redisson/src/main/java/org/redisson/command/CommandAsyncService.java index a9c9f5d5d..37735d6a5 100644 --- a/redisson/src/main/java/org/redisson/command/CommandAsyncService.java +++ b/redisson/src/main/java/org/redisson/command/CommandAsyncService.java @@ -583,10 +583,15 @@ public class CommandAsyncService implements CommandAsyncExecutor { @Override public RFuture evalWriteBatchedAsync(Codec codec, RedisCommand command, String script, List keys, SlotCallback callback) { - return evalWriteBatchedAsync(false, codec, command, script, keys, callback); + return evalBatchedAsync(false, codec, command, script, keys, callback); } - private RFuture evalWriteBatchedAsync(boolean readOnly, Codec codec, RedisCommand command, String script, List keys, SlotCallback callback) { + @Override + public RFuture evalReadBatchedAsync(Codec codec, RedisCommand command, String script, List keys, SlotCallback callback) { + return evalBatchedAsync(true, codec, command, script, keys, callback); + } + + private RFuture evalBatchedAsync(boolean readOnly, Codec codec, RedisCommand command, String script, List keys, SlotCallback callback) { if (!connectionManager.isClusterMode()) { Object[] keysArray = callback.createKeys(keys); Object[] paramsArray = callback.createParams(Collections.emptyList()); From d85a8f60c22ec122cf11203868d75f71a3ee81a4 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Wed, 8 Nov 2023 12:58:25 +0300 Subject: [PATCH 02/23] refactoring --- redisson/src/test/java/org/redisson/BaseMapTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/redisson/src/test/java/org/redisson/BaseMapTest.java b/redisson/src/test/java/org/redisson/BaseMapTest.java index 59fbe26e7..d799838e4 100644 --- a/redisson/src/test/java/org/redisson/BaseMapTest.java +++ b/redisson/src/test/java/org/redisson/BaseMapTest.java @@ -155,8 +155,6 @@ public abstract class BaseMapTest extends BaseTest { @Test public void testRandomEntries() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("6.2.0") > 0); - RMap map = getMap("map"); Map e1 = map.randomEntries(1); assertThat(e1).isEmpty(); From c68e3e7bce89d82edc8a7a98a597ee277550bf2a Mon Sep 17 00:00:00 2001 From: Manuel Polo Date: Wed, 8 Nov 2023 21:20:23 +0100 Subject: [PATCH 03/23] Update RedissonRemoteService.java Log error when can't process the remote service request Signed-off-by: Manuel Polo --- .../src/main/java/org/redisson/RedissonRemoteService.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/redisson/src/main/java/org/redisson/RedissonRemoteService.java b/redisson/src/main/java/org/redisson/RedissonRemoteService.java index 833b9e934..fecb9747a 100644 --- a/redisson/src/main/java/org/redisson/RedissonRemoteService.java +++ b/redisson/src/main/java/org/redisson/RedissonRemoteService.java @@ -364,6 +364,13 @@ public class RedissonRemoteService extends BaseRemoteService implements RRemoteS } else { executeMethod(remoteInterface, requestQueue, executor, request, bean); } + }) + .exceptionally(exc -> { + if (exc instanceof RedissonShutdownException) { + return null; + } + log.error("Can't process the remote service request with id {}", requestId, exc); + return null; }); }); } From cc4573b4a8b7ec7b7f272766a6d707a5109e9742 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Fri, 10 Nov 2023 10:43:50 +0300 Subject: [PATCH 04/23] Fixed - RLocalCachedMap.containsKey() does not work properly if storeCacheMiss = true. #5411 --- .../org/redisson/RedissonLocalCachedMap.java | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/redisson/src/main/java/org/redisson/RedissonLocalCachedMap.java b/redisson/src/main/java/org/redisson/RedissonLocalCachedMap.java index 8e0a288f6..a0b69b5b6 100644 --- a/redisson/src/main/java/org/redisson/RedissonLocalCachedMap.java +++ b/redisson/src/main/java/org/redisson/RedissonLocalCachedMap.java @@ -233,13 +233,27 @@ public class RedissonLocalCachedMap extends RedissonMap implements R return new CompletableFutureWrapper<>(f); } - CompletableFuture promise = new CompletableFuture<>(); - promise.thenAccept(value -> { - if (storeCacheMiss || value != null) { - cachePut(cacheKey, key, value); + String name = getRawName(key); + RFuture future = containsKeyOperationAsync(name, key); + CompletionStage result = future.thenCompose(res -> { + if (hasNoLoader()) { + if (!res && storeCacheMiss) { + cachePut(cacheKey, key, null); + } + return CompletableFuture.completedFuture(res); + } + if (!res) { + CompletableFuture f = loadValue((K) key, false); + return f.thenApply(value -> { + if (storeCacheMiss || value != null) { + cachePut(cacheKey, key, value); + } + return value != null; + }); } + return CompletableFuture.completedFuture(res); }); - return containsKeyAsync(key, promise); + return new CompletableFutureWrapper<>(result); } return new CompletableFutureWrapper<>(cacheValue.getValue() != null); From 6a89e870af50874d1392229598d7b076f9472ed3 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Fri, 10 Nov 2023 12:13:02 +0300 Subject: [PATCH 05/23] Fixed - getBlockingDeque(), getDeque(), getPriorityDeque(), getPriorityBlockingDeque() throw NoClassDefFoundError if JDK version < 21 #5402 --- .../java/org/redisson/BaseRedissonList.java | 897 ++++++++++++++++ .../main/java/org/redisson/RedissonDeque.java | 8 +- .../main/java/org/redisson/RedissonList.java | 961 +----------------- .../org/redisson/RedissonPriorityDeque.java | 3 - .../org/redisson/RedissonPriorityQueue.java | 2 +- .../main/java/org/redisson/RedissonQueue.java | 2 +- .../RedissonBlockingQueueReactive.java | 9 +- .../reactive/RedissonListReactive.java | 13 +- .../redisson/rx/RedissonBlockingQueueRx.java | 7 +- .../java/org/redisson/rx/RedissonListRx.java | 11 +- .../RedissonBlockingQueueReactiveTest.java | 2 +- .../rx/RedissonBlockingDequeRxTest.java | 2 +- 12 files changed, 926 insertions(+), 991 deletions(-) create mode 100644 redisson/src/main/java/org/redisson/BaseRedissonList.java diff --git a/redisson/src/main/java/org/redisson/BaseRedissonList.java b/redisson/src/main/java/org/redisson/BaseRedissonList.java new file mode 100644 index 000000000..c2dda802a --- /dev/null +++ b/redisson/src/main/java/org/redisson/BaseRedissonList.java @@ -0,0 +1,897 @@ +/** + * Copyright (c) 2013-2022 Nikita Koksharov + * + * 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.redisson; + +import org.redisson.api.*; +import org.redisson.api.listener.*; +import org.redisson.api.mapreduce.RCollectionMapReduce; +import org.redisson.client.RedisClient; +import org.redisson.client.RedisException; +import org.redisson.client.codec.Codec; +import org.redisson.client.codec.StringCodec; +import org.redisson.client.protocol.RedisCommand; +import org.redisson.client.protocol.RedisCommands; +import org.redisson.client.protocol.convertor.BooleanNumberReplayConvertor; +import org.redisson.client.protocol.convertor.Convertor; +import org.redisson.client.protocol.convertor.IntegerReplayConvertor; +import org.redisson.command.CommandAsyncExecutor; +import org.redisson.iterator.RedissonBaseIterator; +import org.redisson.iterator.RedissonListIterator; +import org.redisson.mapreduce.RedissonCollectionMapReduce; +import org.redisson.misc.CompletableFutureWrapper; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import java.util.function.Predicate; + +import static org.redisson.client.protocol.RedisCommands.*; + +/** + * Base list implementation + * + * @author Nikita Koksharov + * + * @param the type of elements held in this collection + */ +public class BaseRedissonList extends RedissonExpirable { + + private RedissonClient redisson; + + BaseRedissonList(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) { + super(commandExecutor, name); + this.redisson = redisson; + } + + BaseRedissonList(Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) { + super(codec, commandExecutor, name); + this.redisson = redisson; + } + + public RCollectionMapReduce mapReduce() { + return new RedissonCollectionMapReduce(this, redisson, commandExecutor); + } + + public int size() { + return get(sizeAsync()); + } + + public RFuture sizeAsync() { + return commandExecutor.readAsync(getRawName(), codec, LLEN_INT, getRawName()); + } + + public boolean isEmpty() { + return size() == 0; + } + + public boolean contains(Object o) { + return get(containsAsync(o)); + } + + public Iterator iterator() { + return listIterator(); + } + + public Object[] toArray() { + List list = readAll(); + return list.toArray(); + } + + public List readAll() { + return get(readAllAsync()); + } + + public RFuture> readAllAsync() { + return commandExecutor.readAsync(getRawName(), codec, LRANGE, getRawName(), 0, -1); + } + + public T[] toArray(T[] a) { + List list = readAll(); + return list.toArray(a); + } + + public boolean add(V e) { + return get(addAsync(e)); + } + + public RFuture addAsync(V e) { + return addAsync(e, RPUSH_BOOLEAN); + } + + protected RFuture addAsync(V e, RedisCommand command) { + return commandExecutor.writeAsync(getRawName(), codec, command, getRawName(), encode(e)); + } + + public boolean remove(Object o) { + return get(removeAsync(o)); + } + + public RFuture removeAsync(Object o) { + return removeAsync(o, 1); + } + + public RFuture removeAsync(Object o, int count) { + return commandExecutor.writeAsync(getRawName(), codec, LREM, getRawName(), count, encode(o)); + } + + public boolean remove(Object o, int count) { + return get(removeAsync(o, count)); + } + + public RFuture containsAllAsync(Collection c) { + if (c.isEmpty()) { + return new CompletableFutureWrapper<>(true); + } + + return commandExecutor.evalReadAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN, + "local items = redis.call('lrange', KEYS[1], 0, -1) " + + "for i=1, #items do " + + "for j = 1, #ARGV, 1 do " + + "if items[i] == ARGV[j] then " + + "table.remove(ARGV, j) " + + "end " + + "end " + + "end " + + "return #ARGV == 0 and 1 or 0", + Collections.singletonList(getRawName()), encode(c).toArray()); + } + + public boolean containsAll(Collection c) { + return get(containsAllAsync(c)); + } + + public boolean addAll(Collection c) { + return get(addAllAsync(c)); + } + + public RFuture addAllAsync(Collection c) { + if (c.isEmpty()) { + return new CompletableFutureWrapper<>(false); + } + + List args = new ArrayList(c.size() + 1); + args.add(getRawName()); + encode(args, c); + return commandExecutor.writeAsync(getRawName(), codec, RPUSH_BOOLEAN, args.toArray()); + } + + public RFuture addAllAsync(int index, Collection coll) { + if (index < 0) { + throw new IndexOutOfBoundsException("index: " + index); + } + + if (coll.isEmpty()) { + return new CompletableFutureWrapper<>(false); + } + + if (index == 0) { // prepend elements to list + List elements = new ArrayList(); + encode(elements, coll); + Collections.reverse(elements); + elements.add(0, getRawName()); + + return commandExecutor.writeAsync(getRawName(), codec, LPUSH_BOOLEAN, elements.toArray()); + } + + List args = new ArrayList(coll.size() + 1); + args.add(index); + encode(args, coll); + + return commandExecutor.evalWriteNoRetryAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN, + "local ind = table.remove(ARGV, 1); " + // index is the first parameter + "local size = redis.call('llen', KEYS[1]); " + + "assert(tonumber(ind) <= size, 'index: ' .. ind .. ' but current size: ' .. size); " + + "local tail = redis.call('lrange', KEYS[1], ind, -1); " + + "redis.call('ltrim', KEYS[1], 0, ind - 1); " + + "for i=1, #ARGV, 5000 do " + + "redis.call('rpush', KEYS[1], unpack(ARGV, i, math.min(i+4999, #ARGV))); " + + "end " + + "if #tail > 0 then " + + "for i=1, #tail, 5000 do " + + "redis.call('rpush', KEYS[1], unpack(tail, i, math.min(i+4999, #tail))); " + + "end " + + "end;" + + "return 1;", + Collections.singletonList(getRawName()), args.toArray()); + } + + public boolean addAll(int index, Collection coll) { + return get(addAllAsync(index, coll)); + } + + public RFuture removeAllAsync(Collection c) { + if (c.isEmpty()) { + return new CompletableFutureWrapper<>(false); + } + + return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN, + "local v = 0 " + + "for i = 1, #ARGV, 1 do " + + "if redis.call('lrem', KEYS[1], 0, ARGV[i]) == 1 " + + "then v = 1 end " + +"end " + + "return v ", + Collections.singletonList(getRawName()), encode(c).toArray()); + } + + public boolean removeAll(Collection c) { + return get(removeAllAsync(c)); + } + + public boolean retainAll(Collection c) { + return get(retainAllAsync(c)); + } + + public RFuture retainAllAsync(Collection c) { + if (c.isEmpty()) { + return deleteAsync(); + } + + return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN, + "local changed = 0 " + + "local items = redis.call('lrange', KEYS[1], 0, -1) " + + "local i = 1 " + + "while i <= #items do " + + "local element = items[i] " + + "local isInAgrs = false " + + "for j = 1, #ARGV, 1 do " + + "if ARGV[j] == element then " + + "isInAgrs = true " + + "break " + + "end " + + "end " + + "if isInAgrs == false then " + + "redis.call('LREM', KEYS[1], 0, element) " + + "changed = 1 " + + "end " + + "i = i + 1 " + + "end " + + "return changed ", + Collections.singletonList(getRawName()), encode(c).toArray()); + } + + + public void clear() { + delete(); + } + + public RFuture getAsync(int index) { + return commandExecutor.readAsync(getRawName(), codec, LINDEX, getRawName(), index); + } + + public List get(int... indexes) { + return get(getAsync(indexes)); + } + + public Iterator distributedIterator(final int count) { + String iteratorName = "__redisson_list_cursor_{" + getRawName() + "}"; + return distributedIterator(iteratorName, count); + } + + public Iterator distributedIterator(final String iteratorName, final int count) { + return new RedissonBaseIterator() { + + @Override + protected ScanResult iterator(RedisClient client, long nextIterPos) { + return distributedScanIterator(iteratorName, count); + } + + @Override + protected void remove(Object value) { + BaseRedissonList.this.remove((V) value); + } + }; + } + + private ScanResult distributedScanIterator(String iteratorName, int count) { + return get(distributedScanIteratorAsync(iteratorName, count)); + } + + private RFuture> distributedScanIteratorAsync(String iteratorName, int count) { + return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_SCAN, + "local start_index = redis.call('get', KEYS[2]); " + + "if start_index ~= false then " + + "start_index = tonumber(start_index); " + + "else " + + "start_index = 0;" + + "end;" + + "if start_index == -1 then " + + "return {0, {}};" + + "end;" + + "local end_index = start_index + ARGV[1];" + + "local result; " + + "result = redis.call('lrange', KEYS[1], start_index, end_index - 1); " + + "if end_index > redis.call('llen', KEYS[1]) then " + + "end_index = -1;" + + "end; " + + "redis.call('setex', KEYS[2], 3600, end_index);" + + "return {end_index, result};", + Arrays.asList(getRawName(), iteratorName), count); + } + + public RFuture> getAsync(int... indexes) { + List params = new ArrayList(); + for (Integer index : indexes) { + params.add(index); + } + return commandExecutor.evalReadAsync(getRawName(), codec, RedisCommands.EVAL_LIST, + "local result = {}; " + + "for i = 1, #ARGV, 1 do " + + "local value = redis.call('lindex', KEYS[1], ARGV[i]);" + + "table.insert(result, value);" + + "end; " + + "return result;", + Collections.singletonList(getRawName()), params.toArray()); + } + + + public V get(int index) { + return getValue(index); + } + + V getValue(int index) { + return get(getAsync(index)); + } + + public V set(int index, V element) { + try { + return get(setAsync(index, element)); + } catch (RedisException e) { + if (e.getCause() instanceof IndexOutOfBoundsException) { + throw (IndexOutOfBoundsException) e.getCause(); + } + throw e; + } + } + + public RFuture setAsync(int index, V element) { + RFuture future = commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_OBJECT, + "local v = redis.call('lindex', KEYS[1], ARGV[1]); " + + "redis.call('lset', KEYS[1], ARGV[1], ARGV[2]); " + + "return v", + Collections.singletonList(getRawName()), index, encode(element)); + CompletionStage f = future.handle((res, e) -> { + if (e != null) { + if (e.getMessage().contains("ERR index out of range")) { + throw new CompletionException(new IndexOutOfBoundsException("index out of range")); + } + throw new CompletionException(e); + } + return res; + }); + return new CompletableFutureWrapper<>(f); + } + + public void fastSet(int index, V element) { + get(fastSetAsync(index, element)); + } + + public RFuture fastSetAsync(int index, V element) { + return commandExecutor.writeAsync(getRawName(), codec, RedisCommands.LSET, getRawName(), index, encode(element)); + } + + public void add(int index, V element) { + addAll(index, Collections.singleton(element)); + } + + public RFuture addAsync(int index, V element) { + return addAllAsync(index, Collections.singleton(element)); + } + + public V remove(int index) { + return get(removeAsync(index)); + } + + public RFuture removeAsync(int index) { + if (index == 0) { + return commandExecutor.writeAsync(getRawName(), codec, LPOP, getRawName()); + } + + return commandExecutor.evalWriteAsync(getRawName(), codec, EVAL_OBJECT, + "local v = redis.call('lindex', KEYS[1], ARGV[1]); " + + "redis.call('lset', KEYS[1], ARGV[1], 'DELETED_BY_REDISSON');" + + "redis.call('lrem', KEYS[1], 1, 'DELETED_BY_REDISSON');" + + "return v", + Collections.singletonList(getRawName()), index); + } + + + public void fastRemove(int index) { + get(fastRemoveAsync(index)); + } + + public RFuture fastRemoveAsync(int index) { + return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_VOID, + "redis.call('lset', KEYS[1], ARGV[1], 'DELETED_BY_REDISSON');" + + "redis.call('lrem', KEYS[1], 1, 'DELETED_BY_REDISSON');", + Collections.singletonList(getRawName()), index); + } + + public int indexOf(Object o) { + return get(indexOfAsync(o)); + } + + public RFuture containsAsync(Object o) { + return indexOfAsync(o, new BooleanNumberReplayConvertor(-1L)); + } + + public RFuture indexOfAsync(Object o, Convertor convertor) { + return commandExecutor.evalReadAsync(getRawName(), codec, new RedisCommand("EVAL", convertor), + "local key = KEYS[1] " + + "local obj = ARGV[1] " + + "local items = redis.call('lrange', key, 0, -1) " + + "for i=1,#items do " + + "if items[i] == obj then " + + "return i - 1 " + + "end " + + "end " + + "return -1", + Collections.singletonList(getRawName()), encode(o)); + } + + public RFuture indexOfAsync(Object o) { + return indexOfAsync(o, new IntegerReplayConvertor()); + } + + public int lastIndexOf(Object o) { + return get(lastIndexOfAsync(o)); + } + + public RFuture lastIndexOfAsync(Object o) { + return commandExecutor.evalReadAsync(getRawName(), codec, RedisCommands.EVAL_INTEGER, + "local key = KEYS[1] " + + "local obj = ARGV[1] " + + "local items = redis.call('lrange', key, 0, -1) " + + "for i = #items, 1, -1 do " + + "if items[i] == obj then " + + "return i - 1 " + + "end " + + "end " + + "return -1", + Collections.singletonList(getRawName()), encode(o)); + } + + public RFuture lastIndexOfAsync(Object o, Convertor convertor) { + return commandExecutor.evalReadAsync(getRawName(), codec, new RedisCommand("EVAL", convertor), + "local key = KEYS[1] " + + "local obj = ARGV[1] " + + "local items = redis.call('lrange', key, 0, -1) " + + "for i = #items, 1, -1 do " + + "if items[i] == obj then " + + "return i - 1 " + + "end " + + "end " + + "return -1", + Collections.singletonList(getRawName()), encode(o)); + } + + public void trim(int fromIndex, int toIndex) { + get(trimAsync(fromIndex, toIndex)); + } + + public RFuture trimAsync(int fromIndex, int toIndex) { + return commandExecutor.writeAsync(getRawName(), codec, RedisCommands.LTRIM, getRawName(), fromIndex, toIndex); + } + + public ListIterator listIterator() { + return listIterator(0); + } + + public ListIterator listIterator(int ind) { + return new RedissonListIterator(ind) { + + @Override + public V getValue(int index) { + return BaseRedissonList.this.getValue(index); + } + + @Override + public V remove(int index) { + return BaseRedissonList.this.remove(index); + } + + @Override + public void fastSet(int index, V value) { + BaseRedissonList.this.fastSet(index, value); + } + + @Override + public void add(int index, V value) { + BaseRedissonList.this.add(index, value); + } + }; + } + + public RList subList(int fromIndex, int toIndex) { + int size = size(); + if (fromIndex < 0 || toIndex > size) { + throw new IndexOutOfBoundsException("fromIndex: " + fromIndex + " toIndex: " + toIndex + " size: " + size); + } + if (fromIndex > toIndex) { + throw new IllegalArgumentException("fromIndex: " + fromIndex + " toIndex: " + toIndex); + } + + return new RedissonSubList(codec, commandExecutor, getRawName(), fromIndex, toIndex); + } + + @Override + @SuppressWarnings("AvoidInlineConditionals") + public String toString() { + Iterator it = iterator(); + if (! it.hasNext()) + return "[]"; + + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (;;) { + V e = it.next(); + sb.append(e == this ? "(this Collection)" : e); + if (! it.hasNext()) + return sb.append(']').toString(); + sb.append(',').append(' '); + } + } + + @Override + @SuppressWarnings("AvoidInlineConditionals") + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof List)) + return false; + + Iterator e1 = iterator(); + Iterator e2 = ((List) o).iterator(); + while (e1.hasNext() && e2.hasNext()) { + V o1 = e1.next(); + Object o2 = e2.next(); + if (!(o1==null ? o2==null : o1.equals(o2))) + return false; + } + return !(e1.hasNext() || e2.hasNext()); + } + + @Override + @SuppressWarnings("AvoidInlineConditionals") + public int hashCode() { + int hashCode = 1; + Iterable ii = () -> iterator(); + for (V e : ii) { + hashCode = 31*hashCode + (e==null ? 0 : e.hashCode()); + } + return hashCode; + } + + public RFuture addAfterAsync(V elementToFind, V element) { + return commandExecutor.writeAsync(getRawName(), codec, RedisCommands.LINSERT_INT, getRawName(), "AFTER", encode(elementToFind), encode(element)); + } + + public RFuture addBeforeAsync(V elementToFind, V element) { + return commandExecutor.writeAsync(getRawName(), codec, RedisCommands.LINSERT_INT, getRawName(), "BEFORE", encode(elementToFind), encode(element)); + } + + public int addAfter(V elementToFind, V element) { + return get(addAfterAsync(elementToFind, element)); + } + + public int addBefore(V elementToFind, V element) { + return get(addBeforeAsync(elementToFind, element)); + } + + public List readSort(SortOrder order) { + return get(readSortAsync(order)); + } + + public RFuture> readSortAsync(SortOrder order) { + return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), order); + } + + public List readSort(SortOrder order, int offset, int count) { + return get(readSortAsync(order, offset, count)); + } + + public RFuture> readSortAsync(SortOrder order, int offset, int count) { + return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), "LIMIT", offset, count, order); + } + + public List readSort(String byPattern, SortOrder order) { + return get(readSortAsync(byPattern, order)); + } + + public RFuture> readSortAsync(String byPattern, SortOrder order) { + return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), "BY", byPattern, order); + } + + public List readSort(String byPattern, SortOrder order, int offset, int count) { + return get(readSortAsync(byPattern, order, offset, count)); + } + + public RFuture> readSortAsync(String byPattern, SortOrder order, int offset, int count) { + return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), "BY", byPattern, "LIMIT", offset, count, order); + } + + public Collection readSort(String byPattern, List getPatterns, SortOrder order) { + return (Collection) get(readSortAsync(byPattern, getPatterns, order)); + } + + public RFuture> readSortAsync(String byPattern, List getPatterns, SortOrder order) { + return readSortAsync(byPattern, getPatterns, order, -1, -1); + } + + public Collection readSort(String byPattern, List getPatterns, SortOrder order, int offset, int count) { + return (Collection) get(readSortAsync(byPattern, getPatterns, order, offset, count)); + } + + public RFuture> readSortAsync(String byPattern, List getPatterns, SortOrder order, int offset, int count) { + return readSortAsync(byPattern, getPatterns, order, offset, count, false); + } + + public List readSortAlpha(SortOrder order) { + return get(readSortAlphaAsync(order)); + } + + public RFuture> readSortAlphaAsync(SortOrder order) { + return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), "ALPHA", order); + } + + public List readSortAlpha(SortOrder order, int offset, int count) { + return get(readSortAlphaAsync(order, offset, count)); + } + + public RFuture> readSortAlphaAsync(SortOrder order, int offset, int count) { + return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), "LIMIT", offset, count, "ALPHA", order); + } + + public List readSortAlpha(String byPattern, SortOrder order) { + return get(readSortAlphaAsync(byPattern, order)); + } + + public RFuture> readSortAlphaAsync(String byPattern, SortOrder order) { + return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), "BY", byPattern, "ALPHA", order); + } + + public List readSortAlpha(String byPattern, SortOrder order, int offset, int count) { + return get(readSortAlphaAsync(byPattern, order, offset, count)); + } + + public RFuture> readSortAlphaAsync(String byPattern, SortOrder order, int offset, int count) { + return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), "BY", byPattern, "LIMIT", offset, count, "ALPHA", order); + } + + public Collection readSortAlpha(String byPattern, List getPatterns, SortOrder order) { + return (Collection) get(readSortAlphaAsync(byPattern, getPatterns, order)); + } + + public RFuture> readSortAlphaAsync(String byPattern, List getPatterns, SortOrder order) { + return readSortAlphaAsync(byPattern, getPatterns, order, -1, -1); + } + + public Collection readSortAlpha(String byPattern, List getPatterns, SortOrder order, int offset, int count) { + return (Collection) get(readSortAlphaAsync(byPattern, getPatterns, order, offset, count)); + } + + public RFuture> readSortAlphaAsync(String byPattern, List getPatterns, SortOrder order, int offset, int count) { + return readSortAsync(byPattern, getPatterns, order, offset, count, true); + } + + public int sortTo(String destName, SortOrder order) { + return get(sortToAsync(destName, order)); + } + + public RFuture sortToAsync(String destName, SortOrder order) { + return sortToAsync(destName, null, Collections.emptyList(), order, -1, -1); + } + + public int sortTo(String destName, SortOrder order, int offset, int count) { + return get(sortToAsync(destName, order, offset, count)); + } + + public RFuture sortToAsync(String destName, SortOrder order, int offset, int count) { + return sortToAsync(destName, null, Collections.emptyList(), order, offset, count); + } + + public int sortTo(String destName, String byPattern, SortOrder order, int offset, int count) { + return get(sortToAsync(destName, byPattern, order, offset, count)); + } + + public int sortTo(String destName, String byPattern, SortOrder order) { + return get(sortToAsync(destName, byPattern, order)); + } + + public RFuture sortToAsync(String destName, String byPattern, SortOrder order) { + return sortToAsync(destName, byPattern, Collections.emptyList(), order, -1, -1); + } + + public RFuture sortToAsync(String destName, String byPattern, SortOrder order, int offset, int count) { + return sortToAsync(destName, byPattern, Collections.emptyList(), order, offset, count); + } + + public int sortTo(String destName, String byPattern, List getPatterns, SortOrder order) { + return get(sortToAsync(destName, byPattern, getPatterns, order)); + } + + public RFuture sortToAsync(String destName, String byPattern, List getPatterns, SortOrder order) { + return sortToAsync(destName, byPattern, getPatterns, order, -1, -1); + } + + public int sortTo(String destName, String byPattern, List getPatterns, SortOrder order, int offset, int count) { + return get(sortToAsync(destName, byPattern, getPatterns, order, offset, count)); + } + + public RFuture sortToAsync(String destName, String byPattern, List getPatterns, SortOrder order, int offset, int count) { + List params = new ArrayList(); + params.add(getRawName()); + if (byPattern != null) { + params.add("BY"); + params.add(byPattern); + } + if (offset != -1 && count != -1) { + params.add("LIMIT"); + } + if (offset != -1) { + params.add(offset); + } + if (count != -1) { + params.add(count); + } + for (String pattern : getPatterns) { + params.add("GET"); + params.add(pattern); + } + params.add(order); + params.add("STORE"); + params.add(destName); + + return commandExecutor.writeAsync(getRawName(), codec, RedisCommands.SORT_TO, params.toArray()); + } + + private RFuture> readSortAsync(String byPattern, List getPatterns, SortOrder order, int offset, int count, boolean alpha) { + List params = new ArrayList(); + params.add(getRawName()); + if (byPattern != null) { + params.add("BY"); + params.add(byPattern); + } + if (offset != -1 && count != -1) { + params.add("LIMIT"); + } + if (offset != -1) { + params.add(offset); + } + if (count != -1) { + params.add(count); + } + if (getPatterns != null) { + for (String pattern : getPatterns) { + params.add("GET"); + params.add(pattern); + } + } + if (alpha) { + params.add("ALPHA"); + } + if (order != null) { + params.add(order); + } + + return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, params.toArray()); + } + + public RFuture> rangeAsync(int toIndex) { + return rangeAsync(0, toIndex); + } + + public RFuture> rangeAsync(int fromIndex, int toIndex) { + return commandExecutor.readAsync(getRawName(), codec, LRANGE, getRawName(), fromIndex, toIndex); + } + + public List range(int toIndex) { + return get(rangeAsync(toIndex)); + } + + public List range(int fromIndex, int toIndex) { + return get(rangeAsync(fromIndex, toIndex)); + } + + @Override + public int addListener(ObjectListener listener) { + if (listener instanceof ListAddListener) { + return addListener("__keyevent@*:rpush", (ListAddListener) listener, ListAddListener::onListAdd); + } + if (listener instanceof ListRemoveListener) { + return addListener("__keyevent@*:lrem", (ListRemoveListener) listener, ListRemoveListener::onListRemove); + } + if (listener instanceof ListTrimListener) { + return addListener("__keyevent@*:ltrim", (ListTrimListener) listener, ListTrimListener::onListTrim); + } + if (listener instanceof ListSetListener) { + return addListener("__keyevent@*:lset", (ListSetListener) listener, ListSetListener::onListSet); + } + if (listener instanceof ListInsertListener) { + return addListener("__keyevent@*:linsert", (ListInsertListener) listener, ListInsertListener::onListInsert); + } + return super.addListener(listener); + } + + @Override + public RFuture addListenerAsync(ObjectListener listener) { + if (listener instanceof ListAddListener) { + return addListenerAsync("__keyevent@*:rpush", (ListAddListener) listener, ListAddListener::onListAdd); + } + if (listener instanceof ListRemoveListener) { + return addListenerAsync("__keyevent@*:lrem", (ListRemoveListener) listener, ListRemoveListener::onListRemove); + } + if (listener instanceof ListTrimListener) { + return addListenerAsync("__keyevent@*:ltrim", (ListTrimListener) listener, ListTrimListener::onListTrim); + } + if (listener instanceof ListSetListener) { + return addListenerAsync("__keyevent@*:lset", (ListSetListener) listener, ListSetListener::onListSet); + } + if (listener instanceof ListInsertListener) { + return addListenerAsync("__keyevent@*:linsert", (ListInsertListener) listener, ListInsertListener::onListInsert); + } + return super.addListenerAsync(listener); + } + + @Override + public void removeListener(int listenerId) { + RPatternTopic addTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:rpush"); + addTopic.removeListener(listenerId); + + RPatternTopic remTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:lrem"); + remTopic.removeListener(listenerId); + + RPatternTopic trimTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:ltrim"); + trimTopic.removeListener(listenerId); + + RPatternTopic setTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:lset"); + setTopic.removeListener(listenerId); + + RPatternTopic insertTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:linsert"); + insertTopic.removeListener(listenerId); + + super.removeListener(listenerId); + } + + @Override + public RFuture removeListenerAsync(int listenerId) { + RPatternTopic addTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:rpush"); + RFuture f1 = addTopic.removeListenerAsync(listenerId); + + RPatternTopic remTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:lrem"); + RFuture f2 = remTopic.removeListenerAsync(listenerId); + + RPatternTopic trimTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:ltrim"); + RFuture f3 = trimTopic.removeListenerAsync(listenerId); + + RPatternTopic setTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:lset"); + RFuture f4 = setTopic.removeListenerAsync(listenerId); + + RPatternTopic insertTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:linsert"); + RFuture f5 = insertTopic.removeListenerAsync(listenerId); + + RFuture f6 = super.removeListenerAsync(listenerId); + + CompletableFuture f = CompletableFuture.allOf(f1.toCompletableFuture(), f2.toCompletableFuture(), f3.toCompletableFuture(), + f4.toCompletableFuture(), f5.toCompletableFuture(), f5.toCompletableFuture(), f6.toCompletableFuture()); + return new CompletableFutureWrapper<>(f); + } + + public boolean removeIf(Predicate filter) { + throw new UnsupportedOperationException(); + } +} diff --git a/redisson/src/main/java/org/redisson/RedissonDeque.java b/redisson/src/main/java/org/redisson/RedissonDeque.java index 8e9b89005..33739a17b 100644 --- a/redisson/src/main/java/org/redisson/RedissonDeque.java +++ b/redisson/src/main/java/org/redisson/RedissonDeque.java @@ -15,8 +15,6 @@ */ package org.redisson; -import java.util.*; - import org.redisson.api.RDeque; import org.redisson.api.RFuture; import org.redisson.api.RedissonClient; @@ -28,6 +26,8 @@ import org.redisson.client.protocol.RedisCommands; import org.redisson.client.protocol.decoder.ListFirstObjectDecoder; import org.redisson.command.CommandAsyncExecutor; +import java.util.*; + /** * Distributed and concurrent implementation of {@link java.util.Queue} * @@ -332,8 +332,4 @@ public class RedissonDeque extends RedissonQueue implements RDeque { return remove(o, -1); } - public RedissonDeque reversed() { - throw new UnsupportedOperationException(); - } - } diff --git a/redisson/src/main/java/org/redisson/RedissonList.java b/redisson/src/main/java/org/redisson/RedissonList.java index 36871d9d9..d823d819e 100644 --- a/redisson/src/main/java/org/redisson/RedissonList.java +++ b/redisson/src/main/java/org/redisson/RedissonList.java @@ -15,31 +15,10 @@ */ package org.redisson; -import org.redisson.api.*; -import org.redisson.api.listener.*; -import org.redisson.api.mapreduce.RCollectionMapReduce; -import org.redisson.client.RedisClient; -import org.redisson.client.RedisException; +import org.redisson.api.RList; +import org.redisson.api.RedissonClient; import org.redisson.client.codec.Codec; -import org.redisson.client.codec.StringCodec; -import org.redisson.client.protocol.RedisCommand; -import org.redisson.client.protocol.RedisCommands; -import org.redisson.client.protocol.convertor.BooleanNumberReplayConvertor; -import org.redisson.client.protocol.convertor.Convertor; -import org.redisson.client.protocol.convertor.IntegerReplayConvertor; import org.redisson.command.CommandAsyncExecutor; -import org.redisson.iterator.RedissonBaseIterator; -import org.redisson.iterator.RedissonListIterator; -import org.redisson.mapreduce.RedissonCollectionMapReduce; -import org.redisson.misc.CompletableFutureWrapper; - -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.CompletionStage; -import java.util.function.Predicate; - -import static org.redisson.client.protocol.RedisCommands.*; /** * Distributed and concurrent implementation of {@link java.util.List} @@ -48,944 +27,14 @@ import static org.redisson.client.protocol.RedisCommands.*; * * @param the type of elements held in this collection */ -public class RedissonList extends RedissonExpirable implements RList { +public class RedissonList extends BaseRedissonList implements RList { - private RedissonClient redisson; - public RedissonList(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) { - super(commandExecutor, name); - this.redisson = redisson; + super(commandExecutor, name, redisson); } public RedissonList(Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) { - super(codec, commandExecutor, name); - this.redisson = redisson; - } - - @Override - public RCollectionMapReduce mapReduce() { - return new RedissonCollectionMapReduce(this, redisson, commandExecutor); - } - - @Override - public int size() { - return get(sizeAsync()); - } - - public RFuture sizeAsync() { - return commandExecutor.readAsync(getRawName(), codec, LLEN_INT, getRawName()); - } - - @Override - public boolean isEmpty() { - return size() == 0; - } - - @Override - public boolean contains(Object o) { - return get(containsAsync(o)); - } - - @Override - public Iterator iterator() { - return listIterator(); - } - - @Override - public Object[] toArray() { - List list = readAll(); - return list.toArray(); - } - - @Override - public List readAll() { - return get(readAllAsync()); - } - - @Override - public RFuture> readAllAsync() { - return commandExecutor.readAsync(getRawName(), codec, LRANGE, getRawName(), 0, -1); - } - - @Override - public T[] toArray(T[] a) { - List list = readAll(); - return list.toArray(a); - } - - @Override - public boolean add(V e) { - return get(addAsync(e)); - } - - @Override - public RFuture addAsync(V e) { - return addAsync(e, RPUSH_BOOLEAN); - } - - protected RFuture addAsync(V e, RedisCommand command) { - return commandExecutor.writeAsync(getRawName(), codec, command, getRawName(), encode(e)); - } - - @Override - public boolean remove(Object o) { - return get(removeAsync(o)); - } - - @Override - public RFuture removeAsync(Object o) { - return removeAsync(o, 1); - } - - @Override - public RFuture removeAsync(Object o, int count) { - return commandExecutor.writeAsync(getRawName(), codec, LREM, getRawName(), count, encode(o)); - } - - @Override - public boolean remove(Object o, int count) { - return get(removeAsync(o, count)); - } - - @Override - public RFuture containsAllAsync(Collection c) { - if (c.isEmpty()) { - return new CompletableFutureWrapper<>(true); - } - - return commandExecutor.evalReadAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN, - "local items = redis.call('lrange', KEYS[1], 0, -1) " + - "for i=1, #items do " + - "for j = 1, #ARGV, 1 do " + - "if items[i] == ARGV[j] then " + - "table.remove(ARGV, j) " + - "end " + - "end " + - "end " + - "return #ARGV == 0 and 1 or 0", - Collections.singletonList(getRawName()), encode(c).toArray()); - } - - @Override - public boolean containsAll(Collection c) { - return get(containsAllAsync(c)); - } - - @Override - public boolean addAll(Collection c) { - return get(addAllAsync(c)); - } - - @Override - public RFuture addAllAsync(Collection c) { - if (c.isEmpty()) { - return new CompletableFutureWrapper<>(false); - } - - List args = new ArrayList(c.size() + 1); - args.add(getRawName()); - encode(args, c); - return commandExecutor.writeAsync(getRawName(), codec, RPUSH_BOOLEAN, args.toArray()); - } - - @Override - public RFuture addAllAsync(int index, Collection coll) { - if (index < 0) { - throw new IndexOutOfBoundsException("index: " + index); - } - - if (coll.isEmpty()) { - return new CompletableFutureWrapper<>(false); - } - - if (index == 0) { // prepend elements to list - List elements = new ArrayList(); - encode(elements, coll); - Collections.reverse(elements); - elements.add(0, getRawName()); - - return commandExecutor.writeAsync(getRawName(), codec, LPUSH_BOOLEAN, elements.toArray()); - } - - List args = new ArrayList(coll.size() + 1); - args.add(index); - encode(args, coll); - - return commandExecutor.evalWriteNoRetryAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN, - "local ind = table.remove(ARGV, 1); " + // index is the first parameter - "local size = redis.call('llen', KEYS[1]); " + - "assert(tonumber(ind) <= size, 'index: ' .. ind .. ' but current size: ' .. size); " + - "local tail = redis.call('lrange', KEYS[1], ind, -1); " + - "redis.call('ltrim', KEYS[1], 0, ind - 1); " + - "for i=1, #ARGV, 5000 do " - + "redis.call('rpush', KEYS[1], unpack(ARGV, i, math.min(i+4999, #ARGV))); " - + "end " + - "if #tail > 0 then " + - "for i=1, #tail, 5000 do " - + "redis.call('rpush', KEYS[1], unpack(tail, i, math.min(i+4999, #tail))); " - + "end " - + "end;" + - "return 1;", - Collections.singletonList(getRawName()), args.toArray()); - } - - @Override - public boolean addAll(int index, Collection coll) { - return get(addAllAsync(index, coll)); - } - - @Override - public RFuture removeAllAsync(Collection c) { - if (c.isEmpty()) { - return new CompletableFutureWrapper<>(false); - } - - return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN, - "local v = 0 " + - "for i = 1, #ARGV, 1 do " - + "if redis.call('lrem', KEYS[1], 0, ARGV[i]) == 1 " - + "then v = 1 end " - +"end " - + "return v ", - Collections.singletonList(getRawName()), encode(c).toArray()); - } - - @Override - public boolean removeAll(Collection c) { - return get(removeAllAsync(c)); - } - - @Override - public boolean retainAll(Collection c) { - return get(retainAllAsync(c)); - } - - @Override - public RFuture retainAllAsync(Collection c) { - if (c.isEmpty()) { - return deleteAsync(); - } - - return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN, - "local changed = 0 " + - "local items = redis.call('lrange', KEYS[1], 0, -1) " - + "local i = 1 " - + "while i <= #items do " - + "local element = items[i] " - + "local isInAgrs = false " - + "for j = 1, #ARGV, 1 do " - + "if ARGV[j] == element then " - + "isInAgrs = true " - + "break " - + "end " - + "end " - + "if isInAgrs == false then " - + "redis.call('LREM', KEYS[1], 0, element) " - + "changed = 1 " - + "end " - + "i = i + 1 " - + "end " - + "return changed ", - Collections.singletonList(getRawName()), encode(c).toArray()); - } - - - @Override - public void clear() { - delete(); - } - - @Override - public RFuture getAsync(int index) { - return commandExecutor.readAsync(getRawName(), codec, LINDEX, getRawName(), index); - } - - public List get(int... indexes) { - return get(getAsync(indexes)); - } - - @Override - public Iterator distributedIterator(final int count) { - String iteratorName = "__redisson_list_cursor_{" + getRawName() + "}"; - return distributedIterator(iteratorName, count); - } - - @Override - public Iterator distributedIterator(final String iteratorName, final int count) { - return new RedissonBaseIterator() { - - @Override - protected ScanResult iterator(RedisClient client, long nextIterPos) { - return distributedScanIterator(iteratorName, count); - } - - @Override - protected void remove(Object value) { - RedissonList.this.remove((V) value); - } - }; - } - - private ScanResult distributedScanIterator(String iteratorName, int count) { - return get(distributedScanIteratorAsync(iteratorName, count)); - } - - private RFuture> distributedScanIteratorAsync(String iteratorName, int count) { - return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_SCAN, - "local start_index = redis.call('get', KEYS[2]); " - + "if start_index ~= false then " - + "start_index = tonumber(start_index); " - + "else " - + "start_index = 0;" - + "end;" - + "if start_index == -1 then " - + "return {0, {}};" - + "end;" - + "local end_index = start_index + ARGV[1];" - + "local result; " - + "result = redis.call('lrange', KEYS[1], start_index, end_index - 1); " - + "if end_index > redis.call('llen', KEYS[1]) then " - + "end_index = -1;" - + "end; " - + "redis.call('setex', KEYS[2], 3600, end_index);" - + "return {end_index, result};", - Arrays.asList(getRawName(), iteratorName), count); - } - - public RFuture> getAsync(int... indexes) { - List params = new ArrayList(); - for (Integer index : indexes) { - params.add(index); - } - return commandExecutor.evalReadAsync(getRawName(), codec, RedisCommands.EVAL_LIST, - "local result = {}; " + - "for i = 1, #ARGV, 1 do " - + "local value = redis.call('lindex', KEYS[1], ARGV[i]);" - + "table.insert(result, value);" + - "end; " + - "return result;", - Collections.singletonList(getRawName()), params.toArray()); - } - - - @Override - public V get(int index) { - return getValue(index); - } - - V getValue(int index) { - return get(getAsync(index)); - } - - @Override - public V set(int index, V element) { - try { - return get(setAsync(index, element)); - } catch (RedisException e) { - if (e.getCause() instanceof IndexOutOfBoundsException) { - throw (IndexOutOfBoundsException) e.getCause(); - } - throw e; - } - } - - @Override - public RFuture setAsync(int index, V element) { - RFuture future = commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_OBJECT, - "local v = redis.call('lindex', KEYS[1], ARGV[1]); " + - "redis.call('lset', KEYS[1], ARGV[1], ARGV[2]); " + - "return v", - Collections.singletonList(getRawName()), index, encode(element)); - CompletionStage f = future.handle((res, e) -> { - if (e != null) { - if (e.getMessage().contains("ERR index out of range")) { - throw new CompletionException(new IndexOutOfBoundsException("index out of range")); - } - throw new CompletionException(e); - } - return res; - }); - return new CompletableFutureWrapper<>(f); - } - - @Override - public void fastSet(int index, V element) { - get(fastSetAsync(index, element)); - } - - @Override - public RFuture fastSetAsync(int index, V element) { - return commandExecutor.writeAsync(getRawName(), codec, RedisCommands.LSET, getRawName(), index, encode(element)); - } - - @Override - public void add(int index, V element) { - addAll(index, Collections.singleton(element)); - } - - @Override - public RFuture addAsync(int index, V element) { - return addAllAsync(index, Collections.singleton(element)); - } - - @Override - public V remove(int index) { - return get(removeAsync(index)); - } - - @Override - public RFuture removeAsync(int index) { - if (index == 0) { - return commandExecutor.writeAsync(getRawName(), codec, LPOP, getRawName()); - } - - return commandExecutor.evalWriteAsync(getRawName(), codec, EVAL_OBJECT, - "local v = redis.call('lindex', KEYS[1], ARGV[1]); " + - "redis.call('lset', KEYS[1], ARGV[1], 'DELETED_BY_REDISSON');" + - "redis.call('lrem', KEYS[1], 1, 'DELETED_BY_REDISSON');" + - "return v", - Collections.singletonList(getRawName()), index); - } - - - @Override - public void fastRemove(int index) { - get(fastRemoveAsync(index)); - } - - @Override - public RFuture fastRemoveAsync(int index) { - return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_VOID, - "redis.call('lset', KEYS[1], ARGV[1], 'DELETED_BY_REDISSON');" + - "redis.call('lrem', KEYS[1], 1, 'DELETED_BY_REDISSON');", - Collections.singletonList(getRawName()), index); - } - - @Override - public int indexOf(Object o) { - return get(indexOfAsync(o)); - } - - @Override - public RFuture containsAsync(Object o) { - return indexOfAsync(o, new BooleanNumberReplayConvertor(-1L)); - } - - public RFuture indexOfAsync(Object o, Convertor convertor) { - return commandExecutor.evalReadAsync(getRawName(), codec, new RedisCommand("EVAL", convertor), - "local key = KEYS[1] " + - "local obj = ARGV[1] " + - "local items = redis.call('lrange', key, 0, -1) " + - "for i=1,#items do " + - "if items[i] == obj then " + - "return i - 1 " + - "end " + - "end " + - "return -1", - Collections.singletonList(getRawName()), encode(o)); - } - - @Override - public RFuture indexOfAsync(Object o) { - return indexOfAsync(o, new IntegerReplayConvertor()); - } - - @Override - public int lastIndexOf(Object o) { - return get(lastIndexOfAsync(o)); - } - - @Override - public RFuture lastIndexOfAsync(Object o) { - return commandExecutor.evalReadAsync(getRawName(), codec, RedisCommands.EVAL_INTEGER, - "local key = KEYS[1] " + - "local obj = ARGV[1] " + - "local items = redis.call('lrange', key, 0, -1) " + - "for i = #items, 1, -1 do " + - "if items[i] == obj then " + - "return i - 1 " + - "end " + - "end " + - "return -1", - Collections.singletonList(getRawName()), encode(o)); - } - - public RFuture lastIndexOfAsync(Object o, Convertor convertor) { - return commandExecutor.evalReadAsync(getRawName(), codec, new RedisCommand("EVAL", convertor), - "local key = KEYS[1] " + - "local obj = ARGV[1] " + - "local items = redis.call('lrange', key, 0, -1) " + - "for i = #items, 1, -1 do " + - "if items[i] == obj then " + - "return i - 1 " + - "end " + - "end " + - "return -1", - Collections.singletonList(getRawName()), encode(o)); - } - - @Override - public void trim(int fromIndex, int toIndex) { - get(trimAsync(fromIndex, toIndex)); - } - - @Override - public RFuture trimAsync(int fromIndex, int toIndex) { - return commandExecutor.writeAsync(getRawName(), codec, RedisCommands.LTRIM, getRawName(), fromIndex, toIndex); + super(codec, commandExecutor, name, redisson); } - @Override - public ListIterator listIterator() { - return listIterator(0); - } - - @Override - public ListIterator listIterator(int ind) { - return new RedissonListIterator(ind) { - - @Override - public V getValue(int index) { - return RedissonList.this.getValue(index); - } - - @Override - public V remove(int index) { - return RedissonList.this.remove(index); - } - - @Override - public void fastSet(int index, V value) { - RedissonList.this.fastSet(index, value); - } - - @Override - public void add(int index, V value) { - RedissonList.this.add(index, value); - } - }; - } - - @Override - public RList subList(int fromIndex, int toIndex) { - int size = size(); - if (fromIndex < 0 || toIndex > size) { - throw new IndexOutOfBoundsException("fromIndex: " + fromIndex + " toIndex: " + toIndex + " size: " + size); - } - if (fromIndex > toIndex) { - throw new IllegalArgumentException("fromIndex: " + fromIndex + " toIndex: " + toIndex); - } - - return new RedissonSubList(codec, commandExecutor, getRawName(), fromIndex, toIndex); - } - - @Override - @SuppressWarnings("AvoidInlineConditionals") - public String toString() { - Iterator it = iterator(); - if (! it.hasNext()) - return "[]"; - - StringBuilder sb = new StringBuilder(); - sb.append('['); - for (;;) { - V e = it.next(); - sb.append(e == this ? "(this Collection)" : e); - if (! it.hasNext()) - return sb.append(']').toString(); - sb.append(',').append(' '); - } - } - - @Override - @SuppressWarnings("AvoidInlineConditionals") - public boolean equals(Object o) { - if (o == this) - return true; - if (!(o instanceof List)) - return false; - - Iterator e1 = iterator(); - Iterator e2 = ((List) o).iterator(); - while (e1.hasNext() && e2.hasNext()) { - V o1 = e1.next(); - Object o2 = e2.next(); - if (!(o1==null ? o2==null : o1.equals(o2))) - return false; - } - return !(e1.hasNext() || e2.hasNext()); - } - - @Override - @SuppressWarnings("AvoidInlineConditionals") - public int hashCode() { - int hashCode = 1; - for (V e : this) { - hashCode = 31*hashCode + (e==null ? 0 : e.hashCode()); - } - return hashCode; - } - - @Override - public RFuture addAfterAsync(V elementToFind, V element) { - return commandExecutor.writeAsync(getRawName(), codec, RedisCommands.LINSERT_INT, getRawName(), "AFTER", encode(elementToFind), encode(element)); - } - - @Override - public RFuture addBeforeAsync(V elementToFind, V element) { - return commandExecutor.writeAsync(getRawName(), codec, RedisCommands.LINSERT_INT, getRawName(), "BEFORE", encode(elementToFind), encode(element)); - } - - @Override - public int addAfter(V elementToFind, V element) { - return get(addAfterAsync(elementToFind, element)); - } - - @Override - public int addBefore(V elementToFind, V element) { - return get(addBeforeAsync(elementToFind, element)); - } - - @Override - public List readSort(SortOrder order) { - return get(readSortAsync(order)); - } - - @Override - public RFuture> readSortAsync(SortOrder order) { - return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), order); - } - - @Override - public List readSort(SortOrder order, int offset, int count) { - return get(readSortAsync(order, offset, count)); - } - - @Override - public RFuture> readSortAsync(SortOrder order, int offset, int count) { - return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), "LIMIT", offset, count, order); - } - - @Override - public List readSort(String byPattern, SortOrder order) { - return get(readSortAsync(byPattern, order)); - } - - @Override - public RFuture> readSortAsync(String byPattern, SortOrder order) { - return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), "BY", byPattern, order); - } - - @Override - public List readSort(String byPattern, SortOrder order, int offset, int count) { - return get(readSortAsync(byPattern, order, offset, count)); - } - - @Override - public RFuture> readSortAsync(String byPattern, SortOrder order, int offset, int count) { - return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), "BY", byPattern, "LIMIT", offset, count, order); - } - - @Override - public Collection readSort(String byPattern, List getPatterns, SortOrder order) { - return (Collection) get(readSortAsync(byPattern, getPatterns, order)); - } - - @Override - public RFuture> readSortAsync(String byPattern, List getPatterns, SortOrder order) { - return readSortAsync(byPattern, getPatterns, order, -1, -1); - } - - @Override - public Collection readSort(String byPattern, List getPatterns, SortOrder order, int offset, int count) { - return (Collection) get(readSortAsync(byPattern, getPatterns, order, offset, count)); - } - - @Override - public RFuture> readSortAsync(String byPattern, List getPatterns, SortOrder order, int offset, int count) { - return readSortAsync(byPattern, getPatterns, order, offset, count, false); - } - - @Override - public List readSortAlpha(SortOrder order) { - return get(readSortAlphaAsync(order)); - } - - @Override - public RFuture> readSortAlphaAsync(SortOrder order) { - return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), "ALPHA", order); - } - - @Override - public List readSortAlpha(SortOrder order, int offset, int count) { - return get(readSortAlphaAsync(order, offset, count)); - } - - @Override - public RFuture> readSortAlphaAsync(SortOrder order, int offset, int count) { - return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), "LIMIT", offset, count, "ALPHA", order); - } - - @Override - public List readSortAlpha(String byPattern, SortOrder order) { - return get(readSortAlphaAsync(byPattern, order)); - } - - @Override - public RFuture> readSortAlphaAsync(String byPattern, SortOrder order) { - return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), "BY", byPattern, "ALPHA", order); - } - - @Override - public List readSortAlpha(String byPattern, SortOrder order, int offset, int count) { - return get(readSortAlphaAsync(byPattern, order, offset, count)); - } - - @Override - public RFuture> readSortAlphaAsync(String byPattern, SortOrder order, int offset, int count) { - return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, getRawName(), "BY", byPattern, "LIMIT", offset, count, "ALPHA", order); - } - - @Override - public Collection readSortAlpha(String byPattern, List getPatterns, SortOrder order) { - return (Collection) get(readSortAlphaAsync(byPattern, getPatterns, order)); - } - - @Override - public RFuture> readSortAlphaAsync(String byPattern, List getPatterns, SortOrder order) { - return readSortAlphaAsync(byPattern, getPatterns, order, -1, -1); - } - - @Override - public Collection readSortAlpha(String byPattern, List getPatterns, SortOrder order, int offset, int count) { - return (Collection) get(readSortAlphaAsync(byPattern, getPatterns, order, offset, count)); - } - - @Override - public RFuture> readSortAlphaAsync(String byPattern, List getPatterns, SortOrder order, int offset, int count) { - return readSortAsync(byPattern, getPatterns, order, offset, count, true); - } - - @Override - public int sortTo(String destName, SortOrder order) { - return get(sortToAsync(destName, order)); - } - - @Override - public RFuture sortToAsync(String destName, SortOrder order) { - return sortToAsync(destName, null, Collections.emptyList(), order, -1, -1); - } - - @Override - public int sortTo(String destName, SortOrder order, int offset, int count) { - return get(sortToAsync(destName, order, offset, count)); - } - - @Override - public RFuture sortToAsync(String destName, SortOrder order, int offset, int count) { - return sortToAsync(destName, null, Collections.emptyList(), order, offset, count); - } - - @Override - public int sortTo(String destName, String byPattern, SortOrder order, int offset, int count) { - return get(sortToAsync(destName, byPattern, order, offset, count)); - } - - @Override - public int sortTo(String destName, String byPattern, SortOrder order) { - return get(sortToAsync(destName, byPattern, order)); - } - - @Override - public RFuture sortToAsync(String destName, String byPattern, SortOrder order) { - return sortToAsync(destName, byPattern, Collections.emptyList(), order, -1, -1); - } - - @Override - public RFuture sortToAsync(String destName, String byPattern, SortOrder order, int offset, int count) { - return sortToAsync(destName, byPattern, Collections.emptyList(), order, offset, count); - } - - @Override - public int sortTo(String destName, String byPattern, List getPatterns, SortOrder order) { - return get(sortToAsync(destName, byPattern, getPatterns, order)); - } - - @Override - public RFuture sortToAsync(String destName, String byPattern, List getPatterns, SortOrder order) { - return sortToAsync(destName, byPattern, getPatterns, order, -1, -1); - } - - @Override - public int sortTo(String destName, String byPattern, List getPatterns, SortOrder order, int offset, int count) { - return get(sortToAsync(destName, byPattern, getPatterns, order, offset, count)); - } - - @Override - public RFuture sortToAsync(String destName, String byPattern, List getPatterns, SortOrder order, int offset, int count) { - List params = new ArrayList(); - params.add(getRawName()); - if (byPattern != null) { - params.add("BY"); - params.add(byPattern); - } - if (offset != -1 && count != -1) { - params.add("LIMIT"); - } - if (offset != -1) { - params.add(offset); - } - if (count != -1) { - params.add(count); - } - for (String pattern : getPatterns) { - params.add("GET"); - params.add(pattern); - } - params.add(order); - params.add("STORE"); - params.add(destName); - - return commandExecutor.writeAsync(getRawName(), codec, RedisCommands.SORT_TO, params.toArray()); - } - - private RFuture> readSortAsync(String byPattern, List getPatterns, SortOrder order, int offset, int count, boolean alpha) { - List params = new ArrayList(); - params.add(getRawName()); - if (byPattern != null) { - params.add("BY"); - params.add(byPattern); - } - if (offset != -1 && count != -1) { - params.add("LIMIT"); - } - if (offset != -1) { - params.add(offset); - } - if (count != -1) { - params.add(count); - } - if (getPatterns != null) { - for (String pattern : getPatterns) { - params.add("GET"); - params.add(pattern); - } - } - if (alpha) { - params.add("ALPHA"); - } - if (order != null) { - params.add(order); - } - - return commandExecutor.readAsync(getRawName(), codec, RedisCommands.SORT_LIST, params.toArray()); - } - - @Override - public RFuture> rangeAsync(int toIndex) { - return rangeAsync(0, toIndex); - } - - @Override - public RFuture> rangeAsync(int fromIndex, int toIndex) { - return commandExecutor.readAsync(getRawName(), codec, LRANGE, getRawName(), fromIndex, toIndex); - } - - @Override - public List range(int toIndex) { - return get(rangeAsync(toIndex)); - } - - @Override - public List range(int fromIndex, int toIndex) { - return get(rangeAsync(fromIndex, toIndex)); - } - - @Override - public int addListener(ObjectListener listener) { - if (listener instanceof ListAddListener) { - return addListener("__keyevent@*:rpush", (ListAddListener) listener, ListAddListener::onListAdd); - } - if (listener instanceof ListRemoveListener) { - return addListener("__keyevent@*:lrem", (ListRemoveListener) listener, ListRemoveListener::onListRemove); - } - if (listener instanceof ListTrimListener) { - return addListener("__keyevent@*:ltrim", (ListTrimListener) listener, ListTrimListener::onListTrim); - } - if (listener instanceof ListSetListener) { - return addListener("__keyevent@*:lset", (ListSetListener) listener, ListSetListener::onListSet); - } - if (listener instanceof ListInsertListener) { - return addListener("__keyevent@*:linsert", (ListInsertListener) listener, ListInsertListener::onListInsert); - } - return super.addListener(listener); - } - - @Override - public RFuture addListenerAsync(ObjectListener listener) { - if (listener instanceof ListAddListener) { - return addListenerAsync("__keyevent@*:rpush", (ListAddListener) listener, ListAddListener::onListAdd); - } - if (listener instanceof ListRemoveListener) { - return addListenerAsync("__keyevent@*:lrem", (ListRemoveListener) listener, ListRemoveListener::onListRemove); - } - if (listener instanceof ListTrimListener) { - return addListenerAsync("__keyevent@*:ltrim", (ListTrimListener) listener, ListTrimListener::onListTrim); - } - if (listener instanceof ListSetListener) { - return addListenerAsync("__keyevent@*:lset", (ListSetListener) listener, ListSetListener::onListSet); - } - if (listener instanceof ListInsertListener) { - return addListenerAsync("__keyevent@*:linsert", (ListInsertListener) listener, ListInsertListener::onListInsert); - } - return super.addListenerAsync(listener); - } - - @Override - public void removeListener(int listenerId) { - RPatternTopic addTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:rpush"); - addTopic.removeListener(listenerId); - - RPatternTopic remTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:lrem"); - remTopic.removeListener(listenerId); - - RPatternTopic trimTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:ltrim"); - trimTopic.removeListener(listenerId); - - RPatternTopic setTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:lset"); - setTopic.removeListener(listenerId); - - RPatternTopic insertTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:linsert"); - insertTopic.removeListener(listenerId); - - super.removeListener(listenerId); - } - - @Override - public RFuture removeListenerAsync(int listenerId) { - RPatternTopic addTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:rpush"); - RFuture f1 = addTopic.removeListenerAsync(listenerId); - - RPatternTopic remTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:lrem"); - RFuture f2 = remTopic.removeListenerAsync(listenerId); - - RPatternTopic trimTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:ltrim"); - RFuture f3 = trimTopic.removeListenerAsync(listenerId); - - RPatternTopic setTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:lset"); - RFuture f4 = setTopic.removeListenerAsync(listenerId); - - RPatternTopic insertTopic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, "__keyevent@*:linsert"); - RFuture f5 = insertTopic.removeListenerAsync(listenerId); - - RFuture f6 = super.removeListenerAsync(listenerId); - - CompletableFuture f = CompletableFuture.allOf(f1.toCompletableFuture(), f2.toCompletableFuture(), f3.toCompletableFuture(), - f4.toCompletableFuture(), f5.toCompletableFuture(), f5.toCompletableFuture(), f6.toCompletableFuture()); - return new CompletableFutureWrapper<>(f); - } - - @Override - public boolean removeIf(Predicate filter) { - throw new UnsupportedOperationException(); - } } diff --git a/redisson/src/main/java/org/redisson/RedissonPriorityDeque.java b/redisson/src/main/java/org/redisson/RedissonPriorityDeque.java index e8b9bc65a..9f1d1e86b 100644 --- a/redisson/src/main/java/org/redisson/RedissonPriorityDeque.java +++ b/redisson/src/main/java/org/redisson/RedissonPriorityDeque.java @@ -329,7 +329,4 @@ public class RedissonPriorityDeque extends RedissonPriorityQueue implement throw new UnsupportedOperationException(); } - public RedissonPriorityDeque reversed() { - throw new UnsupportedOperationException(); - } } diff --git a/redisson/src/main/java/org/redisson/RedissonPriorityQueue.java b/redisson/src/main/java/org/redisson/RedissonPriorityQueue.java index 0d8d111d3..67606649e 100644 --- a/redisson/src/main/java/org/redisson/RedissonPriorityQueue.java +++ b/redisson/src/main/java/org/redisson/RedissonPriorityQueue.java @@ -38,7 +38,7 @@ import java.util.function.Supplier; * * @param value type */ -public class RedissonPriorityQueue extends RedissonList implements RPriorityQueue { +public class RedissonPriorityQueue extends BaseRedissonList implements RPriorityQueue { public static class BinarySearchResult { diff --git a/redisson/src/main/java/org/redisson/RedissonQueue.java b/redisson/src/main/java/org/redisson/RedissonQueue.java index cf56c84dd..58cbd180f 100644 --- a/redisson/src/main/java/org/redisson/RedissonQueue.java +++ b/redisson/src/main/java/org/redisson/RedissonQueue.java @@ -33,7 +33,7 @@ import java.util.NoSuchElementException; * * @param the type of elements held in this collection */ -public class RedissonQueue extends RedissonList implements RQueue { +public class RedissonQueue extends BaseRedissonList implements RQueue { public RedissonQueue(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) { super(commandExecutor, name, redisson); diff --git a/redisson/src/main/java/org/redisson/reactive/RedissonBlockingQueueReactive.java b/redisson/src/main/java/org/redisson/reactive/RedissonBlockingQueueReactive.java index 445b91f67..c4c0a99e7 100644 --- a/redisson/src/main/java/org/redisson/reactive/RedissonBlockingQueueReactive.java +++ b/redisson/src/main/java/org/redisson/reactive/RedissonBlockingQueueReactive.java @@ -15,14 +15,13 @@ */ package org.redisson.reactive; -import java.util.concurrent.Callable; - +import org.redisson.BaseRedissonList; import org.redisson.api.RBlockingQueue; import org.redisson.api.RFuture; -import org.redisson.api.RListAsync; - import reactor.core.publisher.Flux; +import java.util.concurrent.Callable; + /** * * @author Nikita Koksharov @@ -34,7 +33,7 @@ public class RedissonBlockingQueueReactive extends RedissonListReactive { private final RBlockingQueue queue; public RedissonBlockingQueueReactive(RBlockingQueue queue) { - super((RListAsync) queue); + super((BaseRedissonList) queue); this.queue = queue; } diff --git a/redisson/src/main/java/org/redisson/reactive/RedissonListReactive.java b/redisson/src/main/java/org/redisson/reactive/RedissonListReactive.java index c98e91b82..a9d1ae4f6 100644 --- a/redisson/src/main/java/org/redisson/reactive/RedissonListReactive.java +++ b/redisson/src/main/java/org/redisson/reactive/RedissonListReactive.java @@ -15,18 +15,17 @@ */ package org.redisson.reactive; -import java.util.function.Consumer; -import java.util.function.LongConsumer; - import org.reactivestreams.Publisher; +import org.redisson.BaseRedissonList; import org.redisson.RedissonList; import org.redisson.api.RFuture; -import org.redisson.api.RListAsync; import org.redisson.client.codec.Codec; - import reactor.core.publisher.Flux; import reactor.core.publisher.FluxSink; +import java.util.function.Consumer; +import java.util.function.LongConsumer; + /** * Distributed and concurrent implementation of {@link java.util.List} * @@ -36,9 +35,9 @@ import reactor.core.publisher.FluxSink; */ public class RedissonListReactive { - private final RListAsync instance; + private final BaseRedissonList instance; - public RedissonListReactive(RListAsync instance) { + public RedissonListReactive(BaseRedissonList instance) { this.instance = instance; } diff --git a/redisson/src/main/java/org/redisson/rx/RedissonBlockingQueueRx.java b/redisson/src/main/java/org/redisson/rx/RedissonBlockingQueueRx.java index d813b2ea8..900215eca 100644 --- a/redisson/src/main/java/org/redisson/rx/RedissonBlockingQueueRx.java +++ b/redisson/src/main/java/org/redisson/rx/RedissonBlockingQueueRx.java @@ -15,10 +15,9 @@ */ package org.redisson.rx; -import org.redisson.api.RBlockingQueueAsync; -import org.redisson.api.RListAsync; - import io.reactivex.rxjava3.core.Flowable; +import org.redisson.BaseRedissonList; +import org.redisson.api.RBlockingQueueAsync; /** * @@ -31,7 +30,7 @@ public class RedissonBlockingQueueRx extends RedissonListRx { private final RBlockingQueueAsync queue; public RedissonBlockingQueueRx(RBlockingQueueAsync queue) { - super((RListAsync) queue); + super((BaseRedissonList) queue); this.queue = queue; } diff --git a/redisson/src/main/java/org/redisson/rx/RedissonListRx.java b/redisson/src/main/java/org/redisson/rx/RedissonListRx.java index 558e17265..e4ad27cf1 100644 --- a/redisson/src/main/java/org/redisson/rx/RedissonListRx.java +++ b/redisson/src/main/java/org/redisson/rx/RedissonListRx.java @@ -15,13 +15,12 @@ */ package org.redisson.rx; -import org.reactivestreams.Publisher; -import org.redisson.api.RFuture; -import org.redisson.api.RListAsync; - import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.functions.LongConsumer; import io.reactivex.rxjava3.processors.ReplayProcessor; +import org.reactivestreams.Publisher; +import org.redisson.BaseRedissonList; +import org.redisson.api.RFuture; /** * Distributed and concurrent implementation of {@link java.util.List} @@ -32,9 +31,9 @@ import io.reactivex.rxjava3.processors.ReplayProcessor; */ public class RedissonListRx { - private final RListAsync instance; + private final BaseRedissonList instance; - public RedissonListRx(RListAsync instance) { + public RedissonListRx(BaseRedissonList instance) { this.instance = instance; } diff --git a/redisson/src/test/java/org/redisson/RedissonBlockingQueueReactiveTest.java b/redisson/src/test/java/org/redisson/RedissonBlockingQueueReactiveTest.java index 9a5b9fa25..bdfb28a30 100644 --- a/redisson/src/test/java/org/redisson/RedissonBlockingQueueReactiveTest.java +++ b/redisson/src/test/java/org/redisson/RedissonBlockingQueueReactiveTest.java @@ -44,7 +44,7 @@ public class RedissonBlockingQueueReactiveTest extends BaseReactiveTest { .repeat() .subscribe(); - Awaitility.await().atMost(Duration.ofSeconds(1)).untilAsserted(() -> { + Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> { assertThat(counter.get()).isEqualTo(100); }); } diff --git a/redisson/src/test/java/org/redisson/rx/RedissonBlockingDequeRxTest.java b/redisson/src/test/java/org/redisson/rx/RedissonBlockingDequeRxTest.java index 199d1bf70..410e61835 100644 --- a/redisson/src/test/java/org/redisson/rx/RedissonBlockingDequeRxTest.java +++ b/redisson/src/test/java/org/redisson/rx/RedissonBlockingDequeRxTest.java @@ -52,7 +52,7 @@ public class RedissonBlockingDequeRxTest extends BaseRxTest { RBlockingDequeRx blockingDeque = redisson.getBlockingDeque("blocking_deque"); long start = System.currentTimeMillis(); String redisTask = sync(blockingDeque.pollLastAndOfferFirstTo("deque", 1, TimeUnit.SECONDS)); - assertThat(System.currentTimeMillis() - start).isBetween(950L, 1500L); + assertThat(System.currentTimeMillis() - start).isBetween(950L, 1600L); assertThat(redisTask).isNull(); } From 78c934773f16d9720fbf996eac3493c18ef51200 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Sat, 11 Nov 2023 08:37:22 +0300 Subject: [PATCH 06/23] refactoring --- redisson/src/main/java/org/redisson/client/RedisClient.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/redisson/src/main/java/org/redisson/client/RedisClient.java b/redisson/src/main/java/org/redisson/client/RedisClient.java index 08a19b6e4..0eb442a5a 100644 --- a/redisson/src/main/java/org/redisson/client/RedisClient.java +++ b/redisson/src/main/java/org/redisson/client/RedisClient.java @@ -80,11 +80,6 @@ public final class RedisClient { private boolean hasOwnResolver; private volatile boolean shutdown; - private final AtomicLong firstFailTime = new AtomicLong(0); - - private Runnable connectedListener; - private Runnable disconnectedListener; - public static RedisClient create(RedisClientConfig config) { return new RedisClient(config); } From b474606998f4342cb193d4b532657e1f788fc963 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Sat, 11 Nov 2023 10:06:46 +0300 Subject: [PATCH 07/23] refactoring --- .../java/org/redisson/RedissonBatchTest.java | 1 + .../java/org/redisson/RedissonBucketTest.java | 1 - .../executor/RedissonExecutorServiceTest.java | 36 +++++++++---------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/redisson/src/test/java/org/redisson/RedissonBatchTest.java b/redisson/src/test/java/org/redisson/RedissonBatchTest.java index 773c1a354..b76833351 100644 --- a/redisson/src/test/java/org/redisson/RedissonBatchTest.java +++ b/redisson/src/test/java/org/redisson/RedissonBatchTest.java @@ -419,6 +419,7 @@ public class RedissonBatchTest extends BaseTest { @ParameterizedTest @MethodSource("data") + @Timeout(20) public void testSyncSlaves(BatchOptions batchOptions) throws FailedToStartRedisException, IOException, InterruptedException { RedisRunner master1 = new RedisRunner().randomPort().randomDir().nosave(); RedisRunner master2 = new RedisRunner().randomPort().randomDir().nosave(); diff --git a/redisson/src/test/java/org/redisson/RedissonBucketTest.java b/redisson/src/test/java/org/redisson/RedissonBucketTest.java index 10cfca425..6eb66d8f3 100755 --- a/redisson/src/test/java/org/redisson/RedissonBucketTest.java +++ b/redisson/src/test/java/org/redisson/RedissonBucketTest.java @@ -184,7 +184,6 @@ public class RedissonBucketTest extends BaseTest { @Test public void testSizeInMemory() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("4.0.0") > 0); RBucket al = redisson.getBucket("test"); al.set(1234); assertThat(al.sizeInMemory()).isEqualTo(51); diff --git a/redisson/src/test/java/org/redisson/executor/RedissonExecutorServiceTest.java b/redisson/src/test/java/org/redisson/executor/RedissonExecutorServiceTest.java index 4490d45a0..8eb5a3db0 100644 --- a/redisson/src/test/java/org/redisson/executor/RedissonExecutorServiceTest.java +++ b/redisson/src/test/java/org/redisson/executor/RedissonExecutorServiceTest.java @@ -1,7 +1,20 @@ package org.redisson.executor; -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; +import mockit.Invocation; +import mockit.Mock; +import mockit.MockUp; +import org.junit.jupiter.api.*; +import org.redisson.BaseTest; +import org.redisson.RedisRunner; +import org.redisson.Redisson; +import org.redisson.RedissonNode; +import org.redisson.api.*; +import org.redisson.api.annotation.RInject; +import org.redisson.api.executor.TaskFinishedListener; +import org.redisson.api.executor.TaskStartedListener; +import org.redisson.config.Config; +import org.redisson.config.RedissonNodeConfig; +import org.redisson.connection.balancer.RandomLoadBalancer; import java.io.IOException; import java.io.Serializable; @@ -12,22 +25,8 @@ import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.redisson.*; -import org.redisson.api.*; -import org.redisson.api.annotation.RInject; -import org.redisson.api.executor.TaskFinishedListener; -import org.redisson.api.executor.TaskStartedListener; -import org.redisson.config.Config; -import org.redisson.config.RedissonNodeConfig; -import org.redisson.connection.balancer.RandomLoadBalancer; - -import mockit.Invocation; -import mockit.Mock; -import mockit.MockUp; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; public class RedissonExecutorServiceTest extends BaseTest { @@ -455,6 +454,7 @@ public class RedissonExecutorServiceTest extends BaseTest { } @Test + @Timeout(1) public void testRejectExecute() { Assertions.assertThrows(RejectedExecutionException.class, () -> { RExecutorService e = redisson.getExecutorService("test"); From 642731ce33eeadf2083cdb561a92355e5402c034 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Sat, 11 Nov 2023 12:05:28 +0300 Subject: [PATCH 08/23] refactoring --- redisson/src/main/java/org/redisson/client/RedisClient.java | 1 - 1 file changed, 1 deletion(-) diff --git a/redisson/src/main/java/org/redisson/client/RedisClient.java b/redisson/src/main/java/org/redisson/client/RedisClient.java index 0eb442a5a..e948757a3 100644 --- a/redisson/src/main/java/org/redisson/client/RedisClient.java +++ b/redisson/src/main/java/org/redisson/client/RedisClient.java @@ -51,7 +51,6 @@ import java.net.InetSocketAddress; import java.net.SocketOption; import java.net.UnknownHostException; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; /** From 4d043770ec177b2b4665b97b94b48595bcafaa6f Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Sat, 11 Nov 2023 12:06:31 +0300 Subject: [PATCH 09/23] refactoring --- .../test/java/org/redisson/RedissonScoredSortedSetTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index b06dec69d..810089d8f 100644 --- a/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -34,6 +34,10 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(1.2, "v2"); set.add(1.3, "v3"); + RScoredSortedSet set2 = redisson.getScoredSortedSet("test2"); + + ScoredEntry s3 = set2.firstEntry(); + assertThat(s3).isNull(); ScoredEntry s = set.firstEntry(); assertThat(s).isEqualTo(new ScoredEntry<>(1.1, "v1")); ScoredEntry s2 = set.lastEntry(); From 72e4df47e1fa37ce84063f2800ebe10e626d6971 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Sat, 11 Nov 2023 12:06:42 +0300 Subject: [PATCH 10/23] test fixed --- redisson/src/test/java/org/redisson/RedissonSearchTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/redisson/src/test/java/org/redisson/RedissonSearchTest.java b/redisson/src/test/java/org/redisson/RedissonSearchTest.java index e85300a2e..b6a55dfc2 100644 --- a/redisson/src/test/java/org/redisson/RedissonSearchTest.java +++ b/redisson/src/test/java/org/redisson/RedissonSearchTest.java @@ -78,13 +78,13 @@ public class RedissonSearchTest extends BaseTest { assertThat(r3.getTotal()).isEqualTo(1); assertThat(r3.getCursorId()).isPositive(); - assertThat(new HashSet<>(r3.getAttributes())).isEqualTo(new HashSet<>(Arrays.asList(m2.readAllMap()))); + assertThat(new HashSet<>(r3.getAttributes())).isEqualTo(new HashSet<>(Arrays.asList(m.readAllMap()))); AggregationResult r2 = s.readCursor("idx", r3.getCursorId()); assertThat(r2.getTotal()).isEqualTo(1); assertThat(r2.getCursorId()).isPositive(); - assertThat(new HashSet<>(r2.getAttributes())).isEqualTo(new HashSet<>(Arrays.asList(m.readAllMap()))); + assertThat(new HashSet<>(r2.getAttributes())).isEqualTo(new HashSet<>(Arrays.asList(m2.readAllMap()))); } @@ -128,7 +128,7 @@ public class RedissonSearchTest extends BaseTest { AggregationResult r = s.aggregate("idx", "*", AggregationOptions.defaults() .load("t1", "t2")); - assertThat(r.getTotal()).isEqualTo(2); + assertThat(r.getTotal()).isEqualTo(1); assertThat(r.getCursorId()).isEqualTo(-1); assertThat(new HashSet<>(r.getAttributes())).isEqualTo(new HashSet<>(Arrays.asList(m2.readAllMap(), m.readAllMap()))); } From 1fa4416a375556c16c49a1495e1b7f4e61efffe6 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Sat, 11 Nov 2023 12:09:14 +0300 Subject: [PATCH 11/23] Fixed - BZMPOP command timeout isn't applied --- .../java/org/redisson/client/protocol/RedisCommands.java | 2 +- .../src/main/java/org/redisson/command/RedisExecutor.java | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java b/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java index e628a7916..934883eee 100644 --- a/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -282,7 +282,7 @@ public interface RedisCommands { RedisCommand> BLPOP = new RedisCommand>("BLPOP", new ObjectListReplayDecoder()); RedisCommand> BRPOP = new RedisCommand>("BRPOP", new ObjectListReplayDecoder()); RedisCommand>> BZMPOP = new RedisCommand<>("BZMPOP", ZMPOP.getReplayMultiDecoder()); - RedisCommand> BZMPOP_SINGLE_LIST = new RedisCommand<>("BZMPOP", ZMPOP_VALUES.getReplayMultiDecoder()); + RedisCommand> BZMPOP_SINGLE_LIST = new RedisCommand("BZMPOP", ZMPOP_VALUES.getReplayMultiDecoder(), new EmptyListConvertor()); RedisCommand BLPOP_VALUE = new RedisCommand("BLPOP", new ListObjectDecoder(1)); RedisCommand BLMOVE = new RedisCommand("BLMOVE"); RedisCommand BRPOP_VALUE = new RedisCommand("BRPOP", new ListObjectDecoder(1)); diff --git a/redisson/src/main/java/org/redisson/command/RedisExecutor.java b/redisson/src/main/java/org/redisson/command/RedisExecutor.java index 5d8deb676..0943fa43b 100644 --- a/redisson/src/main/java/org/redisson/command/RedisExecutor.java +++ b/redisson/src/main/java/org/redisson/command/RedisExecutor.java @@ -378,7 +378,11 @@ public class RedisExecutor { } } } else { - popTimeout = Long.valueOf(params[params.length - 1].toString()) * 1000; + if (RedisCommands.BZMPOP.getName().equals(command.getName())) { + popTimeout = Long.valueOf(params[0].toString()) * 1000; + } else { + popTimeout = Long.valueOf(params[params.length - 1].toString()) * 1000; + } } handleBlockingOperations(attemptPromise, connection, popTimeout); From b619cec1bc0439b4aade9f93116cf5cfc57b60fe Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Sat, 11 Nov 2023 13:19:37 +0300 Subject: [PATCH 12/23] refactoring --- redisson/src/main/java/org/redisson/codec/Kryo5Codec.java | 2 +- redisson/src/main/java/org/redisson/codec/ProtobufCodec.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/redisson/src/main/java/org/redisson/codec/Kryo5Codec.java b/redisson/src/main/java/org/redisson/codec/Kryo5Codec.java index f9213325f..1d17402b1 100644 --- a/redisson/src/main/java/org/redisson/codec/Kryo5Codec.java +++ b/redisson/src/main/java/org/redisson/codec/Kryo5Codec.java @@ -51,7 +51,7 @@ import static com.esotericsoftware.kryo.util.Util.className; */ public class Kryo5Codec extends BaseCodec { - private static class SimpleInstantiatorStrategy implements org.objenesis.strategy.InstantiatorStrategy { + private static final class SimpleInstantiatorStrategy implements org.objenesis.strategy.InstantiatorStrategy { private final StdInstantiatorStrategy ss = new StdInstantiatorStrategy(); diff --git a/redisson/src/main/java/org/redisson/codec/ProtobufCodec.java b/redisson/src/main/java/org/redisson/codec/ProtobufCodec.java index e2e583949..b8e6b5014 100644 --- a/redisson/src/main/java/org/redisson/codec/ProtobufCodec.java +++ b/redisson/src/main/java/org/redisson/codec/ProtobufCodec.java @@ -183,7 +183,7 @@ public class ProtobufCodec extends BaseCodec { }; } - private static class ProtostuffUtils { + private static final class ProtostuffUtils { @SuppressWarnings("unchecked") public static byte[] serialize(T obj) { From bae989b6fc2fd0643faf4140248b727511d11699 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Sat, 11 Nov 2023 13:19:55 +0300 Subject: [PATCH 13/23] libs updated --- pom.xml | 4 ++-- redisson-spring-boot-starter/pom.xml | 2 +- redisson/pom.xml | 9 ++++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 531bd0304..98717a17d 100644 --- a/pom.xml +++ b/pom.xml @@ -175,14 +175,14 @@ org.testcontainers testcontainers-bom - 1.18.3 + 1.19.1 pom import io.netty netty-bom - 4.1.100.Final + 4.1.101.Final pom import diff --git a/redisson-spring-boot-starter/pom.xml b/redisson-spring-boot-starter/pom.xml index 213c1bb44..e043385e8 100644 --- a/redisson-spring-boot-starter/pom.xml +++ b/redisson-spring-boot-starter/pom.xml @@ -35,7 +35,7 @@ com.mycila license-maven-plugin - 3.0 + 4.3 ${basedir}
${basedir}/../header.txt
diff --git a/redisson/pom.xml b/redisson/pom.xml index 5d3328046..69288ef5d 100644 --- a/redisson/pom.xml +++ b/redisson/pom.xml @@ -85,7 +85,7 @@ io.projectreactor reactor-core - 3.5.3 + 3.5.11 org.reactivestreams @@ -489,7 +489,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.2.1 + 3.3.1 verify @@ -500,7 +500,6 @@ true - false /checkstyle.xml checkstyle.config.path=${basedir} @@ -508,7 +507,7 @@ com.puppycrawl.tools checkstyle - 10.8.0 + 10.12.4 @@ -599,7 +598,7 @@ com.mycila license-maven-plugin - 2.11 + 4.3 ${basedir}
${basedir}/../header.txt
From 4c783d36c21490ece1da89db7d879f8c0cdbb440 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Mon, 13 Nov 2023 09:10:57 +0300 Subject: [PATCH 14/23] Spring session test deleted --- .../redisson/spring/session/HttpConfig.java | 32 --- .../spring/session/HttpConfigTimeout.java | 21 -- .../spring/session/HttpInitializer.java | 16 -- .../session/RedissonSessionManagerTest.java | 244 ------------------ .../spring/session/SessionEventsListener.java | 40 --- .../redisson/spring/session/TestServlet.java | 96 ------- .../redisson/spring/session/TomcatServer.java | 64 ----- .../redisson/spring/session/WebConfig.java | 30 --- .../spring/session/WebInitializer.java | 14 - 9 files changed, 557 deletions(-) delete mode 100644 redisson/src/test/java/org/redisson/spring/session/HttpConfig.java delete mode 100644 redisson/src/test/java/org/redisson/spring/session/HttpConfigTimeout.java delete mode 100644 redisson/src/test/java/org/redisson/spring/session/HttpInitializer.java delete mode 100644 redisson/src/test/java/org/redisson/spring/session/RedissonSessionManagerTest.java delete mode 100644 redisson/src/test/java/org/redisson/spring/session/SessionEventsListener.java delete mode 100644 redisson/src/test/java/org/redisson/spring/session/TestServlet.java delete mode 100644 redisson/src/test/java/org/redisson/spring/session/TomcatServer.java delete mode 100644 redisson/src/test/java/org/redisson/spring/session/WebConfig.java delete mode 100644 redisson/src/test/java/org/redisson/spring/session/WebInitializer.java diff --git a/redisson/src/test/java/org/redisson/spring/session/HttpConfig.java b/redisson/src/test/java/org/redisson/spring/session/HttpConfig.java deleted file mode 100644 index a9cb33203..000000000 --- a/redisson/src/test/java/org/redisson/spring/session/HttpConfig.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.redisson.spring.session; - -import org.redisson.Redisson; -import org.redisson.api.RedissonClient; -import org.redisson.spring.session.config.EnableRedissonHttpSession; -import org.redisson.spring.session.config.EnableRedissonWebSession; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.web.reactive.DispatcherHandler; -import org.springframework.web.server.WebHandler; -import org.springframework.web.server.adapter.WebHttpHandlerBuilder; - -@EnableRedissonHttpSession -//@EnableRedissonWebSession -public class HttpConfig { - - @Bean - public RedissonClient redisson() { - return Redisson.create(); - } - - @Bean - public SessionEventsListener listener() { - return new SessionEventsListener(); - } - - @Bean(WebHttpHandlerBuilder.WEB_HANDLER_BEAN_NAME) - public WebHandler dispatcherHandler(ApplicationContext context) { - return new DispatcherHandler(context); - } - -} diff --git a/redisson/src/test/java/org/redisson/spring/session/HttpConfigTimeout.java b/redisson/src/test/java/org/redisson/spring/session/HttpConfigTimeout.java deleted file mode 100644 index 0ba3e4cfc..000000000 --- a/redisson/src/test/java/org/redisson/spring/session/HttpConfigTimeout.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.redisson.spring.session; - -import org.redisson.Redisson; -import org.redisson.api.RedissonClient; -import org.redisson.spring.session.config.EnableRedissonHttpSession; -import org.springframework.context.annotation.Bean; - -@EnableRedissonHttpSession(maxInactiveIntervalInSeconds = 5) -public class HttpConfigTimeout { - - @Bean - public RedissonClient redisson() { - return Redisson.create(); - } - - @Bean - public SessionEventsListener listener() { - return new SessionEventsListener(); - } - -} diff --git a/redisson/src/test/java/org/redisson/spring/session/HttpInitializer.java b/redisson/src/test/java/org/redisson/spring/session/HttpInitializer.java deleted file mode 100644 index 885650e4a..000000000 --- a/redisson/src/test/java/org/redisson/spring/session/HttpInitializer.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.redisson.spring.session; - -import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer; - -public class HttpInitializer extends AbstractHttpSessionApplicationInitializer { - - public static Class CONFIG_CLASS = HttpConfig.class; - - public HttpInitializer() { - super(CONFIG_CLASS); - } - - @Override - public void onStartup(jakarta.servlet.ServletContext servletContext) { - } -} diff --git a/redisson/src/test/java/org/redisson/spring/session/RedissonSessionManagerTest.java b/redisson/src/test/java/org/redisson/spring/session/RedissonSessionManagerTest.java deleted file mode 100644 index 9029beea6..000000000 --- a/redisson/src/test/java/org/redisson/spring/session/RedissonSessionManagerTest.java +++ /dev/null @@ -1,244 +0,0 @@ -package org.redisson.spring.session; - -import java.io.IOException; - -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.fluent.Executor; -import org.apache.http.client.fluent.Request; -import org.apache.http.cookie.Cookie; -import org.apache.http.impl.client.BasicCookieStore; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.redisson.RedisRunner; -import org.redisson.RedisRunner.KEYSPACE_EVENTS_OPTIONS; -import org.redisson.RedissonRuntimeEnvironment; -import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.context.support.WebApplicationContextUtils; - -@Deprecated -public class RedissonSessionManagerTest { - - private static RedisRunner.RedisProcess defaultRedisInstance; - - @AfterAll - public static void afterClass() throws IOException, InterruptedException { - defaultRedisInstance.stop(); - } - - @BeforeAll - public static void beforeClass() throws IOException, InterruptedException { - if (!RedissonRuntimeEnvironment.isTravis) { - defaultRedisInstance = new RedisRunner() - .nosave() - .port(6379) - .randomDir() - .notifyKeyspaceEvents(KEYSPACE_EVENTS_OPTIONS.E, - KEYSPACE_EVENTS_OPTIONS.x, - KEYSPACE_EVENTS_OPTIONS.g) - .run(); - - } - } - - @Test - public void testSwitchServer() throws Exception { - // start the server at http://localhost:8080/myapp - TomcatServer server = new TomcatServer("myapp", 8080, "src/test/"); - server.start(); - - Executor executor = Executor.newInstance(); - BasicCookieStore cookieStore = new BasicCookieStore(); - executor.use(cookieStore); - - write(executor, "test", "1234"); - Cookie cookie = cookieStore.getCookies().get(0); - - Executor.closeIdleConnections(); - server.stop(); - - server = new TomcatServer("myapp", 8080, "src/test/"); - server.start(); - - executor = Executor.newInstance(); - cookieStore = new BasicCookieStore(); - cookieStore.addCookie(cookie); - executor.use(cookieStore); - read(executor, "test", "1234"); - remove(executor, "test", "null"); - - Executor.closeIdleConnections(); - server.stop(); - } - - - @Test - public void testWriteReadRemove() throws Exception { - // start the server at http://localhost:8080/myapp - TomcatServer server = new TomcatServer("myapp", 8080, "src/test/"); - server.start(); - - Executor executor = Executor.newInstance(); - - write(executor, "test", "1234"); - read(executor, "test", "1234"); - remove(executor, "test", "null"); - - Executor.closeIdleConnections(); - server.stop(); - } - - @Test - public void testRecreate() throws Exception { - // start the server at http://localhost:8080/myapp - TomcatServer server = new TomcatServer("myapp", 8080, "src/test/"); - server.start(); - - Executor executor = Executor.newInstance(); - - write(executor, "test", "1"); - recreate(executor, "test", "2"); - read(executor, "test", "2"); - - Executor.closeIdleConnections(); - server.stop(); - } - - @Test - public void testUpdate() throws Exception { - // start the server at http://localhost:8080/myapp - TomcatServer server = new TomcatServer("myapp", 8080, "src/test/"); - server.start(); - - Executor executor = Executor.newInstance(); - - write(executor, "test", "1"); - read(executor, "test", "1"); - write(executor, "test", "2"); - read(executor, "test", "2"); - - Executor.closeIdleConnections(); - server.stop(); - } - - @Test - public void testExpire() throws Exception { - HttpInitializer.CONFIG_CLASS = HttpConfigTimeout.class; - // start the server at http://localhost:8080/myapp - TomcatServer server = new TomcatServer("myapp", 8080, "src/test/"); - server.start(); - WebApplicationContext wa = WebApplicationContextUtils.getRequiredWebApplicationContext(server.getServletContext()); - SessionEventsListener listener = wa.getBean(SessionEventsListener.class); - - Executor executor = Executor.newInstance(); - BasicCookieStore cookieStore = new BasicCookieStore(); - executor.use(cookieStore); - - write(executor, "test", "1234"); - Cookie cookie = cookieStore.getCookies().get(0); - - Thread.sleep(50); - - Assertions.assertEquals(1, listener.getSessionCreatedEvents()); - Assertions.assertEquals(0, listener.getSessionExpiredEvents()); - - Executor.closeIdleConnections(); - - Thread.sleep(6000); - - Assertions.assertEquals(1, listener.getSessionCreatedEvents()); - Assertions.assertEquals(1, listener.getSessionExpiredEvents()); - - executor = Executor.newInstance(); - cookieStore = new BasicCookieStore(); - cookieStore.addCookie(cookie); - executor.use(cookieStore); - read(executor, "test", "null"); - - Thread.sleep(50); - - Assertions.assertEquals(2, listener.getSessionCreatedEvents()); - - write(executor, "test", "1234"); - Thread.sleep(3000); - read(executor, "test", "1234"); - Thread.sleep(3000); - Assertions.assertEquals(1, listener.getSessionExpiredEvents()); - Thread.sleep(1000); - Assertions.assertEquals(1, listener.getSessionExpiredEvents()); - Thread.sleep(3000); - Assertions.assertEquals(2, listener.getSessionExpiredEvents()); - - Executor.closeIdleConnections(); - server.stop(); - } - - @Test - public void testInvalidate() throws Exception { - // start the server at http://localhost:8080/myapp - TomcatServer server = new TomcatServer("myapp", 8080, "src/test/"); - server.start(); - WebApplicationContext wa = WebApplicationContextUtils.getRequiredWebApplicationContext(server.getServletContext()); - SessionEventsListener listener = wa.getBean(SessionEventsListener.class); - - Executor executor = Executor.newInstance(); - BasicCookieStore cookieStore = new BasicCookieStore(); - executor.use(cookieStore); - - write(executor, "test", "1234"); - Cookie cookie = cookieStore.getCookies().get(0); - - Thread.sleep(50); - - Assertions.assertEquals(1, listener.getSessionCreatedEvents()); - Assertions.assertEquals(0, listener.getSessionDeletedEvents()); - - invalidate(executor); - - Assertions.assertEquals(1, listener.getSessionCreatedEvents()); - Assertions.assertEquals(1, listener.getSessionDeletedEvents()); - - Executor.closeIdleConnections(); - - executor = Executor.newInstance(); - cookieStore = new BasicCookieStore(); - cookieStore.addCookie(cookie); - executor.use(cookieStore); - read(executor, "test", "null"); - - Executor.closeIdleConnections(); - server.stop(); - } - - private void write(Executor executor, String key, String value) throws IOException, ClientProtocolException { - String url = "http://localhost:8080/myapp/write?key=" + key + "&value=" + value; - String response = executor.execute(Request.Get(url)).returnContent().asString(); - Assertions.assertEquals("OK", response); - } - - private void read(Executor executor, String key, String value) throws IOException, ClientProtocolException { - String url = "http://localhost:8080/myapp/read?key=" + key; - String response = executor.execute(Request.Get(url)).returnContent().asString(); - Assertions.assertEquals(value, response); - } - - private void remove(Executor executor, String key, String value) throws IOException, ClientProtocolException { - String url = "http://localhost:8080/myapp/remove?key=" + key; - String response = executor.execute(Request.Get(url)).returnContent().asString(); - Assertions.assertEquals(value, response); - } - - private void invalidate(Executor executor) throws IOException, ClientProtocolException { - String url = "http://localhost:8080/myapp/invalidate"; - String response = executor.execute(Request.Get(url)).returnContent().asString(); - Assertions.assertEquals("OK", response); - } - - private void recreate(Executor executor, String key, String value) throws IOException, ClientProtocolException { - String url = "http://localhost:8080/myapp/recreate?key=" + key + "&value=" + value; - String response = executor.execute(Request.Get(url)).returnContent().asString(); - Assertions.assertEquals("OK", response); - } - -} diff --git a/redisson/src/test/java/org/redisson/spring/session/SessionEventsListener.java b/redisson/src/test/java/org/redisson/spring/session/SessionEventsListener.java deleted file mode 100644 index 549b7b89f..000000000 --- a/redisson/src/test/java/org/redisson/spring/session/SessionEventsListener.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.redisson.spring.session; - -import org.springframework.context.ApplicationListener; -import org.springframework.session.events.AbstractSessionEvent; -import org.springframework.session.events.SessionCreatedEvent; -import org.springframework.session.events.SessionDeletedEvent; -import org.springframework.session.events.SessionExpiredEvent; - -public class SessionEventsListener implements ApplicationListener { - - private int sessionCreatedEvents; - private int sessionDeletedEvents; - private int sessionExpiredEvents; - - @Override - public void onApplicationEvent(AbstractSessionEvent event) { - if (event instanceof SessionCreatedEvent) { - sessionCreatedEvents++; - } - if (event instanceof SessionDeletedEvent) { - sessionDeletedEvents++; - } - if (event instanceof SessionExpiredEvent) { - sessionExpiredEvents++; - } - } - - public int getSessionCreatedEvents() { - return sessionCreatedEvents; - } - - public int getSessionDeletedEvents() { - return sessionDeletedEvents; - } - - public int getSessionExpiredEvents() { - return sessionExpiredEvents; - } - -} diff --git a/redisson/src/test/java/org/redisson/spring/session/TestServlet.java b/redisson/src/test/java/org/redisson/spring/session/TestServlet.java deleted file mode 100644 index 81478e336..000000000 --- a/redisson/src/test/java/org/redisson/spring/session/TestServlet.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.redisson.spring.session; - -import jakarta.servlet.ServletException; -import jakarta.servlet.annotation.WebServlet; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.servlet.http.HttpSession; - -import java.io.IOException; - -@WebServlet(name = "/testServlet", urlPatterns = "/*") -public class TestServlet extends HttpServlet { - - private static final long serialVersionUID = 1243830648280853203L; - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - HttpSession session = req.getSession(); - - if (req.getPathInfo().equals("/write")) { - String[] params = req.getQueryString().split("&"); - String key = null; - String value = null; - for (String param : params) { - String[] paramLine = param.split("="); - String keyParam = paramLine[0]; - String valueParam = paramLine[1]; - - if ("key".equals(keyParam)) { - key = valueParam; - } - if ("value".equals(keyParam)) { - value = valueParam; - } - } - session.setAttribute(key, value); - - resp.getWriter().print("OK"); - } else if (req.getPathInfo().equals("/read")) { - String[] params = req.getQueryString().split("&"); - String key = null; - for (String param : params) { - String[] line = param.split("="); - String keyParam = line[0]; - if ("key".equals(keyParam)) { - key = line[1]; - } - } - - Object attr = session.getAttribute(key); - resp.getWriter().print(attr); - } else if (req.getPathInfo().equals("/remove")) { - String[] params = req.getQueryString().split("&"); - String key = null; - for (String param : params) { - String[] line = param.split("="); - String keyParam = line[0]; - if ("key".equals(keyParam)) { - key = line[1]; - } - } - - session.removeAttribute(key); - resp.getWriter().print(String.valueOf(session.getAttribute(key))); - } else if (req.getPathInfo().equals("/invalidate")) { - session.invalidate(); - - resp.getWriter().print("OK"); - } else if (req.getPathInfo().equals("/recreate")) { - session.invalidate(); - - session = req.getSession(); - - String[] params = req.getQueryString().split("&"); - String key = null; - String value = null; - for (String param : params) { - String[] paramLine = param.split("="); - String keyParam = paramLine[0]; - String valueParam = paramLine[1]; - - if ("key".equals(keyParam)) { - key = valueParam; - } - if ("value".equals(keyParam)) { - value = valueParam; - } - } - session.setAttribute(key, value); - - resp.getWriter().print("OK"); - } - } - -} diff --git a/redisson/src/test/java/org/redisson/spring/session/TomcatServer.java b/redisson/src/test/java/org/redisson/spring/session/TomcatServer.java deleted file mode 100644 index 0dfca1864..000000000 --- a/redisson/src/test/java/org/redisson/spring/session/TomcatServer.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.redisson.spring.session; - -import java.io.File; -import java.net.MalformedURLException; - -import jakarta.servlet.ServletContext; -import jakarta.servlet.ServletException; -import org.apache.catalina.LifecycleException; -import org.apache.catalina.core.StandardContext; -import org.apache.catalina.startup.Tomcat; -import org.apache.catalina.webresources.DirResourceSet; -import org.apache.catalina.webresources.StandardRoot; - -public class TomcatServer { - - private Tomcat tomcat = new Tomcat(); - private StandardContext ctx; - - public TomcatServer(String contextPath, int port, String appBase) throws MalformedURLException, ServletException { - if(contextPath == null || appBase == null || appBase.length() == 0) { - throw new IllegalArgumentException("Context path or appbase should not be null"); - } - if(!contextPath.startsWith("/")) { - contextPath = "/" + contextPath; - } - - tomcat.setBaseDir("."); // location where temp dir is created - tomcat.setPort(port); - tomcat.getHost().setAppBase("."); - - ctx = (StandardContext) tomcat.addWebapp(contextPath, appBase); - ctx.setDelegate(true); - - File additionWebInfClasses = new File("target/test-classes"); - StandardRoot resources = new StandardRoot(); - DirResourceSet webResourceSet = new DirResourceSet(); - webResourceSet.setBase(additionWebInfClasses.toString()); - webResourceSet.setWebAppMount("/WEB-INF/classes"); - resources.addPostResources(webResourceSet); - ctx.setResources(resources); - } - - /** - * Start the tomcat embedded server - */ - public void start() throws LifecycleException { - tomcat.start(); - } - - /** - * Stop the tomcat embedded server - */ - public void stop() throws LifecycleException { - tomcat.stop(); - tomcat.destroy(); - tomcat.getServer().await(); - } - - public ServletContext getServletContext() { - return ctx.getServletContext(); - } - - -} \ No newline at end of file diff --git a/redisson/src/test/java/org/redisson/spring/session/WebConfig.java b/redisson/src/test/java/org/redisson/spring/session/WebConfig.java deleted file mode 100644 index 2ff1172ee..000000000 --- a/redisson/src/test/java/org/redisson/spring/session/WebConfig.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.redisson.spring.session; - -import org.redisson.Redisson; -import org.redisson.api.RedissonClient; -import org.redisson.spring.session.config.EnableRedissonWebSession; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.web.reactive.DispatcherHandler; -import org.springframework.web.server.WebHandler; -import org.springframework.web.server.adapter.WebHttpHandlerBuilder; - -@EnableRedissonWebSession -public class WebConfig { - - @Bean - public RedissonClient redisson() { - return Redisson.create(); - } - - @Bean - public SessionEventsListener listener() { - return new SessionEventsListener(); - } - - @Bean(WebHttpHandlerBuilder.WEB_HANDLER_BEAN_NAME) - public WebHandler dispatcherHandler(ApplicationContext context) { - return new DispatcherHandler(context); - } - -} diff --git a/redisson/src/test/java/org/redisson/spring/session/WebInitializer.java b/redisson/src/test/java/org/redisson/spring/session/WebInitializer.java deleted file mode 100644 index e374502d4..000000000 --- a/redisson/src/test/java/org/redisson/spring/session/WebInitializer.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.redisson.spring.session; - -import org.springframework.web.server.adapter.AbstractReactiveWebInitializer; - -public class WebInitializer extends AbstractReactiveWebInitializer { - - public static Class CONFIG_CLASS = HttpConfig.class; - - @Override - protected Class[] getConfigClasses() { - return new Class[] {CONFIG_CLASS}; - } - -} From ae9150d0232b47ecc008730313215fab38c46bd0 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Mon, 13 Nov 2023 09:28:38 +0300 Subject: [PATCH 15/23] JSON and Search module tests fixed --- .../test/java/org/redisson/BaseStackTest.java | 41 +++++++++++++++++++ .../org/redisson/RedissonJsonBucketTest.java | 2 +- .../java/org/redisson/RedissonSearchTest.java | 2 +- 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 redisson/src/test/java/org/redisson/BaseStackTest.java diff --git a/redisson/src/test/java/org/redisson/BaseStackTest.java b/redisson/src/test/java/org/redisson/BaseStackTest.java new file mode 100644 index 000000000..c0f713c09 --- /dev/null +++ b/redisson/src/test/java/org/redisson/BaseStackTest.java @@ -0,0 +1,41 @@ +package org.redisson; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class BaseStackTest { + + + @Container + private static final GenericContainer REDIS = + new GenericContainer<>("redis/redis-stack") + .withExposedPorts(6379); + + protected static RedissonClient redisson; + + @BeforeAll + public static void beforeAll() { + Config config = new Config(); + config.useSingleServer() + .setAddress("redis://127.0.0.1:" + REDIS.getFirstMappedPort()); + redisson = Redisson.create(config); + } + + @BeforeEach + public void beforeEach() { + redisson.getKeys().flushall(); + } + + @AfterAll + public static void afterAll() { + redisson.shutdown(); + } + +} diff --git a/redisson/src/test/java/org/redisson/RedissonJsonBucketTest.java b/redisson/src/test/java/org/redisson/RedissonJsonBucketTest.java index ba418eb5c..b4d6aa338 100644 --- a/redisson/src/test/java/org/redisson/RedissonJsonBucketTest.java +++ b/redisson/src/test/java/org/redisson/RedissonJsonBucketTest.java @@ -13,7 +13,7 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; -public class RedissonJsonBucketTest extends BaseTest { +public class RedissonJsonBucketTest extends BaseStackTest { public static class NestedType { diff --git a/redisson/src/test/java/org/redisson/RedissonSearchTest.java b/redisson/src/test/java/org/redisson/RedissonSearchTest.java index b6a55dfc2..d4455e42c 100644 --- a/redisson/src/test/java/org/redisson/RedissonSearchTest.java +++ b/redisson/src/test/java/org/redisson/RedissonSearchTest.java @@ -22,7 +22,7 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; -public class RedissonSearchTest extends BaseTest { +public class RedissonSearchTest extends BaseStackTest { public static class SimpleObject { From 015205b30b5df7a178353d1ee7a573bb6b9bd3df Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Mon, 13 Nov 2023 09:32:24 +0300 Subject: [PATCH 16/23] refactoring --- redisson/src/test/java/org/redisson/BaseStackTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/redisson/src/test/java/org/redisson/BaseStackTest.java b/redisson/src/test/java/org/redisson/BaseStackTest.java index c0f713c09..9c19c08ea 100644 --- a/redisson/src/test/java/org/redisson/BaseStackTest.java +++ b/redisson/src/test/java/org/redisson/BaseStackTest.java @@ -22,10 +22,15 @@ public class BaseStackTest { @BeforeAll public static void beforeAll() { + Config config = createConfig(); + redisson = Redisson.create(config); + } + + protected static Config createConfig() { Config config = new Config(); config.useSingleServer() .setAddress("redis://127.0.0.1:" + REDIS.getFirstMappedPort()); - redisson = Redisson.create(config); + return config; } @BeforeEach From eee947d12b2a12a4b95d8699fa0fa140f0220e7c Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Mon, 13 Nov 2023 09:43:45 +0300 Subject: [PATCH 17/23] test fixed --- .../src/test/java/org/redisson/RedissonSearchTest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/redisson/src/test/java/org/redisson/RedissonSearchTest.java b/redisson/src/test/java/org/redisson/RedissonSearchTest.java index d4455e42c..3a22f1d29 100644 --- a/redisson/src/test/java/org/redisson/RedissonSearchTest.java +++ b/redisson/src/test/java/org/redisson/RedissonSearchTest.java @@ -52,10 +52,10 @@ public class RedissonSearchTest extends BaseStackTest { @Test public void testMapAggregateWithCursor() { - RMap m = redisson.getMap("doc:1", new CompositeCodec(StringCodec.INSTANCE, redisson.getConfig().getCodec())); + RMap m = redisson.getMap("doc:1", new CompositeCodec(StringCodec.INSTANCE, redisson.getConfig().getCodec())); m.put("t1", new SimpleObject("name1")); m.put("t2", new SimpleObject("name2")); - RMap m2 = redisson.getMap("doc:2", new CompositeCodec(StringCodec.INSTANCE, redisson.getConfig().getCodec())); + RMap m2 = redisson.getMap("doc:2", new CompositeCodec(StringCodec.INSTANCE, redisson.getConfig().getCodec())); m2.put("t1", new SimpleObject("name3")); m2.put("t2", new SimpleObject("name4")); @@ -78,13 +78,14 @@ public class RedissonSearchTest extends BaseStackTest { assertThat(r3.getTotal()).isEqualTo(1); assertThat(r3.getCursorId()).isPositive(); - assertThat(new HashSet<>(r3.getAttributes())).isEqualTo(new HashSet<>(Arrays.asList(m.readAllMap()))); + assertThat(r3.getAttributes()).hasSize(1).isSubsetOf(m.readAllMap(), m2.readAllMap()); AggregationResult r2 = s.readCursor("idx", r3.getCursorId()); assertThat(r2.getTotal()).isEqualTo(1); assertThat(r2.getCursorId()).isPositive(); - assertThat(new HashSet<>(r2.getAttributes())).isEqualTo(new HashSet<>(Arrays.asList(m2.readAllMap()))); + assertThat(r3.getAttributes()).isNotEqualTo(r2.getAttributes()); + assertThat(r2.getAttributes()).hasSize(1).isSubsetOf(m.readAllMap(), m2.readAllMap()); } From d1c5448fa2a729f956be165baba17e143dc2cdae Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Mon, 13 Nov 2023 09:45:46 +0300 Subject: [PATCH 18/23] test fixed --- .../redisson/RedissonScoredSortedSetTest.java | 40 +------------------ 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index 810089d8f..faaadb984 100644 --- a/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -25,7 +25,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.junit.jupiter.api.Assertions.assertTrue; -public class RedissonScoredSortedSetTest extends BaseTest { +public class RedissonScoredSortedSetTest extends BaseStackTest { @Test public void testEntries() { @@ -127,8 +127,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testRandom() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("6.2.0") > 0); - RScoredSortedSet set = redisson.getScoredSortedSet("test"); set.add(1, 10); set.add(2, 20); @@ -143,8 +141,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testTakeFirst() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("5.0.0") > 0); - final RScoredSortedSet queue1 = redisson.getScoredSortedSet("queue:pollany"); Executors.newSingleThreadScheduledExecutor().schedule(() -> { RScoredSortedSet queue2 = redisson.getScoredSortedSet("queue:pollany1"); @@ -160,8 +156,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testPollFirstFromAny() throws InterruptedException { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("5.0.0") > 0); - final RScoredSortedSet queue1 = redisson.getScoredSortedSet("queue:pollany"); Executors.newSingleThreadScheduledExecutor().schedule(() -> { RScoredSortedSet queue2 = redisson.getScoredSortedSet("queue:pollany1"); @@ -180,8 +174,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testPollFirstFromAnyCount() { -// Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("7.0.0") > 0); - RScoredSortedSet queue1 = redisson.getScoredSortedSet("queue:pollany"); RScoredSortedSet queue2 = redisson.getScoredSortedSet("queue:pollany1"); RScoredSortedSet queue3 = redisson.getScoredSortedSet("queue:pollany2"); @@ -206,8 +198,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testPollFirstEntriesFromAnyCount() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("7.0.0") > 0); - RScoredSortedSet queue1 = redisson.getScoredSortedSet("queue:pollany"); RScoredSortedSet queue2 = redisson.getScoredSortedSet("queue:pollany1"); RScoredSortedSet queue3 = redisson.getScoredSortedSet("queue:pollany2"); @@ -234,8 +224,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testPollLastEntriesFromAnyCount() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("7.0.0") > 0); - RScoredSortedSet queue1 = redisson.getScoredSortedSet("queue:pollany"); RScoredSortedSet queue2 = redisson.getScoredSortedSet("queue:pollany1"); RScoredSortedSet queue3 = redisson.getScoredSortedSet("queue:pollany2"); @@ -262,8 +250,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testPollFirstEntriesFromAnyTimeout() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("7.0.0") > 0); - RScoredSortedSet queue1 = redisson.getScoredSortedSet("queue:pollany"); RScoredSortedSet queue2 = redisson.getScoredSortedSet("queue:pollany1"); RScoredSortedSet queue3 = redisson.getScoredSortedSet("queue:pollany2"); @@ -290,8 +276,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testPollLastEntriesFromAnyTimeout() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("7.0.0") > 0); - RScoredSortedSet queue1 = redisson.getScoredSortedSet("queue:pollany"); RScoredSortedSet queue2 = redisson.getScoredSortedSet("queue:pollany1"); RScoredSortedSet queue3 = redisson.getScoredSortedSet("queue:pollany2"); @@ -318,8 +302,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testPollLastFromAnyCount() { -// Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("7.0.0") > 0); - RScoredSortedSet queue1 = redisson.getScoredSortedSet("queue:pollany"); RScoredSortedSet queue2 = redisson.getScoredSortedSet("queue:pollany1"); RScoredSortedSet queue3 = redisson.getScoredSortedSet("queue:pollany2"); @@ -344,8 +326,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testPollLastFromAny() throws InterruptedException { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("5.0.0") > 0); - final RScoredSortedSet queue1 = redisson.getScoredSortedSet("queue:pollany"); Executors.newSingleThreadScheduledExecutor().schedule(() -> { RScoredSortedSet queue2 = redisson.getScoredSortedSet("queue:pollany1"); @@ -824,8 +804,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testPollLastTimeout() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("5.0.0") > 0); - RScoredSortedSet set = redisson.getScoredSortedSet("simple"); assertThat(set.pollLast(1, TimeUnit.SECONDS)).isNull(); @@ -839,8 +817,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testPollFirstTimeout() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("5.0.0") > 0); - RScoredSortedSet set = redisson.getScoredSortedSet("simple"); assertThat(set.pollFirst(1, TimeUnit.SECONDS)).isNull(); @@ -854,8 +830,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testPollFirstTimeoutCount() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("7.0.0") > 0); - RScoredSortedSet set = redisson.getScoredSortedSet("simple"); assertThat(set.pollFirst(1, TimeUnit.SECONDS)).isNull(); @@ -875,8 +849,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testPollLastTimeoutCount() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("7.0.0") > 0); - RScoredSortedSet set = redisson.getScoredSortedSet("simple"); assertThat(set.pollFirst(1, TimeUnit.SECONDS)).isNull(); @@ -1730,8 +1702,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testReadIntersection() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("6.2.0") > 0); - RScoredSortedSet set1 = redisson.getScoredSortedSet("simple1"); set1.add(1, "one"); set1.add(2, "two"); @@ -1837,8 +1807,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testRangeTo() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("6.2.0") > 0); - RScoredSortedSet set1 = redisson.getScoredSortedSet("simple1"); for (int i = 0; i < 10; i++) { set1.add(i, i); @@ -1852,8 +1820,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testRevRange() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("6.2.0") > 0); - RScoredSortedSet set1 = redisson.getScoredSortedSet("simple1"); for (int i = 0; i < 10; i++) { set1.add(i, i); @@ -1867,8 +1833,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testRangeToScored() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("6.2.0") > 0); - RScoredSortedSet set1 = redisson.getScoredSortedSet("simple1"); for (int i = 0; i < 10; i++) { set1.add(i, i); @@ -1882,8 +1846,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { @Test public void testReadUnion() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("6.2.0") > 0); - RScoredSortedSet set1 = redisson.getScoredSortedSet("simple1"); set1.add(1, "one"); set1.add(2, "two"); From 2ca874f949e668c67acea4d4d1374db32b4e250e Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Mon, 13 Nov 2023 10:16:54 +0300 Subject: [PATCH 19/23] test fixed --- .../test/java/org/redisson/RedissonGeoTest.java | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/redisson/src/test/java/org/redisson/RedissonGeoTest.java b/redisson/src/test/java/org/redisson/RedissonGeoTest.java index b80414ae7..7eeb2e975 100644 --- a/redisson/src/test/java/org/redisson/RedissonGeoTest.java +++ b/redisson/src/test/java/org/redisson/RedissonGeoTest.java @@ -13,7 +13,7 @@ import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -public class RedissonGeoTest extends BaseTest { +public class RedissonGeoTest extends BaseStackTest { @Test public void testAdd() { @@ -37,8 +37,6 @@ public class RedissonGeoTest extends BaseTest { @Test public void testTryAdd() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("6.2.0") > 0); - RGeo geo = redisson.getGeo("test"); assertThat(geo.add(2.51, 3.12, "city1")).isEqualTo(1); assertThat(geo.tryAdd(2.5, 3.1, "city1")).isFalse(); @@ -143,8 +141,6 @@ public class RedissonGeoTest extends BaseTest { @Test public void testBox() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("6.2.0") > 0); - RGeo geo = redisson.getGeo("test"); geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), new GeoEntry(15.087269, 37.502669, "Catania")); @@ -156,8 +152,6 @@ public class RedissonGeoTest extends BaseTest { @Test public void testBoxWithDistance() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("6.2.0") > 0); - RGeo geo = redisson.getGeo("test"); geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), new GeoEntry(15.087269, 37.502669, "Catania")); @@ -172,8 +166,6 @@ public class RedissonGeoTest extends BaseTest { @Test public void testBoxWithPosition() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("6.2.0") > 0); - RGeo geo = redisson.getGeo("test"); geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), new GeoEntry(15.087269, 37.502669, "Catania")); @@ -188,8 +180,6 @@ public class RedissonGeoTest extends BaseTest { @Test public void testBoxStoreSearch() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("6.2.0") > 0); - RGeo geoSource = redisson.getGeo("test"); RGeo geoDest = redisson.getGeo("test-store"); geoSource.add(new GeoEntry(13.361389, 38.115556, "Palermo"), new GeoEntry(15.087269, 37.502669, "Catania")); @@ -202,8 +192,6 @@ public class RedissonGeoTest extends BaseTest { @Test public void testBoxStoreSorted() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("6.2.0") > 0); - RGeo geoSource = redisson.getGeo("test"); RGeo geoDest = redisson.getGeo("test-store"); geoSource.add(new GeoEntry(13.361389, 38.115556, "Palermo"), new GeoEntry(15.087269, 37.502669, "Catania")); From dfafb6740e10dd6b95fb43f8b096b0ec4a565130 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Mon, 13 Nov 2023 10:41:28 +0300 Subject: [PATCH 20/23] refactoring --- ...ackTest.java => DockerRedisStackTest.java} | 3 +- .../java/org/redisson/RedisDockerTest.java | 85 +++++++++++++++ .../org/redisson/RedissonFunctionTest.java | 102 +++++++----------- .../java/org/redisson/RedissonGeoTest.java | 5 +- .../org/redisson/RedissonJsonBucketTest.java | 2 +- .../redisson/RedissonScoredSortedSetTest.java | 3 +- .../java/org/redisson/RedissonSearchTest.java | 2 +- 7 files changed, 126 insertions(+), 76 deletions(-) rename redisson/src/test/java/org/redisson/{BaseStackTest.java => DockerRedisStackTest.java} (97%) create mode 100644 redisson/src/test/java/org/redisson/RedisDockerTest.java diff --git a/redisson/src/test/java/org/redisson/BaseStackTest.java b/redisson/src/test/java/org/redisson/DockerRedisStackTest.java similarity index 97% rename from redisson/src/test/java/org/redisson/BaseStackTest.java rename to redisson/src/test/java/org/redisson/DockerRedisStackTest.java index 9c19c08ea..57695da67 100644 --- a/redisson/src/test/java/org/redisson/BaseStackTest.java +++ b/redisson/src/test/java/org/redisson/DockerRedisStackTest.java @@ -10,8 +10,7 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @Testcontainers -public class BaseStackTest { - +public class DockerRedisStackTest { @Container private static final GenericContainer REDIS = diff --git a/redisson/src/test/java/org/redisson/RedisDockerTest.java b/redisson/src/test/java/org/redisson/RedisDockerTest.java new file mode 100644 index 000000000..ca500d30b --- /dev/null +++ b/redisson/src/test/java/org/redisson/RedisDockerTest.java @@ -0,0 +1,85 @@ +package org.redisson; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.redisson.api.*; +import org.redisson.config.Config; +import org.redisson.config.Protocol; +import org.redisson.misc.RedisURI; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.startupcheck.MinimumDurationRunningStartupCheckStrategy; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.time.Duration; +import java.util.function.Consumer; + +import static org.assertj.core.api.Assertions.assertThat; + +@Testcontainers +public class RedisDockerTest { + + @Container + private static final GenericContainer REDIS = + new GenericContainer<>("redis:7.2") + .withExposedPorts(6379); + + protected static RedissonClient redisson; + + @BeforeAll + public static void beforeAll() { + Config config = createConfig(); + redisson = Redisson.create(config); + } + + protected static Config createConfig() { + Config config = new Config(); + config.setProtocol(Protocol.RESP3); + config.useSingleServer() + .setAddress("redis://127.0.0.1:" + REDIS.getFirstMappedPort()); + return config; + } + + protected void testInCluster(Consumer redissonCallback) { + GenericContainer redisClusterContainer = + new GenericContainer<>("vishnunair/docker-redis-cluster") + .withExposedPorts(6379, 6380, 6381, 6382, 6383, 6384) + .withStartupCheckStrategy(new MinimumDurationRunningStartupCheckStrategy(Duration.ofSeconds(7))); + redisClusterContainer.start(); + + Config config = new Config(); + config.setProtocol(Protocol.RESP3); + config.useClusterServers() + .setNatMapper(new NatMapper() { + @Override + public RedisURI map(RedisURI uri) { + if (redisClusterContainer.getMappedPort(uri.getPort()) == null) { + return uri; + } + return new RedisURI(uri.getScheme(), redisClusterContainer.getHost(), redisClusterContainer.getMappedPort(uri.getPort())); + } + }) + .addNodeAddress("redis://127.0.0.1:" + redisClusterContainer.getFirstMappedPort()); + RedissonClient redisson = Redisson.create(config); + + try { + redissonCallback.accept(redisson); + } finally { + redisson.shutdown(); + redisClusterContainer.stop(); + } + + } + + @BeforeEach + public void beforeEach() { + redisson.getKeys().flushall(); + } + + @AfterAll + public static void afterAll() { + redisson.shutdown(); + } + +} diff --git a/redisson/src/test/java/org/redisson/RedissonFunctionTest.java b/redisson/src/test/java/org/redisson/RedissonFunctionTest.java index 2e9a912a2..6a09060e9 100644 --- a/redisson/src/test/java/org/redisson/RedissonFunctionTest.java +++ b/redisson/src/test/java/org/redisson/RedissonFunctionTest.java @@ -1,28 +1,15 @@ package org.redisson; -import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.redisson.api.*; import org.redisson.client.codec.LongCodec; import org.redisson.client.codec.StringCodec; -import org.redisson.config.Config; -import org.redisson.connection.balancer.RandomLoadBalancer; -import org.redisson.misc.RedisURI; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.startupcheck.MinimumDurationRunningStartupCheckStrategy; -import java.time.Duration; import java.util.*; import static org.assertj.core.api.Assertions.assertThat; -public class RedissonFunctionTest extends BaseTest { - - @BeforeAll - public static void check() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("7.0.0") > 0); - } +public class RedissonFunctionTest extends RedisDockerTest { @Test public void testEmpty() { @@ -54,58 +41,41 @@ public class RedissonFunctionTest extends BaseTest { } @Test - public void testCluster() throws InterruptedException { - GenericContainer redisClusterContainer = - new GenericContainer<>("vishnunair/docker-redis-cluster") - .withExposedPorts(6379, 6380, 6381, 6382, 6383, 6384) - .withStartupCheckStrategy(new MinimumDurationRunningStartupCheckStrategy(Duration.ofSeconds(7))); - redisClusterContainer.start(); - - Config config = new Config(); - config.useClusterServers() - .setNatMapper(new NatMapper() { - @Override - public RedisURI map(RedisURI uri) { - if (redisClusterContainer.getMappedPort(uri.getPort()) == null) { - return uri; - } - return new RedisURI(uri.getScheme(), redisClusterContainer.getHost(), redisClusterContainer.getMappedPort(uri.getPort())); - } - }) - .addNodeAddress("redis://127.0.0.1:" + redisClusterContainer.getFirstMappedPort()); - RedissonClient redisson = Redisson.create(config); - - Map testMap = new HashMap<>(); - testMap.put("a", "b"); - testMap.put("c", "d"); - testMap.put("e", "f"); - testMap.put("g", "h"); - testMap.put("i", "j"); - testMap.put("k", "l"); - - RFunction f = redisson.getFunction(); - f.flush(); - f.load("lib", "redis.register_function('myfun', function(keys, args) return args[1] end)"); - - // waiting for the function replication to all nodes - Thread.sleep(5000); - - RBatch batch = redisson.createBatch(); - RFunctionAsync function = batch.getFunction(); - for (Map.Entry property : testMap.entrySet()) { - List key = Collections.singletonList(property.getKey()); - function.callAsync( - FunctionMode.READ, - "myfun", - FunctionResult.VALUE, - key, - property.getValue()); - } - List results = (List) batch.execute().getResponses(); - assertThat(results).containsExactly("b", "d", "f", "h", "j", "l"); - - redisson.shutdown(); - redisClusterContainer.stop(); + public void testCluster() { + testInCluster(r -> { + Map testMap = new HashMap<>(); + testMap.put("a", "b"); + testMap.put("c", "d"); + testMap.put("e", "f"); + testMap.put("g", "h"); + testMap.put("i", "j"); + testMap.put("k", "l"); + + RFunction f = redisson.getFunction(); + f.flush(); + f.load("lib", "redis.register_function('myfun', function(keys, args) return args[1] end)"); + + // waiting for the function replication to all nodes + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + RBatch batch = redisson.createBatch(); + RFunctionAsync function = batch.getFunction(); + for (Map.Entry property : testMap.entrySet()) { + List key = Collections.singletonList(property.getKey()); + function.callAsync( + FunctionMode.READ, + "myfun", + FunctionResult.VALUE, + key, + property.getValue()); + } + List results = (List) batch.execute().getResponses(); + assertThat(results).containsExactly("b", "d", "f", "h", "j", "l"); + }); } @Test diff --git a/redisson/src/test/java/org/redisson/RedissonGeoTest.java b/redisson/src/test/java/org/redisson/RedissonGeoTest.java index 7eeb2e975..95520db46 100644 --- a/redisson/src/test/java/org/redisson/RedissonGeoTest.java +++ b/redisson/src/test/java/org/redisson/RedissonGeoTest.java @@ -1,19 +1,16 @@ package org.redisson; -import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.redisson.api.*; import org.redisson.api.geo.GeoSearchArgs; -import java.io.IOException; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -public class RedissonGeoTest extends BaseStackTest { +public class RedissonGeoTest extends RedisDockerTest { @Test public void testAdd() { diff --git a/redisson/src/test/java/org/redisson/RedissonJsonBucketTest.java b/redisson/src/test/java/org/redisson/RedissonJsonBucketTest.java index b4d6aa338..5448d4426 100644 --- a/redisson/src/test/java/org/redisson/RedissonJsonBucketTest.java +++ b/redisson/src/test/java/org/redisson/RedissonJsonBucketTest.java @@ -13,7 +13,7 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; -public class RedissonJsonBucketTest extends BaseStackTest { +public class RedissonJsonBucketTest extends DockerRedisStackTest { public static class NestedType { diff --git a/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index faaadb984..f9b2dd6a0 100644 --- a/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -1,7 +1,6 @@ package org.redisson; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.redisson.api.*; import org.redisson.api.listener.ScoredSortedSetAddListener; @@ -25,7 +24,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.junit.jupiter.api.Assertions.assertTrue; -public class RedissonScoredSortedSetTest extends BaseStackTest { +public class RedissonScoredSortedSetTest extends RedisDockerTest { @Test public void testEntries() { diff --git a/redisson/src/test/java/org/redisson/RedissonSearchTest.java b/redisson/src/test/java/org/redisson/RedissonSearchTest.java index 3a22f1d29..c3f39486f 100644 --- a/redisson/src/test/java/org/redisson/RedissonSearchTest.java +++ b/redisson/src/test/java/org/redisson/RedissonSearchTest.java @@ -22,7 +22,7 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; -public class RedissonSearchTest extends BaseStackTest { +public class RedissonSearchTest extends DockerRedisStackTest { public static class SimpleObject { From 2a36add360beb3d61c128490666d62900fa39473 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Mon, 13 Nov 2023 10:50:28 +0300 Subject: [PATCH 21/23] test fixed --- .../src/test/java/org/redisson/RedissonBlockingDequeTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/redisson/src/test/java/org/redisson/RedissonBlockingDequeTest.java b/redisson/src/test/java/org/redisson/RedissonBlockingDequeTest.java index 9d3787f91..5a152ec97 100644 --- a/redisson/src/test/java/org/redisson/RedissonBlockingDequeTest.java +++ b/redisson/src/test/java/org/redisson/RedissonBlockingDequeTest.java @@ -15,12 +15,10 @@ import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; -public class RedissonBlockingDequeTest extends BaseTest { +public class RedissonBlockingDequeTest extends RedisDockerTest { @Test public void testMove() { - Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("6.2.0") > 0); - RBlockingDeque deque1 = redisson.getBlockingDeque("deque1"); RBlockingDeque deque2 = redisson.getBlockingDeque("deque2"); From be92e5a2c57a070ef97d77824a3be3dc6040a2fb Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Mon, 13 Nov 2023 11:18:29 +0300 Subject: [PATCH 22/23] test fixed --- .../java/org/redisson/RedissonSetCacheTest.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/redisson/src/test/java/org/redisson/RedissonSetCacheTest.java b/redisson/src/test/java/org/redisson/RedissonSetCacheTest.java index 917e7d735..5c0ad93d4 100644 --- a/redisson/src/test/java/org/redisson/RedissonSetCacheTest.java +++ b/redisson/src/test/java/org/redisson/RedissonSetCacheTest.java @@ -13,10 +13,9 @@ import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import static org.assertj.core.api.Assertions.as; import static org.assertj.core.api.Assertions.assertThat; -public class RedissonSetCacheTest extends BaseTest { +public class RedissonSetCacheTest extends RedisDockerTest { public static class SimpleBean implements Serializable { @@ -175,9 +174,8 @@ public class RedissonSetCacheTest extends BaseTest { assertThat(set).contains("123"); Thread.sleep(500); - - assertThat(set.size()).isEqualTo(1); - assertThat(set).doesNotContain("123"); + + assertThat(set.contains("123")).isFalse(); assertThat(set.add("123", 1, TimeUnit.SECONDS)).isTrue(); set.destroy(); @@ -214,10 +212,11 @@ public class RedissonSetCacheTest extends BaseTest { public void testAddExpireThenAdd() throws InterruptedException, ExecutionException { RSetCache set = redisson.getSetCache("simple31"); assertThat(set.add("123", 500, TimeUnit.MILLISECONDS)).isTrue(); - + assertThat(set.size()).isEqualTo(1); + Thread.sleep(500); - assertThat(set.size()).isEqualTo(1); + assertThat(set.size()).isZero(); assertThat(set.contains("123")).isFalse(); assertThat(set.add("123")).isTrue(); From 68dd44783573cb86a954675a37a60220e01907eb Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Mon, 13 Nov 2023 11:24:18 +0300 Subject: [PATCH 23/23] Feature - RESP3 protocol support. protocol setting added. #5413 --- .../java/org/redisson/RedissonJsonBucket.java | 52 ++++++++++- .../java/org/redisson/RedissonSearch.java | 93 +++++++++++++++---- .../redisson/client/RedisClientConfig.java | 17 +++- .../client/handler/BaseConnectionHandler.java | 7 +- .../client/handler/CommandDecoder.java | 57 ++++++++++-- .../client/handler/CommandPubSubDecoder.java | 2 +- .../convertor/LongNumberConvertor.java | 60 ++++++++++++ .../AggregationCursorResultDecoderV2.java | 55 +++++++++++ .../decoder/AggregationResultDecoderV2.java | 56 +++++++++++ .../protocol/decoder/IndexInfoDecoder.java | 4 + .../decoder/ListFirstObjectDecoder.java | 3 +- .../ObjectFirstScoreReplayDecoder.java | 2 +- .../decoder/ObjectMapReplayDecoder.java | 8 ++ .../ScoredSortedSetRandomMapDecoder.java | 15 +++ .../decoder/ScoredSortedSetReplayDecoder.java | 4 + .../decoder/SearchResultDecoderV2.java | 55 +++++++++++ .../main/java/org/redisson/config/Config.java | 22 ++++- .../java/org/redisson/config/Protocol.java | 30 ++++++ .../MasterSlaveConnectionManager.java | 1 + .../test/java/org/redisson/RedisRunner.java | 50 ++++++---- .../redisson/RedissonRuntimeEnvironment.java | 1 + 21 files changed, 533 insertions(+), 61 deletions(-) create mode 100644 redisson/src/main/java/org/redisson/client/protocol/convertor/LongNumberConvertor.java create mode 100644 redisson/src/main/java/org/redisson/client/protocol/decoder/AggregationCursorResultDecoderV2.java create mode 100644 redisson/src/main/java/org/redisson/client/protocol/decoder/AggregationResultDecoderV2.java create mode 100644 redisson/src/main/java/org/redisson/client/protocol/decoder/SearchResultDecoderV2.java create mode 100644 redisson/src/main/java/org/redisson/config/Protocol.java diff --git a/redisson/src/main/java/org/redisson/RedissonJsonBucket.java b/redisson/src/main/java/org/redisson/RedissonJsonBucket.java index e89886e85..53b09d363 100644 --- a/redisson/src/main/java/org/redisson/RedissonJsonBucket.java +++ b/redisson/src/main/java/org/redisson/RedissonJsonBucket.java @@ -22,11 +22,18 @@ import org.redisson.client.codec.LongCodec; import org.redisson.client.codec.StringCodec; import org.redisson.client.protocol.RedisCommand; import org.redisson.client.protocol.RedisCommands; +import org.redisson.client.protocol.RedisStrictCommand; +import org.redisson.client.protocol.convertor.JsonTypeConvertor; +import org.redisson.client.protocol.convertor.LongNumberConvertor; import org.redisson.client.protocol.convertor.NumberConvertor; +import org.redisson.client.protocol.decoder.ListFirstObjectDecoder; +import org.redisson.client.protocol.decoder.ListMultiDecoder2; import org.redisson.client.protocol.decoder.ObjectListReplayDecoder; +import org.redisson.client.protocol.decoder.StringListListReplayDecoder; import org.redisson.codec.JsonCodec; import org.redisson.codec.JsonCodecWrapper; import org.redisson.command.CommandAsyncExecutor; +import org.redisson.config.Protocol; import java.math.BigDecimal; import java.time.Duration; @@ -87,6 +94,9 @@ public class RedissonJsonBucket extends RedissonExpirable implements RJsonBuc @Override public RFuture getAsync() { + if (getServiceManager().getCfg().getProtocol() == Protocol.RESP3) { + return commandExecutor.readAsync(getRawName(), codec, RedisCommands.JSON_GET, getRawName(), "."); + } return commandExecutor.readAsync(getRawName(), codec, RedisCommands.JSON_GET, getRawName()); } @@ -97,6 +107,12 @@ public class RedissonJsonBucket extends RedissonExpirable implements RJsonBuc @Override public RFuture getAsync(JsonCodec codec, String... paths) { + if (getServiceManager().getCfg().getProtocol() == Protocol.RESP3) { + if (paths.length == 0) { + paths = new String[]{"."}; + } + } + List args = new ArrayList<>(); args.add(getRawName()); args.addAll(Arrays.asList(paths)); @@ -761,7 +777,13 @@ public class RedissonJsonBucket extends RedissonExpirable implements RJsonBuc @Override public RFuture incrementAndGetAsync(String path, T delta) { - return commandExecutor.writeAsync(getRawName(), StringCodec.INSTANCE, new RedisCommand<>("JSON.NUMINCRBY", new NumberConvertor(delta.getClass())), + RedisCommand command; + if (getServiceManager().getCfg().getProtocol() == Protocol.RESP3) { + command = new RedisCommand<>("JSON.NUMINCRBY", new ListFirstObjectDecoder(), new LongNumberConvertor(delta.getClass())); + } else { + command = new RedisCommand<>("JSON.NUMINCRBY", new NumberConvertor(delta.getClass())); + } + return commandExecutor.writeAsync(getRawName(), StringCodec.INSTANCE, command, getRawName(), path, new BigDecimal(delta.toString()).toPlainString()); } @@ -784,7 +806,12 @@ public class RedissonJsonBucket extends RedissonExpirable implements RJsonBuc @Override public RFuture countKeysAsync() { - return commandExecutor.writeAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.JSON_OBJLEN, getRawName()); + RedisStrictCommand command = RedisCommands.JSON_OBJLEN; + if (getServiceManager().getCfg().getProtocol() == Protocol.RESP3) { + command = new RedisStrictCommand("JSON.OBJLEN", new ListFirstObjectDecoder()); + } + + return commandExecutor.writeAsync(getRawName(), LongCodec.INSTANCE, command, getRawName()); } @Override @@ -814,7 +841,12 @@ public class RedissonJsonBucket extends RedissonExpirable implements RJsonBuc @Override public RFuture> getKeysAsync() { - return commandExecutor.readAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.JSON_OBJKEYS, getRawName()); + RedisCommand command = RedisCommands.JSON_OBJKEYS; + if (getServiceManager().getCfg().getProtocol() == Protocol.RESP3) { + command = new RedisCommand("JSON.OBJKEYS", + new ListMultiDecoder2(new ListFirstObjectDecoder(), new StringListListReplayDecoder())); + } + return commandExecutor.readAsync(getRawName(), LongCodec.INSTANCE, command, getRawName()); } @Override @@ -864,7 +896,12 @@ public class RedissonJsonBucket extends RedissonExpirable implements RJsonBuc @Override public RFuture getTypeAsync() { - return commandExecutor.readAsync(getRawName(), StringCodec.INSTANCE, RedisCommands.JSON_TYPE, getRawName()); + RedisCommand command = RedisCommands.JSON_TYPE; + if (getServiceManager().getCfg().getProtocol() == Protocol.RESP3) { + command = new RedisCommand("JSON.TYPE", new ListFirstObjectDecoder(), new JsonTypeConvertor()); + } + + return commandExecutor.readAsync(getRawName(), StringCodec.INSTANCE, command, getRawName()); } @Override @@ -874,7 +911,12 @@ public class RedissonJsonBucket extends RedissonExpirable implements RJsonBuc @Override public RFuture getTypeAsync(String path) { - return commandExecutor.readAsync(getRawName(), StringCodec.INSTANCE, RedisCommands.JSON_TYPE, getRawName(), path); + RedisCommand command = RedisCommands.JSON_TYPE; + if (getServiceManager().getCfg().getProtocol() == Protocol.RESP3) { + command = new RedisCommand("JSON.TYPE", new ListFirstObjectDecoder(), new JsonTypeConvertor()); + } + + return commandExecutor.readAsync(getRawName(), StringCodec.INSTANCE, command, getRawName(), path); } @Override diff --git a/redisson/src/main/java/org/redisson/RedissonSearch.java b/redisson/src/main/java/org/redisson/RedissonSearch.java index 41f59fb58..a02daacc9 100644 --- a/redisson/src/main/java/org/redisson/RedissonSearch.java +++ b/redisson/src/main/java/org/redisson/RedissonSearch.java @@ -22,13 +22,17 @@ import org.redisson.api.search.aggregate.*; import org.redisson.api.search.index.*; import org.redisson.api.search.query.*; import org.redisson.client.codec.Codec; +import org.redisson.client.codec.DoubleCodec; import org.redisson.client.codec.LongCodec; import org.redisson.client.codec.StringCodec; +import org.redisson.client.protocol.RedisCommand; import org.redisson.client.protocol.RedisCommands; import org.redisson.client.protocol.RedisStrictCommand; +import org.redisson.client.protocol.convertor.EmptyMapConvertor; import org.redisson.client.protocol.decoder.*; import org.redisson.codec.CompositeCodec; import org.redisson.command.CommandAsyncExecutor; +import org.redisson.config.Protocol; import java.math.BigDecimal; import java.util.ArrayList; @@ -151,7 +155,7 @@ public class RedissonSearch implements RSearch { } args.add("VECTOR"); args.add("HNSW"); - args.add(params.getCount()); + args.add(params.getCount()*2); args.add("TYPE"); args.add(params.getType()); args.add("DIM"); @@ -473,14 +477,27 @@ public class RedissonSearch implements RSearch { args.add(options.getDialect()); } - RedisStrictCommand command = new RedisStrictCommand<>("FT.SEARCH", - new ListMultiDecoder2(new SearchResultDecoder(), - new ObjectMapReplayDecoder(new CompositeCodec(StringCodec.INSTANCE, codec)), - new ObjectListReplayDecoder())); + RedisStrictCommand command; + if (isResp3()) { + command = new RedisStrictCommand<>("FT.SEARCH", + new ListMultiDecoder2(new SearchResultDecoderV2(), + new ObjectListReplayDecoder(), + new ObjectMapReplayDecoder(), + new ObjectMapReplayDecoder(new CompositeCodec(StringCodec.INSTANCE, codec)))); + } else { + command = new RedisStrictCommand<>("FT.SEARCH", + new ListMultiDecoder2(new SearchResultDecoder(), + new ObjectMapReplayDecoder(new CompositeCodec(StringCodec.INSTANCE, codec)), + new ObjectListReplayDecoder())); + } return commandExecutor.writeAsync(indexName, StringCodec.INSTANCE, command, args.toArray()); } + private boolean isResp3() { + return commandExecutor.getServiceManager().getCfg().getProtocol() == Protocol.RESP3; + } + private String value(double score, boolean exclusive) { StringBuilder element = new StringBuilder(); if (Double.isInfinite(score)) { @@ -593,19 +610,35 @@ public class RedissonSearch implements RSearch { } RedisStrictCommand command; - if (options.isWithCursor()) { - command = new RedisStrictCommand<>("FT.AGGREGATE", - new ListMultiDecoder2(new AggregationCursorResultDecoder(), - new ObjectListReplayDecoder(), - new ObjectMapReplayDecoder(new CompositeCodec(StringCodec.INSTANCE, codec)))); + if (isResp3()) { + if (options.isWithCursor()) { + command = new RedisStrictCommand<>("FT.AGGREGATE", + new ListMultiDecoder2(new AggregationCursorResultDecoderV2(), + new ObjectListReplayDecoder(), + new ObjectListReplayDecoder(), + new ObjectMapReplayDecoder(), + new ObjectMapReplayDecoder(new CompositeCodec(StringCodec.INSTANCE, codec)))); + } else { + command = new RedisStrictCommand<>("FT.AGGREGATE", + new ListMultiDecoder2(new AggregationResultDecoderV2(), + new ObjectListReplayDecoder(), + new ObjectMapReplayDecoder(), + new ObjectMapReplayDecoder(new CompositeCodec(StringCodec.INSTANCE, codec)))); + } } else { - command = new RedisStrictCommand<>("FT.AGGREGATE", - new ListMultiDecoder2(new AggregationResultDecoder(), - new ObjectMapReplayDecoder(new CompositeCodec(StringCodec.INSTANCE, codec)), - new ObjectListReplayDecoder())); + if (options.isWithCursor()) { + command = new RedisStrictCommand<>("FT.AGGREGATE", + new ListMultiDecoder2(new AggregationCursorResultDecoder(), + new ObjectListReplayDecoder(), + new ObjectMapReplayDecoder(new CompositeCodec(StringCodec.INSTANCE, codec)))); + } else { + command = new RedisStrictCommand<>("FT.AGGREGATE", + new ListMultiDecoder2(new AggregationResultDecoder(), + new ObjectMapReplayDecoder(new CompositeCodec(StringCodec.INSTANCE, codec)), + new ObjectListReplayDecoder())); + } } - return commandExecutor.writeAsync(indexName, StringCodec.INSTANCE, command, args.toArray()); } @@ -703,10 +736,20 @@ public class RedissonSearch implements RSearch { @Override public RFuture readCursorAsync(String indexName, long cursorId) { - RedisStrictCommand command = new RedisStrictCommand<>("FT.CURSOR", "READ", - new ListMultiDecoder2(new AggregationCursorResultDecoder(), - new ObjectListReplayDecoder(), - new ObjectMapReplayDecoder(new CompositeCodec(StringCodec.INSTANCE, codec)))); + RedisStrictCommand command; + if (isResp3()) { + command = new RedisStrictCommand<>("FT.CURSOR", "READ", + new ListMultiDecoder2(new AggregationCursorResultDecoderV2(), + new ObjectListReplayDecoder(), + new ObjectListReplayDecoder(), + new ObjectMapReplayDecoder(), + new ObjectMapReplayDecoder(new CompositeCodec(StringCodec.INSTANCE, codec)))); + } else { + command = new RedisStrictCommand<>("FT.CURSOR", "READ", + new ListMultiDecoder2(new AggregationCursorResultDecoder(), + new ObjectListReplayDecoder(), + new ObjectMapReplayDecoder(new CompositeCodec(StringCodec.INSTANCE, codec)))); + } return commandExecutor.writeAsync(indexName, StringCodec.INSTANCE, command, indexName, cursorId); } @@ -824,7 +867,17 @@ public class RedissonSearch implements RSearch { args.add(options.getDialect()); } - return commandExecutor.readAsync(indexName, StringCodec.INSTANCE, RedisCommands.FT_SPELLCHECK, args.toArray()); + RedisCommand>> command = RedisCommands.FT_SPELLCHECK; + if (isResp3()) { + command = new RedisCommand<>("FT.SPELLCHECK", + new ListMultiDecoder2( + new ListObjectDecoder(1), + new ObjectMapReplayDecoder(), + new ListFirstObjectDecoder(new EmptyMapConvertor()), + new ObjectMapReplayDecoder(new CompositeCodec(StringCodec.INSTANCE, DoubleCodec.INSTANCE)))); + } + + return commandExecutor.readAsync(indexName, StringCodec.INSTANCE, command, args.toArray()); } @Override diff --git a/redisson/src/main/java/org/redisson/client/RedisClientConfig.java b/redisson/src/main/java/org/redisson/client/RedisClientConfig.java index 5f3829648..c0681ce58 100644 --- a/redisson/src/main/java/org/redisson/client/RedisClientConfig.java +++ b/redisson/src/main/java/org/redisson/client/RedisClientConfig.java @@ -20,10 +20,7 @@ import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.resolver.AddressResolverGroup; import io.netty.util.Timer; -import org.redisson.config.CommandMapper; -import org.redisson.config.CredentialsResolver; -import org.redisson.config.DefaultCommandMapper; -import org.redisson.config.SslProvider; +import org.redisson.config.*; import org.redisson.misc.RedisURI; import javax.net.ssl.KeyManagerFactory; @@ -85,6 +82,8 @@ public class RedisClientConfig { private FailedNodeDetector failedNodeDetector = new FailedConnectionDetector(); + private Protocol protocol = Protocol.RESP2; + public RedisClientConfig() { } @@ -129,6 +128,7 @@ public class RedisClientConfig { this.tcpKeepAliveIdle = config.tcpKeepAliveIdle; this.tcpKeepAliveInterval = config.tcpKeepAliveInterval; this.tcpUserTimeout = config.tcpUserTimeout; + this.protocol = config.protocol; } public NettyHook getNettyHook() { @@ -457,4 +457,13 @@ public class RedisClientConfig { this.failedNodeDetector = failedNodeDetector; return this; } + + public Protocol getProtocol() { + return protocol; + } + + public RedisClientConfig setProtocol(Protocol protocol) { + this.protocol = protocol; + return this; + } } diff --git a/redisson/src/main/java/org/redisson/client/handler/BaseConnectionHandler.java b/redisson/src/main/java/org/redisson/client/handler/BaseConnectionHandler.java index 712bdfd84..f2cb38c19 100644 --- a/redisson/src/main/java/org/redisson/client/handler/BaseConnectionHandler.java +++ b/redisson/src/main/java/org/redisson/client/handler/BaseConnectionHandler.java @@ -19,6 +19,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import org.redisson.client.*; import org.redisson.client.protocol.RedisCommands; +import org.redisson.config.Protocol; import java.net.InetSocketAddress; import java.util.ArrayList; @@ -79,8 +80,10 @@ public abstract class BaseConnectionHandler extends C }); futures.add(f.toCompletableFuture()); -// CompletionStage f1 = connection.async(RedisCommands.HELLO, "3"); -// futures.add(f1.toCompletableFuture()); + if (redisClient.getConfig().getProtocol() == Protocol.RESP3) { + CompletionStage f1 = connection.async(RedisCommands.HELLO, "3"); + futures.add(f1.toCompletableFuture()); + } if (config.getDatabase() != 0) { CompletionStage future = connection.async(RedisCommands.SELECT, config.getDatabase()); diff --git a/redisson/src/main/java/org/redisson/client/handler/CommandDecoder.java b/redisson/src/main/java/org/redisson/client/handler/CommandDecoder.java index ce7a83543..74951774c 100644 --- a/redisson/src/main/java/org/redisson/client/handler/CommandDecoder.java +++ b/redisson/src/main/java/org/redisson/client/handler/CommandDecoder.java @@ -164,7 +164,11 @@ public class CommandDecoder extends ReplayingDecoder { protected void skipDecode(ByteBuf in) throws IOException{ int code = in.readByte(); - if (code == '+') { + if (code == '_') { + in.skipBytes(2); + } else if (code == ',') { + skipString(in); + } else if (code == '+') { skipString(in); } else if (code == '-') { skipString(in); @@ -172,7 +176,14 @@ public class CommandDecoder extends ReplayingDecoder { skipString(in); } else if (code == '$') { skipBytes(in); - } else if (code == '*') { + } else if (code == '=') { + skipBytes(in); + } else if (code == '%') { + long size = readLong(in); + for (int i = 0; i < size * 2; i++) { + skipDecode(in); + } + } else if (code == '*' || code == '>' || code == '~') { long size = readLong(in); for (int i = 0; i < size; i++) { skipDecode(in); @@ -335,9 +346,21 @@ public class CommandDecoder extends ReplayingDecoder { protected void decode(ByteBuf in, CommandData data, List parts, Channel channel, boolean skipConvertor, List> commandsData) throws IOException { int code = in.readByte(); - if (code == '+') { + if (code == '_') { + readCRLF(in); + Object result = null; + handleResult(data, parts, result, false); + } else if (code == '+') { String result = readString(in); + handleResult(data, parts, result, skipConvertor); + } else if (code == ',') { + String str = readString(in); + Double result = Double.NaN; + if (!"nan".equals(str)) { + result = Double.valueOf(str); + } + handleResult(data, parts, result, skipConvertor); } else if (code == '-') { String error = readString(in); @@ -386,6 +409,15 @@ public class CommandDecoder extends ReplayingDecoder { } else if (code == ':') { Long result = readLong(in); handleResult(data, parts, result, false); + } else if (code == '=') { + ByteBuf buf = readBytes(in); + Object result = null; + if (buf != null) { + buf.skipBytes(3); + Decoder decoder = selectDecoder(data, parts); + result = decoder.decode(buf, state()); + } + handleResult(data, parts, result, false); } else if (code == '$') { ByteBuf buf = readBytes(in); Object result = null; @@ -394,7 +426,7 @@ public class CommandDecoder extends ReplayingDecoder { result = decoder.decode(buf, state()); } handleResult(data, parts, result, false); - } else if (code == '*') { + } else if (code == '*' || code == '>' || code == '~') { long size = readLong(in); List respParts = new ArrayList(Math.max((int) size, 0)); @@ -403,7 +435,16 @@ public class CommandDecoder extends ReplayingDecoder { decodeList(in, data, parts, channel, size, respParts, skipConvertor, commandsData); state().decLevel(); - + + } else if (code == '%') { + long size = readLong(in) * 2; + List respParts = new ArrayList(Math.max((int) size, 0)); + + state().incLevel(); + + decodeList(in, data, parts, channel, size, respParts, skipConvertor, commandsData); + + state().decLevel(); } else { String dataStr = in.toString(0, in.writerIndex(), CharsetUtil.UTF_8); throw new IllegalStateException("Can't decode replay: " + dataStr); @@ -420,7 +461,7 @@ public class CommandDecoder extends ReplayingDecoder { in.skipBytes(len + 2); return result; } - + @SuppressWarnings("unchecked") private void decodeList(ByteBuf in, CommandData data, List parts, Channel channel, long size, List respParts, boolean skipConvertor, List> commandsData) @@ -514,6 +555,10 @@ public class CommandDecoder extends ReplayingDecoder { return buffer; } + private void readCRLF(ByteBuf is) { + is.skipBytes(2); + } + private long readLong(ByteBuf is) throws IOException { long size = 0; int sign = 1; diff --git a/redisson/src/main/java/org/redisson/client/handler/CommandPubSubDecoder.java b/redisson/src/main/java/org/redisson/client/handler/CommandPubSubDecoder.java index 2e08831e5..4ccb9cfe8 100644 --- a/redisson/src/main/java/org/redisson/client/handler/CommandPubSubDecoder.java +++ b/redisson/src/main/java/org/redisson/client/handler/CommandPubSubDecoder.java @@ -204,7 +204,7 @@ public class CommandPubSubDecoder extends CommandDecoder { }); } } else { - if (data != null && data.getCommand().getName().equals("PING")) { + if (data != null) { super.decodeResult(data, parts, channel, result); } } diff --git a/redisson/src/main/java/org/redisson/client/protocol/convertor/LongNumberConvertor.java b/redisson/src/main/java/org/redisson/client/protocol/convertor/LongNumberConvertor.java new file mode 100644 index 000000000..c690d71eb --- /dev/null +++ b/redisson/src/main/java/org/redisson/client/protocol/convertor/LongNumberConvertor.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2013-2022 Nikita Koksharov + * + * 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.redisson.client.protocol.convertor; + +import java.math.BigDecimal; + +/** + * + * @author Nikita Koksharov + * + */ +public class LongNumberConvertor implements Convertor { + + private Class resultClass; + + public LongNumberConvertor(Class resultClass) { + super(); + this.resultClass = resultClass; + } + + @Override + public Object convert(Object result) { + if (result instanceof Long) { + Long res = (Long) result; + if (resultClass.isAssignableFrom(Long.class)) { + return res; + } + if (resultClass.isAssignableFrom(Integer.class)) { + return res.intValue(); + } + if (resultClass.isAssignableFrom(BigDecimal.class)) { + return new BigDecimal(res); + } + } + if (result instanceof Double) { + Double res = (Double) result; + if (resultClass.isAssignableFrom(Float.class)) { + return ((Double) result).floatValue(); + } + if (resultClass.isAssignableFrom(Double.class)) { + return res; + } + } + throw new IllegalStateException("Wrong value type!"); + } + +} diff --git a/redisson/src/main/java/org/redisson/client/protocol/decoder/AggregationCursorResultDecoderV2.java b/redisson/src/main/java/org/redisson/client/protocol/decoder/AggregationCursorResultDecoderV2.java new file mode 100644 index 000000000..05d70b096 --- /dev/null +++ b/redisson/src/main/java/org/redisson/client/protocol/decoder/AggregationCursorResultDecoderV2.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2013-2022 Nikita Koksharov + * + * 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.redisson.client.protocol.decoder; + +import org.redisson.api.search.aggregate.AggregationResult; +import org.redisson.client.handler.State; + +import java.util.*; + +/** + * + * @author Nikita Koksharov + * + */ +public class AggregationCursorResultDecoderV2 implements MultiDecoder { + + @Override + public Object decode(List parts, State state) { + if (parts.isEmpty()) { + return new AggregationResult(0, Collections.emptyList(), -1); + } + + List attrs = (List) parts.get(0); + Map m = new HashMap<>(); + for (int i = 0; i < attrs.size(); i++) { + if (i % 2 != 0) { + m.put(attrs.get(i-1).toString(), attrs.get(i)); + } + } + + List> docs = new ArrayList<>(); + List> results = (List>) m.get("results"); + for (Map result : results) { + Map map = (Map) result.get("extra_attributes"); + docs.add(map); + } + Long total = (Long) m.get("total_results"); + long cursorId = (long) parts.get(1); + return new AggregationResult(total, docs, cursorId); + } + +} diff --git a/redisson/src/main/java/org/redisson/client/protocol/decoder/AggregationResultDecoderV2.java b/redisson/src/main/java/org/redisson/client/protocol/decoder/AggregationResultDecoderV2.java new file mode 100644 index 000000000..75713bc69 --- /dev/null +++ b/redisson/src/main/java/org/redisson/client/protocol/decoder/AggregationResultDecoderV2.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2013-2022 Nikita Koksharov + * + * 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.redisson.client.protocol.decoder; + +import org.redisson.api.search.aggregate.AggregationResult; +import org.redisson.client.handler.State; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * + * @author Nikita Koksharov + * + */ +public class AggregationResultDecoderV2 implements MultiDecoder { + + @Override + public Object decode(List parts, State state) { + if (parts.isEmpty()) { + return null; + } + + Map m = new HashMap<>(); + for (int i = 0; i < parts.size(); i++) { + if (i % 2 != 0) { + m.put(parts.get(i-1).toString(), parts.get(i)); + } + } + + List> docs = new ArrayList<>(); + List> results = (List>) m.get("results"); + for (Map result : results) { + Map attrs = (Map) result.get("extra_attributes"); + docs.add(attrs); + } + Long total = (Long) m.get("total_results"); + return new AggregationResult(total, docs); + } + +} diff --git a/redisson/src/main/java/org/redisson/client/protocol/decoder/IndexInfoDecoder.java b/redisson/src/main/java/org/redisson/client/protocol/decoder/IndexInfoDecoder.java index f6b53fcc6..a6da5ce93 100644 --- a/redisson/src/main/java/org/redisson/client/protocol/decoder/IndexInfoDecoder.java +++ b/redisson/src/main/java/org/redisson/client/protocol/decoder/IndexInfoDecoder.java @@ -79,6 +79,10 @@ public class IndexInfoDecoder implements MultiDecoder { if (result.get(prop).toString().contains("nan")) { return 0L; } + if (result.get(prop) instanceof Double) { + Double d = (Double) result.get(prop); + return d.longValue(); + } return Long.valueOf(result.get(prop).toString()); } } diff --git a/redisson/src/main/java/org/redisson/client/protocol/decoder/ListFirstObjectDecoder.java b/redisson/src/main/java/org/redisson/client/protocol/decoder/ListFirstObjectDecoder.java index eab2a7a5e..9d8853ca8 100644 --- a/redisson/src/main/java/org/redisson/client/protocol/decoder/ListFirstObjectDecoder.java +++ b/redisson/src/main/java/org/redisson/client/protocol/decoder/ListFirstObjectDecoder.java @@ -18,6 +18,7 @@ package org.redisson.client.protocol.decoder; import org.redisson.client.codec.Codec; import org.redisson.client.handler.State; import org.redisson.client.protocol.Decoder; +import org.redisson.client.protocol.ScoredEntry; import org.redisson.client.protocol.convertor.Convertor; import java.util.List; @@ -54,7 +55,7 @@ public class ListFirstObjectDecoder implements MultiDecoder { @Override public Object decode(List parts, State state) { - if (inner != null) { + if (inner != null && !parts.isEmpty() && !(parts.get(0) instanceof ScoredEntry)) { parts = (List) inner.decode(parts, state); } if (!parts.isEmpty()) { diff --git a/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstScoreReplayDecoder.java b/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstScoreReplayDecoder.java index f422fcf2a..e4a49cde5 100644 --- a/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstScoreReplayDecoder.java +++ b/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstScoreReplayDecoder.java @@ -43,7 +43,7 @@ public class ObjectFirstScoreReplayDecoder implements MultiDecoder { if (parts.isEmpty()) { return null; } - return (Double) parts.get(1); + return (Double) parts.get(parts.size()-1); } } diff --git a/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectMapReplayDecoder.java b/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectMapReplayDecoder.java index 208e614ef..456c72d7a 100644 --- a/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectMapReplayDecoder.java +++ b/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectMapReplayDecoder.java @@ -22,6 +22,7 @@ import org.redisson.client.protocol.Decoder; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; /** * @@ -57,6 +58,13 @@ public class ObjectMapReplayDecoder implements MultiDecoder> { @Override public Map decode(List parts, State state) { + if (!parts.isEmpty() && parts.get(0) instanceof Map) { + return ((List>) (Object) parts) + .stream() + .flatMap(v -> v.entrySet().stream()) + .collect(Collectors.toMap(v -> v.getKey(), v -> v.getValue())); + } + Map result = MultiDecoder.newLinkedHashMap(parts.size()/2); for (int i = 0; i < parts.size(); i++) { if (i % 2 != 0) { diff --git a/redisson/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetRandomMapDecoder.java b/redisson/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetRandomMapDecoder.java index 6f0c7642c..a44c17362 100644 --- a/redisson/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetRandomMapDecoder.java +++ b/redisson/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetRandomMapDecoder.java @@ -20,6 +20,10 @@ import org.redisson.client.codec.DoubleCodec; import org.redisson.client.handler.State; import org.redisson.client.protocol.Decoder; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + /** * * @author Nikita Koksharov @@ -35,4 +39,15 @@ public class ScoredSortedSetRandomMapDecoder extends ObjectMapReplayDecoder decode(List parts, State state) { + if (!parts.isEmpty() && parts.get(0) instanceof Map) { + return ((List>) (Object) parts) + .stream() + .flatMap(v -> v.entrySet().stream()) + .collect(Collectors.toMap(v -> v.getKey(), v -> v.getValue())); + } + + return super.decode(parts, state); + } } diff --git a/redisson/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetReplayDecoder.java b/redisson/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetReplayDecoder.java index 40bb1da0c..b0057de90 100644 --- a/redisson/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetReplayDecoder.java +++ b/redisson/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetReplayDecoder.java @@ -17,6 +17,7 @@ package org.redisson.client.protocol.decoder; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import org.redisson.client.codec.Codec; import org.redisson.client.codec.DoubleCodec; @@ -42,6 +43,9 @@ public class ScoredSortedSetReplayDecoder implements MultiDecoder> decode(List parts, State state) { + if (!parts.isEmpty() && parts.get(0) instanceof List) { + return ((List>>) (Object) parts).stream().flatMap(v -> v.stream()).collect(Collectors.toList()); + } List> result = new ArrayList<>(); for (int i = 0; i < parts.size(); i += 2) { result.add(new ScoredEntry(((Number) parts.get(i+1)).doubleValue(), (T) parts.get(i))); diff --git a/redisson/src/main/java/org/redisson/client/protocol/decoder/SearchResultDecoderV2.java b/redisson/src/main/java/org/redisson/client/protocol/decoder/SearchResultDecoderV2.java new file mode 100644 index 000000000..33265ae63 --- /dev/null +++ b/redisson/src/main/java/org/redisson/client/protocol/decoder/SearchResultDecoderV2.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2013-2022 Nikita Koksharov + * + * 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.redisson.client.protocol.decoder; + +import org.redisson.api.search.query.Document; +import org.redisson.api.search.query.SearchResult; +import org.redisson.client.handler.State; + +import java.util.*; + +/** + * + * @author Nikita Koksharov + * + */ +public class SearchResultDecoderV2 implements MultiDecoder { + + @Override + public Object decode(List parts, State state) { + if (parts.isEmpty()) { + return new SearchResult(0, Collections.emptyList()); + } + + Map m = new HashMap<>(); + for (int i = 0; i < parts.size(); i++) { + if (i % 2 != 0) { + m.put(parts.get(i-1).toString(), parts.get(i)); + } + } + + List docs = new ArrayList<>(); + List> results = (List>) m.get("results"); + for (Map result : results) { + String id = (String) result.get("id"); + Map attrs = (Map) result.get("extra_attributes"); + docs.add(new Document(id, attrs)); + } + Long total = (Long) m.get("total_results"); + return new SearchResult(total, docs); + } + +} diff --git a/redisson/src/main/java/org/redisson/config/Config.java b/redisson/src/main/java/org/redisson/config/Config.java index 911192977..7a97416cb 100644 --- a/redisson/src/main/java/org/redisson/config/Config.java +++ b/redisson/src/main/java/org/redisson/config/Config.java @@ -96,6 +96,8 @@ public class Config { private boolean lazyInitialization; + private Protocol protocol = Protocol.RESP2; + public Config() { } @@ -127,6 +129,7 @@ public class Config { setAddressResolverGroupFactory(oldConf.getAddressResolverGroupFactory()); setReliableTopicWatchdogTimeout(oldConf.getReliableTopicWatchdogTimeout()); setLazyInitialization(oldConf.isLazyInitialization()); + setProtocol(oldConf.getProtocol()); if (oldConf.getSingleServerConfig() != null) { setSingleServerConfig(new SingleServerConfig(oldConf.getSingleServerConfig())); @@ -879,10 +882,27 @@ public class Config { * * @param lazyInitialization true connects to Redis only when first Redis call is made, * false connects to Redis during Redisson instance creation. - * @return + * @return config */ public Config setLazyInitialization(boolean lazyInitialization) { this.lazyInitialization = lazyInitialization; return this; } + + public Protocol getProtocol() { + return protocol; + } + + /** + * Defines Redis protocol version. + *

+ * Default value is RESP2 + * + * @param protocol Redis protocol version + * @return config + */ + public Config setProtocol(Protocol protocol) { + this.protocol = protocol; + return this; + } } diff --git a/redisson/src/main/java/org/redisson/config/Protocol.java b/redisson/src/main/java/org/redisson/config/Protocol.java new file mode 100644 index 000000000..8f7216e91 --- /dev/null +++ b/redisson/src/main/java/org/redisson/config/Protocol.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2013-2022 Nikita Koksharov + * + * 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.redisson.config; + +/** + * Redis protocol version + * + * @author Nikita Koksharov + * + */ +public enum Protocol { + + RESP2, + + RESP3 + +} diff --git a/redisson/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java b/redisson/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java index 2b6f9b98a..2187687a3 100644 --- a/redisson/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java +++ b/redisson/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java @@ -361,6 +361,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager { .setPassword(config.getPassword()) .setNettyHook(serviceManager.getCfg().getNettyHook()) .setFailedNodeDetector(config.getFailedSlaveNodeDetector()) + .setProtocol(serviceManager.getCfg().getProtocol()) .setCommandMapper(config.getCommandMapper()) .setCredentialsResolver(config.getCredentialsResolver()) .setConnectedListener(addr -> { diff --git a/redisson/src/test/java/org/redisson/RedisRunner.java b/redisson/src/test/java/org/redisson/RedisRunner.java index f1efc3313..2b71a6bb1 100644 --- a/redisson/src/test/java/org/redisson/RedisRunner.java +++ b/redisson/src/test/java/org/redisson/RedisRunner.java @@ -201,7 +201,7 @@ public class RedisRunner { private boolean randomDir = false; private ArrayList bindAddr = new ArrayList<>(); private int port = 6379; - private int retryCount = Integer.MAX_VALUE; + private int retryCount = 10; private boolean randomPort = false; private String sentinelFile; private String clusterFile; @@ -294,7 +294,16 @@ public class RedisRunner { throw new FailedToStartRedisException(); } Runtime.getRuntime().addShutdownHook(new Thread(() -> { - rp.stop(); + if (RedissonRuntimeEnvironment.isWindows + && RedissonRuntimeEnvironment.redisBinaryPath.contains("cmd")) { + try { + Runtime.getRuntime().exec("C:\\redis\\redis-server-stop.cmd"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + rp.stop(); + } })); return rp; } @@ -927,31 +936,32 @@ public class RedisRunner { } public int stop() { - if (runner.isNosave() && !runner.isRandomDir()) { - RedisClient c = createDefaultRedisClientInstance(); - RedisConnection connection = c.connect(); + if (runner.isNosave()) { + RedisClientConfig config = new RedisClientConfig(); + config.setConnectTimeout(1000); + config.setAddress(runner.getInitialBindAddr(), runner.getPort()); + RedisClient c = RedisClient.create(config); + + RedisConnection connection = null; try { - connection.async(new RedisStrictCommand("SHUTDOWN", "NOSAVE", new VoidReplayConvertor())) - .get(3, TimeUnit.SECONDS); - } catch (InterruptedException interruptedException) { - //shutdown via command failed, lets wait and kill it later. - } catch (ExecutionException | TimeoutException e) { + connection = c.connect(); + connection.async(new RedisStrictCommand("SHUTDOWN", "NOSAVE", new VoidReplayConvertor())); + } catch (Exception e) { // skip } c.shutdown(); - connection.closeAsync().syncUninterruptibly(); } Process p = redisProcess; p.destroy(); - boolean normalTermination = false; - try { - normalTermination = p.waitFor(5, TimeUnit.SECONDS); - } catch (InterruptedException ex) { - //OK lets hurry up by force kill; - } - if (!normalTermination) { - p = p.destroyForcibly(); - } +// boolean normalTermination = false; +// try { +// normalTermination = p.waitFor(5, TimeUnit.SECONDS); +// } catch (InterruptedException ex) { +// //OK lets hurry up by force kill; +// } +// if (!normalTermination) { +// p = p.destroyForcibly(); +// } cleanup(); int exitCode = p.exitValue(); return exitCode == 1 && RedissonRuntimeEnvironment.isWindows ? 0 : exitCode; diff --git a/redisson/src/test/java/org/redisson/RedissonRuntimeEnvironment.java b/redisson/src/test/java/org/redisson/RedissonRuntimeEnvironment.java index 6966ce531..236d37dae 100644 --- a/redisson/src/test/java/org/redisson/RedissonRuntimeEnvironment.java +++ b/redisson/src/test/java/org/redisson/RedissonRuntimeEnvironment.java @@ -14,6 +14,7 @@ public class RedissonRuntimeEnvironment { public static final String OS; public static final boolean isWindows; private static final String MAC_PATH = "/usr/local/opt/redis/bin/redis-server"; +// private static final String WINDOW_PATH = "C:\\redis\\redis-server2.cmd"; private static final String WINDOW_PATH = "C:\\redis\\redis-server.exe"; static {