From e6892e96c907e818adf8cf256b33effdc2b3b407 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Fri, 18 Mar 2016 21:42:39 +0000 Subject: [PATCH 001/120] added travis config --- .travis.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..55e7e10ba --- /dev/null +++ b/.travis.yml @@ -0,0 +1,23 @@ +# Copyright 2014 Nikita Koksharov, Nickolay Borbit +# +# 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. + +language: java +jdk: + - oraclejdk8 +services: + - redis-server +cache: + directories: + - $HOME/.m2 +script: mvn -DargLine="-DredisBinary=`which redis-server`" -Punit-test -Ptravis clean verify From 94bf56043718cf5f1845ffbbfc64d6483378bb3a Mon Sep 17 00:00:00 2001 From: jackygurui Date: Fri, 18 Mar 2016 22:29:36 +0000 Subject: [PATCH 002/120] use the latest version of redis compiled from source --- .travis.yml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 55e7e10ba..f685894fa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,9 +15,22 @@ language: java jdk: - oraclejdk8 -services: - - redis-server +env: + - REDIS_VERSION=3.0.7 cache: directories: - $HOME/.m2 -script: mvn -DargLine="-DredisBinary=`which redis-server`" -Punit-test -Ptravis clean verify +install: + - export REDIS_BIN=$HOME/redis/${REDIS_VERSION}/bin + - > + wget -c https://github.com/antirez/redis/archive/${REDIS_VERSION}.tar.gz -O redis-${REDIS_VERSION}.tar.gz + tar -xvf redis-${REDIS_VERSION}.tar.gz + make -C redis-${REDIS_VERSION} PREFIX=$HOME/redis/${REDIS_VERSION} install +before_script: + - > + $REDIS_BIN/redis-server --daemonize yes + - sleep 3 + - $REDIS_BIN/redis-cli PING + - export REDIS_VERSION="$(redis-cli INFO SERVER | sed -n 2p)" + - echo $REDIS_VERSION +script: mvn -DargLine="-DredisBinary=$REDIS_BIN" -Punit-test -Ptravis clean verify From 3fea33c76314c64e9eb6389f8d7155ee8dd57fe1 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Fri, 18 Mar 2016 22:31:58 +0000 Subject: [PATCH 003/120] fixed travis sytax issue --- .travis.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index f685894fa..ce9484f40 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,13 +22,11 @@ cache: - $HOME/.m2 install: - export REDIS_BIN=$HOME/redis/${REDIS_VERSION}/bin - - > - wget -c https://github.com/antirez/redis/archive/${REDIS_VERSION}.tar.gz -O redis-${REDIS_VERSION}.tar.gz - tar -xvf redis-${REDIS_VERSION}.tar.gz - make -C redis-${REDIS_VERSION} PREFIX=$HOME/redis/${REDIS_VERSION} install + - wget -c https://github.com/antirez/redis/archive/${REDIS_VERSION}.tar.gz -O redis-${REDIS_VERSION}.tar.gz + - tar -xvf redis-${REDIS_VERSION}.tar.gz + - make -C redis-${REDIS_VERSION} PREFIX=$HOME/redis/${REDIS_VERSION} install before_script: - - > - $REDIS_BIN/redis-server --daemonize yes + - $REDIS_BIN/redis-server --daemonize yes - sleep 3 - $REDIS_BIN/redis-cli PING - export REDIS_VERSION="$(redis-cli INFO SERVER | sed -n 2p)" From e8a054708a39c8fdd891a600b8fef2bce15ca732 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Mon, 21 Mar 2016 22:56:57 +0000 Subject: [PATCH 004/120] added sudo false We are going to the container --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index ce9484f40..13aeda99b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +sudo: false + language: java jdk: - oraclejdk8 From 9913a98f4f2f48d4326cb58f5586c0a5f4a95019 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Mon, 21 Mar 2016 22:58:49 +0000 Subject: [PATCH 005/120] make redis binary executable by all --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 13aeda99b..d0dc4f540 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,7 @@ install: - wget -c https://github.com/antirez/redis/archive/${REDIS_VERSION}.tar.gz -O redis-${REDIS_VERSION}.tar.gz - tar -xvf redis-${REDIS_VERSION}.tar.gz - make -C redis-${REDIS_VERSION} PREFIX=$HOME/redis/${REDIS_VERSION} install + - chmod +x $REDIS_BIN/* before_script: - $REDIS_BIN/redis-server --daemonize yes - sleep 3 From 23e188895eab523e2fb319b0cd6365332ee78184 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Mon, 21 Mar 2016 23:01:10 +0000 Subject: [PATCH 006/120] fixed redisBinary issue fixed redisBinary been set to the directory instead of the binary --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d0dc4f540..0153205e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,11 +27,10 @@ install: - wget -c https://github.com/antirez/redis/archive/${REDIS_VERSION}.tar.gz -O redis-${REDIS_VERSION}.tar.gz - tar -xvf redis-${REDIS_VERSION}.tar.gz - make -C redis-${REDIS_VERSION} PREFIX=$HOME/redis/${REDIS_VERSION} install - - chmod +x $REDIS_BIN/* before_script: - $REDIS_BIN/redis-server --daemonize yes - sleep 3 - $REDIS_BIN/redis-cli PING - export REDIS_VERSION="$(redis-cli INFO SERVER | sed -n 2p)" - echo $REDIS_VERSION -script: mvn -DargLine="-DredisBinary=$REDIS_BIN" -Punit-test -Ptravis clean verify +script: mvn -DargLine="-DredisBinary=$REDIS_BIN/redis-server" -Punit-test -Ptravis clean verify From 0b26f2160d8753cf0a3404a6944ce06074d1b7e9 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Tue, 22 Mar 2016 00:06:09 +0000 Subject: [PATCH 007/120] add lines to change system settings increase the operating system limit to the maximum number of file descriptors per process --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 0153205e2..9e243b571 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,8 @@ install: - tar -xvf redis-${REDIS_VERSION}.tar.gz - make -C redis-${REDIS_VERSION} PREFIX=$HOME/redis/${REDIS_VERSION} install before_script: + - sysctl -w fs.file-max=100000 + - ulimit -Sn 100000 - $REDIS_BIN/redis-server --daemonize yes - sleep 3 - $REDIS_BIN/redis-cli PING From 8fe49a02286de2f7d6de87c1f753b6bfa7928af9 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Tue, 22 Mar 2016 00:10:56 +0000 Subject: [PATCH 008/120] revert the change both sysctl and ulimit command was unsuccessful --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9e243b571..0153205e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,8 +28,6 @@ install: - tar -xvf redis-${REDIS_VERSION}.tar.gz - make -C redis-${REDIS_VERSION} PREFIX=$HOME/redis/${REDIS_VERSION} install before_script: - - sysctl -w fs.file-max=100000 - - ulimit -Sn 100000 - $REDIS_BIN/redis-server --daemonize yes - sleep 3 - $REDIS_BIN/redis-cli PING From 516202c95fcc1f4862a15629f8d924c5c1eded5f Mon Sep 17 00:00:00 2001 From: jackygurui Date: Tue, 22 Mar 2016 15:27:56 +0000 Subject: [PATCH 009/120] change the test redis version to 2.8.24. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0153205e2..1ccd879c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ language: java jdk: - oraclejdk8 env: - - REDIS_VERSION=3.0.7 + - REDIS_VERSION=2.8.24 cache: directories: - $HOME/.m2 From 3ada401c04764dfdbe409bd014f8a6eb6c80ab67 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Tue, 22 Mar 2016 21:21:59 +0000 Subject: [PATCH 010/120] Some tweaks to try Trying to use vm instead of container to see if the problem with inconsistent result is getting better. Switching back to use redis v3.0.7 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1ccd879c5..2a21106c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,13 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -sudo: false +sudo: true language: java jdk: - oraclejdk8 env: - - REDIS_VERSION=2.8.24 + - REDIS_VERSION=3.0.7 cache: directories: - $HOME/.m2 From 49ceb09b60da41971353dbb5bc9fc8e2aa352f95 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Tue, 22 Mar 2016 21:57:55 +0000 Subject: [PATCH 011/120] fixes and tweaks fixed sudo value should been required instead of true changed to use trusty distribution to see if inconsistent issue getting better --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2a21106c1..3bfa75efc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -sudo: true +sudo: required +dist: trusty language: java jdk: From 86fd0362123c4d0870bfde50b97adf5f3ee2f0d4 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Tue, 22 Mar 2016 22:48:22 +0000 Subject: [PATCH 012/120] disable sudo --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3bfa75efc..276b6f248 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -sudo: required +sudo: false dist: trusty language: java From 255db08d835f319a4365478240a4d76132b66ffd Mon Sep 17 00:00:00 2001 From: jackygurui Date: Tue, 22 Mar 2016 23:30:39 +0000 Subject: [PATCH 013/120] change testManyConnections connection size to 2000 --- src/test/java/org/redisson/RedissonTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/redisson/RedissonTest.java b/src/test/java/org/redisson/RedissonTest.java index 92b551c11..7eb2da834 100644 --- a/src/test/java/org/redisson/RedissonTest.java +++ b/src/test/java/org/redisson/RedissonTest.java @@ -264,8 +264,8 @@ public class RedissonTest { public void testManyConnections() { Config redisConfig = new Config(); redisConfig.useSingleServer() - .setConnectionMinimumIdleSize(10000) - .setConnectionPoolSize(10000) + .setConnectionMinimumIdleSize(2000) + .setConnectionPoolSize(2000) .setAddress("localhost:6379"); RedissonClient r = Redisson.create(redisConfig); r.shutdown(); From 709a3f1c31657560408bf540259ca9839b55cda2 Mon Sep 17 00:00:00 2001 From: Rui Gu Date: Wed, 23 Mar 2016 11:56:18 +0000 Subject: [PATCH 014/120] Revert "change testManyConnections connection size to 2000" This reverts commit 255db08d835f319a4365478240a4d76132b66ffd. --- src/test/java/org/redisson/RedissonTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/redisson/RedissonTest.java b/src/test/java/org/redisson/RedissonTest.java index 7eb2da834..92b551c11 100644 --- a/src/test/java/org/redisson/RedissonTest.java +++ b/src/test/java/org/redisson/RedissonTest.java @@ -264,8 +264,8 @@ public class RedissonTest { public void testManyConnections() { Config redisConfig = new Config(); redisConfig.useSingleServer() - .setConnectionMinimumIdleSize(2000) - .setConnectionPoolSize(2000) + .setConnectionMinimumIdleSize(10000) + .setConnectionPoolSize(10000) .setAddress("localhost:6379"); RedissonClient r = Redisson.create(redisConfig); r.shutdown(); From 449e6e3df476b079ac6d113e20bbdbfa41d38cfb Mon Sep 17 00:00:00 2001 From: Rui Gu Date: Wed, 23 Mar 2016 12:11:57 +0000 Subject: [PATCH 015/120] added travisEnv property used to ignore some tests ignore testManyConnections when travisEnv property is set to true --- .travis.yml | 2 +- src/test/java/org/redisson/RedissonTest.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 276b6f248..e2347a31c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,4 +34,4 @@ before_script: - $REDIS_BIN/redis-cli PING - export REDIS_VERSION="$(redis-cli INFO SERVER | sed -n 2p)" - echo $REDIS_VERSION -script: mvn -DargLine="-DredisBinary=$REDIS_BIN/redis-server" -Punit-test -Ptravis clean verify +script: mvn -DargLine="-DredisBinary=$REDIS_BIN/redis-server -DtravisEnv=true" -Punit-test -Ptravis clean verify diff --git a/src/test/java/org/redisson/RedissonTest.java b/src/test/java/org/redisson/RedissonTest.java index 92b551c11..75f5519bf 100644 --- a/src/test/java/org/redisson/RedissonTest.java +++ b/src/test/java/org/redisson/RedissonTest.java @@ -12,6 +12,7 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Assert; +import org.junit.Assume; import org.junit.Test; import org.redisson.RedisRunner.RedisProcess; import org.redisson.client.RedisConnectionException; @@ -262,6 +263,7 @@ public class RedissonTest { @Test public void testManyConnections() { + Assume.assumeFalse(Boolean.valueOf(System.getProperty("travisEnv"))); Config redisConfig = new Config(); redisConfig.useSingleServer() .setConnectionMinimumIdleSize(10000) From cf3a53888ac51f798b5ef1a580a27daf4bdeaa33 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Thu, 24 Mar 2016 12:25:42 +0300 Subject: [PATCH 016/120] Update CHANGELOG.md --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b9295791..4ffd4f0fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ Redisson Releases History ================================ ####Please Note: trunk is current development branch. +####23-Mar-2016 - version 2.2.10 released + +Feature - __new object added__ `RRemoteService` +Feature - __new object added__ `RSetMultimapCache` +Feature - __new object added__ `RListMultimapCache` +Improvement - ability to cancel BRPOP and BLPOP async command execution +Improvement - Config params validation +Improvement - test RedisRunner improvements (thanks to jackygurui) +Improvement - `Double.NEGATIVE_INFINITY` and `Double.POSITIVE_INFINITY` handling for ScoredSortedSet (thanks to jackygurui) +Fixed - MOVED, ASK handling in cluster mode using RBatch +Fixed - delete and expire logic for Multimap objects +Fixed - `RLock.tryLockAsync` NPE +Fixed - possible NPE during Redisson version logging +Fixed - Netty threads shutdown after connection error + ####04-Mar-2016 - version 2.2.9 released Feature - __new object added__ `RSetMultimap` From a523da1a9ce4e9095f0ea8d1a6d0e81601cb9577 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Thu, 24 Mar 2016 13:54:29 +0300 Subject: [PATCH 017/120] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3bf37cb73..2d94762a1 100644 --- a/README.md +++ b/README.md @@ -81,12 +81,12 @@ Include the following to your dependency list: org.redisson redisson - 2.2.9 + 2.2.10 ### Gradle - compile 'org.redisson:redisson:2.2.9' + compile 'org.redisson:redisson:2.2.10' ### Supported by From bb7a14c4ba91666c31f50db76c6752e9b5a4a715 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 24 Mar 2016 18:02:42 +0300 Subject: [PATCH 018/120] RemoteService ack support added --- .../org/redisson/RedissonRemoteService.java | 119 +++++++++++++----- .../redisson/RedissonShutdownException.java | 11 ++ .../org/redisson/codec/JsonJacksonCodec.java | 1 + .../redisson/command/CommandAsyncService.java | 3 +- .../MasterSlaveConnectionManager.java | 4 +- .../org/redisson/core/RRemoteService.java | 64 +++++++++- .../redisson/misc/InfinitySemaphoreLatch.java | 7 +- .../remote/RRemoteServiceResponse.java | 5 + .../org/redisson/remote/RemoteServiceAck.java | 11 ++ .../RemoteServiceAckTimeoutException.java | 21 ++++ .../{ => remote}/RemoteServiceKey.java | 2 +- .../{ => remote}/RemoteServiceMethod.java | 2 +- .../{ => remote}/RemoteServiceRequest.java | 20 ++- .../{ => remote}/RemoteServiceResponse.java | 4 +- .../remote/RemoteServiceTimeoutException.java | 17 +++ .../redisson/RedissonRemoteServiceTest.java | 12 +- 16 files changed, 251 insertions(+), 52 deletions(-) create mode 100644 src/main/java/org/redisson/RedissonShutdownException.java create mode 100644 src/main/java/org/redisson/remote/RRemoteServiceResponse.java create mode 100644 src/main/java/org/redisson/remote/RemoteServiceAck.java create mode 100644 src/main/java/org/redisson/remote/RemoteServiceAckTimeoutException.java rename src/main/java/org/redisson/{ => remote}/RemoteServiceKey.java (98%) rename src/main/java/org/redisson/{ => remote}/RemoteServiceMethod.java (97%) rename src/main/java/org/redisson/{ => remote}/RemoteServiceRequest.java (70%) rename src/main/java/org/redisson/{ => remote}/RemoteServiceResponse.java (92%) create mode 100644 src/main/java/org/redisson/remote/RemoteServiceTimeoutException.java diff --git a/src/main/java/org/redisson/RedissonRemoteService.java b/src/main/java/org/redisson/RedissonRemoteService.java index b80124260..ba99b01a4 100644 --- a/src/main/java/org/redisson/RedissonRemoteService.java +++ b/src/main/java/org/redisson/RedissonRemoteService.java @@ -23,12 +23,18 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import org.redisson.client.RedisException; -import org.redisson.client.RedisTimeoutException; import org.redisson.core.MessageListener; import org.redisson.core.RBlockingQueue; import org.redisson.core.RRemoteService; import org.redisson.core.RTopic; +import org.redisson.remote.RRemoteServiceResponse; +import org.redisson.remote.RemoteServiceAck; +import org.redisson.remote.RemoteServiceAckTimeoutException; +import org.redisson.remote.RemoteServiceKey; +import org.redisson.remote.RemoteServiceMethod; +import org.redisson.remote.RemoteServiceRequest; +import org.redisson.remote.RemoteServiceResponse; +import org.redisson.remote.RemoteServiceTimeoutException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -81,71 +87,126 @@ public class RedissonRemoteService implements RRemoteService { @Override public void operationComplete(Future future) throws Exception { if (!future.isSuccess()) { + if (future.cause() instanceof RedissonShutdownException) { + return; + } + subscribe(remoteInterface, requestQueue); return; } + subscribe(remoteInterface, requestQueue); RemoteServiceRequest request = future.getNow(); + if (System.currentTimeMillis() - request.getDate() > request.getAckTimeout()) { + log.debug("request: {} has been skipped due to ackTimeout"); + return; + } + RemoteServiceMethod method = beans.get(new RemoteServiceKey(remoteInterface, request.getMethodName())); String responseName = "redisson_remote_service:{" + remoteInterface.getName() + "}:" + request.getRequestId(); - RTopic topic = redisson.getTopic(responseName); - RemoteServiceResponse response; - try { - Object result = method.getMethod().invoke(method.getBean(), request.getArgs()); - response = new RemoteServiceResponse(result); - } catch (Exception e) { - response = new RemoteServiceResponse(e.getCause()); - log.error("Can't execute: " + request, e); + RTopic topic = redisson.getTopic(responseName); + Future ackClientsFuture = topic.publishAsync(new RemoteServiceAck()); + ackClientsFuture.addListener(new FutureListener() { + @Override + public void operationComplete(Future future) throws Exception { + if (!future.isSuccess()) { + log.error("Can't send ack for request: " + request, future.cause()); + return; + } + if (future.getNow() == 0) { + log.error("Client has not received ack for request: {}", request); + return; + } + + invokeMethod(request, method, topic); + } + }); + } + + }); + } + + private void invokeMethod(RemoteServiceRequest request, RemoteServiceMethod method, + RTopic topic) { + final AtomicReference responseHolder = new AtomicReference(); + try { + Object result = method.getMethod().invoke(method.getBean(), request.getArgs()); + RemoteServiceResponse response = new RemoteServiceResponse(result); + responseHolder.set(response); + } catch (Exception e) { + RemoteServiceResponse response = new RemoteServiceResponse(e.getCause()); + responseHolder.set(response); + log.error("Can't execute: " + request, e); + } + + Future clientsFuture = topic.publishAsync(responseHolder.get()); + clientsFuture.addListener(new FutureListener() { + @Override + public void operationComplete(Future future) throws Exception { + if (!future.isSuccess()) { + return; } - long clients = topic.publish(response); - if (clients == 0) { - log.error("None of clients has not received a response: {} for request: {}", response, request); + if (future.getNow() == 0) { + log.error("None of clients has not received a response: {} for request: {}", responseHolder.get(), request); } - - subscribe(remoteInterface, requestQueue); } }); } - + @Override public T get(Class remoteInterface) { return get(remoteInterface, -1, null); } @Override - public T get(final Class remoteInterface, final int timeout, final TimeUnit timeUnit) { + public T get(final Class remoteInterface, final long executionTimeout, final TimeUnit executionTimeUnit) { + return get(remoteInterface, executionTimeout, executionTimeUnit, 1, TimeUnit.SECONDS); + } + + public T get(final Class remoteInterface, final long executionTimeout, final TimeUnit executionTimeUnit, + final long ackTimeout, final TimeUnit ackTimeUnit) { InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String requestId = generateRequestId(); - + String requestQueueName = "redisson_remote_service:{" + remoteInterface.getName() + "}"; RBlockingQueue requestQueue = redisson.getBlockingQueue(requestQueueName); - RemoteServiceRequest request = new RemoteServiceRequest(requestId, method.getName(), args); + RemoteServiceRequest request = new RemoteServiceRequest(requestId, method.getName(), args, ackTimeUnit.toMillis(ackTimeout), System.currentTimeMillis()); requestQueue.add(request); String responseName = "redisson_remote_service:{" + remoteInterface.getName() + "}:" + requestId; - final RTopic topic = redisson.getTopic(responseName); + final CountDownLatch ackLatch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference response = new AtomicReference(); - int listenerId = topic.addListener(new MessageListener() { + final AtomicReference response = new AtomicReference(); + final RTopic topic = redisson.getTopic(responseName); + int listenerId = topic.addListener(new MessageListener() { @Override - public void onMessage(String channel, RemoteServiceResponse msg) { - response.set(msg); - latch.countDown(); + public void onMessage(String channel, RRemoteServiceResponse msg) { + if (msg instanceof RemoteServiceResponse) { + response.set(msg); + latch.countDown(); + } else { + ackLatch.countDown(); + } } }); - if (timeout == -1) { + if (!ackLatch.await(ackTimeout, ackTimeUnit)) { + topic.removeListener(listenerId); + throw new RemoteServiceAckTimeoutException("No ACK response after " + ackTimeUnit.toMillis(ackTimeout) + "ms for request: " + request); + } + + if (executionTimeout == -1) { latch.await(); } else { - if (!latch.await(timeout, timeUnit)) { + if (!latch.await(executionTimeout, executionTimeUnit)) { topic.removeListener(listenerId); - throw new RedisTimeoutException("No response after " + timeUnit.toMillis(timeout) + "ms for request: " + request); + throw new RemoteServiceTimeoutException("No response after " + executionTimeUnit.toMillis(executionTimeout) + "ms for request: " + request); } } topic.removeListener(listenerId); - RemoteServiceResponse msg = response.get(); + RemoteServiceResponse msg = (RemoteServiceResponse) response.get(); if (msg.getError() != null) { throw msg.getError(); } diff --git a/src/main/java/org/redisson/RedissonShutdownException.java b/src/main/java/org/redisson/RedissonShutdownException.java new file mode 100644 index 000000000..629255e0e --- /dev/null +++ b/src/main/java/org/redisson/RedissonShutdownException.java @@ -0,0 +1,11 @@ +package org.redisson; + +public class RedissonShutdownException extends RuntimeException { + + private static final long serialVersionUID = -2694051226420789395L; + + public RedissonShutdownException(String message) { + super(message); + } + +} diff --git a/src/main/java/org/redisson/codec/JsonJacksonCodec.java b/src/main/java/org/redisson/codec/JsonJacksonCodec.java index ab518b0ed..e90c5cef2 100755 --- a/src/main/java/org/redisson/codec/JsonJacksonCodec.java +++ b/src/main/java/org/redisson/codec/JsonJacksonCodec.java @@ -124,6 +124,7 @@ public class JsonJacksonCodec implements Codec { .withCreatorVisibility(JsonAutoDetect.Visibility.NONE)); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); objectMapper.configure(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN, true); + objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); objectMapper.addMixIn(Throwable.class, ThrowableMixIn.class); } diff --git a/src/main/java/org/redisson/command/CommandAsyncService.java b/src/main/java/org/redisson/command/CommandAsyncService.java index faba6d1fc..2abe97b96 100644 --- a/src/main/java/org/redisson/command/CommandAsyncService.java +++ b/src/main/java/org/redisson/command/CommandAsyncService.java @@ -27,6 +27,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.redisson.RedisClientResult; +import org.redisson.RedissonShutdownException; import org.redisson.SlotCallback; import org.redisson.client.RedisAskException; import org.redisson.client.RedisConnection; @@ -354,7 +355,7 @@ public class CommandAsyncService implements CommandAsyncExecutor { } if (!connectionManager.getShutdownLatch().acquire()) { - mainPromise.setFailure(new IllegalStateException("Redisson is shutdown")); + mainPromise.setFailure(new RedissonShutdownException("Redisson is shutdown")); return; } diff --git a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java index 065e66f75..fe2a22872 100644 --- a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java +++ b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java @@ -677,8 +677,10 @@ public class MasterSlaveConnectionManager implements ConnectionManager { @Override public void shutdown() { + shutdownLatch.close(); shutdownPromise.trySuccess(true); - shutdownLatch.closeAndAwaitUninterruptibly(); + shutdownLatch.awaitUninterruptibly(); + for (MasterSlaveEntry entry : entries.values()) { entry.shutdown(); } diff --git a/src/main/java/org/redisson/core/RRemoteService.java b/src/main/java/org/redisson/core/RRemoteService.java index 1b75c5dc3..0461188ed 100644 --- a/src/main/java/org/redisson/core/RRemoteService.java +++ b/src/main/java/org/redisson/core/RRemoteService.java @@ -17,6 +17,46 @@ package org.redisson.core; import java.util.concurrent.TimeUnit; +import org.redisson.remote.RemoteServiceAckTimeoutException; + +/** + * Allows to execute object methods remotely between Redisson instances (Server side and Client side instances in terms of remote invocation). + *

+ * 1. Server side instance (worker instance). Register object with RRemoteService instance. + *

+ * + * RRemoteService remoteService = redisson.getRemoteService();
+ *
+ * // register remote service before any remote invocation
+ * remoteService.register(SomeServiceInterface.class, someServiceImpl); + *
+ *

+ * 2. Client side instance. Invokes method remotely. + *

+ * + * RRemoteService remoteService = redisson.getRemoteService();
+ * SomeServiceInterface service = remoteService.get(SomeServiceInterface.class);
+ *
+ * String result = service.doSomeStuff(1L, "secondParam", new AnyParam()); + *
+ *

+ *

+ * There are two timeouts during execution: + *

+ * Acknowledge (Ack) timeout.Client side instance waits for acknowledge message from Server side instance. + *

+ * If acknowledge has not been received by Client side instance then RemoteServiceAckTimeoutException will be thrown. + * And next invocation attempt can be made. + *

+ * If acknowledge has not been received Client side instance but Server side instance has received invocation message already. + * In this case invocation will be skipped, due to ack timeout checking by Server side instance. + *

+ * Execution timeout. Client side instance received acknowledge message. If it hasn't received any result or error + * from server side during execution timeout then RemoteServiceTimeoutException will be thrown. + * + * @author Nikita Koksharov + * + */ public interface RRemoteService { /** @@ -37,7 +77,8 @@ public interface RRemoteService { void register(Class remoteInterface, T object, int executorsAmount); /** - * Get remote service object for remote invocations + * Get remote service object for remote invocations. + * Uses ack timeout = 1000 ms by default * * @param remoteInterface * @return @@ -46,13 +87,26 @@ public interface RRemoteService { /** * Get remote service object for remote invocations - * with specified invocation timeout + * with specified invocation timeout. Uses ack timeout = 1000 ms by default + * + * @param remoteInterface + * @param executionTimeout - invocation timeout + * @param executionTimeUnit + * @return + */ + T get(Class remoteInterface, long executionTimeout, TimeUnit executionTimeUnit); + + /** + * Get remote service object for remote invocations + * with specified invocation and ack timeouts * * @param remoteInterface - * @param timeout - invocation timeout - * @param timeUnit + * @param executionTimeout - invocation timeout + * @param executionTimeUnit + * @param ackTimeout - ack timeout + * @param ackTimeUnit * @return */ - T get(Class remoteInterface, int timeout, TimeUnit timeUnit); + T get(Class remoteInterface, long executionTimeout, TimeUnit executionTimeUnit, long ackTimeout, TimeUnit ackTimeUnit); } diff --git a/src/main/java/org/redisson/misc/InfinitySemaphoreLatch.java b/src/main/java/org/redisson/misc/InfinitySemaphoreLatch.java index 1242ce571..e737cd711 100644 --- a/src/main/java/org/redisson/misc/InfinitySemaphoreLatch.java +++ b/src/main/java/org/redisson/misc/InfinitySemaphoreLatch.java @@ -83,9 +83,12 @@ public class InfinitySemaphoreLatch extends AbstractQueuedSynchronizer { return closed; } + public void close() { + closed = true; + } + // waiting for an open state - public final boolean closeAndAwaitUninterruptibly() { - closed = true; + public final boolean awaitUninterruptibly() { try { return await(15, TimeUnit.SECONDS); } catch (InterruptedException e) { diff --git a/src/main/java/org/redisson/remote/RRemoteServiceResponse.java b/src/main/java/org/redisson/remote/RRemoteServiceResponse.java new file mode 100644 index 000000000..de563757e --- /dev/null +++ b/src/main/java/org/redisson/remote/RRemoteServiceResponse.java @@ -0,0 +1,5 @@ +package org.redisson.remote; + +public interface RRemoteServiceResponse { + +} diff --git a/src/main/java/org/redisson/remote/RemoteServiceAck.java b/src/main/java/org/redisson/remote/RemoteServiceAck.java new file mode 100644 index 000000000..497fe1e7b --- /dev/null +++ b/src/main/java/org/redisson/remote/RemoteServiceAck.java @@ -0,0 +1,11 @@ +package org.redisson.remote; + +/** + * Worker sends this message when it has received a {@link RemoteServiceRequest}. + * + * @author Nikita Koksharov + * + */ +public class RemoteServiceAck implements RRemoteServiceResponse { + +} diff --git a/src/main/java/org/redisson/remote/RemoteServiceAckTimeoutException.java b/src/main/java/org/redisson/remote/RemoteServiceAckTimeoutException.java new file mode 100644 index 000000000..76d4b979e --- /dev/null +++ b/src/main/java/org/redisson/remote/RemoteServiceAckTimeoutException.java @@ -0,0 +1,21 @@ +package org.redisson.remote; + +/** + * Rises when remote method executor has not answered + * within Ack timeout. + *

+ * Method invocation has not been started in this case. + * So a new invocation attempt can be made. + * + * @author Nikita Koksharov + * + */ +public class RemoteServiceAckTimeoutException extends RuntimeException { + + private static final long serialVersionUID = 1820133675653636587L; + + public RemoteServiceAckTimeoutException(String message) { + super(message); + } + +} diff --git a/src/main/java/org/redisson/RemoteServiceKey.java b/src/main/java/org/redisson/remote/RemoteServiceKey.java similarity index 98% rename from src/main/java/org/redisson/RemoteServiceKey.java rename to src/main/java/org/redisson/remote/RemoteServiceKey.java index 5cadb3a5a..20f7ea9ff 100644 --- a/src/main/java/org/redisson/RemoteServiceKey.java +++ b/src/main/java/org/redisson/remote/RemoteServiceKey.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.redisson; +package org.redisson.remote; public class RemoteServiceKey { diff --git a/src/main/java/org/redisson/RemoteServiceMethod.java b/src/main/java/org/redisson/remote/RemoteServiceMethod.java similarity index 97% rename from src/main/java/org/redisson/RemoteServiceMethod.java rename to src/main/java/org/redisson/remote/RemoteServiceMethod.java index 153c82d19..26998214f 100644 --- a/src/main/java/org/redisson/RemoteServiceMethod.java +++ b/src/main/java/org/redisson/remote/RemoteServiceMethod.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.redisson; +package org.redisson.remote; import java.lang.reflect.Method; diff --git a/src/main/java/org/redisson/RemoteServiceRequest.java b/src/main/java/org/redisson/remote/RemoteServiceRequest.java similarity index 70% rename from src/main/java/org/redisson/RemoteServiceRequest.java rename to src/main/java/org/redisson/remote/RemoteServiceRequest.java index 434784456..f47324f10 100644 --- a/src/main/java/org/redisson/RemoteServiceRequest.java +++ b/src/main/java/org/redisson/remote/RemoteServiceRequest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.redisson; +package org.redisson.remote; import java.util.Arrays; @@ -22,15 +22,27 @@ public class RemoteServiceRequest { private String requestId; private String methodName; private Object[] args; + private long ackTimeout; + private long date; public RemoteServiceRequest() { } - public RemoteServiceRequest(String requestId, String methodName, Object[] args) { + public RemoteServiceRequest(String requestId, String methodName, Object[] args, long ackTimeout, long date) { super(); this.requestId = requestId; this.methodName = methodName; this.args = args; + this.ackTimeout = ackTimeout; + this.date = date; + } + + public long getDate() { + return date; + } + + public long getAckTimeout() { + return ackTimeout; } public String getRequestId() { @@ -47,8 +59,8 @@ public class RemoteServiceRequest { @Override public String toString() { - return "RemoteServiceRequest[requestId=" + requestId + ", methodName=" + methodName + ", args=" - + Arrays.toString(args) + "]"; + return "RemoteServiceRequest [requestId=" + requestId + ", methodName=" + methodName + ", args=" + + Arrays.toString(args) + ", ackTimeout=" + ackTimeout + ", date=" + date + "]"; } } diff --git a/src/main/java/org/redisson/RemoteServiceResponse.java b/src/main/java/org/redisson/remote/RemoteServiceResponse.java similarity index 92% rename from src/main/java/org/redisson/RemoteServiceResponse.java rename to src/main/java/org/redisson/remote/RemoteServiceResponse.java index 09ebc5999..55d074870 100644 --- a/src/main/java/org/redisson/RemoteServiceResponse.java +++ b/src/main/java/org/redisson/remote/RemoteServiceResponse.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.redisson; +package org.redisson.remote; -public class RemoteServiceResponse { +public class RemoteServiceResponse implements RRemoteServiceResponse { private Object result; private Throwable error; diff --git a/src/main/java/org/redisson/remote/RemoteServiceTimeoutException.java b/src/main/java/org/redisson/remote/RemoteServiceTimeoutException.java new file mode 100644 index 000000000..27316abf6 --- /dev/null +++ b/src/main/java/org/redisson/remote/RemoteServiceTimeoutException.java @@ -0,0 +1,17 @@ +package org.redisson.remote; + +/** + * Rises when invocation timeout has been occurred + * + * @author Nikita Koksharov + * + */ +public class RemoteServiceTimeoutException extends RuntimeException { + + private static final long serialVersionUID = -1749266931994840256L; + + public RemoteServiceTimeoutException(String message) { + super(message); + } + +} diff --git a/src/test/java/org/redisson/RedissonRemoteServiceTest.java b/src/test/java/org/redisson/RedissonRemoteServiceTest.java index c8daedd90..5739bc7bc 100644 --- a/src/test/java/org/redisson/RedissonRemoteServiceTest.java +++ b/src/test/java/org/redisson/RedissonRemoteServiceTest.java @@ -1,14 +1,14 @@ package org.redisson; -import org.junit.Assert; -import org.junit.Test; -import org.redisson.client.RedisTimeoutException; - -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; import java.util.concurrent.TimeUnit; +import org.junit.Assert; +import org.junit.Test; +import org.redisson.remote.RemoteServiceTimeoutException; + public class RedissonRemoteServiceTest extends BaseTest { public interface RemoteInterface { @@ -59,7 +59,7 @@ public class RedissonRemoteServiceTest extends BaseTest { } - @Test(expected = RedisTimeoutException.class) + @Test(expected = RemoteServiceTimeoutException.class) public void testTimeout() throws InterruptedException { RedissonClient r1 = Redisson.create(); r1.getRemoteSerivce().register(RemoteInterface.class, new RemoteImpl()); From c2414c3864f49956bb3b93b2f8aa22a7da088a17 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 24 Mar 2016 18:03:00 +0300 Subject: [PATCH 019/120] licenses added --- .../org/redisson/RedissonShutdownException.java | 15 +++++++++++++++ .../redisson/remote/RRemoteServiceResponse.java | 15 +++++++++++++++ .../org/redisson/remote/RemoteServiceAck.java | 15 +++++++++++++++ .../remote/RemoteServiceAckTimeoutException.java | 15 +++++++++++++++ .../remote/RemoteServiceTimeoutException.java | 15 +++++++++++++++ 5 files changed, 75 insertions(+) diff --git a/src/main/java/org/redisson/RedissonShutdownException.java b/src/main/java/org/redisson/RedissonShutdownException.java index 629255e0e..ddfcb1aa1 100644 --- a/src/main/java/org/redisson/RedissonShutdownException.java +++ b/src/main/java/org/redisson/RedissonShutdownException.java @@ -1,3 +1,18 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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; public class RedissonShutdownException extends RuntimeException { diff --git a/src/main/java/org/redisson/remote/RRemoteServiceResponse.java b/src/main/java/org/redisson/remote/RRemoteServiceResponse.java index de563757e..67305dd00 100644 --- a/src/main/java/org/redisson/remote/RRemoteServiceResponse.java +++ b/src/main/java/org/redisson/remote/RRemoteServiceResponse.java @@ -1,3 +1,18 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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.remote; public interface RRemoteServiceResponse { diff --git a/src/main/java/org/redisson/remote/RemoteServiceAck.java b/src/main/java/org/redisson/remote/RemoteServiceAck.java index 497fe1e7b..2d34c4804 100644 --- a/src/main/java/org/redisson/remote/RemoteServiceAck.java +++ b/src/main/java/org/redisson/remote/RemoteServiceAck.java @@ -1,3 +1,18 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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.remote; /** diff --git a/src/main/java/org/redisson/remote/RemoteServiceAckTimeoutException.java b/src/main/java/org/redisson/remote/RemoteServiceAckTimeoutException.java index 76d4b979e..566fc61b9 100644 --- a/src/main/java/org/redisson/remote/RemoteServiceAckTimeoutException.java +++ b/src/main/java/org/redisson/remote/RemoteServiceAckTimeoutException.java @@ -1,3 +1,18 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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.remote; /** diff --git a/src/main/java/org/redisson/remote/RemoteServiceTimeoutException.java b/src/main/java/org/redisson/remote/RemoteServiceTimeoutException.java index 27316abf6..b5d5720c0 100644 --- a/src/main/java/org/redisson/remote/RemoteServiceTimeoutException.java +++ b/src/main/java/org/redisson/remote/RemoteServiceTimeoutException.java @@ -1,3 +1,18 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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.remote; /** From c503d445987d2a27f89eeafe98b41897cbf41091 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 24 Mar 2016 19:47:00 +0300 Subject: [PATCH 020/120] Compilation fixed --- src/main/java/org/redisson/RedissonRemoteService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/redisson/RedissonRemoteService.java b/src/main/java/org/redisson/RedissonRemoteService.java index ba99b01a4..d424128a3 100644 --- a/src/main/java/org/redisson/RedissonRemoteService.java +++ b/src/main/java/org/redisson/RedissonRemoteService.java @@ -95,15 +95,15 @@ public class RedissonRemoteService implements RRemoteService { } subscribe(remoteInterface, requestQueue); - RemoteServiceRequest request = future.getNow(); + final RemoteServiceRequest request = future.getNow(); if (System.currentTimeMillis() - request.getDate() > request.getAckTimeout()) { log.debug("request: {} has been skipped due to ackTimeout"); return; } - RemoteServiceMethod method = beans.get(new RemoteServiceKey(remoteInterface, request.getMethodName())); + final RemoteServiceMethod method = beans.get(new RemoteServiceKey(remoteInterface, request.getMethodName())); String responseName = "redisson_remote_service:{" + remoteInterface.getName() + "}:" + request.getRequestId(); - RTopic topic = redisson.getTopic(responseName); + final RTopic topic = redisson.getTopic(responseName); Future ackClientsFuture = topic.publishAsync(new RemoteServiceAck()); ackClientsFuture.addListener(new FutureListener() { @Override @@ -125,7 +125,7 @@ public class RedissonRemoteService implements RRemoteService { }); } - private void invokeMethod(RemoteServiceRequest request, RemoteServiceMethod method, + private void invokeMethod(final RemoteServiceRequest request, RemoteServiceMethod method, RTopic topic) { final AtomicReference responseHolder = new AtomicReference(); try { From af01f891b713b8b01f6c453a79f5ca40a5fb6777 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 24 Mar 2016 19:47:22 +0300 Subject: [PATCH 021/120] libs updated --- pom.xml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 106c3a909..111ee57f8 100644 --- a/pom.xml +++ b/pom.xml @@ -100,33 +100,33 @@ io.netty netty-transport-native-epoll - 4.0.34.Final + 4.0.35.Final provided io.netty netty-common - 4.0.34.Final + 4.0.35.Final io.netty netty-codec - 4.0.34.Final + 4.0.35.Final io.netty netty-buffer - 4.0.34.Final + 4.0.35.Final io.netty netty-transport - 4.0.34.Final + 4.0.35.Final io.netty netty-handler - 4.0.34.Final + 4.0.35.Final @@ -145,6 +145,7 @@ com.jayway.awaitility awaitility 1.7.0 + test junit From faf8dd5975b3c7aeb18881a3c391b291004cb3cb Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Thu, 24 Mar 2016 19:49:13 +0300 Subject: [PATCH 022/120] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2d94762a1..2753486b5 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ Features * [Spring cache](http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html) integration * Supports [Reactive Streams](http://www.reactive-streams.org) * Supports [Redis pipelining](http://redis.io/topics/pipelining) (command batches) +* Supports [Remote services](https://github.com/mrniko/redisson/wiki/5.-distributed-objects#513-remote-service) * Supports Android platform * Supports auto-reconnect * Supports failed to send command auto-retry From 166735b5a1b55903486c7b252e27d585bfade846 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 25 Mar 2016 12:20:19 +0300 Subject: [PATCH 023/120] Redisson shutdown handling when queue.take or poll methods were invoked --- src/main/java/org/redisson/command/CommandAsyncService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/redisson/command/CommandAsyncService.java b/src/main/java/org/redisson/command/CommandAsyncService.java index 2abe97b96..b6c753077 100644 --- a/src/main/java/org/redisson/command/CommandAsyncService.java +++ b/src/main/java/org/redisson/command/CommandAsyncService.java @@ -488,7 +488,7 @@ public class CommandAsyncService implements CommandAsyncExecutor { final FutureListener listener = new FutureListener() { @Override public void operationComplete(Future future) throws Exception { - details.getMainPromise().cancel(true); + details.getMainPromise().tryFailure(new RedissonShutdownException("Redisson is shutdown")); } }; details.getMainPromise().addListener(new FutureListener() { @@ -496,6 +496,9 @@ public class CommandAsyncService implements CommandAsyncExecutor { public void operationComplete(Future future) throws Exception { connectionManager.getShutdownPromise().removeListener(listener); if (!future.isCancelled()) { + if (future.cause() instanceof RedissonShutdownException) { + details.getAttemptPromise().cancel(true); + } return; } // cancel handling for commands from skipTimeout collection From 4f138fde01a9c9dcd2c341dcfff80c567a7bf970 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 25 Mar 2016 14:19:29 +0300 Subject: [PATCH 024/120] refactoring --- .../client/handler/CommandDecoder.java | 28 +++--- .../client/handler/CommandsQueue.java | 2 +- .../MasterSlaveConnectionManager.java | 93 ++++++++++--------- 3 files changed, 66 insertions(+), 57 deletions(-) diff --git a/src/main/java/org/redisson/client/handler/CommandDecoder.java b/src/main/java/org/redisson/client/handler/CommandDecoder.java index d8ce3d733..f0b5ce652 100644 --- a/src/main/java/org/redisson/client/handler/CommandDecoder.java +++ b/src/main/java/org/redisson/client/handler/CommandDecoder.java @@ -68,11 +68,11 @@ public class CommandDecoder extends ReplayingDecoder { private static final char ZERO = '0'; // It is not needed to use concurrent map because responses are coming consecutive - private final Map> messageDecoders = new HashMap>(); - private final Map> channels = PlatformDependent.newConcurrentHashMap(); + private final Map> pubSubMessageDecoders = new HashMap>(); + private final Map> pubSubChannels = PlatformDependent.newConcurrentHashMap(); - public void addChannel(String channel, CommandData data) { - channels.put(channel, data); + public void addPubSubCommand(String channel, CommandData data) { + pubSubChannels.put(channel, data); } @Override @@ -271,14 +271,14 @@ public class CommandDecoder extends ReplayingDecoder { } else { if (result instanceof PubSubStatusMessage) { String channelName = ((PubSubStatusMessage) result).getChannel(); - CommandData d = channels.get(channelName); + CommandData d = pubSubChannels.get(channelName); if (Arrays.asList("PSUBSCRIBE", "SUBSCRIBE").contains(d.getCommand().getName())) { - channels.remove(channelName); - messageDecoders.put(channelName, d.getMessageDecoder()); + pubSubChannels.remove(channelName); + pubSubMessageDecoders.put(channelName, d.getMessageDecoder()); } if (Arrays.asList("PUNSUBSCRIBE", "UNSUBSCRIBE").contains(d.getCommand().getName())) { - channels.remove(channelName); - messageDecoders.remove(channelName); + pubSubChannels.remove(channelName); + pubSubMessageDecoders.remove(channelName); } } @@ -314,17 +314,17 @@ public class CommandDecoder extends ReplayingDecoder { if (data == null) { if (Arrays.asList("subscribe", "psubscribe", "punsubscribe", "unsubscribe").contains(parts.get(0))) { String channelName = (String) parts.get(1); - CommandData commandData = channels.get(channelName); + CommandData commandData = pubSubChannels.get(channelName); if (commandData == null) { return null; } return commandData.getCommand().getReplayMultiDecoder(); } else if (parts.get(0).equals("message")) { String channelName = (String) parts.get(1); - return messageDecoders.get(channelName); + return pubSubMessageDecoders.get(channelName); } else if (parts.get(0).equals("pmessage")) { String patternName = (String) parts.get(1); - return messageDecoders.get(patternName); + return pubSubMessageDecoders.get(patternName); } } @@ -335,11 +335,11 @@ public class CommandDecoder extends ReplayingDecoder { if (data == null) { if (parts.size() == 2 && parts.get(0).equals("message")) { String channelName = (String) parts.get(1); - return messageDecoders.get(channelName); + return pubSubMessageDecoders.get(channelName); } if (parts.size() == 3 && parts.get(0).equals("pmessage")) { String patternName = (String) parts.get(1); - return messageDecoders.get(patternName); + return pubSubMessageDecoders.get(patternName); } return currentDecoder; } diff --git a/src/main/java/org/redisson/client/handler/CommandsQueue.java b/src/main/java/org/redisson/client/handler/CommandsQueue.java index 985ad2656..ff65b9d4c 100644 --- a/src/main/java/org/redisson/client/handler/CommandsQueue.java +++ b/src/main/java/org/redisson/client/handler/CommandsQueue.java @@ -82,7 +82,7 @@ public class CommandsQueue extends ChannelDuplexHandler { if (!pubSubOps.isEmpty()) { for (CommandData cd : pubSubOps) { for (Object channel : cd.getParams()) { - ch.pipeline().get(CommandDecoder.class).addChannel(channel.toString(), cd); + ch.pipeline().get(CommandDecoder.class).addPubSubCommand(channel.toString(), cd); } } } else { diff --git a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java index fe2a22872..c0a11c4aa 100644 --- a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java +++ b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java @@ -551,7 +551,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager { for (Entry mapEntry : name2PubSubConnection.entrySet()) { for (RedisPubSubConnection redisPubSubConnection : allPubSubConnections) { PubSubConnectionEntry pubSubEntry = mapEntry.getValue(); - final String channelName = mapEntry.getKey(); + String channelName = mapEntry.getKey(); if (!pubSubEntry.getConnection().equals(redisPubSubConnection)) { continue; @@ -560,55 +560,64 @@ public class MasterSlaveConnectionManager implements ConnectionManager { synchronized (pubSubEntry) { pubSubEntry.close(); - final Collection listeners = pubSubEntry.getListeners(channelName); + Collection listeners = pubSubEntry.getListeners(channelName); if (pubSubEntry.getConnection().getPatternChannels().get(channelName) != null) { - Codec subscribeCodec = punsubscribe(channelName); - if (!listeners.isEmpty()) { - Future future = psubscribe(channelName, subscribeCodec); - future.addListener(new FutureListener() { - @Override - public void operationComplete(Future future) - throws Exception { - if (!future.isSuccess()) { - log.error("Can't resubscribe topic channel: " + channelName); - return; - } - - PubSubConnectionEntry newEntry = future.getNow(); - for (RedisPubSubListener redisPubSubListener : listeners) { - newEntry.addListener(channelName, redisPubSubListener); - } - log.debug("resubscribed listeners for '{}' channel-pattern", channelName); - } - }); - } + reattachPatternPubSubListeners(channelName, listeners); } else { - Codec subscribeCodec = unsubscribe(channelName); - if (!listeners.isEmpty()) { - Future future = subscribe(subscribeCodec, channelName, null); - future.addListener(new FutureListener() { - - @Override - public void operationComplete(Future future) - throws Exception { - if (!future.isSuccess()) { - log.error("Can't resubscribe topic channel: " + channelName); - return; - } - PubSubConnectionEntry newEntry = future.getNow(); - for (RedisPubSubListener redisPubSubListener : listeners) { - newEntry.addListener(channelName, redisPubSubListener); - } - log.debug("resubscribed listeners for '{}' channel", channelName); - } - }); - } + reattachPubSubListeners(channelName, listeners); } } } } } + private void reattachPubSubListeners(final String channelName, final Collection listeners) { + Codec subscribeCodec = unsubscribe(channelName); + if (!listeners.isEmpty()) { + Future future = subscribe(subscribeCodec, channelName, null); + future.addListener(new FutureListener() { + + @Override + public void operationComplete(Future future) + throws Exception { + if (!future.isSuccess()) { + log.error("Can't resubscribe topic channel: " + channelName); + return; + } + PubSubConnectionEntry newEntry = future.getNow(); + for (RedisPubSubListener redisPubSubListener : listeners) { + newEntry.addListener(channelName, redisPubSubListener); + } + log.debug("resubscribed listeners for '{}' channel", channelName); + } + }); + } + } + + private void reattachPatternPubSubListeners(final String channelName, + final Collection listeners) { + Codec subscribeCodec = punsubscribe(channelName); + if (!listeners.isEmpty()) { + Future future = psubscribe(channelName, subscribeCodec); + future.addListener(new FutureListener() { + @Override + public void operationComplete(Future future) + throws Exception { + if (!future.isSuccess()) { + log.error("Can't resubscribe topic channel: " + channelName); + return; + } + + PubSubConnectionEntry newEntry = future.getNow(); + for (RedisPubSubListener redisPubSubListener : listeners) { + newEntry.addListener(channelName, redisPubSubListener); + } + log.debug("resubscribed listeners for '{}' channel-pattern", channelName); + } + }); + } + } + protected void slaveDown(ClusterSlotRange slotRange, String host, int port, FreezeReason freezeReason) { MasterSlaveEntry entry = getEntry(slotRange); slaveDown(entry, host, port, freezeReason); From 1a2ea4db43f5e7d3075aa02621894c940f5a8a6a Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 25 Mar 2016 15:33:34 +0300 Subject: [PATCH 025/120] Long BlockingQueue\Deque commands reattaching. #449 --- .../org/redisson/client/RedisConnection.java | 10 +++++ .../client/handler/ConnectionWatchdog.java | 33 ++++++++++++--- .../client/protocol/QueueCommand.java | 3 ++ .../redisson/command/CommandAsyncService.java | 7 +--- .../redisson/RedissonBlockingQueueTest.java | 40 +++++++++++++++++++ 5 files changed, 83 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/redisson/client/RedisConnection.java b/src/main/java/org/redisson/client/RedisConnection.java index 53f1c9383..9db33c5c0 100644 --- a/src/main/java/org/redisson/client/RedisConnection.java +++ b/src/main/java/org/redisson/client/RedisConnection.java @@ -18,8 +18,10 @@ package org.redisson.client; import java.util.concurrent.TimeUnit; import org.redisson.client.codec.Codec; +import org.redisson.client.handler.CommandsQueue; import org.redisson.client.protocol.CommandData; import org.redisson.client.protocol.CommandsData; +import org.redisson.client.protocol.QueueCommand; import org.redisson.client.protocol.RedisCommand; import org.redisson.client.protocol.RedisCommands; import org.redisson.client.protocol.RedisStrictCommand; @@ -59,6 +61,14 @@ public class RedisConnection implements RedisCommands { return (C) channel.attr(RedisConnection.CONNECTION).get(); } + public CommandData getCurrentCommand() { + QueueCommand command = channel.attr(CommandsQueue.CURRENT_COMMAND).get(); + if (command instanceof CommandData) { + return (CommandData)command; + } + return null; + } + public long getLastUsageTime() { return lastUsageTime; } diff --git a/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java b/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java index 81a030ce6..1afce25e6 100644 --- a/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java +++ b/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java @@ -22,6 +22,8 @@ import org.redisson.client.RedisConnection; import org.redisson.client.RedisException; import org.redisson.client.RedisPubSubConnection; import org.redisson.client.codec.Codec; +import org.redisson.client.protocol.CommandData; +import org.redisson.client.protocol.QueueCommand; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -121,18 +123,16 @@ public class ConnectionWatchdog extends ChannelInboundHandlerAdapter { @Override public void operationComplete(Future future) throws Exception { if (future.isSuccess()) { - connection.updateChannel(channel); - resubscribe(connection); + refresh(connection, channel); } } }); } else { - connection.updateChannel(channel); - resubscribe(connection); + refresh(connection, channel); } } - private void resubscribe(RedisConnection connection) { + private void reattachPubSub(RedisConnection connection) { if (connection instanceof RedisPubSubConnection) { RedisPubSubConnection conn = (RedisPubSubConnection) connection; for (Entry entry : conn.getChannels().entrySet()) { @@ -149,4 +149,27 @@ public class ConnectionWatchdog extends ChannelInboundHandlerAdapter { ctx.channel().close(); } + private void refresh(RedisConnection connection, Channel channel) { + CommandData commandData = connection.getCurrentCommand(); + connection.updateChannel(channel); + + reattachBlockingQueue(connection, commandData); + reattachPubSub(connection); + } + + private void reattachBlockingQueue(RedisConnection connection, final CommandData commandData) { + if (commandData != null + && QueueCommand.TIMEOUTLESS_COMMANDS.contains(commandData.getCommand().getName())) { + ChannelFuture future = connection.send(commandData); + future.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + if (!future.isSuccess()) { + log.error("Can't reconnect blocking queue to new connection {}", commandData); + } + } + }); + } + } + } diff --git a/src/main/java/org/redisson/client/protocol/QueueCommand.java b/src/main/java/org/redisson/client/protocol/QueueCommand.java index 028810a34..33582c982 100644 --- a/src/main/java/org/redisson/client/protocol/QueueCommand.java +++ b/src/main/java/org/redisson/client/protocol/QueueCommand.java @@ -23,6 +23,9 @@ import java.util.Set; public interface QueueCommand { Set PUBSUB_COMMANDS = new HashSet(Arrays.asList("PSUBSCRIBE", "SUBSCRIBE", "PUNSUBSCRIBE", "UNSUBSCRIBE")); + + Set TIMEOUTLESS_COMMANDS = new HashSet(Arrays.asList(RedisCommands.BLPOP_VALUE.getName(), + RedisCommands.BRPOP_VALUE.getName(), RedisCommands.BRPOPLPUSH.getName())); List> getPubSubOperations(); diff --git a/src/main/java/org/redisson/command/CommandAsyncService.java b/src/main/java/org/redisson/command/CommandAsyncService.java index b6c753077..1abdce13d 100644 --- a/src/main/java/org/redisson/command/CommandAsyncService.java +++ b/src/main/java/org/redisson/command/CommandAsyncService.java @@ -20,7 +20,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -39,6 +38,7 @@ import org.redisson.client.WriteRedisConnectionException; import org.redisson.client.codec.Codec; import org.redisson.client.protocol.CommandData; import org.redisson.client.protocol.CommandsData; +import org.redisson.client.protocol.QueueCommand; import org.redisson.client.protocol.RedisCommand; import org.redisson.client.protocol.RedisCommands; import org.redisson.cluster.ClusterSlotRange; @@ -68,9 +68,6 @@ public class CommandAsyncService implements CommandAsyncExecutor { final ConnectionManager connectionManager; - private final Set skipTimeout = new HashSet(Arrays.asList(RedisCommands.BLPOP_VALUE.getName(), - RedisCommands.BRPOP_VALUE.getName(), RedisCommands.BRPOPLPUSH.getName())); - public CommandAsyncService(ConnectionManager connectionManager) { this.connectionManager = connectionManager; } @@ -461,7 +458,7 @@ public class CommandAsyncService implements CommandAsyncExecutor { details.getTimeout().cancel(); int timeoutTime = connectionManager.getConfig().getTimeout(); - if (skipTimeout.contains(details.getCommand().getName())) { + if (QueueCommand.TIMEOUTLESS_COMMANDS.contains(details.getCommand().getName())) { Integer popTimeout = Integer.valueOf(details.getParams()[details.getParams().length - 1].toString()); handleBlockingOperations(details, connection); if (popTimeout == 0) { diff --git a/src/test/java/org/redisson/RedissonBlockingQueueTest.java b/src/test/java/org/redisson/RedissonBlockingQueueTest.java index 5fab2294d..e4c5938be 100644 --- a/src/test/java/org/redisson/RedissonBlockingQueueTest.java +++ b/src/test/java/org/redisson/RedissonBlockingQueueTest.java @@ -1,7 +1,9 @@ package org.redisson; +import static com.jayway.awaitility.Awaitility.await; import static org.assertj.core.api.Assertions.*; +import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; @@ -11,18 +13,56 @@ import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; +import org.redisson.RedisRunner.RedisProcess; import org.redisson.core.RBlockingQueue; import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.FutureListener; public class RedissonBlockingQueueTest extends BaseTest { + @Test + public void testTakeReattach() throws InterruptedException, IOException { + RedisProcess runner = new RedisRunner().port(6319).run(); + + Config config = new Config(); + config.useSingleServer().setAddress("127.0.0.1:6319"); + RedissonClient redisson = Redisson.create(config); + final RBlockingQueue queue1 = redisson.getBlockingQueue("queue:pollany"); + Future f = queue1.takeAsync(); + + final AtomicBoolean executed = new AtomicBoolean(); + f.addListener(new FutureListener() { + @Override + public void operationComplete(Future future) throws Exception { + assertThat(future.get()).isEqualTo(123); + executed.set(true); + } + }); + + f.await(1, TimeUnit.SECONDS); + runner.stop(); + + runner = new RedisRunner().port(6319).run(); + queue1.put(123); + + // check connection rotation + for (int i = 0; i < 10; i++) { + queue1.put(i); + } + assertThat(queue1.size()).isEqualTo(10); + + await().atMost(1, TimeUnit.SECONDS).until(() -> assertThat(executed.get()).isTrue()); + runner.stop(); + } + @Test public void testTakeAsyncCancel() { Config config = createConfig(); From f0f97a44d1c7d64615e12641c2eedbf94fd3d4f6 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 25 Mar 2016 16:08:16 +0300 Subject: [PATCH 026/120] Few enhancements. #449 --- .../org/redisson/client/RedisConnection.java | 4 +++ .../redisson/command/CommandAsyncService.java | 15 ++++++-- .../redisson/RedissonBlockingQueueTest.java | 36 +++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/redisson/client/RedisConnection.java b/src/main/java/org/redisson/client/RedisConnection.java index 9db33c5c0..9be80dec3 100644 --- a/src/main/java/org/redisson/client/RedisConnection.java +++ b/src/main/java/org/redisson/client/RedisConnection.java @@ -61,6 +61,10 @@ public class RedisConnection implements RedisCommands { return (C) channel.attr(RedisConnection.CONNECTION).get(); } + public void removeCurrentCommand() { + channel.attr(CommandsQueue.CURRENT_COMMAND).remove(); + } + public CommandData getCurrentCommand() { QueueCommand command = channel.attr(CommandsQueue.CURRENT_COMMAND).get(); if (command instanceof CommandData) { diff --git a/src/main/java/org/redisson/command/CommandAsyncService.java b/src/main/java/org/redisson/command/CommandAsyncService.java index 1abdce13d..f53274f2b 100644 --- a/src/main/java/org/redisson/command/CommandAsyncService.java +++ b/src/main/java/org/redisson/command/CommandAsyncService.java @@ -488,13 +488,14 @@ public class CommandAsyncService implements CommandAsyncExecutor { details.getMainPromise().tryFailure(new RedissonShutdownException("Redisson is shutdown")); } }; + details.getMainPromise().addListener(new FutureListener() { @Override public void operationComplete(Future future) throws Exception { connectionManager.getShutdownPromise().removeListener(listener); if (!future.isCancelled()) { if (future.cause() instanceof RedissonShutdownException) { - details.getAttemptPromise().cancel(true); + details.getAttemptPromise().tryFailure(future.cause()); } return; } @@ -504,6 +505,16 @@ public class CommandAsyncService implements CommandAsyncExecutor { } } }); + + details.getAttemptPromise().addListener(new FutureListener() { + @Override + public void operationComplete(Future future) throws Exception { + if (future.isCancelled()) { + connection.removeCurrentCommand(); + } + } + }); + connectionManager.getShutdownPromise().addListener(listener); } @@ -626,7 +637,7 @@ public class CommandAsyncService implements CommandAsyncExecutor { } details.getMainPromise().setSuccess(res); } else { - details.getMainPromise().setFailure(future.cause()); + details.getMainPromise().tryFailure(future.cause()); } AsyncDetails.release(details); } diff --git a/src/test/java/org/redisson/RedissonBlockingQueueTest.java b/src/test/java/org/redisson/RedissonBlockingQueueTest.java index e4c5938be..e7c006ef9 100644 --- a/src/test/java/org/redisson/RedissonBlockingQueueTest.java +++ b/src/test/java/org/redisson/RedissonBlockingQueueTest.java @@ -28,6 +28,42 @@ import io.netty.util.concurrent.FutureListener; public class RedissonBlockingQueueTest extends BaseTest { + @Test + public void testPollReattach() throws InterruptedException, IOException { + RedisProcess runner = new RedisRunner().port(6319).run(); + + Config config = new Config(); + config.useSingleServer().setAddress("127.0.0.1:6319"); + RedissonClient redisson = Redisson.create(config); + final RBlockingQueue queue1 = redisson.getBlockingQueue("queue:pollany"); + Future f = queue1.pollAsync(10, TimeUnit.SECONDS); + + final AtomicBoolean executed = new AtomicBoolean(); + f.addListener(new FutureListener() { + @Override + public void operationComplete(Future future) throws Exception { + assertThat(future.get()).isEqualTo(123); + executed.set(true); + } + }); + + f.await(1, TimeUnit.SECONDS); + runner.stop(); + + runner = new RedisRunner().port(6319).run(); + queue1.put(123); + + // check connection rotation + for (int i = 0; i < 10; i++) { + queue1.put(i); + } + assertThat(queue1.size()).isEqualTo(10); + + await().atMost(1, TimeUnit.SECONDS).until(() -> assertThat(executed.get()).isTrue()); + runner.stop(); + } + + @Test public void testTakeReattach() throws InterruptedException, IOException { RedisProcess runner = new RedisRunner().port(6319).run(); From 38c906fadb7ab588db26a2e6c48a26d9bcdd4e34 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 25 Mar 2016 17:32:15 +0300 Subject: [PATCH 027/120] Lost connection case handling for BlockingQueue poll method --- .../redisson/command/CommandAsyncService.java | 44 +++++++++++--- .../redisson/RedissonBlockingQueueTest.java | 58 +++++++++---------- 2 files changed, 63 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/redisson/command/CommandAsyncService.java b/src/main/java/org/redisson/command/CommandAsyncService.java index f53274f2b..3a46fdedf 100644 --- a/src/main/java/org/redisson/command/CommandAsyncService.java +++ b/src/main/java/org/redisson/command/CommandAsyncService.java @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.redisson.RedisClientResult; @@ -56,6 +57,7 @@ import io.netty.util.concurrent.DefaultPromise; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.Promise; +import io.netty.util.concurrent.ScheduledFuture; /** * @@ -460,7 +462,7 @@ public class CommandAsyncService implements CommandAsyncExecutor { int timeoutTime = connectionManager.getConfig().getTimeout(); if (QueueCommand.TIMEOUTLESS_COMMANDS.contains(details.getCommand().getName())) { Integer popTimeout = Integer.valueOf(details.getParams()[details.getParams().length - 1].toString()); - handleBlockingOperations(details, connection); + handleBlockingOperations(details, connection, popTimeout); if (popTimeout == 0) { return; } @@ -481,7 +483,7 @@ public class CommandAsyncService implements CommandAsyncExecutor { details.setTimeout(timeout); } - private void handleBlockingOperations(final AsyncDetails details, final RedisConnection connection) { + private void handleBlockingOperations(final AsyncDetails details, final RedisConnection connection, Integer popTimeout) { final FutureListener listener = new FutureListener() { @Override public void operationComplete(Future future) throws Exception { @@ -489,19 +491,41 @@ public class CommandAsyncService implements CommandAsyncExecutor { } }; + final AtomicBoolean canceledByScheduler = new AtomicBoolean(); + final ScheduledFuture scheduledFuture; + if (popTimeout != 0) { + // to handle cases when connection has been lost + scheduledFuture = connectionManager.getGroup().schedule(new Runnable() { + @Override + public void run() { + if (connection.isActive()) { + return; + } + + canceledByScheduler.set(true); + details.getAttemptPromise().trySuccess(null); + } + }, popTimeout, TimeUnit.SECONDS); + } else { + scheduledFuture = null; + } + details.getMainPromise().addListener(new FutureListener() { @Override public void operationComplete(Future future) throws Exception { + if (scheduledFuture != null) { + scheduledFuture.cancel(false); + } connectionManager.getShutdownPromise().removeListener(listener); - if (!future.isCancelled()) { - if (future.cause() instanceof RedissonShutdownException) { - details.getAttemptPromise().tryFailure(future.cause()); - } + // handling cancel operation for commands from skipTimeout collection + if ((future.isCancelled() && details.getAttemptPromise().cancel(true)) + || canceledByScheduler.get()) { + connection.forceReconnectAsync(); return; } - // cancel handling for commands from skipTimeout collection - if (details.getAttemptPromise().cancel(true)) { - connection.forceReconnectAsync(); + + if (future.cause() instanceof RedissonShutdownException) { + details.getAttemptPromise().tryFailure(future.cause()); } } }); @@ -510,6 +534,8 @@ public class CommandAsyncService implements CommandAsyncExecutor { @Override public void operationComplete(Future future) throws Exception { if (future.isCancelled()) { + // command should be removed due to + // ConnectionWatchdog blockingQueue reconnection logic connection.removeCurrentCommand(); } } diff --git a/src/test/java/org/redisson/RedissonBlockingQueueTest.java b/src/test/java/org/redisson/RedissonBlockingQueueTest.java index e7c006ef9..2542ab5ac 100644 --- a/src/test/java/org/redisson/RedissonBlockingQueueTest.java +++ b/src/test/java/org/redisson/RedissonBlockingQueueTest.java @@ -1,7 +1,6 @@ package org.redisson; -import static com.jayway.awaitility.Awaitility.await; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; import java.util.ArrayList; @@ -10,10 +9,11 @@ import java.util.LinkedList; import java.util.NoSuchElementException; import java.util.Queue; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import org.hamcrest.MatcherAssert; @@ -24,29 +24,35 @@ import org.redisson.RedisRunner.RedisProcess; import org.redisson.core.RBlockingQueue; import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; public class RedissonBlockingQueueTest extends BaseTest { @Test - public void testPollReattach() throws InterruptedException, IOException { + public void testPollWithBrokenConnection() throws IOException, InterruptedException, ExecutionException { RedisProcess runner = new RedisRunner().port(6319).run(); Config config = new Config(); config.useSingleServer().setAddress("127.0.0.1:6319"); RedissonClient redisson = Redisson.create(config); - final RBlockingQueue queue1 = redisson.getBlockingQueue("queue:pollany"); - Future f = queue1.pollAsync(10, TimeUnit.SECONDS); + final RBlockingQueue queue1 = redisson.getBlockingQueue("queue:pollTimeout"); + Future f = queue1.pollAsync(5, TimeUnit.SECONDS); - final AtomicBoolean executed = new AtomicBoolean(); - f.addListener(new FutureListener() { - @Override - public void operationComplete(Future future) throws Exception { - assertThat(future.get()).isEqualTo(123); - executed.set(true); - } - }); + f.await(1, TimeUnit.SECONDS); + runner.stop(); + assertThat(f.get()).isNull(); + } + + @Test + public void testPollReattach() throws InterruptedException, IOException, ExecutionException, TimeoutException { + RedisProcess runner = new RedisRunner().port(6319).run(); + + Config config = new Config(); + config.useSingleServer().setAddress("127.0.0.1:6319"); + RedissonClient redisson = Redisson.create(config); + + RBlockingQueue queue1 = redisson.getBlockingQueue("queue:pollany"); + Future f = queue1.pollAsync(10, TimeUnit.SECONDS); f.await(1, TimeUnit.SECONDS); runner.stop(); @@ -59,30 +65,21 @@ public class RedissonBlockingQueueTest extends BaseTest { } assertThat(queue1.size()).isEqualTo(10); - await().atMost(1, TimeUnit.SECONDS).until(() -> assertThat(executed.get()).isTrue()); + Integer result = f.get(1, TimeUnit.SECONDS); + assertThat(result).isEqualTo(123); runner.stop(); } @Test - public void testTakeReattach() throws InterruptedException, IOException { + public void testTakeReattach() throws InterruptedException, IOException, ExecutionException, TimeoutException { RedisProcess runner = new RedisRunner().port(6319).run(); Config config = new Config(); config.useSingleServer().setAddress("127.0.0.1:6319"); RedissonClient redisson = Redisson.create(config); - final RBlockingQueue queue1 = redisson.getBlockingQueue("queue:pollany"); + RBlockingQueue queue1 = redisson.getBlockingQueue("testTakeReattach"); Future f = queue1.takeAsync(); - - final AtomicBoolean executed = new AtomicBoolean(); - f.addListener(new FutureListener() { - @Override - public void operationComplete(Future future) throws Exception { - assertThat(future.get()).isEqualTo(123); - executed.set(true); - } - }); - f.await(1, TimeUnit.SECONDS); runner.stop(); @@ -95,7 +92,8 @@ public class RedissonBlockingQueueTest extends BaseTest { } assertThat(queue1.size()).isEqualTo(10); - await().atMost(1, TimeUnit.SECONDS).until(() -> assertThat(executed.get()).isTrue()); + Integer result = f.get(1, TimeUnit.SECONDS); + assertThat(result).isEqualTo(123); runner.stop(); } @@ -105,7 +103,7 @@ public class RedissonBlockingQueueTest extends BaseTest { config.useSingleServer().setConnectionMinimumIdleSize(1).setConnectionPoolSize(1); RedissonClient redisson = Redisson.create(config); - RBlockingQueue queue1 = redisson.getBlockingQueue("queue:pollany"); + RBlockingQueue queue1 = redisson.getBlockingQueue("testTakeAsyncCancel"); for (int i = 0; i < 10; i++) { Future f = queue1.takeAsync(); f.cancel(true); From 898fff317b1dcea66965742bb0ccd1b01c949a5b Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 25 Mar 2016 17:49:46 +0300 Subject: [PATCH 028/120] reconnection checking during poll operation state scheduled check --- .../java/org/redisson/command/CommandAsyncService.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/redisson/command/CommandAsyncService.java b/src/main/java/org/redisson/command/CommandAsyncService.java index 3a46fdedf..ab0b9e18b 100644 --- a/src/main/java/org/redisson/command/CommandAsyncService.java +++ b/src/main/java/org/redisson/command/CommandAsyncService.java @@ -49,6 +49,7 @@ import org.redisson.connection.NodeSource.Redirect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.util.Timeout; @@ -494,11 +495,15 @@ public class CommandAsyncService implements CommandAsyncExecutor { final AtomicBoolean canceledByScheduler = new AtomicBoolean(); final ScheduledFuture scheduledFuture; if (popTimeout != 0) { - // to handle cases when connection has been lost + // to handle cases when connection has been lost + final Channel orignalChannel = connection.getChannel(); scheduledFuture = connectionManager.getGroup().schedule(new Runnable() { @Override public void run() { - if (connection.isActive()) { + // there is no re-connection was made + // and connection is still active + if (orignalChannel == connection.getChannel() + && connection.isActive()) { return; } From 5cc2fb862a76a89ea633d65f3dd09e4afb63da26 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 29 Mar 2016 12:30:51 +0300 Subject: [PATCH 029/120] RTopic listener execution bug fixed. Performance boost up to 43%! #453 --- .../java/org/redisson/RedissonObject.java | 4 +- .../java/org/redisson/client/RedisClient.java | 5 +- .../org/redisson/client/RedisConnection.java | 10 +-- .../client/handler/ConnectionWatchdog.java | 3 +- .../connection/ClientConnectionsEntry.java | 5 +- .../connection/FastCompleteFuture.java | 75 ------------------- .../redisson/connection/FastFailedFuture.java | 65 ---------------- .../connection/FastSuccessFuture.java | 47 ------------ .../MasterSlaveConnectionManager.java | 11 +-- .../java/org/redisson/RedissonTopicTest.java | 24 ++++++ 10 files changed, 45 insertions(+), 204 deletions(-) delete mode 100644 src/main/java/org/redisson/connection/FastCompleteFuture.java delete mode 100644 src/main/java/org/redisson/connection/FastFailedFuture.java delete mode 100644 src/main/java/org/redisson/connection/FastSuccessFuture.java diff --git a/src/main/java/org/redisson/RedissonObject.java b/src/main/java/org/redisson/RedissonObject.java index a49ffa2d1..7e12d8ac8 100644 --- a/src/main/java/org/redisson/RedissonObject.java +++ b/src/main/java/org/redisson/RedissonObject.java @@ -50,11 +50,11 @@ abstract class RedissonObject implements RObject { } protected Promise newPromise() { - return commandExecutor.getConnectionManager().getGroup().next().newPromise(); + return commandExecutor.getConnectionManager().newPromise(); } protected Future newSucceededFuture(V result) { - return commandExecutor.getConnectionManager().newSucceededFuture(result); + return commandExecutor.getConnectionManager().newSucceededFuture(result); } @Override diff --git a/src/main/java/org/redisson/client/RedisClient.java b/src/main/java/org/redisson/client/RedisClient.java index e5dd7ad5b..2aa645755 100644 --- a/src/main/java/org/redisson/client/RedisClient.java +++ b/src/main/java/org/redisson/client/RedisClient.java @@ -38,6 +38,7 @@ import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GlobalEventExecutor; +import io.netty.util.concurrent.ImmediateEventExecutor; import io.netty.util.concurrent.Promise; public class RedisClient { @@ -95,7 +96,7 @@ public class RedisClient { } public Future connectAsync() { - final Promise f = bootstrap.group().next().newPromise(); + final Promise f = ImmediateEventExecutor.INSTANCE.newPromise(); ChannelFuture channelFuture = bootstrap.connect(); channelFuture.addListener(new ChannelFutureListener() { @Override @@ -122,7 +123,7 @@ public class RedisClient { } public Future connectPubSubAsync() { - final Promise f = bootstrap.group().next().newPromise(); + final Promise f = ImmediateEventExecutor.INSTANCE.newPromise(); ChannelFuture channelFuture = bootstrap.connect(); channelFuture.addListener(new ChannelFutureListener() { @Override diff --git a/src/main/java/org/redisson/client/RedisConnection.java b/src/main/java/org/redisson/client/RedisConnection.java index 9be80dec3..4f6c49dc4 100644 --- a/src/main/java/org/redisson/client/RedisConnection.java +++ b/src/main/java/org/redisson/client/RedisConnection.java @@ -25,13 +25,13 @@ import org.redisson.client.protocol.QueueCommand; import org.redisson.client.protocol.RedisCommand; import org.redisson.client.protocol.RedisCommands; import org.redisson.client.protocol.RedisStrictCommand; -import org.redisson.connection.FastSuccessFuture; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.util.AttributeKey; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; +import io.netty.util.concurrent.ImmediateEventExecutor; import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.ScheduledFuture; @@ -47,7 +47,7 @@ public class RedisConnection implements RedisCommands { private ReconnectListener reconnectListener; private long lastUsageTime; - private final Future acquireFuture = new FastSuccessFuture(this); + private final Future acquireFuture = ImmediateEventExecutor.INSTANCE.newSucceededFuture(this); public RedisConnection(RedisClient redisClient, Channel channel) { super(); @@ -151,13 +151,13 @@ public class RedisConnection implements RedisCommands { } public Future async(Codec encoder, RedisCommand command, Object ... params) { - Promise promise = redisClient.getBootstrap().group().next().newPromise(); + Promise promise = ImmediateEventExecutor.INSTANCE.newPromise(); send(new CommandData(promise, encoder, command, params)); return promise; } public Future asyncWithTimeout(Codec encoder, RedisCommand command, Object ... params) { - final Promise promise = redisClient.getBootstrap().group().next().newPromise(); + final Promise promise = ImmediateEventExecutor.INSTANCE.newPromise(); final ScheduledFuture scheduledFuture = redisClient.getBootstrap().group().next().schedule(new Runnable() { @Override public void run() { @@ -176,7 +176,7 @@ public class RedisConnection implements RedisCommands { } public CommandData create(Codec encoder, RedisCommand command, Object ... params) { - Promise promise = redisClient.getBootstrap().group().next().newPromise(); + Promise promise = ImmediateEventExecutor.INSTANCE.newPromise(); return new CommandData(promise, encoder, command, params); } diff --git a/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java b/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java index 1afce25e6..ac410070c 100644 --- a/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java +++ b/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java @@ -37,6 +37,7 @@ import io.netty.channel.EventLoopGroup; import io.netty.channel.group.ChannelGroup; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; +import io.netty.util.concurrent.ImmediateEventExecutor; import io.netty.util.concurrent.Promise; public class ConnectionWatchdog extends ChannelInboundHandlerAdapter { @@ -117,7 +118,7 @@ public class ConnectionWatchdog extends ChannelInboundHandlerAdapter { if (connection.getReconnectListener() != null) { // new connection used only for channel init RedisConnection rc = new RedisConnection(connection.getRedisClient(), channel); - Promise connectionFuture = bootstrap.group().next().newPromise(); + Promise connectionFuture = ImmediateEventExecutor.INSTANCE.newPromise(); connection.getReconnectListener().onReconnect(rc, connectionFuture); connectionFuture.addListener(new FutureListener() { @Override diff --git a/src/main/java/org/redisson/connection/ClientConnectionsEntry.java b/src/main/java/org/redisson/connection/ClientConnectionsEntry.java index 72a1adaeb..abaf8fd4c 100644 --- a/src/main/java/org/redisson/connection/ClientConnectionsEntry.java +++ b/src/main/java/org/redisson/connection/ClientConnectionsEntry.java @@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; +import io.netty.util.concurrent.ImmediateEventExecutor; import io.netty.util.concurrent.Promise; public class ClientConnectionsEntry { @@ -137,7 +138,7 @@ public class ClientConnectionsEntry { } public Future connect() { - final Promise connectionFuture = client.getBootstrap().group().next().newPromise(); + final Promise connectionFuture = ImmediateEventExecutor.INSTANCE.newPromise(); Future future = client.connectAsync(); future.addListener(new FutureListener() { @Override @@ -186,7 +187,7 @@ public class ClientConnectionsEntry { } public Future connectPubSub() { - final Promise connectionFuture = client.getBootstrap().group().next().newPromise(); + final Promise connectionFuture = ImmediateEventExecutor.INSTANCE.newPromise(); Future future = client.connectPubSubAsync(); future.addListener(new FutureListener() { @Override diff --git a/src/main/java/org/redisson/connection/FastCompleteFuture.java b/src/main/java/org/redisson/connection/FastCompleteFuture.java deleted file mode 100644 index c11645c47..000000000 --- a/src/main/java/org/redisson/connection/FastCompleteFuture.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright 2014 Nikita Koksharov, Nickolay Borbit - * - * 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.connection; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty.util.concurrent.CompleteFuture; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.GenericFutureListener; - -/** - * Invokes Future listeners in the same thread unlike {@code SucceededFuture} does. - * - * @author Nikita Koksharov - * - * @param - */ -public abstract class FastCompleteFuture extends CompleteFuture { - - private static final Logger logger = LoggerFactory.getLogger(FastCompleteFuture.class); - - protected FastCompleteFuture() { - super(null); - } - - @Override - public Future addListener(GenericFutureListener> listener) { - if (listener == null) { - throw new NullPointerException("listener"); - } - - notify(listener); - return this; - } - - private void notify(GenericFutureListener> listener) { - try { - ((GenericFutureListener)listener).operationComplete(this); - } catch (Throwable t) { - if (logger.isWarnEnabled()) { - logger.warn("An exception was thrown by " + listener.getClass().getName() + ".operationComplete()", t); - } - } - } - - @Override - public Future addListeners(GenericFutureListener>... listeners) { - if (listeners == null) { - throw new NullPointerException("listeners"); - } - for (GenericFutureListener> l: listeners) { - if (l == null) { - break; - } - notify(l); - } - return this; - } - - -} diff --git a/src/main/java/org/redisson/connection/FastFailedFuture.java b/src/main/java/org/redisson/connection/FastFailedFuture.java deleted file mode 100644 index 00a61d234..000000000 --- a/src/main/java/org/redisson/connection/FastFailedFuture.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright 2014 Nikita Koksharov, Nickolay Borbit - * - * 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.connection; - -import io.netty.util.concurrent.Future; -import io.netty.util.internal.PlatformDependent; - -/** - * - * @author Nikita Koksharov - * - * @param - */ -public class FastFailedFuture extends FastCompleteFuture { - - private final Throwable cause; - - protected FastFailedFuture(Throwable cause) { - if (cause == null) { - throw new NullPointerException("cause"); - } - this.cause = cause; - } - - @Override - public Throwable cause() { - return cause; - } - - @Override - public boolean isSuccess() { - return false; - } - - @Override - public Future sync() { - PlatformDependent.throwException(cause); - return this; - } - - @Override - public Future syncUninterruptibly() { - PlatformDependent.throwException(cause); - return this; - } - - @Override - public V getNow() { - return null; - } - -} diff --git a/src/main/java/org/redisson/connection/FastSuccessFuture.java b/src/main/java/org/redisson/connection/FastSuccessFuture.java deleted file mode 100644 index 08eab015b..000000000 --- a/src/main/java/org/redisson/connection/FastSuccessFuture.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright 2014 Nikita Koksharov, Nickolay Borbit - * - * 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.connection; - -/** - * - * @author Nikita Koksharov - * - * @param - */ -public class FastSuccessFuture extends FastCompleteFuture { - - private final V result; - - public FastSuccessFuture(V result) { - this.result = result; - } - - @Override - public Throwable cause() { - return null; - } - - @Override - public boolean isSuccess() { - return true; - } - - @Override - public V getNow() { - return result; - } - -} diff --git a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java index c0a11c4aa..125df525d 100644 --- a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java +++ b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java @@ -61,6 +61,7 @@ import io.netty.util.Timer; import io.netty.util.TimerTask; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; +import io.netty.util.concurrent.ImmediateEventExecutor; import io.netty.util.concurrent.Promise; import io.netty.util.internal.PlatformDependent; @@ -136,7 +137,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager { this(config); init(cfg); } - + public MasterSlaveConnectionManager(Config cfg) { Version.logVersion(); @@ -158,7 +159,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager { this.socketChannelClass = NioSocketChannel.class; } this.codec = cfg.getCodec(); - this.shutdownPromise = group.next().newPromise(); + this.shutdownPromise = newPromise(); this.isClusterMode = cfg.isClusterConfig(); } @@ -714,17 +715,17 @@ public class MasterSlaveConnectionManager implements ConnectionManager { @Override public Promise newPromise() { - return group.next().newPromise(); + return ImmediateEventExecutor.INSTANCE.newPromise(); } @Override public Future newSucceededFuture(R value) { - return new FastSuccessFuture(value); + return ImmediateEventExecutor.INSTANCE.newSucceededFuture(value); } @Override public Future newFailedFuture(Throwable cause) { - return new FastFailedFuture(cause); + return ImmediateEventExecutor.INSTANCE.newFailedFuture(cause); } @Override diff --git a/src/test/java/org/redisson/RedissonTopicTest.java b/src/test/java/org/redisson/RedissonTopicTest.java index 7373a5365..dd237b6e1 100644 --- a/src/test/java/org/redisson/RedissonTopicTest.java +++ b/src/test/java/org/redisson/RedissonTopicTest.java @@ -8,6 +8,7 @@ import org.junit.Assert; import org.junit.Test; import org.redisson.core.BaseStatusListener; import org.redisson.core.MessageListener; +import org.redisson.core.RSet; import org.redisson.core.RTopic; public class RedissonTopicTest { @@ -46,6 +47,29 @@ public class RedissonTopicTest { } + @Test + public void testSyncCommands() throws InterruptedException { + RedissonClient redisson = BaseTest.createInstance(); + RTopic topic = redisson.getTopic("system_bus"); + RSet redissonSet = redisson.getSet("set1"); + CountDownLatch latch = new CountDownLatch(1); + topic.addListener(new MessageListener() { + + @Override + public void onMessage(String channel, String msg) { + for (int j = 0; j < 1000; j++) { + redissonSet.contains("" + j); + } + latch.countDown(); + } + }); + + topic.publish("sometext"); + + latch.await(); + redisson.shutdown(); + } + @Test public void testInnerPublish() throws InterruptedException { From f2d82a8981df087e12e1ad8b71465000f3fb4831 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 29 Mar 2016 13:49:13 +0300 Subject: [PATCH 030/120] RSetCache.add methods result fixed. #454 --- .../java/org/redisson/RedissonSetCache.java | 43 ++++++++++++------- .../org/redisson/RedissonSetCacheTest.java | 43 ++++++++++++++----- 2 files changed, 59 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/redisson/RedissonSetCache.java b/src/main/java/org/redisson/RedissonSetCache.java index 97f8526a7..34bc65af4 100644 --- a/src/main/java/org/redisson/RedissonSetCache.java +++ b/src/main/java/org/redisson/RedissonSetCache.java @@ -17,7 +17,6 @@ package org.redisson; import java.io.IOException; import java.net.InetSocketAddress; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -41,8 +40,6 @@ import org.redisson.core.RSetCache; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.handler.codec.base64.Base64; -import io.netty.util.CharsetUtil; import io.netty.util.concurrent.Future; import net.openhft.hashing.LongHashFunction; @@ -320,13 +317,20 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< long timeoutDate = System.currentTimeMillis() + unit.toMillis(ttl); return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN, - "redis.call('zadd', KEYS[2], ARGV[1], ARGV[3]); " + - "if redis.call('hexists', KEYS[1], ARGV[3]) == 0 then " + - "redis.call('hset', KEYS[1], ARGV[3], ARGV[2]); " + - "return 1; " + - "end;" + - "return 0; ", - Arrays.asList(getName(), getTimeoutSetName()), timeoutDate, objectState, key); + "local value = redis.call('hexists', KEYS[1], ARGV[3]); " + + "if value == 1 then " + + "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[3]); " + + "if expireDateScore ~= false and tonumber(expireDateScore) <= tonumber(ARGV[1]) then " + + "redis.call('zadd', KEYS[2], ARGV[2], ARGV[2]); " + + "return 1;" + + "else " + + "return 0;" + + "end; " + + "end;" + + "redis.call('zadd', KEYS[2], ARGV[2], ARGV[3]); " + + "redis.call('hset', KEYS[1], ARGV[3], ARGV[4]); " + + "return 1; ", + Arrays.asList(getName(), getTimeoutSetName()), System.currentTimeMillis(), timeoutDate, key, objectState); } catch (IOException e) { throw new RuntimeException(e); } @@ -342,12 +346,19 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< byte[] objectState = encode(value); byte[] key = hash(objectState); return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN, - "if redis.call('hexists', KEYS[1], ARGV[1]) == 0 then " + - "redis.call('hset', KEYS[1], ARGV[1], ARGV[2]); " + - "return 1; " + - "end; " + - "return 0; ", - Arrays.asList(getName()), key, objectState); + "local value = redis.call('hexists', KEYS[1], ARGV[2]); " + + "if value == 1 then " + + "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[2]); " + + "if expireDateScore ~= false and tonumber(expireDateScore) <= tonumber(ARGV[1]) then " + + "redis.call('zrem', KEYS[2], ARGV[2]); " + + "return 1;" + + "else " + + "return 0;" + + "end; " + + "end;" + + "redis.call('hset', KEYS[1], ARGV[2], ARGV[3]); " + + "return 1; ", + Arrays.asList(getName(), getTimeoutSetName()), System.currentTimeMillis(), key, objectState); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/test/java/org/redisson/RedissonSetCacheTest.java b/src/test/java/org/redisson/RedissonSetCacheTest.java index 9d7c71188..61e489e29 100644 --- a/src/test/java/org/redisson/RedissonSetCacheTest.java +++ b/src/test/java/org/redisson/RedissonSetCacheTest.java @@ -59,43 +59,64 @@ public class RedissonSetCacheTest extends BaseTest { @Test public void testAddExpire() throws InterruptedException, ExecutionException { RSetCache set = redisson.getSetCache("simple3"); - set.add("123", 1, TimeUnit.SECONDS); + assertThat(set.add("123", 500, TimeUnit.MILLISECONDS)).isTrue(); assertThat(set).contains("123"); - Thread.sleep(1000); - + Thread.sleep(500); + + assertThat(set.size()).isEqualTo(1); assertThat(set).doesNotContain("123"); + + assertThat(set.add("123", 1, TimeUnit.SECONDS)).isTrue(); + } @Test public void testAddExpireTwise() throws InterruptedException, ExecutionException { RSetCache set = redisson.getSetCache("simple31"); - set.add("123", 1, TimeUnit.SECONDS); + assertThat(set.add("123", 1, TimeUnit.SECONDS)).isTrue(); Thread.sleep(1000); Assert.assertFalse(set.contains("123")); - set.add("4341", 1, TimeUnit.SECONDS); + assertThat(set.add("4341", 1, TimeUnit.SECONDS)).isTrue(); Thread.sleep(1000); Assert.assertFalse(set.contains("4341")); } + + @Test + public void testAddExpireThenAdd() throws InterruptedException, ExecutionException { + RSetCache set = redisson.getSetCache("simple31"); + assertThat(set.add("123", 500, TimeUnit.MILLISECONDS)).isTrue(); + + Thread.sleep(500); + + assertThat(set.size()).isEqualTo(1); + assertThat(set.contains("123")).isFalse(); + + assertThat(set.add("123")).isTrue(); + Thread.sleep(1000); + + assertThat(set.contains("123")).isTrue(); + } + @Test public void testExpireOverwrite() throws InterruptedException, ExecutionException { RSetCache set = redisson.getSetCache("simple"); - set.add("123", 1, TimeUnit.SECONDS); + assertThat(set.add("123", 1, TimeUnit.SECONDS)).isTrue(); Thread.sleep(800); - set.add("123", 1, TimeUnit.SECONDS); + assertThat(set.add("123", 1, TimeUnit.SECONDS)).isFalse(); - Thread.sleep(800); - Assert.assertTrue(set.contains("123")); + Thread.sleep(100); + assertThat(set.contains("123")).isTrue(); - Thread.sleep(200); + Thread.sleep(100); - Assert.assertFalse(set.contains("123")); + assertThat(set.contains("123")).isFalse(); } @Test From 6adbd9387f20f9959d9078bf8a10b4cd58751905 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 29 Mar 2016 13:49:56 +0300 Subject: [PATCH 031/120] hamcrest usage reduced --- .../java/org/redisson/RedissonDequeTest.java | 45 +++------ .../java/org/redisson/RedissonListTest.java | 99 +++++++------------ .../java/org/redisson/RedissonQueueTest.java | 16 +-- 3 files changed, 57 insertions(+), 103 deletions(-) diff --git a/src/test/java/org/redisson/RedissonDequeTest.java b/src/test/java/org/redisson/RedissonDequeTest.java index 9ccb56362..784810abf 100644 --- a/src/test/java/org/redisson/RedissonDequeTest.java +++ b/src/test/java/org/redisson/RedissonDequeTest.java @@ -1,12 +1,11 @@ package org.redisson; +import static org.assertj.core.api.Assertions.assertThat; + import java.util.ArrayDeque; import java.util.Arrays; import java.util.Deque; -import java.util.Iterator; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; import org.redisson.core.RDeque; @@ -23,7 +22,7 @@ public class RedissonDequeTest extends BaseTest { queue1.removeLastOccurrence(3); - MatcherAssert.assertThat(queue1, Matchers.containsInAnyOrder(3, 2, 1)); + assertThat(queue1).containsExactly(3, 2, 1); } @Test @@ -36,7 +35,7 @@ public class RedissonDequeTest extends BaseTest { queue1.removeFirstOccurrence(3); - MatcherAssert.assertThat(queue1, Matchers.containsInAnyOrder(2, 1, 3)); + assertThat(queue1).containsExactly(2, 1, 3); } @Test @@ -86,7 +85,7 @@ public class RedissonDequeTest extends BaseTest { queue2.addFirst(4); queue1.pollLastAndOfferFirstTo(queue2); - MatcherAssert.assertThat(queue2, Matchers.contains(3, 4, 5, 6)); + assertThat(queue2).containsExactly(3, 4, 5, 6); } @Test @@ -96,8 +95,8 @@ public class RedissonDequeTest extends BaseTest { queue.addFirst(2); queue.addFirst(3); - MatcherAssert.assertThat(queue, Matchers.contains(3, 2, 1)); - } + assertThat(queue).containsExactly(3, 2, 1); + } @Test public void testAddFirst() { @@ -106,7 +105,7 @@ public class RedissonDequeTest extends BaseTest { queue.addFirst(2); queue.addFirst(3); - MatcherAssert.assertThat(queue, Matchers.contains(3, 2, 1)); + assertThat(queue).containsExactly(3, 2, 1); } @Test @@ -116,7 +115,7 @@ public class RedissonDequeTest extends BaseTest { queue.addLast(2); queue.addLast(3); - MatcherAssert.assertThat(queue, Matchers.contains(1, 2, 3)); + assertThat(queue).containsExactly(1, 2, 3); } @Test @@ -126,7 +125,7 @@ public class RedissonDequeTest extends BaseTest { queue.addLast(2); queue.addLast(3); - MatcherAssert.assertThat(queue, Matchers.contains(1, 2, 3)); + assertThat(queue).containsExactly(1, 2, 3); } @Test @@ -136,7 +135,7 @@ public class RedissonDequeTest extends BaseTest { queue.offerFirst(2); queue.offerFirst(3); - MatcherAssert.assertThat(queue, Matchers.contains(3, 2, 1)); + assertThat(queue).containsExactly(3, 2, 1); } @Test @@ -146,7 +145,7 @@ public class RedissonDequeTest extends BaseTest { queue.offerFirst(2); queue.offerFirst(3); - MatcherAssert.assertThat(queue, Matchers.contains(3, 2, 1)); + assertThat(queue).containsExactly(3, 2, 1); } @Test @@ -156,7 +155,7 @@ public class RedissonDequeTest extends BaseTest { queue.offerLast(2); queue.offerLast(3); - MatcherAssert.assertThat(queue, Matchers.contains(1, 2, 3)); + assertThat(queue).containsExactly(1, 2, 3); Assert.assertEquals((Integer)1, queue.poll()); } @@ -166,14 +165,7 @@ public class RedissonDequeTest extends BaseTest { final Deque queue = new ArrayDeque(); queue.addAll(Arrays.asList(1, 2, 3)); - MatcherAssert.assertThat(new Iterable() { - - @Override - public Iterator iterator() { - return queue.descendingIterator(); - } - - }, Matchers.contains(3, 2, 1)); + assertThat(queue.descendingIterator()).containsExactly(3, 2, 1); } @Test @@ -181,14 +173,7 @@ public class RedissonDequeTest extends BaseTest { final RDeque queue = redisson.getDeque("deque"); queue.addAll(Arrays.asList(1, 2, 3)); - MatcherAssert.assertThat(new Iterable() { - - @Override - public Iterator iterator() { - return queue.descendingIterator(); - } - - }, Matchers.contains(3, 2, 1)); + assertThat(queue.descendingIterator()).containsExactly(3, 2, 1); } } diff --git a/src/test/java/org/redisson/RedissonListTest.java b/src/test/java/org/redisson/RedissonListTest.java index 9341d939b..173a5dec7 100644 --- a/src/test/java/org/redisson/RedissonListTest.java +++ b/src/test/java/org/redisson/RedissonListTest.java @@ -1,5 +1,7 @@ package org.redisson; +import static org.assertj.core.api.Assertions.assertThat; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -8,17 +10,11 @@ import java.util.LinkedList; import java.util.List; import java.util.ListIterator; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; import org.redisson.client.RedisException; import org.redisson.core.RList; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; -import static org.assertj.core.api.Assertions.*; - public class RedissonListTest extends BaseTest { @Test @@ -84,37 +80,7 @@ public class RedissonListTest extends BaseTest { test2.add("foo"); test2.add(0, "bar"); - MatcherAssert.assertThat(test2, Matchers.contains("bar", "foo")); - } - - @Test - public void testAddAllAsync() throws InterruptedException { - final RList list = redisson.getList("list"); - list.addAllAsync(Arrays.asList(1L, 2L, 3L)).addListener(new FutureListener() { - @Override - public void operationComplete(Future future) throws Exception { - list.addAllAsync(Arrays.asList(1L, 24L, 3L)); - } - }).awaitUninterruptibly(); - - Thread.sleep(1000); - - Assert.assertThat(list, Matchers.contains(1L, 2L, 3L, 1L, 24L, 3L)); - } - - @Test - public void testAddAsync() throws InterruptedException { - final RList list = redisson.getList("list"); - list.addAsync(1L).addListener(new FutureListener() { - @Override - public void operationComplete(Future future) throws Exception { - list.addAsync(2L); - } - }).awaitUninterruptibly(); - - Thread.sleep(1000); - - Assert.assertThat(list, Matchers.contains(1L, 2L)); + assertThat(test2).containsExactly("bar", "foo"); } @Test @@ -123,7 +89,7 @@ public class RedissonListTest extends BaseTest { list.add(1L); list.add(2L); - Assert.assertThat(list, Matchers.contains(1L, 2L)); + assertThat(list).containsExactly(1L, 2L); } @Test(expected = IllegalStateException.class) @@ -186,17 +152,17 @@ public class RedissonListTest extends BaseTest { Assert.assertFalse(iterator.hasPrevious()); Assert.assertTrue(1 == iterator.next()); iterator.set(3); - Assert.assertThat(list, Matchers.contains(3, 2, 3, 4)); + assertThat(list).containsExactly(3, 2, 3, 4); Assert.assertTrue(2 == iterator.next()); iterator.add(31); - Assert.assertThat(list, Matchers.contains(3, 2, 31, 3, 4)); + assertThat(list).containsExactly(3, 2, 31, 3, 4); Assert.assertTrue(3 == iterator.next()); Assert.assertTrue(4 == iterator.next()); Assert.assertFalse(iterator.hasNext()); iterator.add(71); - Assert.assertThat(list, Matchers.contains(3, 2, 31, 3, 4, 71)); + assertThat(list).containsExactly(3, 2, 31, 3, 4, 71); iterator.add(8); - Assert.assertThat(list, Matchers.contains(3, 2, 31, 3, 4, 71, 8)); + assertThat(list).containsExactly(3, 2, 31, 3, 4, 71, 8); } @Test @@ -212,17 +178,17 @@ public class RedissonListTest extends BaseTest { Assert.assertFalse(iterator.hasPrevious()); Assert.assertTrue(1 == iterator.next()); iterator.set(3); - Assert.assertThat(list, Matchers.contains(3, 2, 3, 4)); + assertThat(list).containsExactly(3, 2, 3, 4); Assert.assertTrue(2 == iterator.next()); iterator.add(31); - Assert.assertThat(list, Matchers.contains(3, 2, 31, 3, 4)); + assertThat(list).containsExactly(3, 2, 31, 3, 4); Assert.assertTrue(3 == iterator.next()); Assert.assertTrue(4 == iterator.next()); Assert.assertFalse(iterator.hasNext()); iterator.add(71); - Assert.assertThat(list, Matchers.contains(3, 2, 31, 3, 4, 71)); + assertThat(list).containsExactly(3, 2, 31, 3, 4, 71); iterator.add(8); - Assert.assertThat(list, Matchers.contains(3, 2, 31, 3, 4, 71, 8)); + assertThat(list).containsExactly(3, 2, 31, 3, 4, 71, 8); } @Test @@ -606,7 +572,7 @@ public class RedissonListTest extends BaseTest { list.set(4, 6); - Assert.assertThat(list, Matchers.contains(1, 2, 3, 4, 6)); + assertThat(list).containsExactly(1, 2, 3, 4, 6); } @Test(expected = IndexOutOfBoundsException.class) @@ -632,7 +598,7 @@ public class RedissonListTest extends BaseTest { list.set(4, 6); - Assert.assertThat(list, Matchers.contains(1, 2, 3, 4, 6)); + assertThat(list).containsExactly(1, 2, 3, 4, 6); } @@ -661,11 +627,11 @@ public class RedissonListTest extends BaseTest { Assert.assertFalse(list.removeAll(Collections.emptyList())); Assert.assertTrue(list.removeAll(Arrays.asList(3, 2, 10, 6))); - Assert.assertThat(list, Matchers.contains(1, 4, 5)); + assertThat(list).containsExactly(1, 4, 5); Assert.assertTrue(list.removeAll(Arrays.asList(4))); - Assert.assertThat(list, Matchers.contains(1, 5)); + assertThat(list).containsExactly(1, 5); Assert.assertTrue(list.removeAll(Arrays.asList(1, 5, 1, 5))); @@ -683,7 +649,7 @@ public class RedissonListTest extends BaseTest { Assert.assertTrue(list.retainAll(Arrays.asList(3, 2, 10, 6))); - Assert.assertThat(list, Matchers.contains(2, 3)); + assertThat(list).containsExactly(2, 3); Assert.assertEquals(2, list.size()); } @@ -717,7 +683,7 @@ public class RedissonListTest extends BaseTest { list.add(2); Assert.assertFalse(list.retainAll(Arrays.asList(1, 2))); // nothing changed - Assert.assertThat(list, Matchers.contains(1, 2)); + assertThat(list).containsExactly(1, 2); } @Test(expected = RedisException.class) @@ -737,19 +703,19 @@ public class RedissonListTest extends BaseTest { list.addAll(2, Arrays.asList(7, 8, 9)); - Assert.assertThat(list, Matchers.contains(1, 2, 7, 8, 9, 3, 4, 5)); + assertThat(list).containsExactly(1, 2, 7, 8, 9, 3, 4, 5); list.addAll(list.size()-1, Arrays.asList(9, 1, 9)); - Assert.assertThat(list, Matchers.contains(1, 2, 7, 8, 9, 3, 4, 9, 1, 9, 5)); + assertThat(list).containsExactly(1, 2, 7, 8, 9, 3, 4, 9, 1, 9, 5); list.addAll(list.size(), Arrays.asList(0, 5)); - Assert.assertThat(list, Matchers.contains(1, 2, 7, 8, 9, 3, 4, 9, 1, 9, 5, 0, 5)); + assertThat(list).containsExactly(1, 2, 7, 8, 9, 3, 4, 9, 1, 9, 5, 0, 5); list.addAll(0, Arrays.asList(6, 7)); - Assert.assertThat(list, Matchers.contains(6,7,1, 2, 7, 8, 9, 3, 4, 9, 1, 9, 5, 0, 5)); + assertThat(list).containsExactly(6,7,1, 2, 7, 8, 9, 3, 4, 9, 1, 9, 5, 0, 5); } @Test @@ -765,15 +731,15 @@ public class RedissonListTest extends BaseTest { list.addAll(list.size()-1, Arrays.asList(9, 1, 9)); - Assert.assertThat(list, Matchers.contains(1, 2, 7, 8, 9, 3, 4, 9, 1, 9, 5)); + assertThat(list).containsExactly(1, 2, 7, 8, 9, 3, 4, 9, 1, 9, 5); list.addAll(list.size(), Arrays.asList(0, 5)); - Assert.assertThat(list, Matchers.contains(1, 2, 7, 8, 9, 3, 4, 9, 1, 9, 5, 0, 5)); + assertThat(list).containsExactly(1, 2, 7, 8, 9, 3, 4, 9, 1, 9, 5, 0, 5); list.addAll(0, Arrays.asList(6,7)); - Assert.assertThat(list, Matchers.contains(6,7,1, 2, 7, 8, 9, 3, 4, 9, 1, 9, 5, 0, 5)); + assertThat(list).containsExactly(6,7,1, 2, 7, 8, 9, 3, 4, 9, 1, 9, 5, 0, 5); } @@ -790,7 +756,7 @@ public class RedissonListTest extends BaseTest { Assert.assertTrue(list.addAll(Arrays.asList(9, 1, 9))); - Assert.assertThat(list, Matchers.contains(1, 2, 3, 4, 5, 7, 8, 9, 9, 1, 9)); + assertThat(list).containsExactly(1, 2, 3, 4, 5, 7, 8, 9, 9, 1, 9); } @Test @@ -855,7 +821,7 @@ public class RedissonListTest extends BaseTest { } } - Assert.assertThat(list, Matchers.contains("1", "4", "5", "3")); + assertThat(list).containsExactly("1", "4", "5", "3"); int iteration = 0; for (Iterator iterator = list.iterator(); iterator.hasNext();) { @@ -957,13 +923,16 @@ public class RedissonListTest extends BaseTest { list.add("4"); list.add("5"); list.add("6"); - Assert.assertThat(list, Matchers.contains("1", "2", "3", "4", "5", "6")); + + assertThat(list).containsExactly("1", "2", "3", "4", "5", "6"); list.remove("2"); - Assert.assertThat(list, Matchers.contains("1", "3", "4", "5", "6")); + + assertThat(list).containsExactly("1", "3", "4", "5", "6"); list.remove("4"); - Assert.assertThat(list, Matchers.contains("1", "3", "5", "6")); + + assertThat(list).containsExactly("1", "3", "5", "6"); } @Test @@ -974,6 +943,6 @@ public class RedissonListTest extends BaseTest { list.add("3"); list.add("e"); - Assert.assertThat(list, Matchers.contains(1, 2L, "3", "e")); + assertThat(list).containsExactly(1, 2L, "3", "e"); } } diff --git a/src/test/java/org/redisson/RedissonQueueTest.java b/src/test/java/org/redisson/RedissonQueueTest.java index cf4df47bb..6834f00e9 100644 --- a/src/test/java/org/redisson/RedissonQueueTest.java +++ b/src/test/java/org/redisson/RedissonQueueTest.java @@ -1,11 +1,11 @@ package org.redisson; +import static org.assertj.core.api.Assertions.assertThat; + import java.util.LinkedList; import java.util.NoSuchElementException; import java.util.Queue; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; import org.redisson.core.RQueue; @@ -20,9 +20,9 @@ public class RedissonQueueTest extends BaseTest { queue.add(3); queue.offer(4); - MatcherAssert.assertThat(queue, Matchers.contains(1, 2, 3, 4)); + assertThat(queue).containsExactly(1, 2, 3, 4); Assert.assertEquals((Integer)1, queue.poll()); - MatcherAssert.assertThat(queue, Matchers.contains(2, 3, 4)); + assertThat(queue).containsExactly(2, 3, 4); Assert.assertEquals((Integer)2, queue.element()); } @@ -34,9 +34,9 @@ public class RedissonQueueTest extends BaseTest { queue.add(3); queue.offer(4); - MatcherAssert.assertThat(queue, Matchers.contains(1, 2, 3, 4)); + assertThat(queue).containsExactly(1, 2, 3, 4); Assert.assertEquals((Integer)1, queue.poll()); - MatcherAssert.assertThat(queue, Matchers.contains(2, 3, 4)); + assertThat(queue).containsExactly(2, 3, 4); Assert.assertEquals((Integer)2, queue.element()); } @@ -51,7 +51,7 @@ public class RedissonQueueTest extends BaseTest { queue.remove(); queue.remove(); - MatcherAssert.assertThat(queue, Matchers.contains(3, 4)); + assertThat(queue).containsExactly(3, 4); queue.remove(); queue.remove(); @@ -69,7 +69,7 @@ public class RedissonQueueTest extends BaseTest { queue.remove(); queue.remove(); - MatcherAssert.assertThat(queue, Matchers.contains(3, 4)); + assertThat(queue).containsExactly(3, 4); queue.remove(); queue.remove(); From a60d3f491e4c8d5d4d74db05278024f540af983f Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 29 Mar 2016 15:30:53 +0300 Subject: [PATCH 032/120] refactoring --- .../cluster/ClusterConnectionManager.java | 10 +++++---- .../connection/ConnectionManager.java | 6 ++--- .../MasterSlaveConnectionManager.java | 12 +++------- .../redisson/connection/MasterSlaveEntry.java | 14 +++++++----- .../balancer/LoadBalancerManager.java | 3 +-- .../balancer/LoadBalancerManagerImpl.java | 22 +++++++++---------- .../connection/pool/ConnectionPool.java | 2 +- 7 files changed, 32 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/redisson/cluster/ClusterConnectionManager.java b/src/main/java/org/redisson/cluster/ClusterConnectionManager.java index d0bc0cf36..7ac9a601e 100644 --- a/src/main/java/org/redisson/cluster/ClusterConnectionManager.java +++ b/src/main/java/org/redisson/cluster/ClusterConnectionManager.java @@ -346,8 +346,9 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { failedSlaves.removeAll(currentPart.getFailedSlaveAddresses()); for (URI uri : failedSlaves) { currentPart.addFailedSlaveAddress(uri); - slaveDown(entry, uri.getHost(), uri.getPort(), FreezeReason.MANAGER); - log.warn("slave: {} has down for slot ranges: {}", uri, currentPart.getSlotRanges()); + if (entry.slaveDown(uri.getHost(), uri.getPort(), FreezeReason.MANAGER)) { + log.warn("slave: {} has down for slot ranges: {}", uri, currentPart.getSlotRanges()); + } } } @@ -358,8 +359,9 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { for (URI uri : removedSlaves) { currentPart.removeSlaveAddress(uri); - slaveDown(entry, uri.getHost(), uri.getPort(), FreezeReason.MANAGER); - log.info("slave {} removed for slot ranges: {}", uri, currentPart.getSlotRanges()); + if (entry.slaveDown(uri.getHost(), uri.getPort(), FreezeReason.MANAGER)) { + log.info("slave {} removed for slot ranges: {}", uri, currentPart.getSlotRanges()); + } } Set addedSlaves = new HashSet(newPart.getSlaveAddresses()); diff --git a/src/main/java/org/redisson/connection/ConnectionManager.java b/src/main/java/org/redisson/connection/ConnectionManager.java index 21a275ddb..4bd1a720c 100644 --- a/src/main/java/org/redisson/connection/ConnectionManager.java +++ b/src/main/java/org/redisson/connection/ConnectionManager.java @@ -23,11 +23,11 @@ import java.util.concurrent.TimeUnit; import org.redisson.MasterSlaveServersConfig; import org.redisson.client.RedisClient; import org.redisson.client.RedisConnection; +import org.redisson.client.RedisPubSubConnection; import org.redisson.client.RedisPubSubListener; import org.redisson.client.codec.Codec; import org.redisson.client.protocol.RedisCommand; import org.redisson.cluster.ClusterSlotRange; -import org.redisson.connection.ClientConnectionsEntry.FreezeReason; import org.redisson.core.NodeType; import org.redisson.misc.InfinitySemaphoreLatch; @@ -44,6 +44,8 @@ import io.netty.util.concurrent.Promise; */ public interface ConnectionManager { + void reattachPubSub(Collection allPubSubConnections); + boolean isClusterMode(); Future newSucceededFuture(R value); @@ -62,8 +64,6 @@ public interface ConnectionManager { Future newFailedFuture(Throwable cause); - void slaveDown(MasterSlaveEntry entry, String host, int port, FreezeReason freezeReason); - Collection getClients(); void shutdownAsync(RedisClient client); diff --git a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java index 125df525d..2bf246e3f 100644 --- a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java +++ b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java @@ -542,13 +542,8 @@ public class MasterSlaveConnectionManager implements ConnectionManager { return null; } - public void slaveDown(MasterSlaveEntry entry, String host, int port, FreezeReason freezeReason) { - Collection allPubSubConnections = entry.slaveDown(host, port, freezeReason); - if (allPubSubConnections.isEmpty()) { - return; - } - - // reattach listeners to other channels + @Override + public void reattachPubSub(Collection allPubSubConnections) { for (Entry mapEntry : name2PubSubConnection.entrySet()) { for (RedisPubSubConnection redisPubSubConnection : allPubSubConnections) { PubSubConnectionEntry pubSubEntry = mapEntry.getValue(); @@ -620,8 +615,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager { } protected void slaveDown(ClusterSlotRange slotRange, String host, int port, FreezeReason freezeReason) { - MasterSlaveEntry entry = getEntry(slotRange); - slaveDown(entry, host, port, freezeReason); + getEntry(slotRange).slaveDown(host, port, freezeReason); } protected void changeMaster(ClusterSlotRange slotRange, String host, int port) { diff --git a/src/main/java/org/redisson/connection/MasterSlaveEntry.java b/src/main/java/org/redisson/connection/MasterSlaveEntry.java index 7582e98fc..139030165 100644 --- a/src/main/java/org/redisson/connection/MasterSlaveEntry.java +++ b/src/main/java/org/redisson/connection/MasterSlaveEntry.java @@ -90,9 +90,11 @@ public class MasterSlaveEntry { return writeConnectionHolder.add(masterEntry); } - public Collection slaveDown(String host, int port, FreezeReason freezeReason) { - Collection conns = slaveBalancer.freeze(host, port, freezeReason); - + public boolean slaveDown(String host, int port, FreezeReason freezeReason) { + if (!slaveBalancer.freeze(host, port, freezeReason)) { + return false; + } + // add master as slave if no more slaves available if (config.getReadMode() == ReadMode.SLAVE && slaveBalancer.getAvailableClients() == 0) { InetSocketAddress addr = masterEntry.getClient().getAddr(); @@ -100,7 +102,7 @@ public class MasterSlaveEntry { log.info("master {}:{} used as slave", addr.getHostName(), addr.getPort()); } } - return conns; + return true; } public Future addSlave(String host, int port) { @@ -134,7 +136,7 @@ public class MasterSlaveEntry { // exclude master from slaves if (config.getReadMode() == ReadMode.SLAVE && (!addr.getHostName().equals(host) || port != addr.getPort())) { - connectionManager.slaveDown(this, addr.getHostName(), addr.getPort(), FreezeReason.SYSTEM); + slaveDown(addr.getHostName(), addr.getPort(), FreezeReason.SYSTEM); log.info("master {}:{} excluded from slaves", addr.getHostName(), addr.getPort()); } return true; @@ -155,7 +157,7 @@ public class MasterSlaveEntry { // more than one slave available, so master can be removed from slaves if (config.getReadMode() == ReadMode.SLAVE && slaveBalancer.getAvailableClients() > 1) { - connectionManager.slaveDown(this, host, port, FreezeReason.SYSTEM); + slaveDown(host, port, FreezeReason.SYSTEM); } connectionManager.shutdownAsync(oldMaster.getClient()); } diff --git a/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java b/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java index 56a372b98..2b28c343f 100644 --- a/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java +++ b/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java @@ -16,7 +16,6 @@ package org.redisson.connection.balancer; import java.net.InetSocketAddress; -import java.util.Collection; import org.redisson.client.RedisConnection; import org.redisson.client.RedisPubSubConnection; @@ -37,7 +36,7 @@ public interface LoadBalancerManager { boolean unfreeze(String host, int port, FreezeReason freezeReason); - Collection freeze(String host, int port, FreezeReason freezeReason); + boolean freeze(String host, int port, FreezeReason freezeReason); Future add(ClientConnectionsEntry entry); diff --git a/src/main/java/org/redisson/connection/balancer/LoadBalancerManagerImpl.java b/src/main/java/org/redisson/connection/balancer/LoadBalancerManagerImpl.java index 45f55ecc0..8c73c4d8d 100644 --- a/src/main/java/org/redisson/connection/balancer/LoadBalancerManagerImpl.java +++ b/src/main/java/org/redisson/connection/balancer/LoadBalancerManagerImpl.java @@ -16,10 +16,6 @@ package org.redisson.connection.balancer; import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; import java.util.Map; import org.redisson.MasterSlaveServersConfig; @@ -99,16 +95,20 @@ public class LoadBalancerManagerImpl implements LoadBalancerManager { return false; } - public Collection freeze(String host, int port, FreezeReason freezeReason) { + public boolean freeze(String host, int port, FreezeReason freezeReason) { InetSocketAddress addr = new InetSocketAddress(host, port); ClientConnectionsEntry connectionEntry = addr2Entry.get(addr); if (connectionEntry == null) { - return Collections.emptyList(); + return false; } synchronized (connectionEntry) { - log.debug("{} freezed", addr); + if (connectionEntry.isFreezed()) { + return false; + } + connectionEntry.setFreezed(true); + // only RECONNECT freeze reason could be replaced if (connectionEntry.getFreezeReason() == null || connectionEntry.getFreezeReason() == FreezeReason.RECONNECT) { @@ -134,11 +134,9 @@ public class LoadBalancerManagerImpl implements LoadBalancerManager { connection.closeAsync(); } - synchronized (connectionEntry) { - List list = new ArrayList(connectionEntry.getAllSubscribeConnections()); - connectionEntry.getAllSubscribeConnections().clear(); - return list; - } + connectionManager.reattachPubSub(connectionEntry.getAllSubscribeConnections()); + connectionEntry.getAllSubscribeConnections().clear(); + return true; } public Future nextPubSubConnection() { diff --git a/src/main/java/org/redisson/connection/pool/ConnectionPool.java b/src/main/java/org/redisson/connection/pool/ConnectionPool.java index 58998a2dd..00c1c6886 100644 --- a/src/main/java/org/redisson/connection/pool/ConnectionPool.java +++ b/src/main/java/org/redisson/connection/pool/ConnectionPool.java @@ -277,7 +277,7 @@ abstract class ConnectionPool { private void checkForReconnect(ClientConnectionsEntry entry) { if (entry.getNodeType() == NodeType.SLAVE) { - connectionManager.slaveDown(masterSlaveEntry, entry.getClient().getAddr().getHostName(), + masterSlaveEntry.slaveDown(entry.getClient().getAddr().getHostName(), entry.getClient().getAddr().getPort(), FreezeReason.RECONNECT); log.warn("slave {} disconnected due to failedAttempts={} limit reached", entry.getClient().getAddr(), config.getFailedAttempts()); scheduleCheck(entry); From 901be4439e6d9643d25d99648c7c637a92fba625 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 29 Mar 2016 17:46:01 +0300 Subject: [PATCH 033/120] Long BlockingQueue methods re-subscription when slave is down --- .../client/handler/ConnectionWatchdog.java | 29 ++-- .../redisson/client/protocol/CommandData.java | 4 + .../connection/ClientConnectionsEntry.java | 12 +- .../connection/ConnectionManager.java | 3 - .../MasterSlaveConnectionManager.java | 72 -------- .../redisson/connection/MasterSlaveEntry.java | 155 +++++++++++++++++- .../balancer/LoadBalancerManager.java | 2 +- .../balancer/LoadBalancerManagerImpl.java | 28 +--- 8 files changed, 185 insertions(+), 120 deletions(-) diff --git a/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java b/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java index ac410070c..54270890d 100644 --- a/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java +++ b/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java @@ -23,7 +23,6 @@ import org.redisson.client.RedisException; import org.redisson.client.RedisPubSubConnection; import org.redisson.client.codec.Codec; import org.redisson.client.protocol.CommandData; -import org.redisson.client.protocol.QueueCommand; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -151,26 +150,28 @@ public class ConnectionWatchdog extends ChannelInboundHandlerAdapter { } private void refresh(RedisConnection connection, Channel channel) { - CommandData commandData = connection.getCurrentCommand(); + CommandData commandData = connection.getCurrentCommand(); connection.updateChannel(channel); reattachBlockingQueue(connection, commandData); reattachPubSub(connection); } - private void reattachBlockingQueue(RedisConnection connection, final CommandData commandData) { - if (commandData != null - && QueueCommand.TIMEOUTLESS_COMMANDS.contains(commandData.getCommand().getName())) { - ChannelFuture future = connection.send(commandData); - future.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (!future.isSuccess()) { - log.error("Can't reconnect blocking queue to new connection {}", commandData); - } - } - }); + private void reattachBlockingQueue(RedisConnection connection, final CommandData commandData) { + if (commandData == null + || !commandData.isBlockingCommand()) { + return; } + + ChannelFuture future = connection.send(commandData); + future.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + if (!future.isSuccess()) { + log.error("Can't reconnect blocking queue to new connection. {}", commandData); + } + } + }); } } diff --git a/src/main/java/org/redisson/client/protocol/CommandData.java b/src/main/java/org/redisson/client/protocol/CommandData.java index 6cb93ad77..a47227c9e 100644 --- a/src/main/java/org/redisson/client/protocol/CommandData.java +++ b/src/main/java/org/redisson/client/protocol/CommandData.java @@ -77,5 +77,9 @@ public class CommandData implements QueueCommand { } return Collections.emptyList(); } + + public boolean isBlockingCommand() { + return QueueCommand.TIMEOUTLESS_COMMANDS.contains(command.getName()) && !promise.isDone(); + } } diff --git a/src/main/java/org/redisson/connection/ClientConnectionsEntry.java b/src/main/java/org/redisson/connection/ClientConnectionsEntry.java index abaf8fd4c..1d9c3d6da 100644 --- a/src/main/java/org/redisson/connection/ClientConnectionsEntry.java +++ b/src/main/java/org/redisson/connection/ClientConnectionsEntry.java @@ -158,19 +158,19 @@ public class ClientConnectionsEntry { } private void addReconnectListener(Promise connectionFuture, T conn) { - connectionManager.getConnectListener().onConnect(connectionFuture, conn, nodeType, connectionManager.getConfig()); - addFireEventListener(connectionFuture); + addFireEventListener(conn, connectionFuture); conn.setReconnectListener(new ReconnectListener() { @Override public void onReconnect(RedisConnection conn, Promise connectionFuture) { - connectionManager.getConnectListener().onConnect(connectionFuture, conn, nodeType, connectionManager.getConfig()); - addFireEventListener(connectionFuture); + addFireEventListener(conn, connectionFuture); } }); } - private void addFireEventListener(Promise connectionFuture) { + private void addFireEventListener(T conn, Promise connectionFuture) { + connectionManager.getConnectListener().onConnect(connectionFuture, conn, nodeType, connectionManager.getConfig()); + if (connectionFuture.isSuccess()) { connectionManager.getConnectionEventsHub().fireConnect(connectionFuture.getNow().getRedisClient().getAddr()); return; @@ -196,10 +196,12 @@ public class ClientConnectionsEntry { connectionFuture.tryFailure(future.cause()); return; } + RedisPubSubConnection conn = future.getNow(); log.debug("new pubsub connection created: {}", conn); addReconnectListener(connectionFuture, conn); + allSubscribeConnections.add(conn); } diff --git a/src/main/java/org/redisson/connection/ConnectionManager.java b/src/main/java/org/redisson/connection/ConnectionManager.java index 4bd1a720c..dd3ad00f2 100644 --- a/src/main/java/org/redisson/connection/ConnectionManager.java +++ b/src/main/java/org/redisson/connection/ConnectionManager.java @@ -23,7 +23,6 @@ import java.util.concurrent.TimeUnit; import org.redisson.MasterSlaveServersConfig; import org.redisson.client.RedisClient; import org.redisson.client.RedisConnection; -import org.redisson.client.RedisPubSubConnection; import org.redisson.client.RedisPubSubListener; import org.redisson.client.codec.Codec; import org.redisson.client.protocol.RedisCommand; @@ -44,8 +43,6 @@ import io.netty.util.concurrent.Promise; */ public interface ConnectionManager { - void reattachPubSub(Collection allPubSubConnections); - boolean isClusterMode(); Future newSucceededFuture(R value); diff --git a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java index 2bf246e3f..63f8e175f 100644 --- a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java +++ b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java @@ -542,78 +542,6 @@ public class MasterSlaveConnectionManager implements ConnectionManager { return null; } - @Override - public void reattachPubSub(Collection allPubSubConnections) { - for (Entry mapEntry : name2PubSubConnection.entrySet()) { - for (RedisPubSubConnection redisPubSubConnection : allPubSubConnections) { - PubSubConnectionEntry pubSubEntry = mapEntry.getValue(); - String channelName = mapEntry.getKey(); - - if (!pubSubEntry.getConnection().equals(redisPubSubConnection)) { - continue; - } - - synchronized (pubSubEntry) { - pubSubEntry.close(); - - Collection listeners = pubSubEntry.getListeners(channelName); - if (pubSubEntry.getConnection().getPatternChannels().get(channelName) != null) { - reattachPatternPubSubListeners(channelName, listeners); - } else { - reattachPubSubListeners(channelName, listeners); - } - } - } - } - } - - private void reattachPubSubListeners(final String channelName, final Collection listeners) { - Codec subscribeCodec = unsubscribe(channelName); - if (!listeners.isEmpty()) { - Future future = subscribe(subscribeCodec, channelName, null); - future.addListener(new FutureListener() { - - @Override - public void operationComplete(Future future) - throws Exception { - if (!future.isSuccess()) { - log.error("Can't resubscribe topic channel: " + channelName); - return; - } - PubSubConnectionEntry newEntry = future.getNow(); - for (RedisPubSubListener redisPubSubListener : listeners) { - newEntry.addListener(channelName, redisPubSubListener); - } - log.debug("resubscribed listeners for '{}' channel", channelName); - } - }); - } - } - - private void reattachPatternPubSubListeners(final String channelName, - final Collection listeners) { - Codec subscribeCodec = punsubscribe(channelName); - if (!listeners.isEmpty()) { - Future future = psubscribe(channelName, subscribeCodec); - future.addListener(new FutureListener() { - @Override - public void operationComplete(Future future) - throws Exception { - if (!future.isSuccess()) { - log.error("Can't resubscribe topic channel: " + channelName); - return; - } - - PubSubConnectionEntry newEntry = future.getNow(); - for (RedisPubSubListener redisPubSubListener : listeners) { - newEntry.addListener(channelName, redisPubSubListener); - } - log.debug("resubscribed listeners for '{}' channel-pattern", channelName); - } - }); - } - } - protected void slaveDown(ClusterSlotRange slotRange, String host, int port, FreezeReason freezeReason) { getEntry(slotRange).slaveDown(host, port, freezeReason); } diff --git a/src/main/java/org/redisson/connection/MasterSlaveEntry.java b/src/main/java/org/redisson/connection/MasterSlaveEntry.java index 139030165..ce8bea5c6 100644 --- a/src/main/java/org/redisson/connection/MasterSlaveEntry.java +++ b/src/main/java/org/redisson/connection/MasterSlaveEntry.java @@ -28,6 +28,9 @@ import org.redisson.ReadMode; import org.redisson.client.RedisClient; import org.redisson.client.RedisConnection; import org.redisson.client.RedisPubSubConnection; +import org.redisson.client.RedisPubSubListener; +import org.redisson.client.codec.Codec; +import org.redisson.client.protocol.CommandData; import org.redisson.cluster.ClusterSlotRange; import org.redisson.connection.ClientConnectionsEntry.FreezeReason; import org.redisson.connection.balancer.LoadBalancerManager; @@ -37,7 +40,10 @@ import org.redisson.core.NodeType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.FutureListener; /** * @@ -91,7 +97,8 @@ public class MasterSlaveEntry { } public boolean slaveDown(String host, int port, FreezeReason freezeReason) { - if (!slaveBalancer.freeze(host, port, freezeReason)) { + ClientConnectionsEntry entry = slaveBalancer.freeze(host, port, freezeReason); + if (entry == null) { return false; } @@ -102,8 +109,154 @@ public class MasterSlaveEntry { log.info("master {}:{} used as slave", addr.getHostName(), addr.getPort()); } } + + // close all connections + while (true) { + final RedisConnection connection = entry.pollConnection(); + if (connection == null) { + break; + } + + connection.closeAsync().addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + reattachBlockingQueue(connection); + } + }); + } + + // close all pub/sub connections + while (true) { + RedisPubSubConnection connection = entry.pollSubscribeConnection(); + if (connection == null) { + break; + } + connection.closeAsync(); + } + + for (RedisPubSubConnection connection : entry.getAllSubscribeConnections()) { + reattachPubSub(connection); + } + entry.getAllSubscribeConnections().clear(); + return true; } + + private void reattachPubSub(RedisPubSubConnection redisPubSubConnection) { + for (String channelName : redisPubSubConnection.getChannels().keySet()) { + PubSubConnectionEntry pubSubEntry = connectionManager.getPubSubEntry(channelName); + + synchronized (pubSubEntry) { + pubSubEntry.close(); + + Collection listeners = pubSubEntry.getListeners(channelName); + reattachPubSubListeners(channelName, listeners); + } + } + + for (String channelName : redisPubSubConnection.getPatternChannels().keySet()) { + PubSubConnectionEntry pubSubEntry = connectionManager.getPubSubEntry(channelName); + + synchronized (pubSubEntry) { + pubSubEntry.close(); + + Collection listeners = pubSubEntry.getListeners(channelName); + reattachPatternPubSubListeners(channelName, listeners); + } + } + } + + private void reattachPubSubListeners(final String channelName, final Collection listeners) { + Codec subscribeCodec = connectionManager.unsubscribe(channelName); + if (!listeners.isEmpty()) { + Future future = connectionManager.subscribe(subscribeCodec, channelName, null); + future.addListener(new FutureListener() { + + @Override + public void operationComplete(Future future) + throws Exception { + if (!future.isSuccess()) { + log.error("Can't resubscribe topic channel: " + channelName); + return; + } + PubSubConnectionEntry newEntry = future.getNow(); + for (RedisPubSubListener redisPubSubListener : listeners) { + newEntry.addListener(channelName, redisPubSubListener); + } + log.debug("resubscribed listeners for '{}' channel", channelName); + } + }); + } + } + + private void reattachPatternPubSubListeners(final String channelName, + final Collection listeners) { + Codec subscribeCodec = connectionManager.punsubscribe(channelName); + if (!listeners.isEmpty()) { + Future future = connectionManager.psubscribe(channelName, subscribeCodec); + future.addListener(new FutureListener() { + @Override + public void operationComplete(Future future) + throws Exception { + if (!future.isSuccess()) { + log.error("Can't resubscribe topic channel: " + channelName); + return; + } + + PubSubConnectionEntry newEntry = future.getNow(); + for (RedisPubSubListener redisPubSubListener : listeners) { + newEntry.addListener(channelName, redisPubSubListener); + } + log.debug("resubscribed listeners for '{}' channel-pattern", channelName); + } + }); + } + } + + private void reattachBlockingQueue(RedisConnection connection) { + final CommandData commandData = connection.getCurrentCommand(); + + if (commandData == null + || !commandData.isBlockingCommand()) { + return; + } + + Future newConnection = connectionReadOp(); + newConnection.addListener(new FutureListener() { + @Override + public void operationComplete(Future future) throws Exception { + if (!future.isSuccess()) { + log.error("Can't resubscribe blocking queue {}", commandData); + return; + } + + final RedisConnection newConnection = future.getNow(); + + final FutureListener listener = new FutureListener() { + @Override + public void operationComplete(Future future) throws Exception { + releaseRead(newConnection); + } + }; + commandData.getPromise().addListener(listener); + if (commandData.getPromise().isDone()) { + return; + } + ChannelFuture channelFuture = newConnection.send(commandData); + channelFuture.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + if (!future.isSuccess()) { + listener.operationComplete(null); + commandData.getPromise().removeListener(listener); + releaseRead(newConnection); + log.error("Can't resubscribe blocking queue {}", commandData); + } + } + }); + } + }); + } public Future addSlave(String host, int port) { return addSlave(host, port, true, NodeType.SLAVE); diff --git a/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java b/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java index 2b28c343f..edef7d2bb 100644 --- a/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java +++ b/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java @@ -36,7 +36,7 @@ public interface LoadBalancerManager { boolean unfreeze(String host, int port, FreezeReason freezeReason); - boolean freeze(String host, int port, FreezeReason freezeReason); + ClientConnectionsEntry freeze(String host, int port, FreezeReason freezeReason); Future add(ClientConnectionsEntry entry); diff --git a/src/main/java/org/redisson/connection/balancer/LoadBalancerManagerImpl.java b/src/main/java/org/redisson/connection/balancer/LoadBalancerManagerImpl.java index 8c73c4d8d..38135c62d 100644 --- a/src/main/java/org/redisson/connection/balancer/LoadBalancerManagerImpl.java +++ b/src/main/java/org/redisson/connection/balancer/LoadBalancerManagerImpl.java @@ -95,16 +95,16 @@ public class LoadBalancerManagerImpl implements LoadBalancerManager { return false; } - public boolean freeze(String host, int port, FreezeReason freezeReason) { + public ClientConnectionsEntry freeze(String host, int port, FreezeReason freezeReason) { InetSocketAddress addr = new InetSocketAddress(host, port); ClientConnectionsEntry connectionEntry = addr2Entry.get(addr); if (connectionEntry == null) { - return false; + return null; } synchronized (connectionEntry) { if (connectionEntry.isFreezed()) { - return false; + return null; } connectionEntry.setFreezed(true); @@ -116,27 +116,7 @@ public class LoadBalancerManagerImpl implements LoadBalancerManager { } } - // close all connections - while (true) { - RedisConnection connection = connectionEntry.pollConnection(); - if (connection == null) { - break; - } - connection.closeAsync(); - } - - // close all pub/sub connections - while (true) { - RedisPubSubConnection connection = connectionEntry.pollSubscribeConnection(); - if (connection == null) { - break; - } - connection.closeAsync(); - } - - connectionManager.reattachPubSub(connectionEntry.getAllSubscribeConnections()); - connectionEntry.getAllSubscribeConnections().clear(); - return true; + return connectionEntry; } public Future nextPubSubConnection() { From 0f542267cbce01621e38d2b64118c0985f2f17d3 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 29 Mar 2016 18:01:55 +0300 Subject: [PATCH 034/120] RedissonMultiLock refactoring --- .../org/redisson/core/RedissonMultiLock.java | 47 ++----------------- 1 file changed, 4 insertions(+), 43 deletions(-) diff --git a/src/main/java/org/redisson/core/RedissonMultiLock.java b/src/main/java/org/redisson/core/RedissonMultiLock.java index 3d5633aac..83ee1a785 100644 --- a/src/main/java/org/redisson/core/RedissonMultiLock.java +++ b/src/main/java/org/redisson/core/RedissonMultiLock.java @@ -20,19 +20,16 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; -import io.netty.util.concurrent.DefaultPromise; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; +import io.netty.util.concurrent.ImmediateEventExecutor; import io.netty.util.concurrent.Promise; -import io.netty.util.internal.PlatformDependent; /** * Groups multiple independent locks and handles them as one lock. @@ -75,52 +72,16 @@ public class RedissonMultiLock implements Lock { } public void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference result = new AtomicReference(); - Promise promise = new DefaultPromise() { - public Promise setSuccess(Void result) { - latch.countDown(); - return this; - }; - - public Promise setFailure(Throwable cause) { - result.set(cause); - latch.countDown(); - return this; - }; - }; + Promise promise = ImmediateEventExecutor.INSTANCE.newPromise(); lock(promise, 0, leaseTime, unit); - latch.await(); - if (result.get() instanceof Throwable) { - PlatformDependent.throwException((Throwable)result.get()); - } + promise.sync(); } @Override public void lockInterruptibly() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference result = new AtomicReference(); - Promise promise = new DefaultPromise() { - public Promise setSuccess(Void result) { - latch.countDown(); - return this; - }; - - public Promise setFailure(Throwable cause) { - result.set(cause); - latch.countDown(); - return this; - }; - }; - - lock(promise, 0, -1, null); - - latch.await(); - if (result.get() instanceof Throwable) { - PlatformDependent.throwException((Throwable)result.get()); - } + lockInterruptibly(-1, null); } private void lock(final Promise promise, final long waitTime, final long leaseTime, final TimeUnit unit) throws InterruptedException { From 8495095697104ea4ef395606d5eac9199d860dbd Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 29 Mar 2016 20:04:46 +0300 Subject: [PATCH 035/120] RemoteService uses queue for responses --- .../org/redisson/RedissonRemoteService.java | 92 ++++++++----------- .../org/redisson/core/RRemoteService.java | 11 ++- .../redisson/remote/RemoteServiceRequest.java | 9 +- 3 files changed, 54 insertions(+), 58 deletions(-) diff --git a/src/main/java/org/redisson/RedissonRemoteService.java b/src/main/java/org/redisson/RedissonRemoteService.java index d424128a3..5e2c813df 100644 --- a/src/main/java/org/redisson/RedissonRemoteService.java +++ b/src/main/java/org/redisson/RedissonRemoteService.java @@ -18,15 +18,15 @@ package org.redisson; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.List; import java.util.Map; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import org.redisson.core.MessageListener; +import org.redisson.core.RBatch; import org.redisson.core.RBlockingQueue; +import org.redisson.core.RBlockingQueueAsync; import org.redisson.core.RRemoteService; -import org.redisson.core.RTopic; import org.redisson.remote.RRemoteServiceResponse; import org.redisson.remote.RemoteServiceAck; import org.redisson.remote.RemoteServiceAckTimeoutException; @@ -44,6 +44,11 @@ import io.netty.util.concurrent.FutureListener; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.ThreadLocalRandom; +/** + * + * @author Nikita Koksharov + * + */ public class RedissonRemoteService implements RRemoteService { private static final Logger log = LoggerFactory.getLogger(RedissonRemoteService.class); @@ -102,22 +107,18 @@ public class RedissonRemoteService implements RRemoteService { } final RemoteServiceMethod method = beans.get(new RemoteServiceKey(remoteInterface, request.getMethodName())); - String responseName = "redisson_remote_service:{" + remoteInterface.getName() + "}:" + request.getRequestId(); - final RTopic topic = redisson.getTopic(responseName); - Future ackClientsFuture = topic.publishAsync(new RemoteServiceAck()); - ackClientsFuture.addListener(new FutureListener() { + final String responseName = "redisson_remote_service:{" + remoteInterface.getName() + "}:" + request.getRequestId(); + + Future> ackClientsFuture = send(request.getAckTimeout(), responseName, new RemoteServiceAck()); + ackClientsFuture.addListener(new FutureListener>() { @Override - public void operationComplete(Future future) throws Exception { + public void operationComplete(Future> future) throws Exception { if (!future.isSuccess()) { log.error("Can't send ack for request: " + request, future.cause()); return; } - if (future.getNow() == 0) { - log.error("Client has not received ack for request: {}", request); - return; - } - invokeMethod(request, method, topic); + invokeMethod(request, method, responseName); } }); } @@ -125,8 +126,7 @@ public class RedissonRemoteService implements RRemoteService { }); } - private void invokeMethod(final RemoteServiceRequest request, RemoteServiceMethod method, - RTopic topic) { + private void invokeMethod(final RemoteServiceRequest request, RemoteServiceMethod method, String responseName) { final AtomicReference responseHolder = new AtomicReference(); try { Object result = method.getMethod().invoke(method.getBean(), request.getArgs()); @@ -138,24 +138,21 @@ public class RedissonRemoteService implements RRemoteService { log.error("Can't execute: " + request, e); } - Future clientsFuture = topic.publishAsync(responseHolder.get()); - clientsFuture.addListener(new FutureListener() { + Future> clientsFuture = send(request.getResponseTimeout(), responseName, responseHolder.get()); + clientsFuture.addListener(new FutureListener>() { @Override - public void operationComplete(Future future) throws Exception { + public void operationComplete(Future> future) throws Exception { if (!future.isSuccess()) { + log.error("Can't send response: " + responseHolder.get() + " for request: " + request, future.cause()); return; } - - if (future.getNow() == 0) { - log.error("None of clients has not received a response: {} for request: {}", responseHolder.get(), request); - } } }); } @Override public T get(Class remoteInterface) { - return get(remoteInterface, -1, null); + return get(remoteInterface, 30, TimeUnit.SECONDS); } @Override @@ -172,45 +169,26 @@ public class RedissonRemoteService implements RRemoteService { String requestQueueName = "redisson_remote_service:{" + remoteInterface.getName() + "}"; RBlockingQueue requestQueue = redisson.getBlockingQueue(requestQueueName); - RemoteServiceRequest request = new RemoteServiceRequest(requestId, method.getName(), args, ackTimeUnit.toMillis(ackTimeout), System.currentTimeMillis()); + RemoteServiceRequest request = new RemoteServiceRequest(requestId, method.getName(), args, + ackTimeUnit.toMillis(ackTimeout), executionTimeUnit.toMillis(executionTimeout), System.currentTimeMillis()); requestQueue.add(request); String responseName = "redisson_remote_service:{" + remoteInterface.getName() + "}:" + requestId; - final CountDownLatch ackLatch = new CountDownLatch(1); - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference response = new AtomicReference(); - final RTopic topic = redisson.getTopic(responseName); - int listenerId = topic.addListener(new MessageListener() { - @Override - public void onMessage(String channel, RRemoteServiceResponse msg) { - if (msg instanceof RemoteServiceResponse) { - response.set(msg); - latch.countDown(); - } else { - ackLatch.countDown(); - } - } - }); + RBlockingQueue responseQueue = redisson.getBlockingQueue(responseName); - if (!ackLatch.await(ackTimeout, ackTimeUnit)) { - topic.removeListener(listenerId); + RemoteServiceAck ack = (RemoteServiceAck) responseQueue.poll(ackTimeout, ackTimeUnit); + if (ack == null) { throw new RemoteServiceAckTimeoutException("No ACK response after " + ackTimeUnit.toMillis(ackTimeout) + "ms for request: " + request); } - if (executionTimeout == -1) { - latch.await(); - } else { - if (!latch.await(executionTimeout, executionTimeUnit)) { - topic.removeListener(listenerId); - throw new RemoteServiceTimeoutException("No response after " + executionTimeUnit.toMillis(executionTimeout) + "ms for request: " + request); - } + RemoteServiceResponse response = (RemoteServiceResponse) responseQueue.poll(executionTimeout, executionTimeUnit); + if (response == null) { + throw new RemoteServiceTimeoutException("No response after " + executionTimeUnit.toMillis(executionTimeout) + "ms for request: " + request); } - topic.removeListener(listenerId); - RemoteServiceResponse msg = (RemoteServiceResponse) response.get(); - if (msg.getError() != null) { - throw msg.getError(); + if (response.getError() != null) { + throw response.getError(); } - return msg.getResult(); + return response.getResult(); } }; return (T) Proxy.newProxyInstance(remoteInterface.getClassLoader(), new Class[] {remoteInterface}, handler); @@ -222,5 +200,13 @@ public class RedissonRemoteService implements RRemoteService { ThreadLocalRandom.current().nextBytes(id); return ByteBufUtil.hexDump(id); } + + private Future> send(long timeout, String responseName, T response) { + RBatch batch = redisson.createBatch(); + RBlockingQueueAsync queue = batch.getBlockingQueue(responseName); + queue.putAsync(response); + queue.expireAsync(timeout, TimeUnit.MILLISECONDS); + return batch.executeAsync(); + } } diff --git a/src/main/java/org/redisson/core/RRemoteService.java b/src/main/java/org/redisson/core/RRemoteService.java index 0461188ed..f5449b664 100644 --- a/src/main/java/org/redisson/core/RRemoteService.java +++ b/src/main/java/org/redisson/core/RRemoteService.java @@ -17,8 +17,6 @@ package org.redisson.core; import java.util.concurrent.TimeUnit; -import org.redisson.remote.RemoteServiceAckTimeoutException; - /** * Allows to execute object methods remotely between Redisson instances (Server side and Client side instances in terms of remote invocation). *

@@ -78,7 +76,10 @@ public interface RRemoteService { /** * Get remote service object for remote invocations. - * Uses ack timeout = 1000 ms by default + *

+ * Ack timeout = 1000 ms by default + *

+ * Execution timeout = 30 sec by default * * @param remoteInterface * @return @@ -87,7 +88,9 @@ public interface RRemoteService { /** * Get remote service object for remote invocations - * with specified invocation timeout. Uses ack timeout = 1000 ms by default + * with specified invocation timeout. + *

+ * Ack timeout = 1000 ms by default * * @param remoteInterface * @param executionTimeout - invocation timeout diff --git a/src/main/java/org/redisson/remote/RemoteServiceRequest.java b/src/main/java/org/redisson/remote/RemoteServiceRequest.java index f47324f10..3980ac4f0 100644 --- a/src/main/java/org/redisson/remote/RemoteServiceRequest.java +++ b/src/main/java/org/redisson/remote/RemoteServiceRequest.java @@ -23,20 +23,27 @@ public class RemoteServiceRequest { private String methodName; private Object[] args; private long ackTimeout; + private long responseTimeout; private long date; + public RemoteServiceRequest() { } - public RemoteServiceRequest(String requestId, String methodName, Object[] args, long ackTimeout, long date) { + public RemoteServiceRequest(String requestId, String methodName, Object[] args, long ackTimeout, long responseTimeout, long date) { super(); this.requestId = requestId; this.methodName = methodName; this.args = args; this.ackTimeout = ackTimeout; + this.responseTimeout = responseTimeout; this.date = date; } + public long getResponseTimeout() { + return responseTimeout; + } + public long getDate() { return date; } From 66024192aa02ac77d7e82de8714246dc1279b606 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Tue, 29 Mar 2016 22:44:25 +0100 Subject: [PATCH 036/120] travis matrix added matrix environment tags to test against three redis versions. --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0153205e2..32d003cc5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,10 @@ language: java jdk: - oraclejdk8 env: - - REDIS_VERSION=3.0.7 + matrix: + - REDIS_VERSION=3.0.7 + - REDIS_VERSION=2.8.24 + - REDIS_VERSION=3.2.0-rc3 cache: directories: - $HOME/.m2 From 382f50eb983ea8b8b147f2affdf70e2868aa8f91 Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 30 Mar 2016 10:47:47 +0300 Subject: [PATCH 037/120] RScoredSortedSet.removeAllAsync & removeAll methods optimization --- .../java/org/redisson/RedissonScoredSortedSet.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index e4755f679..ee6724f9a 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -330,12 +330,11 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future removeAllAsync(Collection c) { return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 4, ValueType.OBJECTS), - "local v = 0 " + - "for i = 0, table.getn(ARGV), 1 do " - + "if redis.call('zrem', KEYS[1], ARGV[i]) == 1 " - + "then v = 1 end " - +"end " - + "return v ", + "local v = 0;" + + "for i=1, #ARGV, 5000 do " + + "v = v + redis.call('zrem', KEYS[1], unpack(ARGV, i, math.min(i+4999, #ARGV))); " + + "end " + + "return v > 0;", Collections.singletonList(getName()), c.toArray()); } From 49f10ee0273e1a3abf8b146b1e81f620e9e17878 Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 30 Mar 2016 17:47:00 +0300 Subject: [PATCH 038/120] refactoring --- src/main/java/org/redisson/RedissonScoredSortedSet.java | 9 +++------ src/test/java/org/redisson/RedissonSetCacheTest.java | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index ee6724f9a..62c493436 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -30,11 +30,8 @@ import java.util.NoSuchElementException; import org.redisson.client.codec.Codec; import org.redisson.client.codec.ScoredCodec; import org.redisson.client.codec.StringCodec; -import org.redisson.client.protocol.RedisCommand; import org.redisson.client.protocol.RedisCommands; import org.redisson.client.protocol.ScoredEntry; -import org.redisson.client.protocol.RedisCommand.ValueType; -import org.redisson.client.protocol.convertor.BooleanReplayConvertor; import org.redisson.client.protocol.decoder.ListScanResult; import org.redisson.command.CommandAsyncExecutor; import org.redisson.core.RScoredSortedSet; @@ -315,7 +312,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future containsAllAsync(Collection c) { - return commandExecutor.evalReadAsync(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 4, ValueType.OBJECTS), + return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, "local s = redis.call('zrange', KEYS[1], 0, -1);" + "for i = 0, table.getn(s), 1 do " + "for j = 0, table.getn(ARGV), 1 do " @@ -329,7 +326,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future removeAllAsync(Collection c) { - return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 4, ValueType.OBJECTS), + return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, "local v = 0;" + "for i=1, #ARGV, 5000 do " + "v = v + redis.call('zrem', KEYS[1], unpack(ARGV, i, math.min(i+4999, #ARGV))); " @@ -350,7 +347,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future retainAllAsync(Collection c) { - return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 4, ValueType.OBJECTS), + return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, "local changed = 0 " + "local s = redis.call('zrange', KEYS[1], 0, -1) " + "local i = 0 " diff --git a/src/test/java/org/redisson/RedissonSetCacheTest.java b/src/test/java/org/redisson/RedissonSetCacheTest.java index 61e489e29..64e4ddc23 100644 --- a/src/test/java/org/redisson/RedissonSetCacheTest.java +++ b/src/test/java/org/redisson/RedissonSetCacheTest.java @@ -203,7 +203,7 @@ public class RedissonSetCacheTest extends BaseTest { RSetCache set = redisson.getSetCache("set"); for (int i = 0; i < 10000; i++) { set.add(i); - set.add(i*10, 10, TimeUnit.SECONDS); + set.add(i*10, 15, TimeUnit.SECONDS); } Assert.assertTrue(set.retainAll(Arrays.asList(1, 2))); From d854ce0f3a0d13e16e84a6cdc0d3c0215b40c251 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 31 Mar 2016 20:34:31 +0300 Subject: [PATCH 039/120] RGeo implemented. #295 --- src/main/java/org/redisson/Redisson.java | 11 + .../java/org/redisson/RedissonClient.java | 19 ++ src/main/java/org/redisson/RedissonGeo.java | 194 ++++++++++++++++++ .../redisson/client/codec/GeoEntryCodec.java | 45 ++++ .../redisson/client/codec/ScoredCodec.java | 2 +- .../client/handler/CommandEncoder.java | 8 +- .../client/protocol/RedisCommand.java | 2 +- .../client/protocol/RedisCommands.java | 13 +- .../convertor/DoubleReplayConvertor.java | 2 +- .../decoder/FlatNestedMultiDecoder.java | 44 ++++ .../protocol/decoder/GeoDistanceDecoder.java | 58 ++++++ .../decoder/GeoDistanceMapDecoder.java | 68 ++++++ .../protocol/decoder/GeoMapReplayDecoder.java | 48 +++++ .../protocol/decoder/GeoPositionDecoder.java | 50 +++++ .../decoder/GeoPositionMapDecoder.java | 62 ++++++ .../protocol/decoder/NestedMultiDecoder.java | 108 +++++++--- .../protocol/decoder/NestedMultiDecoder2.java | 86 -------- .../connection/decoder/MapGetAllDecoder.java | 4 + src/main/java/org/redisson/core/GeoEntry.java | 43 ++++ .../java/org/redisson/core/GeoPosition.java | 70 +++++++ src/main/java/org/redisson/core/GeoUnit.java | 48 +++++ src/main/java/org/redisson/core/RGeo.java | 51 +++++ .../java/org/redisson/core/RGeoAsync.java | 53 +++++ 23 files changed, 972 insertions(+), 117 deletions(-) create mode 100644 src/main/java/org/redisson/RedissonGeo.java create mode 100644 src/main/java/org/redisson/client/codec/GeoEntryCodec.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/FlatNestedMultiDecoder.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/GeoDistanceDecoder.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/GeoDistanceMapDecoder.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/GeoMapReplayDecoder.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/GeoPositionDecoder.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/GeoPositionMapDecoder.java delete mode 100644 src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder2.java create mode 100644 src/main/java/org/redisson/core/GeoEntry.java create mode 100644 src/main/java/org/redisson/core/GeoPosition.java create mode 100644 src/main/java/org/redisson/core/GeoUnit.java create mode 100644 src/main/java/org/redisson/core/RGeo.java create mode 100644 src/main/java/org/redisson/core/RGeoAsync.java diff --git a/src/main/java/org/redisson/Redisson.java b/src/main/java/org/redisson/Redisson.java index f1fdf2953..a6c88ea08 100755 --- a/src/main/java/org/redisson/Redisson.java +++ b/src/main/java/org/redisson/Redisson.java @@ -49,6 +49,7 @@ import org.redisson.core.RBloomFilter; import org.redisson.core.RBucket; import org.redisson.core.RCountDownLatch; import org.redisson.core.RDeque; +import org.redisson.core.RGeo; import org.redisson.core.RHyperLogLog; import org.redisson.core.RKeys; import org.redisson.core.RLexSortedSet; @@ -180,6 +181,16 @@ public class Redisson implements RedissonClient { public static RedissonReactiveClient createReactive(Config config) { return new RedissonReactive(config); } + + @Override + public RGeo getGeo(String name) { + return new RedissonGeo(commandExecutor, name); + } + + @Override + public RGeo getGeo(String name, Codec codec) { + return new RedissonGeo(codec, commandExecutor, name); + } @Override public RBucket getBucket(String name) { diff --git a/src/main/java/org/redisson/RedissonClient.java b/src/main/java/org/redisson/RedissonClient.java index edb10dced..a83750c7d 100755 --- a/src/main/java/org/redisson/RedissonClient.java +++ b/src/main/java/org/redisson/RedissonClient.java @@ -33,6 +33,7 @@ import org.redisson.core.RBloomFilter; import org.redisson.core.RBucket; import org.redisson.core.RCountDownLatch; import org.redisson.core.RDeque; +import org.redisson.core.RGeo; import org.redisson.core.RHyperLogLog; import org.redisson.core.RKeys; import org.redisson.core.RLexSortedSet; @@ -65,6 +66,24 @@ import org.redisson.core.RTopic; */ public interface RedissonClient { + /** + * Returns geospatial items holder instance by name. + * + * @param name + * @return + */ + RGeo getGeo(String name); + + /** + * Returns geospatial items holder instance by name + * using provided codec for geospatial members. + * + * @param name + * @param geospatial member codec + * @return + */ + RGeo getGeo(String name, Codec codec); + /** * Returns set-based cache instance by name. * Supports value eviction with a given TTL value. diff --git a/src/main/java/org/redisson/RedissonGeo.java b/src/main/java/org/redisson/RedissonGeo.java new file mode 100644 index 000000000..dd36d3764 --- /dev/null +++ b/src/main/java/org/redisson/RedissonGeo.java @@ -0,0 +1,194 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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 java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.redisson.client.codec.Codec; +import org.redisson.client.codec.GeoEntryCodec; +import org.redisson.client.codec.ScoredCodec; +import org.redisson.client.protocol.RedisCommand; +import org.redisson.client.protocol.RedisCommand.ValueType; +import org.redisson.client.protocol.RedisCommands; +import org.redisson.client.protocol.decoder.GeoDistanceDecoder; +import org.redisson.client.protocol.decoder.GeoMapReplayDecoder; +import org.redisson.client.protocol.decoder.GeoPositionDecoder; +import org.redisson.client.protocol.decoder.GeoPositionMapDecoder; +import org.redisson.client.protocol.decoder.MultiDecoder; +import org.redisson.client.protocol.decoder.NestedMultiDecoder; +import org.redisson.client.protocol.decoder.FlatNestedMultiDecoder; +import org.redisson.command.CommandAsyncExecutor; +import org.redisson.connection.decoder.MapGetAllDecoder; +import org.redisson.core.GeoEntry; +import org.redisson.core.GeoPosition; +import org.redisson.core.GeoUnit; +import org.redisson.core.RGeo; + +import io.netty.util.concurrent.Future; + +public class RedissonGeo extends RedissonExpirable implements RGeo { + + public RedissonGeo(CommandAsyncExecutor connectionManager, String name) { + super(connectionManager, name); + } + + public RedissonGeo(Codec codec, CommandAsyncExecutor connectionManager, String name) { + super(codec, connectionManager, name); + } + + @Override + public Future addAsync(double longitude, double latitude, V member) { + return commandExecutor.writeAsync(getName(), RedisCommands.GEOADD, getName(), convert(longitude), convert(latitude), member); + } + + private String convert(double longitude) { + return BigDecimal.valueOf(longitude).toPlainString(); + } + + @Override + public long add(double longitude, double latitude, V member) { + return get(addAsync(longitude, latitude, member)); + } + + @Override + public long add(GeoEntry... entries) { + return get(addAsync(entries)); + } + + @Override + public Future addAsync(GeoEntry... entries) { + List params = new ArrayList(entries.length + 1); + params.add(getName()); + for (GeoEntry entry : entries) { + params.add(entry.getLongitude()); + params.add(entry.getLatitude()); + params.add(entry.getMember()); + } + return commandExecutor.writeAsync(getName(), new GeoEntryCodec(codec), RedisCommands.GEOADD_ENTRIES, params.toArray()); + } + + @Override + public Double dist(V firstMember, V secondMember, GeoUnit geoUnit) { + return get(distAsync(firstMember, secondMember, geoUnit)); + } + + @Override + public Future distAsync(V firstMember, V secondMember, GeoUnit geoUnit) { + return commandExecutor.readAsync(getName(), new ScoredCodec(codec), RedisCommands.GEODIST, getName(), firstMember, secondMember, geoUnit); + } + + @Override + public Map hash(V... members) { + return get(hashAsync(members)); + } + + @Override + public Future> hashAsync(V... members) { + List params = new ArrayList(members.length + 1); + params.add(getName()); + params.addAll(Arrays.asList(members)); + RedisCommand> command = new RedisCommand>("GEOHASH", new MapGetAllDecoder(params), 2, ValueType.OBJECTS); + return commandExecutor.readAsync(getName(), new ScoredCodec(codec), command, params.toArray()); + } + + @Override + public Map pos(V... members) { + return get(posAsync(members)); + } + + @Override + public Future> posAsync(V... members) { + List params = new ArrayList(members.length + 1); + params.add(getName()); + params.addAll(Arrays.asList(members)); + + MultiDecoder> decoder = new NestedMultiDecoder(new GeoPositionDecoder(), new GeoPositionMapDecoder(params)); + RedisCommand> command = new RedisCommand>("GEOPOS", decoder, 2, ValueType.OBJECTS); + return commandExecutor.readAsync(getName(), new ScoredCodec(codec), command, params.toArray()); + } + + @Override + public List radius(double longitude, double latitude, double radius, GeoUnit geoUnit) { + return get(radiusAsync(longitude, latitude, radius, geoUnit)); + } + + @Override + public Future> radiusAsync(double longitude, double latitude, double radius, GeoUnit geoUnit) { + return commandExecutor.readAsync(getName(), codec, RedisCommands.GEORADIUS, getName(), convert(longitude), convert(latitude), radius, geoUnit); + } + + @Override + public Map radiusWithDistance(double longitude, double latitude, double radius, GeoUnit geoUnit) { + return get(radiusWithDistanceAsync(longitude, latitude, radius, geoUnit)); + } + + @Override + public Future> radiusWithDistanceAsync(double longitude, double latitude, double radius, GeoUnit geoUnit) { + MultiDecoder> decoder = new FlatNestedMultiDecoder(new GeoDistanceDecoder(codec), new GeoMapReplayDecoder()); + RedisCommand> command = new RedisCommand>("GEORADIUS", decoder); + return commandExecutor.readAsync(getName(), codec, command, getName(), convert(longitude), convert(latitude), radius, geoUnit, "WITHDIST"); + } + + @Override + public Map radiusWithPosition(double longitude, double latitude, double radius, GeoUnit geoUnit) { + return get(radiusWithPositionAsync(longitude, latitude, radius, geoUnit)); + } + + @Override + public Future> radiusWithPositionAsync(double longitude, double latitude, double radius, GeoUnit geoUnit) { + MultiDecoder> decoder = new NestedMultiDecoder(new GeoPositionDecoder(), new GeoDistanceDecoder(codec), new GeoMapReplayDecoder()); + RedisCommand> command = new RedisCommand>("GEORADIUS", decoder); + return commandExecutor.readAsync(getName(), codec, command, getName(), convert(longitude), convert(latitude), radius, geoUnit, "WITHCOORD"); + } + + @Override + public List radius(V member, double radius, GeoUnit geoUnit) { + return get(radiusAsync(member, radius, geoUnit)); + } + + @Override + public Future> radiusAsync(V member, double radius, GeoUnit geoUnit) { + return commandExecutor.readAsync(getName(), codec, RedisCommands.GEORADIUSBYMEMBER, getName(), member, radius, geoUnit); + } + + @Override + public Map radiusWithDistance(V member, double radius, GeoUnit geoUnit) { + return get(radiusWithDistanceAsync(member, radius, geoUnit)); + } + + @Override + public Future> radiusWithDistanceAsync(V member, double radius, GeoUnit geoUnit) { + MultiDecoder> decoder = new FlatNestedMultiDecoder(new GeoDistanceDecoder(codec), new GeoMapReplayDecoder()); + RedisCommand command = new RedisCommand("GEORADIUSBYMEMBER", decoder, 2); + return commandExecutor.readAsync(getName(), codec, command, getName(), member, radius, geoUnit, "WITHDIST"); + } + + @Override + public Map radiusWithPosition(V member, double radius, GeoUnit geoUnit) { + return get(radiusWithPositionAsync(member, radius, geoUnit)); + } + + @Override + public Future> radiusWithPositionAsync(V member, double radius, GeoUnit geoUnit) { + MultiDecoder> decoder = new NestedMultiDecoder(new GeoPositionDecoder(), new GeoDistanceDecoder(codec), new GeoMapReplayDecoder()); + RedisCommand> command = new RedisCommand>("GEORADIUSBYMEMBER", decoder, 2); + return commandExecutor.readAsync(getName(), codec, command, getName(), member, radius, geoUnit, "WITHCOORD"); + } +} diff --git a/src/main/java/org/redisson/client/codec/GeoEntryCodec.java b/src/main/java/org/redisson/client/codec/GeoEntryCodec.java new file mode 100644 index 000000000..71a985948 --- /dev/null +++ b/src/main/java/org/redisson/client/codec/GeoEntryCodec.java @@ -0,0 +1,45 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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.codec; + +import org.redisson.client.protocol.Encoder; + +public class GeoEntryCodec extends StringCodec { + + private final ThreadLocal pos = new ThreadLocal() { + protected Integer initialValue() { + return 0; + }; + }; + + private final Codec delegate; + + public GeoEntryCodec(Codec delegate) { + super(); + this.delegate = delegate; + } + + @Override + public Encoder getValueEncoder() { + Integer p = pos.get() + 1; + pos.set(p); + if (p % 3 == 0) { + return delegate.getValueEncoder(); + } + return super.getValueEncoder(); + } + +} diff --git a/src/main/java/org/redisson/client/codec/ScoredCodec.java b/src/main/java/org/redisson/client/codec/ScoredCodec.java index 12378bc2f..f4bf3656e 100644 --- a/src/main/java/org/redisson/client/codec/ScoredCodec.java +++ b/src/main/java/org/redisson/client/codec/ScoredCodec.java @@ -19,7 +19,7 @@ import org.redisson.client.protocol.Encoder; public class ScoredCodec extends StringCodec { - public final Codec delegate; + private final Codec delegate; public ScoredCodec(Codec delegate) { super(); diff --git a/src/main/java/org/redisson/client/handler/CommandEncoder.java b/src/main/java/org/redisson/client/handler/CommandEncoder.java index 8c7560908..2a8b0af2c 100644 --- a/src/main/java/org/redisson/client/handler/CommandEncoder.java +++ b/src/main/java/org/redisson/client/handler/CommandEncoder.java @@ -18,6 +18,7 @@ package org.redisson.client.handler; import java.util.List; import org.redisson.client.codec.ByteArrayCodec; +import org.redisson.client.codec.StringCodec; import org.redisson.client.protocol.CommandData; import org.redisson.client.protocol.Encoder; import org.redisson.client.protocol.DefaultParamsEncoder; @@ -112,8 +113,11 @@ public class CommandEncoder extends MessageToByteEncoder { - public enum ValueType {OBJECT, OBJECTS, MAP_VALUE, MAP_KEY, MAP, BINARY} + public enum ValueType {OBJECT, OBJECTS, MAP_VALUE, MAP_KEY, MAP, BINARY, STRING} private ValueType outParamType = ValueType.OBJECT; private List inParamType = Arrays.asList(ValueType.OBJECT); diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index 9528685ce..d56eae403 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -15,10 +15,11 @@ */ package org.redisson.client.protocol; +import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.Map.Entry; +import java.util.Set; import org.redisson.client.protocol.RedisCommand.ValueType; import org.redisson.client.protocol.convertor.BitSetReplayConvertor; @@ -40,7 +41,7 @@ import org.redisson.client.protocol.decoder.ListScanResultReplayDecoder; import org.redisson.client.protocol.decoder.MapScanResult; import org.redisson.client.protocol.decoder.MapScanResultReplayDecoder; import org.redisson.client.protocol.decoder.NestedMultiDecoder; -import org.redisson.client.protocol.decoder.NestedMultiDecoder2; +import org.redisson.client.protocol.decoder.FlatNestedMultiDecoder; import org.redisson.client.protocol.decoder.ObjectFirstResultReplayDecoder; import org.redisson.client.protocol.decoder.ObjectListReplayDecoder; import org.redisson.client.protocol.decoder.ObjectMapEntryReplayDecoder; @@ -57,6 +58,12 @@ import org.redisson.client.protocol.pubsub.PubSubStatusDecoder; public interface RedisCommands { + RedisStrictCommand GEOADD = new RedisStrictCommand("GEOADD", 4); + RedisStrictCommand GEOADD_ENTRIES = new RedisStrictCommand("GEOADD", 2, ValueType.OBJECTS); + RedisCommand GEODIST = new RedisCommand("GEODIST", new DoubleReplayConvertor(), 2, Arrays.asList(ValueType.OBJECT, ValueType.OBJECT, ValueType.STRING)); + RedisCommand> GEORADIUS = new RedisCommand>("GEORADIUS", new ObjectListReplayDecoder()); + RedisCommand> GEORADIUSBYMEMBER = new RedisCommand>("GEORADIUSBYMEMBER", new ObjectListReplayDecoder(), 2); + RedisStrictCommand KEYSLOT = new RedisStrictCommand("CLUSTER", "KEYSLOT", new IntegerReplayConvertor()); RedisStrictCommand GETBIT = new RedisStrictCommand("GETBIT", new BooleanReplayConvertor()); @@ -238,7 +245,7 @@ public interface RedisCommands { RedisStrictCommand> SENTINEL_GET_MASTER_ADDR_BY_NAME = new RedisStrictCommand>("SENTINEL", "GET-MASTER-ADDR-BY-NAME", new StringListReplayDecoder()); RedisCommand>> SENTINEL_SLAVES = new RedisCommand>>("SENTINEL", "SLAVES", - new NestedMultiDecoder2(new ObjectMapReplayDecoder(), new ListResultReplayDecoder()), ValueType.OBJECT + new FlatNestedMultiDecoder(new ObjectMapReplayDecoder(), new ListResultReplayDecoder()), ValueType.OBJECT ); RedisStrictCommand INFO_REPLICATION = new RedisStrictCommand("INFO", "replication", new StringDataDecoder()); diff --git a/src/main/java/org/redisson/client/protocol/convertor/DoubleReplayConvertor.java b/src/main/java/org/redisson/client/protocol/convertor/DoubleReplayConvertor.java index 4bef92164..dd1a51eca 100644 --- a/src/main/java/org/redisson/client/protocol/convertor/DoubleReplayConvertor.java +++ b/src/main/java/org/redisson/client/protocol/convertor/DoubleReplayConvertor.java @@ -19,7 +19,7 @@ public class DoubleReplayConvertor extends SingleConvertor { @Override public Double convert(Object obj) { - if (obj == null) { + if (obj == null || obj.toString().isEmpty()) { return null; } return Double.valueOf(obj.toString()); diff --git a/src/main/java/org/redisson/client/protocol/decoder/FlatNestedMultiDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/FlatNestedMultiDecoder.java new file mode 100644 index 000000000..413694b33 --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/FlatNestedMultiDecoder.java @@ -0,0 +1,44 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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 java.io.IOException; + +import org.redisson.client.handler.State; + +import io.netty.buffer.ByteBuf; + +public class FlatNestedMultiDecoder extends NestedMultiDecoder { + + public FlatNestedMultiDecoder(MultiDecoder firstDecoder, MultiDecoder secondDecoder) { + super(firstDecoder, secondDecoder); + } + + @Override + public Object decode(ByteBuf buf, State state) throws IOException { + return firstDecoder.decode(buf, state); + } + + @Override + public boolean isApplicable(int paramNum, State state) { + DecoderState ds = getDecoder(state); + if (paramNum == 0) { + ds.resetDecoderIndex(); + } + return firstDecoder.isApplicable(paramNum, state); + } + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/GeoDistanceDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/GeoDistanceDecoder.java new file mode 100644 index 000000000..4464f27b5 --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/GeoDistanceDecoder.java @@ -0,0 +1,58 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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 java.io.IOException; +import java.util.List; + +import org.redisson.client.codec.Codec; +import org.redisson.client.codec.DoubleCodec; +import org.redisson.client.handler.State; + +import io.netty.buffer.ByteBuf; +import io.netty.util.CharsetUtil; + +public class GeoDistanceDecoder implements MultiDecoder> { + + private final ThreadLocal pos = new ThreadLocal(); + + private final Codec codec; + + public GeoDistanceDecoder(Codec codec) { + super(); + this.codec = codec; + } + + @Override + public Object decode(ByteBuf buf, State state) throws IOException { + if (pos.get() % 2 == 0) { + return codec.getValueDecoder().decode(buf, state); + } + return DoubleCodec.INSTANCE.getValueDecoder().decode(buf, state); + } + + @Override + public boolean isApplicable(int paramNum, State state) { + pos.set(paramNum); + return true; + } + + @Override + public List decode(List parts, State state) { + return parts; + } + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/GeoDistanceMapDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/GeoDistanceMapDecoder.java new file mode 100644 index 000000000..ac4f98393 --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/GeoDistanceMapDecoder.java @@ -0,0 +1,68 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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 java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.redisson.client.codec.Codec; +import org.redisson.client.codec.DoubleCodec; +import org.redisson.client.handler.State; + +import io.netty.buffer.ByteBuf; +import io.netty.util.CharsetUtil; + +public class GeoDistanceMapDecoder implements MultiDecoder> { + + private final ThreadLocal pos = new ThreadLocal(); + + private final Codec codec; + + public GeoDistanceMapDecoder(Codec codec) { + super(); + this.codec = codec; + } + + @Override + public Object decode(ByteBuf buf, State state) throws IOException { + System.out.println("1 " + buf.toString(CharsetUtil.UTF_8)); + if (pos.get() % 2 == 0) { + return codec.getValueDecoder().decode(buf, state); + } + return DoubleCodec.INSTANCE.getValueDecoder().decode(buf, state); + } + + @Override + public boolean isApplicable(int paramNum, State state) { + pos.set(paramNum); + return true; + } + + @Override + public Map decode(List parts, State state) { + System.out.println(parts); + Map result = new HashMap(parts.size()/2); + for (int i = 0; i < parts.size(); i++) { + if (i % 2 != 0) { + result.put(parts.get(i-1), parts.get(i)); + } + } + return result; + } + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/GeoMapReplayDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/GeoMapReplayDecoder.java new file mode 100644 index 000000000..94c043a47 --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/GeoMapReplayDecoder.java @@ -0,0 +1,48 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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 java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.redisson.client.handler.State; + +import io.netty.buffer.ByteBuf; + +public class GeoMapReplayDecoder implements MultiDecoder> { + + @Override + public Object decode(ByteBuf buf, State state) { + throw new UnsupportedOperationException(); + } + + @Override + public Map decode(List parts, State state) { + Map result = new HashMap(parts.size()); + for (Object object : parts) { + List vals = ((List) object); + result.put(vals.get(0), vals.get(1)); + } + return result; + } + + @Override + public boolean isApplicable(int paramNum, State state) { + return false; + } + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/GeoPositionDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/GeoPositionDecoder.java new file mode 100644 index 000000000..3b3fea8f0 --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/GeoPositionDecoder.java @@ -0,0 +1,50 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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 java.io.IOException; +import java.util.List; + +import org.redisson.client.codec.DoubleCodec; +import org.redisson.client.handler.State; +import org.redisson.core.GeoPosition; + +import io.netty.buffer.ByteBuf; + +public class GeoPositionDecoder implements MultiDecoder { + + @Override + public Double decode(ByteBuf buf, State state) throws IOException { + return (Double) DoubleCodec.INSTANCE.getValueDecoder().decode(buf, state); + } + + @Override + public boolean isApplicable(int paramNum, State state) { + return true; + } + + @Override + public GeoPosition decode(List parts, State state) { + if (parts.isEmpty()) { + return null; + } + + Double longitude = Double.valueOf(parts.get(0).toString()); + Double latitude = Double.valueOf(parts.get(1).toString()); + return new GeoPosition(longitude, latitude); + } + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/GeoPositionMapDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/GeoPositionMapDecoder.java new file mode 100644 index 000000000..07ac3662f --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/GeoPositionMapDecoder.java @@ -0,0 +1,62 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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 java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.redisson.client.handler.State; + +import io.netty.buffer.ByteBuf; + +public class GeoPositionMapDecoder implements MultiDecoder> { + + private final List args; + + public GeoPositionMapDecoder(List args) { + this.args = args; + } + + @Override + public Double decode(ByteBuf buf, State state) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isApplicable(int paramNum, State state) { + return false; + } + + @Override + public Map decode(List parts, State state) { + if (parts.isEmpty()) { + return Collections.emptyMap(); + } + Map result = new HashMap(parts.size()); + for (int index = 0; index < args.size()-1; index++) { + Object value = parts.get(index); + if (value == null || value == Collections.emptyMap()) { + continue; + } + result.put(args.get(index+1), value); + } + return result; + } + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder.java index f8ac24ff1..c2a5f5fdd 100644 --- a/src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder.java +++ b/src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder.java @@ -16,9 +16,6 @@ package org.redisson.client.protocol.decoder; import java.io.IOException; -import java.util.ArrayDeque; -import java.util.Arrays; -import java.util.Deque; import java.util.List; import org.redisson.client.handler.State; @@ -29,53 +26,94 @@ public class NestedMultiDecoder implements MultiDecoder { public static class DecoderState { - Deque> decoders; + int decoderIndex; + + int flipDecoderIndex; - Deque> flipDecoders; - - public DecoderState(MultiDecoder firstDecoder, MultiDecoder secondDecoder) { - super(); - this.decoders = new ArrayDeque>(Arrays.asList(firstDecoder, secondDecoder)); - this.flipDecoders = new ArrayDeque>(Arrays.asList(firstDecoder, secondDecoder, firstDecoder)); + public DecoderState() { } - public Deque> getDecoders() { - return decoders; + public int getDecoderIndex() { + return decoderIndex; } - - public Deque> getFlipDecoders() { - return flipDecoders; + public void resetDecoderIndex() { + decoderIndex = 0; + } + public void incDecoderIndex() { + decoderIndex++; + } + + public int getFlipDecoderIndex() { + return flipDecoderIndex; + } + public void resetFlipDecoderIndex() { + flipDecoderIndex = 0; + } + public void incFlipDecoderIndex() { + flipDecoderIndex++; } } - private final MultiDecoder firstDecoder; - private final MultiDecoder secondDecoder; + protected final MultiDecoder firstDecoder; + protected final MultiDecoder secondDecoder; + private MultiDecoder thirdDecoder; public NestedMultiDecoder(MultiDecoder firstDecoder, MultiDecoder secondDecoder) { this.firstDecoder = firstDecoder; this.secondDecoder = secondDecoder; } + + public NestedMultiDecoder(MultiDecoder firstDecoder, MultiDecoder secondDecoder, MultiDecoder thirdDecoder) { + this.firstDecoder = firstDecoder; + this.secondDecoder = secondDecoder; + this.thirdDecoder = thirdDecoder; + } + @Override public Object decode(ByteBuf buf, State state) throws IOException { DecoderState ds = getDecoder(state); - return ds.getFlipDecoders().peek().decode(buf, state); + + MultiDecoder decoder = null; + if (ds.getFlipDecoderIndex() == 2) { + decoder = firstDecoder; + } + if (ds.getFlipDecoderIndex() == 1) { + decoder = secondDecoder; + } + + return decoder.decode(buf, state); } @Override public boolean isApplicable(int paramNum, State state) { DecoderState ds = getDecoder(state); if (paramNum == 0) { - ds.getFlipDecoders().poll(); + ds.incFlipDecoderIndex(); + ds.resetDecoderIndex(); + } + // used only with thirdDecoder + if (ds.getFlipDecoderIndex() == 3) { + ds.resetFlipDecoderIndex(); + ds.incFlipDecoderIndex(); + } + + MultiDecoder decoder = null; + if (ds.getFlipDecoderIndex() == 2) { + decoder = firstDecoder; + } + if (ds.getFlipDecoderIndex() == 1) { + decoder = secondDecoder; } - return ds.getFlipDecoders().peek().isApplicable(paramNum, state); + + return decoder.isApplicable(paramNum, state); } - private DecoderState getDecoder(State state) { + protected final DecoderState getDecoder(State state) { DecoderState ds = state.getDecoderState(); if (ds == null) { - ds = new DecoderState(firstDecoder, secondDecoder); + ds = new DecoderState(); state.setDecoderState(ds); } return ds; @@ -83,8 +121,32 @@ public class NestedMultiDecoder implements MultiDecoder { @Override public Object decode(List parts, State state) { + if (parts.isEmpty() && state.getDecoderState() == null) { + MultiDecoder decoder = secondDecoder; + if (thirdDecoder != null) { + decoder = thirdDecoder; + } + return decoder.decode(parts, state); + } + DecoderState ds = getDecoder(state); - return ds.getDecoders().poll().decode(parts, state); + if (parts.isEmpty()) { + ds.resetDecoderIndex(); + } + + ds.incDecoderIndex(); + MultiDecoder decoder = null; + if (ds.getDecoderIndex() == 1) { + decoder = firstDecoder; + } + if (ds.getDecoderIndex() == 2) { + decoder = secondDecoder; + } + if (ds.getDecoderIndex() == 3) { + decoder = thirdDecoder; + } + + return decoder.decode(parts, state); } } diff --git a/src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder2.java b/src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder2.java deleted file mode 100644 index 36ebdac71..000000000 --- a/src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder2.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright 2014 Nikita Koksharov, Nickolay Borbit - * - * 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 java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.redisson.client.handler.State; - -import io.netty.buffer.ByteBuf; - -public class NestedMultiDecoder2 implements MultiDecoder { - - private final MultiDecoder firstDecoder; - private final MultiDecoder secondDecoder; - - public NestedMultiDecoder2(MultiDecoder firstDecoder, MultiDecoder secondDecoder) { - this.firstDecoder = firstDecoder; - this.secondDecoder = secondDecoder; - } - - @Override - public Object decode(ByteBuf buf, State state) throws IOException { - return firstDecoder.decode(buf, state); - } - - @Override - public boolean isApplicable(int paramNum, State state) { - if (paramNum == 0) { - setCounter(state, 0); - } - return firstDecoder.isApplicable(paramNum, state); - } - - private Integer getCounter(State state) { - Integer value = state.getDecoderState(); - if (value == null) { - return 0; - } - return value; - } - - private void setCounter(State state, Integer value) { - state.setDecoderState(value); - } - - - @Override - public Object decode(List parts, State state) { - // handle empty result - if (parts.isEmpty() && state.getDecoderState() == null) { - return secondDecoder.decode(parts, state); - } - - int counter = getCounter(state); - if (counter == 2) { - counter = 0; - } - counter++; - setCounter(state, counter); - MultiDecoder decoder = null; - if (counter == 1) { - decoder = firstDecoder; - } - if (counter == 2) { - decoder = secondDecoder; - } - return decoder.decode(parts, state); - } - -} diff --git a/src/main/java/org/redisson/connection/decoder/MapGetAllDecoder.java b/src/main/java/org/redisson/connection/decoder/MapGetAllDecoder.java index 3cf057aaa..35aa171df 100644 --- a/src/main/java/org/redisson/connection/decoder/MapGetAllDecoder.java +++ b/src/main/java/org/redisson/connection/decoder/MapGetAllDecoder.java @@ -16,6 +16,7 @@ package org.redisson.connection.decoder; import java.io.IOException; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -45,6 +46,9 @@ public class MapGetAllDecoder implements MultiDecoder> { @Override public Map decode(List parts, State state) { + if (parts.isEmpty()) { + return Collections.emptyMap(); + } Map result = new HashMap(parts.size()); for (int index = 0; index < args.size()-1; index++) { Object value = parts.get(index); diff --git a/src/main/java/org/redisson/core/GeoEntry.java b/src/main/java/org/redisson/core/GeoEntry.java new file mode 100644 index 000000000..7476b619f --- /dev/null +++ b/src/main/java/org/redisson/core/GeoEntry.java @@ -0,0 +1,43 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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.core; + +public class GeoEntry { + + private final double longitude; + private final double latitude; + private final Object member; + + public GeoEntry(double longitude, double latitude, Object member) { + super(); + this.longitude = longitude; + this.latitude = latitude; + this.member = member; + } + + public double getLatitude() { + return latitude; + } + + public double getLongitude() { + return longitude; + } + + public Object getMember() { + return member; + } + +} diff --git a/src/main/java/org/redisson/core/GeoPosition.java b/src/main/java/org/redisson/core/GeoPosition.java new file mode 100644 index 000000000..545749273 --- /dev/null +++ b/src/main/java/org/redisson/core/GeoPosition.java @@ -0,0 +1,70 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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.core; + +public class GeoPosition { + + private final double longitude; + private final double latitude; + + public GeoPosition(double longitude, double latitude) { + super(); + this.longitude = longitude; + this.latitude = latitude; + } + + public double getLatitude() { + return latitude; + } + + public double getLongitude() { + return longitude; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(latitude); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(longitude); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + GeoPosition other = (GeoPosition) obj; + if (Double.doubleToLongBits(latitude) != Double.doubleToLongBits(other.latitude)) + return false; + if (Double.doubleToLongBits(longitude) != Double.doubleToLongBits(other.longitude)) + return false; + return true; + } + + @Override + public String toString() { + return "GeoPosition [longitude=" + longitude + ", latitude=" + latitude + "]"; + } + +} diff --git a/src/main/java/org/redisson/core/GeoUnit.java b/src/main/java/org/redisson/core/GeoUnit.java new file mode 100644 index 000000000..c4610ff93 --- /dev/null +++ b/src/main/java/org/redisson/core/GeoUnit.java @@ -0,0 +1,48 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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.core; + +public enum GeoUnit { + + METERS { + @Override + public String toString() { + return "m"; + } + }, + + KILOMETERS { + @Override + public String toString() { + return "km"; + } + }, + + MILES { + @Override + public String toString() { + return "mi"; + } + }, + + FEET { + @Override + public String toString() { + return "ft"; + } + } + +} diff --git a/src/main/java/org/redisson/core/RGeo.java b/src/main/java/org/redisson/core/RGeo.java new file mode 100644 index 000000000..c6c32dafc --- /dev/null +++ b/src/main/java/org/redisson/core/RGeo.java @@ -0,0 +1,51 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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.core; + +import java.util.List; +import java.util.Map; + +/** + * + * @author Nikita Koksharov + * + * @param + */ +public interface RGeo extends RExpirable, RGeoAsync { + + long add(double longitude, double latitude, V member); + + long add(GeoEntry... entries); + + Double dist(V firstMember, V secondMember, GeoUnit geoUnit); + + Map hash(V... members); + + Map pos(V... members); + + List radius(double longitude, double latitude, double radius, GeoUnit geoUnit); + + Map radiusWithDistance(double longitude, double latitude, double radius, GeoUnit geoUnit); + + Map radiusWithPosition(double longitude, double latitude, double radius, GeoUnit geoUnit); + + List radius(V member, double radius, GeoUnit geoUnit); + + Map radiusWithDistance(V member, double radius, GeoUnit geoUnit); + + Map radiusWithPosition(V member, double radius, GeoUnit geoUnit); + +} diff --git a/src/main/java/org/redisson/core/RGeoAsync.java b/src/main/java/org/redisson/core/RGeoAsync.java new file mode 100644 index 000000000..e2917d432 --- /dev/null +++ b/src/main/java/org/redisson/core/RGeoAsync.java @@ -0,0 +1,53 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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.core; + +import java.util.List; +import java.util.Map; + +import io.netty.util.concurrent.Future; + +/** + * + * @author Nikita Koksharov + * + * @param + */ +public interface RGeoAsync extends RExpirableAsync { + + Future addAsync(double longitude, double latitude, V member); + + Future addAsync(GeoEntry... entries); + + Future distAsync(V firstMember, V secondMember, GeoUnit geoUnit); + + Future> hashAsync(V... members); + + Future> posAsync(V... members); + + Future> radiusAsync(double longitude, double latitude, double radius, GeoUnit geoUnit); + + Future> radiusWithDistanceAsync(double longitude, double latitude, double radius, GeoUnit geoUnit); + + Future> radiusWithPositionAsync(double longitude, double latitude, double radius, GeoUnit geoUnit); + + Future> radiusAsync(V member, double radius, GeoUnit geoUnit); + + Future> radiusWithDistanceAsync(V member, double radius, GeoUnit geoUnit); + + Future> radiusWithPositionAsync(V member, double radius, GeoUnit geoUnit); + +} From 818a3055674655083b3d965eecf1fba7cc7efcd5 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 1 Apr 2016 12:56:10 +0300 Subject: [PATCH 040/120] RLexSortedSet methods renamed. #459 --- .../org/redisson/RedissonLexSortedSet.java | 161 ++++++++++++++++-- .../org/redisson/RedissonScoredSortedSet.java | 3 + .../java/org/redisson/core/RLexSortedSet.java | 78 +++++++++ .../org/redisson/core/RLexSortedSetAsync.java | 78 +++++++++ .../redisson/RedissonLexSortedSetTest.java | 39 +++-- 5 files changed, 327 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/redisson/RedissonLexSortedSet.java b/src/main/java/org/redisson/RedissonLexSortedSet.java index 2616d064e..35eb8c17a 100644 --- a/src/main/java/org/redisson/RedissonLexSortedSet.java +++ b/src/main/java/org/redisson/RedissonLexSortedSet.java @@ -32,33 +32,64 @@ public class RedissonLexSortedSet extends RedissonScoredSortedSet implem super(StringCodec.INSTANCE, commandExecutor, name); } + @Override + public int removeRange(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { + return removeRangeByLex(fromElement, fromInclusive, toElement, toInclusive); + } + @Override public int removeRangeByLex(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { - return get(removeRangeByLexAsync(fromElement, fromInclusive, toElement, toInclusive)); + return get(removeRangeAsync(fromElement, fromInclusive, toElement, toInclusive)); } + @Override + public int removeRangeHead(String toElement, boolean toInclusive) { + return removeRangeHeadByLex(toElement, toInclusive); + } + @Override public int removeRangeHeadByLex(String toElement, boolean toInclusive) { - return get(removeRangeHeadByLexAsync(toElement, toInclusive)); + return get(removeRangeHeadAsync(toElement, toInclusive)); } + @Override + public Future removeRangeHeadAsync(String toElement, boolean toInclusive) { + return removeRangeHeadByLexAsync(toElement, toInclusive); + } + @Override public Future removeRangeHeadByLexAsync(String toElement, boolean toInclusive) { String toValue = value(toElement, toInclusive); return commandExecutor.writeAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZREMRANGEBYLEX, getName(), "-", toValue); } + + @Override + public int removeRangeTail(String fromElement, boolean fromInclusive) { + return removeRangeTailByLex(fromElement, fromInclusive); + } @Override public int removeRangeTailByLex(String fromElement, boolean fromInclusive) { - return get(removeRangeTailByLexAsync(fromElement, fromInclusive)); + return get(removeRangeTailAsync(fromElement, fromInclusive)); } + @Override + public Future removeRangeTailAsync(String fromElement, boolean fromInclusive) { + return removeRangeTailByLexAsync(fromElement, fromInclusive); + } + @Override public Future removeRangeTailByLexAsync(String fromElement, boolean fromInclusive) { String fromValue = value(fromElement, fromInclusive); return commandExecutor.writeAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZREMRANGEBYLEX, getName(), fromValue, "+"); } + @Override + public Future removeRangeAsync(String fromElement, boolean fromInclusive, String toElement, + boolean toInclusive) { + return removeRangeByLexAsync(fromElement, fromInclusive, toElement, toInclusive); + } + @Override public Future removeRangeByLexAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { String fromValue = value(fromElement, fromInclusive); @@ -67,33 +98,63 @@ public class RedissonLexSortedSet extends RedissonScoredSortedSet implem return commandExecutor.writeAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZREMRANGEBYLEX, getName(), fromValue, toValue); } + @Override + public Collection range(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { + return lexRange(fromElement, fromInclusive, toElement, toInclusive); + } + @Override public Collection lexRange(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { - return get(lexRangeAsync(fromElement, fromInclusive, toElement, toInclusive)); + return get(rangeAsync(fromElement, fromInclusive, toElement, toInclusive)); } + @Override + public Collection rangeHead(String toElement, boolean toInclusive) { + return lexRangeHead(toElement, toInclusive); + } + @Override public Collection lexRangeHead(String toElement, boolean toInclusive) { - return get(lexRangeHeadAsync(toElement, toInclusive)); + return get(rangeHeadAsync(toElement, toInclusive)); } + @Override + public Future> rangeHeadAsync(String toElement, boolean toInclusive) { + return lexRangeHeadAsync(toElement, toInclusive); + } + @Override public Future> lexRangeHeadAsync(String toElement, boolean toInclusive) { String toValue = value(toElement, toInclusive); return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZRANGEBYLEX, getName(), "-", toValue); } + + @Override + public Collection rangeTail(String fromElement, boolean fromInclusive) { + return lexRangeTail(fromElement, fromInclusive); + } @Override public Collection lexRangeTail(String fromElement, boolean fromInclusive) { - return get(lexRangeTailAsync(fromElement, fromInclusive)); + return get(rangeTailAsync(fromElement, fromInclusive)); } + @Override + public Future> rangeTailAsync(String fromElement, boolean fromInclusive) { + return lexRangeTailAsync(fromElement, fromInclusive); + } + @Override public Future> lexRangeTailAsync(String fromElement, boolean fromInclusive) { String fromValue = value(fromElement, fromInclusive); return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZRANGEBYLEX, getName(), fromValue, "+"); } + @Override + public Future> rangeAsync(String fromElement, boolean fromInclusive, String toElement, + boolean toInclusive) { + return lexRangeAsync(fromElement, fromInclusive, toElement, toInclusive); + } @Override public Future> lexRangeAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { @@ -103,33 +164,65 @@ public class RedissonLexSortedSet extends RedissonScoredSortedSet implem return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZRANGEBYLEX, getName(), fromValue, toValue); } + @Override + public Collection range(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive, + int offset, int count) { + return lexRange(fromElement, fromInclusive, toElement, toInclusive, offset, count); + } + @Override public Collection lexRange(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive, int offset, int count) { - return get(lexRangeAsync(fromElement, fromInclusive, toElement, toInclusive, offset, count)); + return get(rangeAsync(fromElement, fromInclusive, toElement, toInclusive, offset, count)); } + @Override + public Collection rangeHead(String toElement, boolean toInclusive, int offset, int count) { + return lexRangeHead(toElement, toInclusive, offset, count); + } + @Override public Collection lexRangeHead(String toElement, boolean toInclusive, int offset, int count) { - return get(lexRangeHeadAsync(toElement, toInclusive, offset, count)); + return get(rangeHeadAsync(toElement, toInclusive, offset, count)); } + @Override + public Future> rangeHeadAsync(String toElement, boolean toInclusive, int offset, int count) { + return lexRangeHeadAsync(toElement, toInclusive, offset, count); + } + @Override public Future> lexRangeHeadAsync(String toElement, boolean toInclusive, int offset, int count) { String toValue = value(toElement, toInclusive); return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZRANGEBYLEX, getName(), "-", toValue, "LIMIT", offset, count); } + @Override + public Collection rangeTail(String fromElement, boolean fromInclusive, int offset, int count) { + return lexRangeTail(fromElement, fromInclusive, offset, count); + } + @Override public Collection lexRangeTail(String fromElement, boolean fromInclusive, int offset, int count) { - return get(lexRangeTailAsync(fromElement, fromInclusive, offset, count)); + return get(rangeTailAsync(fromElement, fromInclusive, offset, count)); } + @Override + public Future> rangeTailAsync(String fromElement, boolean fromInclusive, int offset, int count) { + return lexRangeTailAsync(fromElement, fromInclusive, offset, count); + } + @Override public Future> lexRangeTailAsync(String fromElement, boolean fromInclusive, int offset, int count) { String fromValue = value(fromElement, fromInclusive); return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZRANGEBYLEX, getName(), fromValue, "+", "LIMIT", offset, count); } + @Override + public Future> rangeAsync(String fromElement, boolean fromInclusive, String toElement, + boolean toInclusive, int offset, int count) { + return lexRangeAsync(fromElement, fromInclusive, toElement, toInclusive, offset, count); + } + @Override public Future> lexRangeAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive, int offset, int count) { String fromValue = value(fromElement, fromInclusive); @@ -137,10 +230,20 @@ public class RedissonLexSortedSet extends RedissonScoredSortedSet implem return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZRANGEBYLEX, getName(), fromValue, toValue, "LIMIT", offset, count); } + + @Override + public int countTail(String fromElement, boolean fromInclusive) { + return lexCountTail(fromElement, fromInclusive); + } @Override public int lexCountTail(String fromElement, boolean fromInclusive) { - return get(lexCountTailAsync(fromElement, fromInclusive)); + return get(countTailAsync(fromElement, fromInclusive)); + } + + @Override + public Future countTailAsync(String fromElement, boolean fromInclusive) { + return lexCountTailAsync(fromElement, fromInclusive); } @Override @@ -149,10 +252,20 @@ public class RedissonLexSortedSet extends RedissonScoredSortedSet implem return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZLEXCOUNT, getName(), fromValue, "+"); } + + @Override + public int countHead(String toElement, boolean toInclusive) { + return lexCountHead(toElement, toInclusive); + } @Override public int lexCountHead(String toElement, boolean toInclusive) { - return get(lexCountHeadAsync(toElement, toInclusive)); + return get(countHeadAsync(toElement, toInclusive)); + } + + @Override + public Future countHeadAsync(String toElement, boolean toInclusive) { + return lexCountHeadAsync(toElement, toInclusive); } @Override @@ -162,11 +275,22 @@ public class RedissonLexSortedSet extends RedissonScoredSortedSet implem return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZLEXCOUNT, getName(), "-", toValue); } + @Override + public int count(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { + return lexCount(fromElement, fromInclusive, toElement, toInclusive); + } + @Override public int lexCount(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { - return get(lexCountAsync(fromElement, fromInclusive, toElement, toInclusive)); + return get(countAsync(fromElement, fromInclusive, toElement, toInclusive)); } + @Override + public Future countAsync(String fromElement, boolean fromInclusive, String toElement, + boolean toInclusive) { + return lexCountAsync(fromElement, fromInclusive, toElement, toInclusive); + } + @Override public Future lexCountAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { String fromValue = value(fromElement, fromInclusive); @@ -192,6 +316,9 @@ public class RedissonLexSortedSet extends RedissonScoredSortedSet implem @Override public Future addAllAsync(Collection c) { + if (c.isEmpty()) { + return newSucceededFuture(false); + } List params = new ArrayList(2*c.size()); for (Object param : c) { params.add(0); @@ -210,4 +337,14 @@ public class RedissonLexSortedSet extends RedissonScoredSortedSet implem return get(addAllAsync(c)); } + @Override + public Collection range(int startIndex, int endIndex) { + return valueRange(startIndex, endIndex); + } + + @Override + public Future> rangeAsync(int startIndex, int endIndex) { + return valueRangeAsync(startIndex, endIndex); + } + } diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index 62c493436..0ffb1122d 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -121,6 +121,9 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future addAllAsync(Map objects) { + if (objects.isEmpty()) { + return newSucceededFuture(0L); + } List params = new ArrayList(objects.size()*2+1); params.add(getName()); try { diff --git a/src/main/java/org/redisson/core/RLexSortedSet.java b/src/main/java/org/redisson/core/RLexSortedSet.java index 1f68f6180..598df157f 100644 --- a/src/main/java/org/redisson/core/RLexSortedSet.java +++ b/src/main/java/org/redisson/core/RLexSortedSet.java @@ -20,32 +20,110 @@ import java.util.Set; public interface RLexSortedSet extends RLexSortedSetAsync, Set, RExpirable { + int removeRangeTail(String fromElement, boolean fromInclusive); + + /** + * Use {@link RLexSortedSet#removeRangeTail(String, boolean)} + */ + @Deprecated int removeRangeTailByLex(String fromElement, boolean fromInclusive); + int removeRangeHead(String toElement, boolean toInclusive); + + /** + * Use {@link RLexSortedSet#removeRangeHead(String, boolean)} + */ + @Deprecated int removeRangeHeadByLex(String toElement, boolean toInclusive); + int removeRange(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + + /** + * Use {@link RLexSortedSet#removeRange(String, boolean)} + */ + @Deprecated int removeRangeByLex(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + int countTail(String fromElement, boolean fromInclusive); + + /** + * Use {@link RLexSortedSet#countTail(String, boolean)} + */ + @Deprecated int lexCountTail(String fromElement, boolean fromInclusive); + int countHead(String toElement, boolean toInclusive); + + /** + * Use {@link RLexSortedSet#countHead(String, boolean)} + */ + @Deprecated int lexCountHead(String toElement, boolean toInclusive); + Collection rangeTail(String fromElement, boolean fromInclusive); + + /** + * Use {@link RLexSortedSet#rangeTail(String, boolean)} + */ + @Deprecated Collection lexRangeTail(String fromElement, boolean fromInclusive); + Collection rangeHead(String toElement, boolean toInclusive); + + /** + * Use {@link RLexSortedSet#rangeHead(String, boolean)} + */ + @Deprecated Collection lexRangeHead(String toElement, boolean toInclusive); + Collection range(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + + /** + * Use {@link RLexSortedSet#range(String, boolean, String, boolean)} + */ + @Deprecated Collection lexRange(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + Collection rangeTail(String fromElement, boolean fromInclusive, int offset, int count); + + /** + * Use {@link RLexSortedSet#rangeTail(String, boolean, int, int)} + */ + @Deprecated Collection lexRangeTail(String fromElement, boolean fromInclusive, int offset, int count); + Collection rangeHead(String toElement, boolean toInclusive, int offset, int count); + + /** + * Use {@link RLexSortedSet#rangeHead(String, boolean, int, int)} + */ + @Deprecated Collection lexRangeHead(String toElement, boolean toInclusive, int offset, int count); + Collection range(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive, int offset, int count); + + /** + * Use {@link RLexSortedSet#range(String, boolean, String, boolean, int, int)} + */ + @Deprecated Collection lexRange(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive, int offset, int count); + int count(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + + /** + * Use {@link RLexSortedSet#count(String, boolean, String, boolean)} + */ + @Deprecated int lexCount(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); int rank(String o); + Collection range(int startIndex, int endIndex); + + /** + * Use {@link RLexSortedSet#range(int, int)} + */ + @Deprecated Collection valueRange(int startIndex, int endIndex); } diff --git a/src/main/java/org/redisson/core/RLexSortedSetAsync.java b/src/main/java/org/redisson/core/RLexSortedSetAsync.java index abdb18cb4..6616e25e5 100644 --- a/src/main/java/org/redisson/core/RLexSortedSetAsync.java +++ b/src/main/java/org/redisson/core/RLexSortedSetAsync.java @@ -21,32 +21,110 @@ import io.netty.util.concurrent.Future; public interface RLexSortedSetAsync extends RCollectionAsync { + Future removeRangeAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + + /** + * Use {@link RLexSortedSetAsync#removeRangeAsync(String, boolean, String, boolean)} + */ + @Deprecated Future removeRangeByLexAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + Future removeRangeTailAsync(String fromElement, boolean fromInclusive); + + /** + * Use {@link RLexSortedSetAsync#removeRangeTailAsync(String, boolean, String, boolean)} + */ + @Deprecated Future removeRangeTailByLexAsync(String fromElement, boolean fromInclusive); + Future removeRangeHeadAsync(String toElement, boolean toInclusive); + + /** + * Use {@link RLexSortedSetAsync#removeRangeHeadAsync(String, boolean)} + */ + @Deprecated Future removeRangeHeadByLexAsync(String toElement, boolean toInclusive); + Future countTailAsync(String fromElement, boolean fromInclusive); + + /** + * Use {@link RLexSortedSetAsync#countTailAsync(String, boolean)} + */ + @Deprecated Future lexCountTailAsync(String fromElement, boolean fromInclusive); + Future countHeadAsync(String toElement, boolean toInclusive); + + /** + * Use {@link RLexSortedSetAsync#countHeadAsync(String, boolean)} + */ + @Deprecated Future lexCountHeadAsync(String toElement, boolean toInclusive); + Future> rangeTailAsync(String fromElement, boolean fromInclusive); + + /** + * Use {@link RLexSortedSetAsync#rangeTailAsync(String, boolean)} + */ + @Deprecated Future> lexRangeTailAsync(String fromElement, boolean fromInclusive); + Future> rangeHeadAsync(String toElement, boolean toInclusive); + + /** + * Use {@link RLexSortedSetAsync#rangeHeadAsync(String, boolean)} + */ + @Deprecated Future> lexRangeHeadAsync(String toElement, boolean toInclusive); + Future> rangeAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + + /** + * Use {@link RLexSortedSetAsync#rangeAsync(String, boolean, String, boolean)} + */ + @Deprecated Future> lexRangeAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + Future> rangeTailAsync(String fromElement, boolean fromInclusive, int offset, int count); + + /** + * Use {@link RLexSortedSetAsync#rangeTailAsync(String, boolean, int, int)} + */ + @Deprecated Future> lexRangeTailAsync(String fromElement, boolean fromInclusive, int offset, int count); + Future> rangeHeadAsync(String toElement, boolean toInclusive, int offset, int count); + + /** + * Use {@link RLexSortedSetAsync#rangeHeadAsync(String, boolean, int, int)} + */ + @Deprecated Future> lexRangeHeadAsync(String toElement, boolean toInclusive, int offset, int count); + Future> rangeAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive, int offset, int count); + + /** + * Use {@link RLexSortedSetAsync#rangeAsync(String, boolean, String, boolean, int, int)} + */ + @Deprecated Future> lexRangeAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive, int offset, int count); + Future countAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + + /** + * Use {@link RLexSortedSetAsync#countAsync(String, boolean, String, boolean)} + */ + @Deprecated Future lexCountAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); Future rankAsync(String o); + Future> rangeAsync(int startIndex, int endIndex); + + /** + * Use {@link RLexSortedSetAsync#rangeAsync(int, int)} + */ + @Deprecated Future> valueRangeAsync(int startIndex, int endIndex); } diff --git a/src/test/java/org/redisson/RedissonLexSortedSetTest.java b/src/test/java/org/redisson/RedissonLexSortedSetTest.java index b048b0176..e6ce62d4c 100644 --- a/src/test/java/org/redisson/RedissonLexSortedSetTest.java +++ b/src/test/java/org/redisson/RedissonLexSortedSetTest.java @@ -1,7 +1,6 @@ package org.redisson; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; +import static org.assertj.core.api.Assertions.*; import org.junit.Assert; import org.junit.Test; import org.redisson.core.RLexSortedSet; @@ -20,12 +19,12 @@ public class RedissonLexSortedSetTest extends BaseTest { Assert.assertTrue(set.add("f")); Assert.assertTrue(set.add("g")); - Assert.assertEquals(0, (int)set.removeRangeTailByLex("z", false)); + Assert.assertEquals(0, (int)set.removeRangeTail("z", false)); - Assert.assertEquals(4, (int)set.removeRangeTailByLex("c", false)); - MatcherAssert.assertThat(set, Matchers.contains("a", "b", "c")); - Assert.assertEquals(1, (int)set.removeRangeTailByLex("c", true)); - MatcherAssert.assertThat(set, Matchers.contains("a", "b")); + Assert.assertEquals(4, (int)set.removeRangeTail("c", false)); + assertThat(set).containsExactly("a", "b", "c"); + Assert.assertEquals(1, (int)set.removeRangeTail("c", true)); + assertThat(set).containsExactly("a", "b"); } @@ -40,10 +39,10 @@ public class RedissonLexSortedSetTest extends BaseTest { set.add("f"); set.add("g"); - Assert.assertEquals(2, (int)set.removeRangeHeadByLex("c", false)); - MatcherAssert.assertThat(set, Matchers.contains("c", "d", "e", "f", "g")); - Assert.assertEquals(1, (int)set.removeRangeHeadByLex("c", true)); - MatcherAssert.assertThat(set, Matchers.contains("d", "e", "f", "g")); + Assert.assertEquals(2, (int)set.removeRangeHead("c", false)); + assertThat(set).containsExactly("c", "d", "e", "f", "g"); + Assert.assertEquals(1, (int)set.removeRangeHead("c", true)); + assertThat(set).containsExactly("d", "e", "f", "g"); } @Test @@ -57,8 +56,8 @@ public class RedissonLexSortedSetTest extends BaseTest { set.add("f"); set.add("g"); - Assert.assertEquals(5, set.removeRangeByLex("aaa", true, "g", false)); - MatcherAssert.assertThat(set, Matchers.contains("a", "g")); + Assert.assertEquals(5, set.removeRange("aaa", true, "g", false)); + assertThat(set).containsExactly("a", "g"); } @@ -74,8 +73,8 @@ public class RedissonLexSortedSetTest extends BaseTest { Assert.assertTrue(set.add("f")); Assert.assertTrue(set.add("g")); - MatcherAssert.assertThat(set.lexRangeTail("c", false), Matchers.contains("d", "e", "f", "g")); - MatcherAssert.assertThat(set.lexRangeTail("c", true), Matchers.contains("c", "d", "e", "f", "g")); + assertThat(set.rangeTail("c", false)).containsExactly("d", "e", "f", "g"); + assertThat(set.rangeTail("c", true)).containsExactly("c", "d", "e", "f", "g"); } @@ -90,8 +89,8 @@ public class RedissonLexSortedSetTest extends BaseTest { set.add("f"); set.add("g"); - MatcherAssert.assertThat(set.lexRangeHead("c", false), Matchers.contains("a", "b")); - MatcherAssert.assertThat(set.lexRangeHead("c", true), Matchers.contains("a", "b", "c")); + assertThat(set.rangeHead("c", false)).containsExactly("a", "b"); + assertThat(set.rangeHead("c", true)).containsExactly("a", "b", "c"); } @@ -106,7 +105,7 @@ public class RedissonLexSortedSetTest extends BaseTest { set.add("f"); set.add("g"); - MatcherAssert.assertThat(set.lexRange("aaa", true, "g", false), Matchers.contains("b", "c", "d", "e", "f")); + assertThat(set.range("aaa", true, "g", false)).containsExactly("b", "c", "d", "e", "f"); } @Test @@ -120,8 +119,8 @@ public class RedissonLexSortedSetTest extends BaseTest { set.add("f"); set.add("g"); - Assert.assertEquals(5, (int)set.lexCount("b", true, "f", true)); - Assert.assertEquals(3, (int)set.lexCount("b", false, "f", false)); + assertThat(set.count("b", true, "f", true)).isEqualTo(5); + assertThat(set.count("b", false, "f", false)).isEqualTo(3); } } From a95a08330a3f7a91785114a5079d2f7fdaf52504 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 1 Apr 2016 13:05:49 +0300 Subject: [PATCH 041/120] some deprecated methods removed --- src/main/java/org/redisson/Redisson.java | 10 ---------- src/main/java/org/redisson/RedissonClient.java | 12 ------------ src/test/java/org/redisson/RedissonBucketTest.java | 2 +- .../java/org/redisson/RedissonConcurrentMapTest.java | 2 +- src/test/java/org/redisson/RedissonKeysTest.java | 2 +- .../org/redisson/RedissonSetMultimapCacheTest.java | 2 +- 6 files changed, 4 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/redisson/Redisson.java b/src/main/java/org/redisson/Redisson.java index a6c88ea08..cfd3de6d5 100755 --- a/src/main/java/org/redisson/Redisson.java +++ b/src/main/java/org/redisson/Redisson.java @@ -537,16 +537,6 @@ public class Redisson implements RedissonClient { return new RedisNodes(connectionManager); } - @Override - public void flushdb() { - commandExecutor.get(commandExecutor.writeAllAsync(RedisCommands.FLUSHDB)); - } - - @Override - public void flushall() { - commandExecutor.get(commandExecutor.writeAllAsync(RedisCommands.FLUSHALL)); - } - @Override public boolean isShutdown() { return connectionManager.isShutdown(); diff --git a/src/main/java/org/redisson/RedissonClient.java b/src/main/java/org/redisson/RedissonClient.java index a83750c7d..72c2c31bb 100755 --- a/src/main/java/org/redisson/RedissonClient.java +++ b/src/main/java/org/redisson/RedissonClient.java @@ -658,18 +658,6 @@ public interface RedissonClient { */ NodesGroup getClusterNodesGroup(); - /** - * Use {@link RKeys#flushdb()} - */ - @Deprecated - void flushdb(); - - /** - * Use {@link RKeys#flushall()} - */ - @Deprecated - void flushall(); - /** * Returns {@code true} if this Redisson instance has been shut down. * diff --git a/src/test/java/org/redisson/RedissonBucketTest.java b/src/test/java/org/redisson/RedissonBucketTest.java index 7ed1bba45..ff88c0173 100755 --- a/src/test/java/org/redisson/RedissonBucketTest.java +++ b/src/test/java/org/redisson/RedissonBucketTest.java @@ -59,7 +59,7 @@ public class RedissonBucketTest extends BaseTest { assertThat(r1.trySet("4", 500, TimeUnit.MILLISECONDS)).isFalse(); assertThat(r1.get()).isEqualTo("3"); - Thread.sleep(500); + Thread.sleep(1000); assertThat(r1.get()).isNull(); } diff --git a/src/test/java/org/redisson/RedissonConcurrentMapTest.java b/src/test/java/org/redisson/RedissonConcurrentMapTest.java index ed3903744..adaa1f142 100644 --- a/src/test/java/org/redisson/RedissonConcurrentMapTest.java +++ b/src/test/java/org/redisson/RedissonConcurrentMapTest.java @@ -94,7 +94,7 @@ public class RedissonConcurrentMapTest extends BaseConcurrentTest { } assertMapSize(5, name); - redisson.flushdb(); + redisson.getKeys().flushdb(); redisson.shutdown(); } diff --git a/src/test/java/org/redisson/RedissonKeysTest.java b/src/test/java/org/redisson/RedissonKeysTest.java index 4380d26df..ef6a4d4b9 100644 --- a/src/test/java/org/redisson/RedissonKeysTest.java +++ b/src/test/java/org/redisson/RedissonKeysTest.java @@ -58,7 +58,7 @@ public class RedissonKeysTest extends BaseTest { assertThat(redisson.getKeys().randomKey()).isIn("test1", "test2"); redisson.getKeys().delete("test1"); Assert.assertEquals(redisson.getKeys().randomKey(), "test2"); - redisson.flushdb(); + redisson.getKeys().flushdb(); Assert.assertNull(redisson.getKeys().randomKey()); } diff --git a/src/test/java/org/redisson/RedissonSetMultimapCacheTest.java b/src/test/java/org/redisson/RedissonSetMultimapCacheTest.java index 442855b8c..a4da5c6dd 100644 --- a/src/test/java/org/redisson/RedissonSetMultimapCacheTest.java +++ b/src/test/java/org/redisson/RedissonSetMultimapCacheTest.java @@ -102,7 +102,7 @@ public class RedissonSetMultimapCacheTest extends BaseTest { multimap.put("1", "3"); multimap.expireKey("1", 1, TimeUnit.SECONDS); - Thread.sleep(1000); + Thread.sleep(1500); assertThat(multimap.get("1").size()).isZero(); assertThat(multimap.get("1")).contains(); From 22a3dd2a09c731f54484ad47da2292aa3cc4d6d3 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 1 Apr 2016 14:11:41 +0300 Subject: [PATCH 042/120] RBuckets object added. #460, #430 --- src/main/java/org/redisson/Redisson.java | 16 +- .../java/org/redisson/RedissonBuckets.java | 118 +++++++++++ .../java/org/redisson/RedissonClient.java | 53 ++--- src/main/java/org/redisson/RedissonGeo.java | 2 +- src/main/java/org/redisson/RedissonMap.java | 2 +- .../java/org/redisson/RedissonMapCache.java | 4 +- .../client/codec/DelegateDecoderCodec.java | 34 ++++ .../client/protocol/RedisCommands.java | 1 + .../decoder/GeoDistanceMapDecoder.java | 3 - .../connection/decoder/MapGetAllDecoder.java | 8 +- src/main/java/org/redisson/core/RBuckets.java | 64 ++++++ .../java/org/redisson/RedissonBucketTest.java | 45 ----- .../org/redisson/RedissonBucketsTest.java | 91 +++++++++ .../java/org/redisson/RedissonGeoTest.java | 183 ++++++++++++++++++ 14 files changed, 531 insertions(+), 93 deletions(-) create mode 100644 src/main/java/org/redisson/RedissonBuckets.java create mode 100644 src/main/java/org/redisson/client/codec/DelegateDecoderCodec.java create mode 100644 src/main/java/org/redisson/core/RBuckets.java create mode 100644 src/test/java/org/redisson/RedissonBucketsTest.java create mode 100644 src/test/java/org/redisson/RedissonGeoTest.java diff --git a/src/main/java/org/redisson/Redisson.java b/src/main/java/org/redisson/Redisson.java index cfd3de6d5..fd9e63201 100755 --- a/src/main/java/org/redisson/Redisson.java +++ b/src/main/java/org/redisson/Redisson.java @@ -47,6 +47,7 @@ import org.redisson.core.RBlockingDeque; import org.redisson.core.RBlockingQueue; import org.redisson.core.RBloomFilter; import org.redisson.core.RBucket; +import org.redisson.core.RBuckets; import org.redisson.core.RCountDownLatch; import org.redisson.core.RDeque; import org.redisson.core.RGeo; @@ -202,6 +203,16 @@ public class Redisson implements RedissonClient { return new RedissonBucket(codec, commandExecutor, name); } + @Override + public RBuckets getBuckets() { + return new RedissonBuckets(this, commandExecutor); + } + + @Override + public RBuckets getBuckets(Codec codec) { + return new RedissonBuckets(this, codec, commandExecutor); + } + @Override public List> findBuckets(String pattern) { Collection keys = commandExecutor.get(commandExecutor., String>readAllAsync(RedisCommands.KEYS, pattern)); @@ -260,11 +271,6 @@ public class Redisson implements RedissonClient { commandExecutor.write(params.get(0).toString(), RedisCommands.MSET, params.toArray()); } - @Override - public List> getBuckets(String pattern) { - return findBuckets(pattern); - } - @Override public RHyperLogLog getHyperLogLog(String name) { return new RedissonHyperLogLog(commandExecutor, name); diff --git a/src/main/java/org/redisson/RedissonBuckets.java b/src/main/java/org/redisson/RedissonBuckets.java new file mode 100644 index 000000000..43a118771 --- /dev/null +++ b/src/main/java/org/redisson/RedissonBuckets.java @@ -0,0 +1,118 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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 java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.redisson.client.codec.Codec; +import org.redisson.client.codec.DelegateDecoderCodec; +import org.redisson.client.protocol.RedisCommand; +import org.redisson.client.protocol.RedisCommand.ValueType; +import org.redisson.client.protocol.RedisCommands; +import org.redisson.command.CommandExecutor; +import org.redisson.connection.decoder.MapGetAllDecoder; +import org.redisson.core.RBucket; +import org.redisson.core.RBuckets; + +import io.netty.util.concurrent.Future; + +public class RedissonBuckets implements RBuckets { + + private final Codec codec; + private final CommandExecutor commandExecutor; + private final Redisson redisson; + + public RedissonBuckets(Redisson redisson, CommandExecutor commandExecutor) { + this(redisson, commandExecutor.getConnectionManager().getCodec(), commandExecutor); + } + + public RedissonBuckets(Redisson redisson, Codec codec, CommandExecutor commandExecutor) { + super(); + this.codec = codec; + this.commandExecutor = commandExecutor; + this.redisson = redisson; + } + + @Override + public List> find(String pattern) { + Collection keys = commandExecutor.get(commandExecutor., String>readAllAsync(RedisCommands.KEYS, pattern)); + List> buckets = new ArrayList>(keys.size()); + for (String key : keys) { + if(key == null) { + continue; + } + buckets.add(redisson.getBucket(key, codec)); + } + return buckets; + } + + @Override + public Map get(String... keys) { + if (keys.length == 0) { + return Collections.emptyMap(); + } + + RedisCommand> command = new RedisCommand>("MGET", new MapGetAllDecoder(Arrays.asList(keys), 0), ValueType.OBJECTS); + Future> future = commandExecutor.readAsync(keys[0], new DelegateDecoderCodec(codec), command, keys); + return commandExecutor.get(future); + } + + @Override + public boolean trySet(Map buckets) { + if (buckets.isEmpty()) { + return false; + } + + List params = new ArrayList(buckets.size()); + for (Entry entry : buckets.entrySet()) { + params.add(entry.getKey()); + try { + params.add(codec.getValueEncoder().encode(entry.getValue())); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } + + return commandExecutor.write(params.get(0).toString(), RedisCommands.MSETNX, params.toArray()); + } + + @Override + public void set(Map buckets) { + if (buckets.isEmpty()) { + return; + } + + List params = new ArrayList(buckets.size()); + for (Entry entry : buckets.entrySet()) { + params.add(entry.getKey()); + try { + params.add(codec.getValueEncoder().encode(entry.getValue())); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } + + commandExecutor.write(params.get(0).toString(), RedisCommands.MSET, params.toArray()); + } + +} diff --git a/src/main/java/org/redisson/RedissonClient.java b/src/main/java/org/redisson/RedissonClient.java index 72c2c31bb..fffc0e54b 100755 --- a/src/main/java/org/redisson/RedissonClient.java +++ b/src/main/java/org/redisson/RedissonClient.java @@ -31,6 +31,7 @@ import org.redisson.core.RBlockingDeque; import org.redisson.core.RBlockingQueue; import org.redisson.core.RBloomFilter; import org.redisson.core.RBucket; +import org.redisson.core.RBuckets; import org.redisson.core.RCountDownLatch; import org.redisson.core.RDeque; import org.redisson.core.RGeo; @@ -151,57 +152,43 @@ public interface RedissonClient { RBucket getBucket(String name, Codec codec); /** - *

Returns a list of object holder instances by a key pattern. + * Returns interface for mass operations with Bucket objects. * - *

Supported glob-style patterns:
-     *    h?llo subscribes to hello, hallo and hxllo
-     *    h*llo subscribes to hllo and heeeello
-     *    h[ae]llo subscribes to hello and hallo, but not hillo
-     *    h[^e]llo matches hallo, hbllo, ... but not hello
-     *    h[a-b]llo matches hallo and hbllo
- *

Use \ to escape special characters if you want to match them verbatim. - * - *

Uses KEYS Redis command. - * - * @param pattern * @return */ - List> findBuckets(String pattern); + RBuckets getBuckets(); /** - *

Returns Redis object mapped by key. Result Map is not contains - * key-value entry for null values. - * - *

Uses MGET Redis command. + * Returns interface for mass operations with Bucket objects + * using provided codec for object. * - * @param keys * @return */ - Map loadBucketValues(Collection keys); + RBuckets getBuckets(Codec codec); /** - *

Returns Redis object mapped by key. Result Map is not contains - * key-value entry for null values. - * - *

Uses MGET Redis command. - * - * @param keys - * @return + * Use {@link RBuckets#find(String)} */ - Map loadBucketValues(String ... keys); + @Deprecated + List> findBuckets(String pattern); /** - * Saves Redis object mapped by key. - * - * @param buckets + * Use {@link RBuckets#get(String...)} */ - void saveBuckets(Map buckets); + @Deprecated + Map loadBucketValues(Collection keys); /** - * Use {@link #findBuckets(String)} + * Use {@link RBuckets#get(String...)} */ @Deprecated - List> getBuckets(String pattern); + Map loadBucketValues(String ... keys); + + /** + * Use {@link RBuckets#set(Map)} + */ + @Deprecated + void saveBuckets(Map buckets); /** * Returns HyperLogLog instance by name. diff --git a/src/main/java/org/redisson/RedissonGeo.java b/src/main/java/org/redisson/RedissonGeo.java index dd36d3764..7a62e165c 100644 --- a/src/main/java/org/redisson/RedissonGeo.java +++ b/src/main/java/org/redisson/RedissonGeo.java @@ -104,7 +104,7 @@ public class RedissonGeo extends RedissonExpirable implements RGeo { List params = new ArrayList(members.length + 1); params.add(getName()); params.addAll(Arrays.asList(members)); - RedisCommand> command = new RedisCommand>("GEOHASH", new MapGetAllDecoder(params), 2, ValueType.OBJECTS); + RedisCommand> command = new RedisCommand>("GEOHASH", new MapGetAllDecoder(params, 1), 2, ValueType.OBJECTS); return commandExecutor.readAsync(getName(), new ScoredCodec(codec), command, params.toArray()); } diff --git a/src/main/java/org/redisson/RedissonMap.java b/src/main/java/org/redisson/RedissonMap.java index 245e6a7cd..cb8c8b54a 100644 --- a/src/main/java/org/redisson/RedissonMap.java +++ b/src/main/java/org/redisson/RedissonMap.java @@ -130,7 +130,7 @@ public class RedissonMap extends RedissonExpirable implements RMap { List args = new ArrayList(keys.size() + 1); args.add(getName()); args.addAll(keys); - return commandExecutor.readAsync(getName(), codec, new RedisCommand>("HMGET", new MapGetAllDecoder(args), 2, ValueType.MAP_KEY, ValueType.MAP_VALUE), args.toArray()); + return commandExecutor.readAsync(getName(), codec, new RedisCommand>("HMGET", new MapGetAllDecoder(args, 1), 2, ValueType.MAP_KEY, ValueType.MAP_VALUE), args.toArray()); } @Override diff --git a/src/main/java/org/redisson/RedissonMapCache.java b/src/main/java/org/redisson/RedissonMapCache.java index d529c2b03..0167015c8 100644 --- a/src/main/java/org/redisson/RedissonMapCache.java +++ b/src/main/java/org/redisson/RedissonMapCache.java @@ -162,11 +162,11 @@ public class RedissonMapCache extends RedissonMap implements RMapCac return newSucceededFuture(Collections.emptyMap()); } - List args = new ArrayList(keys.size() + 2); + List args = new ArrayList(keys.size() + 1); args.add(System.currentTimeMillis()); args.addAll(keys); - return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand>("EVAL", new MapGetAllDecoder(args), 7, ValueType.MAP_KEY, ValueType.MAP_VALUE), + return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand>("EVAL", new MapGetAllDecoder(args, 1), 7, ValueType.MAP_KEY, ValueType.MAP_VALUE), "local expireHead = redis.call('zrange', KEYS[2], 0, 0, 'withscores');" + "local expireIdleHead = redis.call('zrange', KEYS[3], 0, 0, 'withscores');" + "local maxDate = table.remove(ARGV, 1); " // index is the first parameter diff --git a/src/main/java/org/redisson/client/codec/DelegateDecoderCodec.java b/src/main/java/org/redisson/client/codec/DelegateDecoderCodec.java new file mode 100644 index 000000000..407cc5b93 --- /dev/null +++ b/src/main/java/org/redisson/client/codec/DelegateDecoderCodec.java @@ -0,0 +1,34 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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.codec; + +import org.redisson.client.protocol.Decoder; + +public class DelegateDecoderCodec extends StringCodec { + + private final Codec delegate; + + public DelegateDecoderCodec(Codec delegate) { + super(); + this.delegate = delegate; + } + + @Override + public Decoder getValueDecoder() { + return delegate.getValueDecoder(); + } + +} diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index d56eae403..e3737d259 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -196,6 +196,7 @@ public interface RedisCommands { RedisStrictCommand> KEYS = new RedisStrictCommand>("KEYS", new StringListReplayDecoder()); RedisCommand> MGET = new RedisCommand>("MGET", new ObjectListReplayDecoder()); RedisStrictCommand MSET = new RedisStrictCommand("MSET", new VoidReplayConvertor()); + RedisStrictCommand MSETNX = new RedisStrictCommand("MSETNX", new BooleanReplayConvertor()); RedisCommand HSETNX = new RedisCommand("HSETNX", new BooleanReplayConvertor(), 2, ValueType.MAP); RedisCommand HSET = new RedisCommand("HSET", new BooleanReplayConvertor(), 2, ValueType.MAP); diff --git a/src/main/java/org/redisson/client/protocol/decoder/GeoDistanceMapDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/GeoDistanceMapDecoder.java index ac4f98393..9e9d526e5 100644 --- a/src/main/java/org/redisson/client/protocol/decoder/GeoDistanceMapDecoder.java +++ b/src/main/java/org/redisson/client/protocol/decoder/GeoDistanceMapDecoder.java @@ -25,7 +25,6 @@ import org.redisson.client.codec.DoubleCodec; import org.redisson.client.handler.State; import io.netty.buffer.ByteBuf; -import io.netty.util.CharsetUtil; public class GeoDistanceMapDecoder implements MultiDecoder> { @@ -40,7 +39,6 @@ public class GeoDistanceMapDecoder implements MultiDecoder> @Override public Object decode(ByteBuf buf, State state) throws IOException { - System.out.println("1 " + buf.toString(CharsetUtil.UTF_8)); if (pos.get() % 2 == 0) { return codec.getValueDecoder().decode(buf, state); } @@ -55,7 +53,6 @@ public class GeoDistanceMapDecoder implements MultiDecoder> @Override public Map decode(List parts, State state) { - System.out.println(parts); Map result = new HashMap(parts.size()/2); for (int i = 0; i < parts.size(); i++) { if (i % 2 != 0) { diff --git a/src/main/java/org/redisson/connection/decoder/MapGetAllDecoder.java b/src/main/java/org/redisson/connection/decoder/MapGetAllDecoder.java index 35aa171df..7ce90ee4f 100644 --- a/src/main/java/org/redisson/connection/decoder/MapGetAllDecoder.java +++ b/src/main/java/org/redisson/connection/decoder/MapGetAllDecoder.java @@ -28,10 +28,12 @@ import io.netty.buffer.ByteBuf; public class MapGetAllDecoder implements MultiDecoder> { + private final int shiftIndex; private final List args; - public MapGetAllDecoder(List args) { + public MapGetAllDecoder(List args, int shiftIndex) { this.args = args; + this.shiftIndex = shiftIndex; } @Override @@ -50,12 +52,12 @@ public class MapGetAllDecoder implements MultiDecoder> { return Collections.emptyMap(); } Map result = new HashMap(parts.size()); - for (int index = 0; index < args.size()-1; index++) { + for (int index = 0; index < args.size()-shiftIndex; index++) { Object value = parts.get(index); if (value == null) { continue; } - result.put(args.get(index+1), value); + result.put(args.get(index+shiftIndex), value); } return result; } diff --git a/src/main/java/org/redisson/core/RBuckets.java b/src/main/java/org/redisson/core/RBuckets.java new file mode 100644 index 000000000..036d7f88f --- /dev/null +++ b/src/main/java/org/redisson/core/RBuckets.java @@ -0,0 +1,64 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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.core; + +import java.util.List; +import java.util.Map; + +public interface RBuckets { + + /** + *

Returns a list of object holder instances by a key pattern. + * + *

Supported glob-style patterns:
+     *    h?llo subscribes to hello, hallo and hxllo
+     *    h*llo subscribes to hllo and heeeello
+     *    h[ae]llo subscribes to hello and hallo, but not hillo
+     *    h[^e]llo matches hallo, hbllo, ... but not hello
+     *    h[a-b]llo matches hallo and hbllo
+ *

Use \ to escape special characters if you want to match them verbatim. + * + * @param pattern + * @return + */ + List> find(String pattern); + + /** + * Returns Redis object mapped by key. Result Map is not contains + * key-value entry for null values. + * + * @param keys + * @return + */ + Map get(String ... keys); + + /** + * Try to save objects mapped by Redis key. + * If at least one of them is already exist then + * don't set none of them. + * + * @param buckets + */ + boolean trySet(Map buckets); + + /** + * Saves objects mapped by Redis key. + * + * @param buckets + */ + void set(Map buckets); + +} diff --git a/src/test/java/org/redisson/RedissonBucketTest.java b/src/test/java/org/redisson/RedissonBucketTest.java index ff88c0173..0688f55b3 100755 --- a/src/test/java/org/redisson/RedissonBucketTest.java +++ b/src/test/java/org/redisson/RedissonBucketTest.java @@ -64,35 +64,6 @@ public class RedissonBucketTest extends BaseTest { assertThat(r1.get()).isNull(); } - @Test - public void testSaveBuckets() { - Map buckets = new HashMap(); - buckets.put("12", 1); - buckets.put("41", 2); - redisson.saveBuckets(buckets); - - RBucket r1 = redisson.getBucket("12"); - assertThat(r1.get()).isEqualTo(1); - - RBucket r2 = redisson.getBucket("41"); - assertThat(r2.get()).isEqualTo(2); - } - - @Test - public void testLoadBucketValues() { - RBucket bucket1 = redisson.getBucket("test1"); - bucket1.set("someValue1"); - RBucket bucket3 = redisson.getBucket("test3"); - bucket3.set("someValue3"); - - Map result = redisson.loadBucketValues("test1", "test2", "test3", "test4"); - Map expected = new HashMap(); - expected.put("test1", "someValue1"); - expected.put("test3", "someValue3"); - - Assert.assertEquals(expected, result); - } - @Test public void testExpire() throws InterruptedException { RBucket bucket = redisson.getBucket("test1"); @@ -175,20 +146,4 @@ public class RedissonBucketTest extends BaseTest { Assert.assertFalse(bucket.isExists()); } - @Test - public void testGetPattern() { - Collection names = Arrays.asList("test:testGetPattern:one", "test:testGetPattern:two"); - Collection vals = Arrays.asList("one-val", "two-val"); - redisson.getBucket("test:testGetPattern:one").set("one-val"); - redisson.getBucket("test:testGetPattern:two").set("two-val"); - List> buckets = redisson.getBuckets("test:testGetPattern:*"); - Assert.assertEquals(2, buckets.size()); - Assert.assertTrue(names.contains(buckets.get(0).getName())); - Assert.assertTrue(names.contains(buckets.get(1).getName())); - Assert.assertTrue(vals.contains(buckets.get(0).get())); - Assert.assertTrue(vals.contains(buckets.get(1).get())); - for (RBucket bucket : buckets) { - bucket.delete(); - } - } } diff --git a/src/test/java/org/redisson/RedissonBucketsTest.java b/src/test/java/org/redisson/RedissonBucketsTest.java new file mode 100644 index 000000000..02d8a29da --- /dev/null +++ b/src/test/java/org/redisson/RedissonBucketsTest.java @@ -0,0 +1,91 @@ +package org.redisson; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.Assert; +import org.junit.Test; +import org.redisson.core.RBucket; + +public class RedissonBucketsTest extends BaseTest { + + @Test + public void testGet() { + RBucket bucket1 = redisson.getBucket("test1"); + bucket1.set("someValue1"); + RBucket bucket3 = redisson.getBucket("test3"); + bucket3.set("someValue3"); + + Map result = redisson.getBuckets().get("test1", "test2", "test3", "test4"); + Map expected = new HashMap(); + expected.put("test1", "someValue1"); + expected.put("test3", "someValue3"); + + Assert.assertEquals(expected, result); + } + + @Test + public void testFind() { + Collection names = Arrays.asList("test:testGetPattern:one", "test:testGetPattern:two"); + Collection vals = Arrays.asList("one-val", "two-val"); + + redisson.getBucket("test:testGetPattern:one").set("one-val"); + redisson.getBucket("test:testGetPattern:two").set("two-val"); + + List> buckets = redisson.getBuckets().find("test:testGetPattern:*"); + Assert.assertEquals(2, buckets.size()); + Assert.assertTrue(names.contains(buckets.get(0).getName())); + Assert.assertTrue(names.contains(buckets.get(1).getName())); + Assert.assertTrue(vals.contains(buckets.get(0).get())); + Assert.assertTrue(vals.contains(buckets.get(1).get())); + for (RBucket bucket : buckets) { + bucket.delete(); + } + } + + + @Test + public void testSet() { + Map buckets = new HashMap(); + buckets.put("12", 1); + buckets.put("41", 2); + redisson.getBuckets().set(buckets); + + RBucket r1 = redisson.getBucket("12"); + assertThat(r1.get()).isEqualTo(1); + + RBucket r2 = redisson.getBucket("41"); + assertThat(r2.get()).isEqualTo(2); + } + + @Test + public void testTrySet() { + redisson.getBucket("12").set("341"); + + Map buckets = new HashMap(); + buckets.put("12", 1); + buckets.put("41", 2); + assertThat(redisson.getBuckets().trySet(buckets)).isFalse(); + + RBucket r2 = redisson.getBucket("41"); + assertThat(r2.get()).isNull(); + + Map buckets2 = new HashMap(); + buckets2.put("61", 1); + buckets2.put("41", 2); + assertThat(redisson.getBuckets().trySet(buckets2)).isTrue(); + + RBucket r1 = redisson.getBucket("61"); + assertThat(r1.get()).isEqualTo(1); + + RBucket r3 = redisson.getBucket("41"); + assertThat(r3.get()).isEqualTo(2); + } + + +} diff --git a/src/test/java/org/redisson/RedissonGeoTest.java b/src/test/java/org/redisson/RedissonGeoTest.java new file mode 100644 index 000000000..fc689e006 --- /dev/null +++ b/src/test/java/org/redisson/RedissonGeoTest.java @@ -0,0 +1,183 @@ +package org.redisson; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.junit.Test; +import org.redisson.core.GeoEntry; +import org.redisson.core.GeoPosition; +import org.redisson.core.GeoUnit; +import org.redisson.core.RGeo; + +public class RedissonGeoTest extends BaseTest { + + @Test + public void testAdd() { + RGeo geo = redisson.getGeo("test"); + assertThat(geo.add(2.51, 3.12, "city1")).isEqualTo(1); + } + + @Test + public void testAddEntries() { + RGeo geo = redisson.getGeo("test"); + assertThat(geo.add(new GeoEntry(3.11, 9.10321, "city1"), new GeoEntry(81.1231, 38.65478, "city2"))).isEqualTo(2); + } + + @Test + public void testDist() { + RGeo geo = redisson.getGeo("test"); + geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), new GeoEntry(15.087269, 37.502669, "Catania")); + + assertThat(geo.dist("Palermo", "Catania", GeoUnit.METERS)).isEqualTo(166274.15156960033D); + } + + @Test + public void testDistEmpty() { + RGeo geo = redisson.getGeo("test"); + + assertThat(geo.dist("Palermo", "Catania", GeoUnit.METERS)).isNull(); + } + + @Test + public void testHash() { + RGeo geo = redisson.getGeo("test"); + geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), new GeoEntry(15.087269, 37.502669, "Catania")); + + Map expected = new LinkedHashMap(); + expected.put("Palermo", "sqc8b49rny0"); + expected.put("Catania", "sqdtr74hyu0"); + assertThat(geo.hash("Palermo", "Catania")).isEqualTo(expected); + } + + @Test + public void testHashEmpty() { + RGeo geo = redisson.getGeo("test"); + + assertThat(geo.hash("Palermo", "Catania")).isEmpty(); + } + + + @Test + public void testPos() { + RGeo geo = redisson.getGeo("test"); + geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), new GeoEntry(15.087269, 37.502669, "Catania")); + + Map expected = new LinkedHashMap(); + expected.put("Palermo", new GeoPosition(13.361389338970184, 38.115556395496299)); + expected.put("Catania", new GeoPosition(15.087267458438873, 37.50266842333162)); + assertThat(geo.pos("test2", "Palermo", "test3", "Catania", "test1")).isEqualTo(expected); + } + + @Test + public void testPosEmpty() { + RGeo geo = redisson.getGeo("test"); + + assertThat(geo.pos("test2", "Palermo", "test3", "Catania", "test1")).isEmpty(); + } + + @Test + public void testRadius() { + RGeo geo = redisson.getGeo("test"); + geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), new GeoEntry(15.087269, 37.502669, "Catania")); + + assertThat(geo.radius(15, 37, 200, GeoUnit.KILOMETERS)).containsExactly("Palermo", "Catania"); + } + + @Test + public void testRadiusEmpty() { + RGeo geo = redisson.getGeo("test"); + + assertThat(geo.radius(15, 37, 200, GeoUnit.KILOMETERS)).isEmpty(); + } + + @Test + public void testRadiusWithDistance() { + RGeo geo = redisson.getGeo("test"); + geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), new GeoEntry(15.087269, 37.502669, "Catania")); + + Map expected = new HashMap(); + expected.put("Palermo", 190.4424); + expected.put("Catania", 56.4413); + assertThat(geo.radiusWithDistance(15, 37, 200, GeoUnit.KILOMETERS)).isEqualTo(expected); + } + + @Test + public void testRadiusWithDistanceEmpty() { + RGeo geo = redisson.getGeo("test"); + + assertThat(geo.radiusWithDistance(15, 37, 200, GeoUnit.KILOMETERS)).isEmpty(); + } + + @Test + public void testRadiusWithPosition() { + RGeo geo = redisson.getGeo("test"); + geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), new GeoEntry(15.087269, 37.502669, "Catania")); + + Map expected = new HashMap(); + expected.put("Palermo", new GeoPosition(13.361389338970184, 38.115556395496299)); + expected.put("Catania", new GeoPosition(15.087267458438873, 37.50266842333162)); + assertThat(geo.radiusWithPosition(15, 37, 200, GeoUnit.KILOMETERS)).isEqualTo(expected); + } + + @Test + public void testRadiusWithPositionEmpty() { + RGeo geo = redisson.getGeo("test"); + + assertThat(geo.radiusWithPosition(15, 37, 200, GeoUnit.KILOMETERS)).isEmpty(); + } + + @Test + public void testRadiusMember() { + RGeo geo = redisson.getGeo("test"); + geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), new GeoEntry(15.087269, 37.502669, "Catania")); + + assertThat(geo.radius("Palermo", 200, GeoUnit.KILOMETERS)).containsExactly("Palermo", "Catania"); + } + + @Test + public void testRadiusMemberEmpty() { + RGeo geo = redisson.getGeo("test"); + + assertThat(geo.radius("Palermo", 200, GeoUnit.KILOMETERS)).isEmpty(); + } + + @Test + public void testRadiusMemberWithDistance() { + RGeo geo = redisson.getGeo("test"); + geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), new GeoEntry(15.087269, 37.502669, "Catania")); + + Map expected = new HashMap(); + expected.put("Palermo", 0.0); + expected.put("Catania", 166.2742); + assertThat(geo.radiusWithDistance("Palermo", 200, GeoUnit.KILOMETERS)).isEqualTo(expected); + } + + @Test + public void testRadiusMemberWithDistanceEmpty() { + RGeo geo = redisson.getGeo("test"); + + assertThat(geo.radiusWithDistance("Palermo", 200, GeoUnit.KILOMETERS)).isEmpty(); + } + + @Test + public void testRadiusMemberWithPosition() { + RGeo geo = redisson.getGeo("test"); + geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), new GeoEntry(15.087269, 37.502669, "Catania")); + + Map expected = new HashMap(); + expected.put("Palermo", new GeoPosition(13.361389338970184, 38.115556395496299)); + expected.put("Catania", new GeoPosition(15.087267458438873, 37.50266842333162)); + assertThat(geo.radiusWithPosition("Palermo", 200, GeoUnit.KILOMETERS)).isEqualTo(expected); + } + + @Test + public void testRadiusMemberWithPositionEmpty() { + RGeo geo = redisson.getGeo("test"); + + assertThat(geo.radiusWithPosition("Palermo", 200, GeoUnit.KILOMETERS)).isEmpty(); + } + +} From 563fb79574c7d95618aec35105cad485b6388159 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Fri, 1 Apr 2016 13:53:16 +0100 Subject: [PATCH 043/120] Fixes a compilation issue. --- src/main/java/org/redisson/RedissonBuckets.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/redisson/RedissonBuckets.java b/src/main/java/org/redisson/RedissonBuckets.java index 43a118771..e9958461c 100644 --- a/src/main/java/org/redisson/RedissonBuckets.java +++ b/src/main/java/org/redisson/RedissonBuckets.java @@ -72,7 +72,7 @@ public class RedissonBuckets implements RBuckets { return Collections.emptyMap(); } - RedisCommand> command = new RedisCommand>("MGET", new MapGetAllDecoder(Arrays.asList(keys), 0), ValueType.OBJECTS); + RedisCommand> command = new RedisCommand>("MGET", new MapGetAllDecoder(Arrays.asList(keys), 0), ValueType.OBJECTS); Future> future = commandExecutor.readAsync(keys[0], new DelegateDecoderCodec(codec), command, keys); return commandExecutor.get(future); } From 7c4deafc7484bb8e84b4e69c04aef842814e7049 Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 4 Apr 2016 11:56:10 +0300 Subject: [PATCH 044/120] SCAN commands result handling fixed. #463 --- .../org/redisson/RedissonBaseIterator.java | 96 +++++++++++++++++++ .../org/redisson/RedissonBaseMapIterator.java | 58 +++++++---- src/main/java/org/redisson/RedissonKeys.java | 51 ++-------- .../org/redisson/RedissonMapIterator.java | 2 +- .../RedissonMultiMapKeysIterator.java | 2 +- .../org/redisson/RedissonScoredSortedSet.java | 47 +-------- src/main/java/org/redisson/RedissonSet.java | 58 +---------- .../java/org/redisson/RedissonSetCache.java | 58 +---------- .../redisson/RedissonSetMultimapValues.java | 59 +----------- .../java/org/redisson/RedissonMapTest.java | 21 ++++ 10 files changed, 185 insertions(+), 267 deletions(-) create mode 100644 src/main/java/org/redisson/RedissonBaseIterator.java diff --git a/src/main/java/org/redisson/RedissonBaseIterator.java b/src/main/java/org/redisson/RedissonBaseIterator.java new file mode 100644 index 000000000..ab1eaa044 --- /dev/null +++ b/src/main/java/org/redisson/RedissonBaseIterator.java @@ -0,0 +1,96 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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 java.net.InetSocketAddress; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +import org.redisson.client.protocol.decoder.ListScanResult; + +abstract class RedissonBaseIterator implements Iterator { + + private List firstValues; + private Iterator iter; + private InetSocketAddress client; + private long nextIterPos; + private long startPos = -1; + + private boolean currentElementRemoved; + private boolean removeExecuted; + private V value; + + @Override + public boolean hasNext() { + if (iter == null || !iter.hasNext()) { + if (nextIterPos == -1) { + return false; + } + long prevIterPos; + do { + prevIterPos = nextIterPos; + ListScanResult res = iterator(client, nextIterPos); + client = res.getRedisClient(); + if (startPos == -1) { + startPos = res.getPos(); + } + if (nextIterPos == 0 && firstValues == null) { + firstValues = res.getValues(); + } else if (res.getValues().equals(firstValues) && res.getPos() == startPos) { + return false; + } + iter = res.getValues().iterator(); + nextIterPos = res.getPos(); + } while (!iter.hasNext() && nextIterPos != prevIterPos); + if (prevIterPos == nextIterPos && !removeExecuted) { + nextIterPos = -1; + } + } + return iter.hasNext(); + } + + abstract ListScanResult iterator(InetSocketAddress client, long nextIterPos); + + @Override + public V next() { + if (!hasNext()) { + throw new NoSuchElementException("No such element"); + } + + value = iter.next(); + currentElementRemoved = false; + return value; + } + + @Override + public void remove() { + if (currentElementRemoved) { + throw new IllegalStateException("Element been already deleted"); + } + if (iter == null) { + throw new IllegalStateException(); + } + + iter.remove(); + remove(value); + currentElementRemoved = true; + removeExecuted = true; + } + + abstract void remove(V value); + +} diff --git a/src/main/java/org/redisson/RedissonBaseMapIterator.java b/src/main/java/org/redisson/RedissonBaseMapIterator.java index 9c2b9c073..66cf04465 100644 --- a/src/main/java/org/redisson/RedissonBaseMapIterator.java +++ b/src/main/java/org/redisson/RedissonBaseMapIterator.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; +import org.redisson.client.protocol.decoder.ListScanResult; import org.redisson.client.protocol.decoder.MapScanResult; import org.redisson.client.protocol.decoder.ScanObjectEntry; @@ -32,10 +33,12 @@ abstract class RedissonBaseMapIterator implements Iterator { private Map firstValues; private Iterator> iter; - protected long iterPos = 0; + protected long nextIterPos; + protected long startPos = -1; protected InetSocketAddress client; private boolean finished; + private boolean currentElementRemoved; private boolean removeExecuted; protected Map.Entry entry; @@ -44,26 +47,41 @@ abstract class RedissonBaseMapIterator implements Iterator { if (finished) { return false; } + if (iter == null || !iter.hasNext()) { - MapScanResult res = iterator(); - client = res.getRedisClient(); - if (iterPos == 0 && firstValues == null) { - firstValues = convert(res.getMap()); - } else { - Map newValues = convert(res.getMap()); - if (newValues.equals(firstValues)) { - finished = true; - free(firstValues); + if (nextIterPos == -1) { + return false; + } + long prevIterPos; + do { + prevIterPos = nextIterPos; + MapScanResult res = iterator(); + client = res.getRedisClient(); + if (startPos == -1) { + startPos = res.getPos(); + } + if (nextIterPos == 0 && firstValues == null) { + firstValues = convert(res.getMap()); + } else { + Map newValues = convert(res.getMap()); + if (newValues.equals(firstValues)) { + finished = true; + free(firstValues); + free(newValues); + firstValues = null; + return false; + } free(newValues); - firstValues = null; - return false; } - free(newValues); + iter = res.getMap().entrySet().iterator(); + nextIterPos = res.getPos(); + } while (!iter.hasNext() && nextIterPos != prevIterPos); + if (prevIterPos == nextIterPos && !removeExecuted) { + nextIterPos = -1; } - iter = res.getMap().entrySet().iterator(); - iterPos = res.getPos(); } return iter.hasNext(); + } protected abstract MapScanResult iterator(); @@ -90,7 +108,7 @@ abstract class RedissonBaseMapIterator implements Iterator { } entry = iter.next(); - removeExecuted = false; + currentElementRemoved = false; return getValue(entry); } @@ -108,14 +126,16 @@ abstract class RedissonBaseMapIterator implements Iterator { @Override public void remove() { - if (removeExecuted) { + if (currentElementRemoved) { throw new IllegalStateException("Element been already deleted"); } + if (iter == null) { + throw new IllegalStateException(); + } - // lazy init iterator - hasNext(); iter.remove(); removeKey(); + currentElementRemoved = true; removeExecuted = true; } diff --git a/src/main/java/org/redisson/RedissonKeys.java b/src/main/java/org/redisson/RedissonKeys.java index e553bb169..afab001b7 100644 --- a/src/main/java/org/redisson/RedissonKeys.java +++ b/src/main/java/org/redisson/RedissonKeys.java @@ -15,6 +15,7 @@ */ package org.redisson; +import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -23,7 +24,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; @@ -90,55 +90,18 @@ public class RedissonKeys implements RKeys { } private Iterator createKeysIterator(final int slot, final String pattern) { - return new Iterator() { - - private List firstValues; - private Iterator iter; - private long iterPos; - - private boolean removeExecuted; - private String value; + return new RedissonBaseIterator() { @Override - public boolean hasNext() { - if (iter == null || !iter.hasNext()) { - ListScanResult res = scanIterator(slot, iterPos, pattern); - if (iterPos == 0 && firstValues == null) { - firstValues = res.getValues(); - } else if (res.getValues().equals(firstValues)) { - return false; - } - iter = res.getValues().iterator(); - iterPos = res.getPos(); - } - return iter.hasNext(); + ListScanResult iterator(InetSocketAddress client, long nextIterPos) { + return RedissonKeys.this.scanIterator(slot, nextIterPos, pattern); } @Override - public String next() { - if (!hasNext()) { - throw new NoSuchElementException("No such element"); - } - - value = iter.next(); - removeExecuted = false; - return value; + void remove(String value) { + RedissonKeys.this.delete(value); } - - @Override - public void remove() { - if (removeExecuted) { - throw new IllegalStateException("Element been already deleted"); - } - if (iter == null) { - throw new IllegalStateException(); - } - - iter.remove(); - delete(value); - removeExecuted = true; - } - + }; } diff --git a/src/main/java/org/redisson/RedissonMapIterator.java b/src/main/java/org/redisson/RedissonMapIterator.java index 4290e58e4..a16cfa3f5 100644 --- a/src/main/java/org/redisson/RedissonMapIterator.java +++ b/src/main/java/org/redisson/RedissonMapIterator.java @@ -29,7 +29,7 @@ public class RedissonMapIterator extends RedissonBaseMapIterator iterator() { - return map.scanIterator(client, iterPos); + return map.scanIterator(client, nextIterPos); } protected void removeKey() { diff --git a/src/main/java/org/redisson/RedissonMultiMapKeysIterator.java b/src/main/java/org/redisson/RedissonMultiMapKeysIterator.java index 03918ee60..375349e95 100644 --- a/src/main/java/org/redisson/RedissonMultiMapKeysIterator.java +++ b/src/main/java/org/redisson/RedissonMultiMapKeysIterator.java @@ -29,7 +29,7 @@ public class RedissonMultiMapKeysIterator extends RedissonBaseMapIterat } protected MapScanResult iterator() { - return map.scanIterator(client, iterPos); + return map.scanIterator(client, nextIterPos); } protected void removeKey() { diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index 0ffb1122d..188460143 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -25,7 +25,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.NoSuchElementException; import org.redisson.client.codec.Codec; import org.redisson.client.codec.ScoredCodec; @@ -245,54 +244,18 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Iterator iterator() { - return new Iterator() { - - private List firstValues; - private Iterator iter; - private InetSocketAddress client; - private long iterPos; - - private boolean removeExecuted; - private V value; + return new RedissonBaseIterator() { @Override - public boolean hasNext() { - if (iter == null || !iter.hasNext()) { - ListScanResult res = scanIterator(client, iterPos); - client = res.getRedisClient(); - if (iterPos == 0 && firstValues == null) { - firstValues = res.getValues(); - } else if (res.getValues().equals(firstValues)) { - return false; - } - iter = res.getValues().iterator(); - iterPos = res.getPos(); - } - return iter.hasNext(); + ListScanResult iterator(InetSocketAddress client, long nextIterPos) { + return scanIterator(client, nextIterPos); } @Override - public V next() { - if (!hasNext()) { - throw new NoSuchElementException("No such element at index"); - } - - value = iter.next(); - removeExecuted = false; - return value; - } - - @Override - public void remove() { - if (removeExecuted) { - throw new IllegalStateException("Element been already deleted"); - } - - iter.remove(); + void remove(V value) { RedissonScoredSortedSet.this.remove(value); - removeExecuted = true; } - + }; } diff --git a/src/main/java/org/redisson/RedissonSet.java b/src/main/java/org/redisson/RedissonSet.java index 9904bcc30..2afeeb563 100644 --- a/src/main/java/org/redisson/RedissonSet.java +++ b/src/main/java/org/redisson/RedissonSet.java @@ -82,66 +82,18 @@ public class RedissonSet extends RedissonExpirable implements RSet { @Override public Iterator iterator() { - return new Iterator() { - - private List firstValues; - private Iterator iter; - private InetSocketAddress client; - private long nextIterPos; - - private boolean currentElementRemoved; - private boolean removeExecuted; - private V value; - - @Override - public boolean hasNext() { - if (iter == null || !iter.hasNext()) { - if (nextIterPos == -1) { - return false; - } - long prevIterPos = nextIterPos; - ListScanResult res = scanIterator(client, nextIterPos); - client = res.getRedisClient(); - if (nextIterPos == 0 && firstValues == null) { - firstValues = res.getValues(); - } else if (res.getValues().equals(firstValues)) { - return false; - } - iter = res.getValues().iterator(); - nextIterPos = res.getPos(); - if (prevIterPos == nextIterPos && !removeExecuted) { - nextIterPos = -1; - } - } - return iter.hasNext(); - } + return new RedissonBaseIterator() { @Override - public V next() { - if (!hasNext()) { - throw new NoSuchElementException("No such element at index"); - } - - value = iter.next(); - currentElementRemoved = false; - return value; + ListScanResult iterator(InetSocketAddress client, long nextIterPos) { + return scanIterator(client, nextIterPos); } @Override - public void remove() { - if (currentElementRemoved) { - throw new IllegalStateException("Element been already deleted"); - } - if (iter == null) { - throw new IllegalStateException(); - } - - iter.remove(); + void remove(V value) { RedissonSet.this.remove(value); - currentElementRemoved = true; - removeExecuted = true; } - + }; } diff --git a/src/main/java/org/redisson/RedissonSetCache.java b/src/main/java/org/redisson/RedissonSetCache.java index 34bc65af4..124fbfbc9 100644 --- a/src/main/java/org/redisson/RedissonSetCache.java +++ b/src/main/java/org/redisson/RedissonSetCache.java @@ -161,66 +161,18 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< @Override public Iterator iterator() { - return new Iterator() { - - private List firstValues; - private Iterator iter; - private InetSocketAddress client; - private long nextIterPos; - - private boolean currentElementRemoved; - private boolean removeExecuted; - private V value; - - @Override - public boolean hasNext() { - if (iter == null || !iter.hasNext()) { - if (nextIterPos == -1) { - return false; - } - long prevIterPos = nextIterPos; - ListScanResult res = scanIterator(client, nextIterPos); - client = res.getRedisClient(); - if (nextIterPos == 0 && firstValues == null) { - firstValues = res.getValues(); - } else if (res.getValues().equals(firstValues)) { - return false; - } - iter = res.getValues().iterator(); - nextIterPos = res.getPos(); - if (prevIterPos == nextIterPos && !removeExecuted) { - nextIterPos = -1; - } - } - return iter.hasNext(); - } + return new RedissonBaseIterator() { @Override - public V next() { - if (!hasNext()) { - throw new NoSuchElementException("No such element at index"); - } - - value = iter.next(); - currentElementRemoved = false; - return value; + ListScanResult iterator(InetSocketAddress client, long nextIterPos) { + return scanIterator(client, nextIterPos); } @Override - public void remove() { - if (currentElementRemoved) { - throw new IllegalStateException("Element been already deleted"); - } - if (iter == null) { - throw new IllegalStateException(); - } - - iter.remove(); + void remove(V value) { RedissonSetCache.this.remove(value); - currentElementRemoved = true; - removeExecuted = true; } - + }; } diff --git a/src/main/java/org/redisson/RedissonSetMultimapValues.java b/src/main/java/org/redisson/RedissonSetMultimapValues.java index 215c82cd4..36d0fefee 100644 --- a/src/main/java/org/redisson/RedissonSetMultimapValues.java +++ b/src/main/java/org/redisson/RedissonSetMultimapValues.java @@ -22,7 +22,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; -import java.util.NoSuchElementException; import java.util.Set; import org.redisson.client.codec.Codec; @@ -128,66 +127,18 @@ public class RedissonSetMultimapValues extends RedissonExpirable implements R @Override public Iterator iterator() { - return new Iterator() { - - private List firstValues; - private Iterator iter; - private InetSocketAddress client; - private long nextIterPos; - - private boolean currentElementRemoved; - private boolean removeExecuted; - private V value; - - @Override - public boolean hasNext() { - if (iter == null || !iter.hasNext()) { - if (nextIterPos == -1) { - return false; - } - long prevIterPos = nextIterPos; - ListScanResult res = scanIterator(client, nextIterPos); - client = res.getRedisClient(); - if (nextIterPos == 0 && firstValues == null) { - firstValues = res.getValues(); - } else if (res.getValues().equals(firstValues)) { - return false; - } - iter = res.getValues().iterator(); - nextIterPos = res.getPos(); - if (prevIterPos == nextIterPos && !removeExecuted) { - nextIterPos = -1; - } - } - return iter.hasNext(); - } + return new RedissonBaseIterator() { @Override - public V next() { - if (!hasNext()) { - throw new NoSuchElementException("No such element at index"); - } - - value = iter.next(); - currentElementRemoved = false; - return value; + ListScanResult iterator(InetSocketAddress client, long nextIterPos) { + return scanIterator(client, nextIterPos); } @Override - public void remove() { - if (currentElementRemoved) { - throw new IllegalStateException("Element been already deleted"); - } - if (iter == null) { - throw new IllegalStateException(); - } - - iter.remove(); + void remove(V value) { RedissonSetMultimapValues.this.remove(value); - currentElementRemoved = true; - removeExecuted = true; } - + }; } diff --git a/src/test/java/org/redisson/RedissonMapTest.java b/src/test/java/org/redisson/RedissonMapTest.java index 968f0e5d3..0a46a6558 100644 --- a/src/test/java/org/redisson/RedissonMapTest.java +++ b/src/test/java/org/redisson/RedissonMapTest.java @@ -23,6 +23,7 @@ import org.redisson.client.codec.StringCodec; import org.redisson.codec.JsonJacksonCodec; import org.redisson.core.Predicate; import org.redisson.core.RMap; +import org.redisson.core.RSet; import io.netty.util.concurrent.Future; @@ -257,6 +258,26 @@ public class RedissonMapTest extends BaseTest { assertThat(val2).isEqualTo(4); } + @Test + public void testIteratorRemoveHighVolume() throws InterruptedException { + RMap map = redisson.getMap("simpleMap"); + for (int i = 0; i < 10000; i++) { + map.put(i, i*10); + } + + int cnt = 0; + Iterator iterator = map.keySet().iterator(); + while (iterator.hasNext()) { + Integer integer = iterator.next(); + iterator.remove(); + cnt++; + } + Assert.assertEquals(10000, cnt); + assertThat(map).isEmpty(); + Assert.assertEquals(0, map.size()); + } + + @Test public void testIterator() { RMap rMap = redisson.getMap("123"); From 5170ba8cc766b5822b22e28b4c10ce0cd84e1cb8 Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 4 Apr 2016 15:12:32 +0300 Subject: [PATCH 045/120] ReactiveSet iterator fixed. #463 --- .../org/redisson/reactive/PublisherAdder.java | 18 ++++++----- .../reactive/SetReactiveIterator.java | 31 +++---------------- 2 files changed, 15 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/redisson/reactive/PublisherAdder.java b/src/main/java/org/redisson/reactive/PublisherAdder.java index 2d2ec7c80..a7ca9b907 100644 --- a/src/main/java/org/redisson/reactive/PublisherAdder.java +++ b/src/main/java/org/redisson/reactive/PublisherAdder.java @@ -15,6 +15,8 @@ */ package org.redisson.reactive; +import java.util.concurrent.atomic.AtomicLong; + import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; import org.redisson.api.RCollectionReactive; @@ -40,9 +42,10 @@ public class PublisherAdder { c.subscribe(new DefaultSubscriber() { + volatile boolean completed; + AtomicLong values = new AtomicLong(); Subscription s; Long lastSize = 0L; - V lastValue; @Override public void onSubscribe(Subscription s) { @@ -52,7 +55,7 @@ public class PublisherAdder { @Override public void onNext(V o) { - lastValue = o; + values.getAndIncrement(); destination.add(o).subscribe(new DefaultSubscriber() { @Override @@ -68,19 +71,18 @@ public class PublisherAdder { @Override public void onNext(Long o) { lastSize = sum(lastSize, o); - } - - @Override - public void onComplete() { - lastValue = null; s.request(1); + if (values.decrementAndGet() == 0 && completed) { + promise.onNext(lastSize); + } } }); } @Override public void onComplete() { - if (lastValue == null) { + completed = true; + if (values.get() == 0) { promise.onNext(lastSize); } } diff --git a/src/main/java/org/redisson/reactive/SetReactiveIterator.java b/src/main/java/org/redisson/reactive/SetReactiveIterator.java index b4855f1b3..a5d78573d 100644 --- a/src/main/java/org/redisson/reactive/SetReactiveIterator.java +++ b/src/main/java/org/redisson/reactive/SetReactiveIterator.java @@ -16,7 +16,6 @@ package org.redisson.reactive; import java.net.InetSocketAddress; -import java.util.ArrayList; import java.util.List; import org.reactivestreams.Publisher; @@ -24,56 +23,36 @@ import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.redisson.client.protocol.decoder.ListScanResult; -import reactor.core.reactivestreams.SubscriberBarrier; import reactor.rx.Stream; +import reactor.rx.subscription.ReactiveSubscription; public abstract class SetReactiveIterator extends Stream { @Override public void subscribe(final Subscriber t) { - t.onSubscribe(new SubscriberBarrier(t) { + t.onSubscribe(new ReactiveSubscription(this, t) { private List firstValues; private long nextIterPos; private InetSocketAddress client; private long currentIndex; - private List prevValues = new ArrayList(); @Override - protected void doRequest(long n) { + protected void onRequest(long n) { currentIndex = n; - if (!prevValues.isEmpty()) { - List vals = new ArrayList(prevValues); - prevValues.clear(); - - handle(vals); - - if (currentIndex == 0) { - return; - } - } - nextValues(); } private void handle(List vals) { for (V val : vals) { - if (currentIndex > 0) { - onNext(val); - } else { - prevValues.add(val); - } - currentIndex--; - if (currentIndex == 0) { - onComplete(); - } + onNext(val); } } protected void nextValues() { - final SubscriberBarrier m = this; + final ReactiveSubscription m = this; scanIteratorReactive(client, nextIterPos).subscribe(new Subscriber>() { @Override From 641a7224125159cb7606196907b4a094c7602d60 Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 4 Apr 2016 15:58:59 +0300 Subject: [PATCH 046/120] [maven-release-plugin] prepare release redisson-2.2.11 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 111ee57f8..b7ebf6ff9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.redisson redisson - 2.2.11-SNAPSHOT + 2.2.11 bundle Redisson @@ -15,7 +15,7 @@ scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git - HEAD + redisson-2.2.11 From ed9dcfa0ac10ec3d6ac3d7a8208967bd50319e26 Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 4 Apr 2016 15:59:06 +0300 Subject: [PATCH 047/120] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b7ebf6ff9..a2d439b28 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.redisson redisson - 2.2.11 + 2.2.12-SNAPSHOT bundle Redisson @@ -15,7 +15,7 @@ scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git - redisson-2.2.11 + HEAD From e065c8c9c1383db07da49a51486b5fc77aed05c8 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Mon, 4 Apr 2016 16:18:45 +0300 Subject: [PATCH 048/120] Update CHANGELOG.md --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ffd4f0fc..54c412b17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ Redisson Releases History ================================ ####Please Note: trunk is current development branch. +####04-Apr-2016 - version 2.2.11 released + +Since this version Redisson has __perfomance boost up to 43%__ + +Feature - __new object added__ `RGeo` +Feature - __new object added__ `RBuckets` +Feature - travis-ci integration (thanks to jackygurui) +Improvement - `RScoredSortedSet.removeAllAsync` & `removeAll` methods optimization +Improvement - `RemoteService` reliability tuned up +Improvement - Reattaching RBlockingQueue\Deque blocking commands (poll, take ...) after Redis failover process or channel reconnection +Fixed - iterator objects may skip results in some cases +Fixed - RTopic listeners hangs during synchronous commands execution inside it +Fixed - Redisson hangs during shutdown if `RBlockingQueue\Deque.take` or `RBlockingQueue\Deque.poll` methods were invoked + + ####23-Mar-2016 - version 2.2.10 released Feature - __new object added__ `RRemoteService` From 51f4c88a26f65a4d1bb663962e5435eb0432f99a Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Tue, 5 Apr 2016 13:21:15 +0300 Subject: [PATCH 049/120] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2753486b5..9c21b0127 100644 --- a/README.md +++ b/README.md @@ -82,12 +82,12 @@ Include the following to your dependency list: org.redisson redisson - 2.2.10 + 2.2.11 ### Gradle - compile 'org.redisson:redisson:2.2.10' + compile 'org.redisson:redisson:2.2.11' ### Supported by From 43bde3e0c042e3fd7ec15605384956c0768c6da4 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 5 Apr 2016 13:43:34 +0300 Subject: [PATCH 050/120] RGeo interface covered by comments. --- src/main/java/org/redisson/core/RGeo.java | 127 +++++++++++++++++- .../java/org/redisson/core/RGeoAsync.java | 114 ++++++++++++++++ 2 files changed, 235 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/redisson/core/RGeo.java b/src/main/java/org/redisson/core/RGeo.java index c6c32dafc..e07c3c02c 100644 --- a/src/main/java/org/redisson/core/RGeo.java +++ b/src/main/java/org/redisson/core/RGeo.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Map; /** + * Geospatial items holder * * @author Nikita Koksharov * @@ -26,26 +27,140 @@ import java.util.Map; */ public interface RGeo extends RExpirable, RGeoAsync { + /** + * Adds geospatial member. + * + * @param entries + * @return number of elements added to the sorted set, + * not including elements already existing for which + * the score was updated + */ long add(double longitude, double latitude, V member); + /** + * Adds geospatial members. + * + * @param entries + * @return number of elements added to the sorted set, + * not including elements already existing for which + * the score was updated + */ long add(GeoEntry... entries); + /** + * Returns distance between members in GeoUnit units. + * + * @see {@link GeoUnit} + * + * @param firstMember + * @param secondMember + * @param geoUnit + * @return + */ Double dist(V firstMember, V secondMember, GeoUnit geoUnit); + /** + * Returns 11 characters Geohash string mapped by defined member. + * + * @param members + * @return + */ Map hash(V... members); - + + /** + * Returns geo-position mapped by defined member. + * + * @param members + * @return + */ Map pos(V... members); + /** + * Returns the members of a sorted set, which are within the + * borders of the area specified with the center location + * and the maximum distance from the center (the radius) + * in GeoUnit units. + * + * @param longitude + * @param latitude + * @param radius + * @param geoUnit + * @return + */ List radius(double longitude, double latitude, double radius, GeoUnit geoUnit); - + + /** + * Returns the distance mapped by member, distance between member and the location. + * Members of a sorted set, which are within the + * borders of the area specified with the center location + * and the maximum distance from the center (the radius) + * in GeoUnit units. + * + * @param longitude + * @param latitude + * @param radius + * @param geoUnit + * @return + */ Map radiusWithDistance(double longitude, double latitude, double radius, GeoUnit geoUnit); - + + /** + * Returns the geo-position mapped by member. + * Members of a sorted set, which are within the + * borders of the area specified with the center location + * and the maximum distance from the center (the radius) + * in GeoUnit units. + * + * @param longitude + * @param latitude + * @param radius + * @param geoUnit + * @return + */ Map radiusWithPosition(double longitude, double latitude, double radius, GeoUnit geoUnit); - + + /** + * Returns the members of a sorted set, which are within the + * borders of the area specified with the defined member location + * and the maximum distance from the defined member location (the radius) + * in GeoUnit units. + * + * @param longitude + * @param latitude + * @param radius + * @param geoUnit + * @return + */ List radius(V member, double radius, GeoUnit geoUnit); - + + /** + * Returns the distance mapped by member, distance between member and the defined member location. + * Members of a sorted set, which are within the + * borders of the area specified with the defined member location + * and the maximum distance from the defined member location (the radius) + * in GeoUnit units. + * + * @param longitude + * @param latitude + * @param radius + * @param geoUnit + * @return + */ Map radiusWithDistance(V member, double radius, GeoUnit geoUnit); - + + /** + * Returns the geo-position mapped by member. + * Members of a sorted set, which are within the + * borders of the area specified with the defined member location + * and the maximum distance from the defined member location (the radius) + * in GeoUnit units. + * + * @param longitude + * @param latitude + * @param radius + * @param geoUnit + * @return + */ Map radiusWithPosition(V member, double radius, GeoUnit geoUnit); } diff --git a/src/main/java/org/redisson/core/RGeoAsync.java b/src/main/java/org/redisson/core/RGeoAsync.java index e2917d432..15420b385 100644 --- a/src/main/java/org/redisson/core/RGeoAsync.java +++ b/src/main/java/org/redisson/core/RGeoAsync.java @@ -28,26 +28,140 @@ import io.netty.util.concurrent.Future; */ public interface RGeoAsync extends RExpirableAsync { + /** + * Adds geospatial member. + * + * @param entries + * @return number of elements added to the sorted set, + * not including elements already existing for which + * the score was updated + */ Future addAsync(double longitude, double latitude, V member); + /** + * Adds geospatial members. + * + * @param entries + * @return number of elements added to the sorted set, + * not including elements already existing for which + * the score was updated + */ Future addAsync(GeoEntry... entries); + /** + * Returns distance between members in GeoUnit units. + * + * @see {@link GeoUnit} + * + * @param firstMember + * @param secondMember + * @param geoUnit + * @return + */ Future distAsync(V firstMember, V secondMember, GeoUnit geoUnit); + /** + * Returns 11 characters Geohash string mapped by defined member. + * + * @param members + * @return + */ Future> hashAsync(V... members); + /** + * Returns geo-position mapped by defined member. + * + * @param members + * @return + */ Future> posAsync(V... members); + /** + * Returns the members of a sorted set, which are within the + * borders of the area specified with the center location + * and the maximum distance from the center (the radius) + * in GeoUnit units. + * + * @param longitude + * @param latitude + * @param radius + * @param geoUnit + * @return + */ Future> radiusAsync(double longitude, double latitude, double radius, GeoUnit geoUnit); + /** + * Returns the distance mapped by member, distance between member and the location. + * Members of a sorted set, which are within the + * borders of the area specified with the center location + * and the maximum distance from the center (the radius) + * in GeoUnit units. + * + * @param longitude + * @param latitude + * @param radius + * @param geoUnit + * @return + */ Future> radiusWithDistanceAsync(double longitude, double latitude, double radius, GeoUnit geoUnit); + /** + * Returns the geo-position mapped by member. + * Members of a sorted set, which are within the + * borders of the area specified with the center location + * and the maximum distance from the center (the radius) + * in GeoUnit units. + * + * @param longitude + * @param latitude + * @param radius + * @param geoUnit + * @return + */ Future> radiusWithPositionAsync(double longitude, double latitude, double radius, GeoUnit geoUnit); + /** + * Returns the members of a sorted set, which are within the + * borders of the area specified with the defined member location + * and the maximum distance from the defined member location (the radius) + * in GeoUnit units. + * + * @param longitude + * @param latitude + * @param radius + * @param geoUnit + * @return + */ Future> radiusAsync(V member, double radius, GeoUnit geoUnit); + /** + * Returns the distance mapped by member, distance between member and the defined member location. + * Members of a sorted set, which are within the + * borders of the area specified with the defined member location + * and the maximum distance from the defined member location (the radius) + * in GeoUnit units. + * + * @param longitude + * @param latitude + * @param radius + * @param geoUnit + * @return + */ Future> radiusWithDistanceAsync(V member, double radius, GeoUnit geoUnit); + /** + * Returns the geo-position mapped by member. + * Members of a sorted set, which are within the + * borders of the area specified with the defined member location + * and the maximum distance from the defined member location (the radius) + * in GeoUnit units. + * + * @param longitude + * @param latitude + * @param radius + * @param geoUnit + * @return + */ Future> radiusWithPositionAsync(V member, double radius, GeoUnit geoUnit); } From 33748b86509ff627be18ef0b62123190cdd202b4 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 5 Apr 2016 15:13:21 +0300 Subject: [PATCH 051/120] lib versions updated --- pom.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index a2d439b28..c8e527aae 100644 --- a/pom.xml +++ b/pom.xml @@ -100,33 +100,33 @@ io.netty netty-transport-native-epoll - 4.0.35.Final + 4.0.36.Final provided io.netty netty-common - 4.0.35.Final + 4.0.36.Final io.netty netty-codec - 4.0.35.Final + 4.0.36.Final io.netty netty-buffer - 4.0.35.Final + 4.0.36.Final io.netty netty-transport - 4.0.35.Final + 4.0.36.Final io.netty netty-handler - 4.0.35.Final + 4.0.36.Final @@ -261,7 +261,7 @@ org.apache.maven.plugins maven-eclipse-plugin - 2.9 + 2.10 true true @@ -310,7 +310,7 @@ maven-compiler-plugin - 3.3 + 3.5.1 ${source.version} ${source.version} @@ -336,7 +336,7 @@ org.apache.maven.plugins maven-source-plugin - 2.4 + 3.0.0 attach-sources @@ -351,7 +351,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.16 + 2.19.1 From d03e0de847f0d5cac23cf3b916164cf74d77b51c Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 8 Apr 2016 12:02:19 +0300 Subject: [PATCH 052/120] Fixed probably thread blocking issues #455 --- .../org/redisson/RedissonCountDownLatch.java | 4 +- src/main/java/org/redisson/RedissonLock.java | 4 +- .../java/org/redisson/RedissonObject.java | 6 +++ .../java/org/redisson/RedissonSemaphore.java | 5 ++- .../org/redisson/client/RedisConnection.java | 40 +++++++++++++------ .../client/handler/CommandDecoder.java | 7 +--- .../client/handler/CommandsQueue.java | 4 +- .../command/CommandAsyncExecutor.java | 3 ++ .../redisson/command/CommandAsyncService.java | 29 +++++++++++++- .../connection/pool/ConnectionPool.java | 2 +- 10 files changed, 76 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/redisson/RedissonCountDownLatch.java b/src/main/java/org/redisson/RedissonCountDownLatch.java index 8f3eeef1f..5a4d660a6 100644 --- a/src/main/java/org/redisson/RedissonCountDownLatch.java +++ b/src/main/java/org/redisson/RedissonCountDownLatch.java @@ -53,7 +53,7 @@ public class RedissonCountDownLatch extends RedissonObject implements RCountDown public void await() throws InterruptedException { Future promise = subscribe(); try { - promise.await(); + get(promise); while (getCount() > 0) { // waiting for open state @@ -71,7 +71,7 @@ public class RedissonCountDownLatch extends RedissonObject implements RCountDown public boolean await(long time, TimeUnit unit) throws InterruptedException { Future promise = subscribe(); try { - if (!promise.await(time, unit)) { + if (!await(promise, time, unit)) { return false; } diff --git a/src/main/java/org/redisson/RedissonLock.java b/src/main/java/org/redisson/RedissonLock.java index 3bc14bf9b..eaf060496 100644 --- a/src/main/java/org/redisson/RedissonLock.java +++ b/src/main/java/org/redisson/RedissonLock.java @@ -110,7 +110,7 @@ public class RedissonLock extends RedissonExpirable implements RLock { } Future future = subscribe(); - future.sync(); + get(future); try { while (true) { @@ -229,7 +229,7 @@ public class RedissonLock extends RedissonExpirable implements RLock { } Future future = subscribe(); - if (!future.await(time, TimeUnit.MILLISECONDS)) { + if (!await(future, time, TimeUnit.MILLISECONDS)) { future.addListener(new FutureListener() { @Override public void operationComplete(Future future) throws Exception { diff --git a/src/main/java/org/redisson/RedissonObject.java b/src/main/java/org/redisson/RedissonObject.java index 7e12d8ac8..6cc3d6000 100644 --- a/src/main/java/org/redisson/RedissonObject.java +++ b/src/main/java/org/redisson/RedissonObject.java @@ -15,6 +15,8 @@ */ package org.redisson; +import java.util.concurrent.TimeUnit; + import org.redisson.client.codec.Codec; import org.redisson.client.protocol.RedisCommands; import org.redisson.command.CommandAsyncExecutor; @@ -45,6 +47,10 @@ abstract class RedissonObject implements RObject { this(commandExecutor.getConnectionManager().getCodec(), commandExecutor, name); } + protected boolean await(Future future, long timeout, TimeUnit timeoutUnit) throws InterruptedException { + return commandExecutor.await(future, timeout, timeoutUnit); + } + protected V get(Future future) { return commandExecutor.get(future); } diff --git a/src/main/java/org/redisson/RedissonSemaphore.java b/src/main/java/org/redisson/RedissonSemaphore.java index e29a4e0a0..a23cd966b 100644 --- a/src/main/java/org/redisson/RedissonSemaphore.java +++ b/src/main/java/org/redisson/RedissonSemaphore.java @@ -70,7 +70,8 @@ public class RedissonSemaphore extends RedissonExpirable implements RSemaphore { return; } - Future future = subscribe().sync(); + Future future = subscribe(); + get(future); try { while (true) { if (tryAcquire(permits)) { @@ -113,7 +114,7 @@ public class RedissonSemaphore extends RedissonExpirable implements RSemaphore { long time = unit.toMillis(waitTime); Future future = subscribe(); - if (!future.await(time, TimeUnit.MILLISECONDS)) { + if (!await(future, time, TimeUnit.MILLISECONDS)) { return false; } diff --git a/src/main/java/org/redisson/client/RedisConnection.java b/src/main/java/org/redisson/client/RedisConnection.java index 4f6c49dc4..fc98647fd 100644 --- a/src/main/java/org/redisson/client/RedisConnection.java +++ b/src/main/java/org/redisson/client/RedisConnection.java @@ -15,6 +15,7 @@ */ package org.redisson.client; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.redisson.client.codec.Codec; @@ -111,21 +112,34 @@ public class RedisConnection implements RedisCommands { return redisClient; } - public R await(Future cmd) { - // TODO change connectTimeout to timeout - if (!cmd.awaitUninterruptibly(redisClient.getTimeout(), TimeUnit.MILLISECONDS)) { - Promise promise = (Promise)cmd; - RedisTimeoutException ex = new RedisTimeoutException("Command execution timeout for " + redisClient.getAddr()); - promise.setFailure(ex); - throw ex; - } - if (!cmd.isSuccess()) { - if (cmd.cause() instanceof RedisException) { - throw (RedisException) cmd.cause(); + public R await(Future future) { + final CountDownLatch l = new CountDownLatch(1); + future.addListener(new FutureListener() { + @Override + public void operationComplete(Future future) throws Exception { + l.countDown(); + } + }); + + try { + // TODO change connectTimeout to timeout + if (!l.await(redisClient.getTimeout(), TimeUnit.MILLISECONDS)) { + Promise promise = (Promise)future; + RedisTimeoutException ex = new RedisTimeoutException("Command execution timeout for " + redisClient.getAddr()); + promise.setFailure(ex); + throw ex; + } + if (!future.isSuccess()) { + if (future.cause() instanceof RedisException) { + throw (RedisException) future.cause(); + } + throw new RedisException("Unexpected exception while processing command", future.cause()); } - throw new RedisException("Unexpected exception while processing command", cmd.cause()); + return future.getNow(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return null; } - return cmd.getNow(); } public T sync(RedisStrictCommand command, Object ... params) { diff --git a/src/main/java/org/redisson/client/handler/CommandDecoder.java b/src/main/java/org/redisson/client/handler/CommandDecoder.java index f0b5ce652..6e4849a00 100644 --- a/src/main/java/org/redisson/client/handler/CommandDecoder.java +++ b/src/main/java/org/redisson/client/handler/CommandDecoder.java @@ -1,5 +1,4 @@ /** - * Copyright 2014 Nikita Koksharov, Nickolay Borbit * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -173,7 +172,7 @@ public class CommandDecoder extends ReplayingDecoder { if (code == '+') { String result = in.readBytes(in.bytesBefore((byte) '\r')).toString(CharsetUtil.UTF_8); in.skipBytes(2); - + handleResult(data, parts, result, false, channel); } else if (code == '-') { String error = in.readBytes(in.bytesBefore((byte) '\r')).toString(CharsetUtil.UTF_8); @@ -206,9 +205,7 @@ public class CommandDecoder extends ReplayingDecoder { } } } else if (code == ':') { - String status = in.readBytes(in.bytesBefore((byte) '\r')).toString(CharsetUtil.UTF_8); - in.skipBytes(2); - Object result = Long.valueOf(status); + Long result = readLong(in); handleResult(data, parts, result, false, channel); } else if (code == '$') { ByteBuf buf = readBytes(in); diff --git a/src/main/java/org/redisson/client/handler/CommandsQueue.java b/src/main/java/org/redisson/client/handler/CommandsQueue.java index ff65b9d4c..96446c90e 100644 --- a/src/main/java/org/redisson/client/handler/CommandsQueue.java +++ b/src/main/java/org/redisson/client/handler/CommandsQueue.java @@ -23,10 +23,10 @@ import org.redisson.client.protocol.QueueCommand; import org.redisson.client.protocol.QueueCommandHolder; import io.netty.channel.Channel; -import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; import io.netty.util.AttributeKey; import io.netty.util.internal.PlatformDependent; @@ -37,7 +37,7 @@ import io.netty.util.internal.PlatformDependent; * @author Nikita Koksharov * */ -public class CommandsQueue extends ChannelDuplexHandler { +public class CommandsQueue extends ChannelOutboundHandlerAdapter { public static final AttributeKey CURRENT_COMMAND = AttributeKey.valueOf("promise"); diff --git a/src/main/java/org/redisson/command/CommandAsyncExecutor.java b/src/main/java/org/redisson/command/CommandAsyncExecutor.java index cddeb2a8b..a218b8f19 100644 --- a/src/main/java/org/redisson/command/CommandAsyncExecutor.java +++ b/src/main/java/org/redisson/command/CommandAsyncExecutor.java @@ -18,6 +18,7 @@ package org.redisson.command; import java.net.InetSocketAddress; import java.util.Collection; import java.util.List; +import java.util.concurrent.TimeUnit; import org.redisson.SlotCallback; import org.redisson.client.RedisException; @@ -38,6 +39,8 @@ public interface CommandAsyncExecutor { RedisException convertException(Future future); + boolean await(Future future, long timeout, TimeUnit timeoutUnit) throws InterruptedException; + V get(Future future); Future writeAsync(Integer slot, Codec codec, RedisCommand command, Object ... params); diff --git a/src/main/java/org/redisson/command/CommandAsyncService.java b/src/main/java/org/redisson/command/CommandAsyncService.java index ab0b9e18b..cf1a506a9 100644 --- a/src/main/java/org/redisson/command/CommandAsyncService.java +++ b/src/main/java/org/redisson/command/CommandAsyncService.java @@ -22,9 +22,11 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import org.redisson.RedisClientResult; import org.redisson.RedissonShutdownException; @@ -82,13 +84,38 @@ public class CommandAsyncService implements CommandAsyncExecutor { @Override public V get(Future future) { - future.awaitUninterruptibly(); + final CountDownLatch l = new CountDownLatch(1); + future.addListener(new FutureListener() { + @Override + public void operationComplete(Future future) throws Exception { + l.countDown(); + } + }); + try { + l.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + // commented out due to blocking issues up to 200 ms per minute for each thread + // future.awaitUninterruptibly(); if (future.isSuccess()) { return future.getNow(); } throw convertException(future); } + @Override + public boolean await(Future future, long timeout, TimeUnit timeoutUnit) throws InterruptedException { + final CountDownLatch l = new CountDownLatch(1); + future.addListener(new FutureListener() { + @Override + public void operationComplete(Future future) throws Exception { + l.countDown(); + } + }); + return l.await(timeout, timeoutUnit); + } + @Override public Future readAsync(InetSocketAddress client, String key, Codec codec, RedisCommand command, Object ... params) { Promise mainPromise = connectionManager.newPromise(); diff --git a/src/main/java/org/redisson/connection/pool/ConnectionPool.java b/src/main/java/org/redisson/connection/pool/ConnectionPool.java index 00c1c6886..87dc8a594 100644 --- a/src/main/java/org/redisson/connection/pool/ConnectionPool.java +++ b/src/main/java/org/redisson/connection/pool/ConnectionPool.java @@ -153,7 +153,7 @@ abstract class ConnectionPool { } } - StringBuilder errorMsg = new StringBuilder("Publish/Subscribe connection pool exhausted! All connections are busy. Try to increase Publish/Subscribe connection pool size."); + StringBuilder errorMsg = new StringBuilder("Connection pool exhausted! All connections are busy. Increase connection pool size."); // if (!freezed.isEmpty()) { // errorMsg.append(" Disconnected hosts: " + freezed); // } From 9bd6a0387bf633b3cc25ceac4f5a8a48e6f96bc2 Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 11 Apr 2016 14:22:48 +0300 Subject: [PATCH 053/120] RMap iterator fixed. #468 --- .../org/redisson/RedissonBaseMapIterator.java | 2 +- .../org/redisson/RedissonMapCacheTest.java | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/redisson/RedissonBaseMapIterator.java b/src/main/java/org/redisson/RedissonBaseMapIterator.java index 66cf04465..d493efb6d 100644 --- a/src/main/java/org/redisson/RedissonBaseMapIterator.java +++ b/src/main/java/org/redisson/RedissonBaseMapIterator.java @@ -64,7 +64,7 @@ abstract class RedissonBaseMapIterator implements Iterator { firstValues = convert(res.getMap()); } else { Map newValues = convert(res.getMap()); - if (newValues.equals(firstValues)) { + if (firstValues.entrySet().containsAll(newValues.entrySet())) { finished = true; free(firstValues); free(newValues); diff --git a/src/test/java/org/redisson/RedissonMapCacheTest.java b/src/test/java/org/redisson/RedissonMapCacheTest.java index 725ae166e..865bf7852 100644 --- a/src/test/java/org/redisson/RedissonMapCacheTest.java +++ b/src/test/java/org/redisson/RedissonMapCacheTest.java @@ -6,9 +6,12 @@ import java.io.Serializable; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.Map; +import java.util.Map.Entry; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import org.hamcrest.MatcherAssert; @@ -200,6 +203,47 @@ public class RedissonMapCacheTest extends BaseTest { Assert.assertEquals(0, cache.size()); } + @Test + public void testIteratorRemoveHighVolume() throws InterruptedException { + RMapCache map = redisson.getMapCache("simpleMap"); + for (int i = 0; i < 10000; i++) { + map.put(i, i*10); + } + + int cnt = 0; + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Entry entry = iterator.next(); + iterator.remove(); + cnt++; + } + Assert.assertEquals(10000, cnt); + assertThat(map).isEmpty(); + Assert.assertEquals(0, map.size()); + } + + @Test + public void testIteratorRandomRemoveHighVolume() throws InterruptedException { + RMapCache map = redisson.getMapCache("simpleMap"); + for (int i = 0; i < 10000; i++) { + map.put(i, i*10); + } + + int cnt = 0; + int removed = 0; + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Entry entry = iterator.next(); + if (ThreadLocalRandom.current().nextBoolean()) { + iterator.remove(); + removed++; + } + cnt++; + } + Assert.assertEquals(10000, cnt); + assertThat(map.size()).isEqualTo(cnt - removed); + } + @Test public void testClearExpire() throws InterruptedException { RMapCache cache = redisson.getMapCache("simple"); From 89312c230f2e10b4236b55143dbb17862ddbc4d9 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 12 Apr 2016 10:49:35 +0300 Subject: [PATCH 054/120] LUA arrays start index fixed --- src/main/java/org/redisson/RedissonList.java | 13 ++++++------- .../org/redisson/RedissonListMultimapValues.java | 10 +++++----- src/main/java/org/redisson/RedissonMap.java | 2 +- .../org/redisson/RedissonScoredSortedSet.java | 12 ++++++------ src/main/java/org/redisson/RedissonSet.java | 15 +++++++-------- src/main/java/org/redisson/RedissonSetCache.java | 7 +++---- .../org/redisson/RedissonSetMultimapValues.java | 14 +++++++------- src/main/java/org/redisson/RedissonSubList.java | 9 ++++----- 8 files changed, 39 insertions(+), 43 deletions(-) diff --git a/src/main/java/org/redisson/RedissonList.java b/src/main/java/org/redisson/RedissonList.java index 734ec97ad..8b2d33039 100644 --- a/src/main/java/org/redisson/RedissonList.java +++ b/src/main/java/org/redisson/RedissonList.java @@ -143,13 +143,13 @@ public class RedissonList extends RedissonExpirable implements RList { return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, "local items = redis.call('lrange', KEYS[1], 0, -1) " + "for i=1, #items do " + - "for j = 0, table.getn(ARGV), 1 do " + + "for j = 1, #ARGV, 1 do " + "if items[i] == ARGV[j] then " + "table.remove(ARGV, j) " + "end " + "end " + "end " + - "return table.getn(ARGV) == 0 and 1 or 0", + "return #ARGV == 0 and 1 or 0", Collections.singletonList(getName()), c.toArray()); } @@ -222,7 +222,7 @@ public class RedissonList extends RedissonExpirable implements RList { public Future removeAllAsync(Collection c) { return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, "local v = 0 " + - "for i = 0, table.getn(ARGV), 1 do " + "for i = 1, #ARGV, 1 do " + "if redis.call('lrem', KEYS[1], 0, ARGV[i]) == 1 " + "then v = 1 end " +"end " @@ -246,11 +246,10 @@ public class RedissonList extends RedissonExpirable implements RList { "local changed = 0 " + "local items = redis.call('lrange', KEYS[1], 0, -1) " + "local i = 1 " - + "local s = table.getn(items) " - + "while i <= s do " + + "while i <= #items do " + "local element = items[i] " + "local isInAgrs = false " - + "for j = 0, table.getn(ARGV), 1 do " + + "for j = 1, #ARGV, 1 do " + "if ARGV[j] == element then " + "isInAgrs = true " + "break " @@ -390,7 +389,7 @@ public class RedissonList extends RedissonExpirable implements RList { "local key = KEYS[1] " + "local obj = ARGV[1] " + "local items = redis.call('lrange', key, 0, -1) " + - "for i = #items, 0, -1 do " + + "for i = #items, 1, -1 do " + "if items[i] == obj then " + "return i - 1 " + "end " + diff --git a/src/main/java/org/redisson/RedissonListMultimapValues.java b/src/main/java/org/redisson/RedissonListMultimapValues.java index 1c6ed6d5f..abffc6e61 100644 --- a/src/main/java/org/redisson/RedissonListMultimapValues.java +++ b/src/main/java/org/redisson/RedissonListMultimapValues.java @@ -199,13 +199,13 @@ public class RedissonListMultimapValues extends RedissonExpirable implements + "return 0;" + "end; " + "local items = redis.call('lrange', KEYS[2], 0, -1);" + - "for i = 0, #items, 1 do " + - "for j = 2, table.getn(ARGV), 1 do " + "for i = 1, #items, 1 do " + + "for j = 2, #ARGV, 1 do " + "if ARGV[j] == items[i] " + "then table.remove(ARGV, j) end " + "end; " + "end;" - + "return table.getn(ARGV) == 2 and 1 or 0; ", + + "return #ARGV == 2 and 1 or 0; ", Arrays.asList(timeoutSetName, getName()), args.toArray()); } @@ -340,7 +340,7 @@ public class RedissonListMultimapValues extends RedissonExpirable implements "local changed = 0; " + "local s = redis.call('lrange', KEYS[2], 0, -1); " - + "local i = 0; " + + "local i = 1; " + "while i <= #s do " + "local element = s[i]; " + "local isInAgrs = false; " @@ -508,7 +508,7 @@ public class RedissonListMultimapValues extends RedissonExpirable implements + "end; " + "local items = redis.call('lrange', KEYS[1], 0, -1) " + - "for i = #items, 0, -1 do " + + "for i = #items, 1, -1 do " + "if items[i] == ARGV[1] then " + "return i - 1 " + "end " + diff --git a/src/main/java/org/redisson/RedissonMap.java b/src/main/java/org/redisson/RedissonMap.java index cb8c8b54a..e72d4b074 100644 --- a/src/main/java/org/redisson/RedissonMap.java +++ b/src/main/java/org/redisson/RedissonMap.java @@ -107,7 +107,7 @@ public class RedissonMap extends RedissonExpirable implements RMap { public Future containsValueAsync(Object value) { return commandExecutor.evalReadAsync(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 4), "local s = redis.call('hvals', KEYS[1]);" + - "for i = 0, table.getn(s), 1 do " + "for i = 1, #s, 1 do " + "if ARGV[1] == s[i] then " + "return 1 " + "end " diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index 188460143..01875ae2b 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -280,13 +280,13 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc public Future containsAllAsync(Collection c) { return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, "local s = redis.call('zrange', KEYS[1], 0, -1);" + - "for i = 0, table.getn(s), 1 do " + - "for j = 0, table.getn(ARGV), 1 do " + "for i = 1, #s, 1 do " + + "for j = 1, #ARGV, 1 do " + "if ARGV[j] == s[i] " + "then table.remove(ARGV, j) end " + "end; " + "end;" - + "return table.getn(ARGV) == 0 and 1 or 0; ", + + "return #ARGV == 0 and 1 or 0; ", Collections.singletonList(getName()), c.toArray()); } @@ -316,11 +316,11 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, "local changed = 0 " + "local s = redis.call('zrange', KEYS[1], 0, -1) " - + "local i = 0 " - + "while i <= table.getn(s) do " + + "local i = 1 " + + "while i <= #s do " + "local element = s[i] " + "local isInAgrs = false " - + "for j = 0, table.getn(ARGV), 1 do " + + "for j = 1, #ARGV, 1 do " + "if ARGV[j] == element then " + "isInAgrs = true " + "break " diff --git a/src/main/java/org/redisson/RedissonSet.java b/src/main/java/org/redisson/RedissonSet.java index 2afeeb563..d2e728195 100644 --- a/src/main/java/org/redisson/RedissonSet.java +++ b/src/main/java/org/redisson/RedissonSet.java @@ -22,7 +22,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.NoSuchElementException; import java.util.Set; import org.redisson.client.codec.Codec; @@ -168,13 +167,13 @@ public class RedissonSet extends RedissonExpirable implements RSet { public Future containsAllAsync(Collection c) { return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, "local s = redis.call('smembers', KEYS[1]);" + - "for i = 0, table.getn(s), 1 do " + - "for j = 0, table.getn(ARGV), 1 do " + "for i = 1, #s, 1 do " + + "for j = 1, #ARGV, 1 do " + "if ARGV[j] == s[i] " + "then table.remove(ARGV, j) end " + "end; " + "end;" - + "return table.getn(ARGV) == 0 and 1 or 0; ", + + "return #ARGV == 0 and 1 or 0; ", Collections.singletonList(getName()), c.toArray()); } @@ -205,11 +204,11 @@ public class RedissonSet extends RedissonExpirable implements RSet { return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, "local changed = 0 " + "local s = redis.call('smembers', KEYS[1]) " - + "local i = 0 " - + "while i <= table.getn(s) do " + + "local i = 1 " + + "while i <= #s do " + "local element = s[i] " + "local isInAgrs = false " - + "for j = 0, table.getn(ARGV), 1 do " + + "for j = 1, #ARGV, 1 do " + "if ARGV[j] == element then " + "isInAgrs = true " + "break " @@ -229,7 +228,7 @@ public class RedissonSet extends RedissonExpirable implements RSet { public Future removeAllAsync(Collection c) { return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, "local v = 0 " + - "for i = 0, table.getn(ARGV), 1 do " + "for i = 1, #ARGV, 1 do " + "if redis.call('srem', KEYS[1], ARGV[i]) == 1 " + "then v = 1 end " +"end " diff --git a/src/main/java/org/redisson/RedissonSetCache.java b/src/main/java/org/redisson/RedissonSetCache.java index 124fbfbc9..a4c1e13b6 100644 --- a/src/main/java/org/redisson/RedissonSetCache.java +++ b/src/main/java/org/redisson/RedissonSetCache.java @@ -23,7 +23,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -336,14 +335,14 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< public Future containsAllAsync(Collection c) { return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, "local s = redis.call('hvals', KEYS[1]);" + - "for i = 0, table.getn(s), 1 do " + - "for j = 0, table.getn(ARGV), 1 do " + "for i = 1, #s, 1 do " + + "for j = 1, #ARGV, 1 do " + "if ARGV[j] == s[i] then " + "table.remove(ARGV, j) " + "end " + "end; " + "end;" - + "return table.getn(ARGV) == 0 and 1 or 0; ", + + "return #ARGV == 0 and 1 or 0; ", Collections.singletonList(getName()), c.toArray()); } diff --git a/src/main/java/org/redisson/RedissonSetMultimapValues.java b/src/main/java/org/redisson/RedissonSetMultimapValues.java index 36d0fefee..c3546e05e 100644 --- a/src/main/java/org/redisson/RedissonSetMultimapValues.java +++ b/src/main/java/org/redisson/RedissonSetMultimapValues.java @@ -251,13 +251,13 @@ public class RedissonSetMultimapValues extends RedissonExpirable implements R + "return 0;" + "end; " + "local s = redis.call('smembers', KEYS[2]);" + - "for i = 0, table.getn(s), 1 do " + - "for j = 2, table.getn(ARGV), 1 do " + "for i = 1, #s, 1 do " + + "for j = 2, #ARGV, 1 do " + "if ARGV[j] == s[i] " + "then table.remove(ARGV, j) end " + "end; " + "end;" - + "return table.getn(ARGV) == 2 and 1 or 0; ", + + "return #ARGV == 2 and 1 or 0; ", Arrays.asList(timeoutSetName, getName()), args.toArray()); } @@ -307,11 +307,11 @@ public class RedissonSetMultimapValues extends RedissonExpirable implements R "local changed = 0 " + "local s = redis.call('smembers', KEYS[2]) " - + "local i = 0 " - + "while i <= table.getn(s) do " + + "local i = 1 " + + "while i <= #s do " + "local element = s[i] " + "local isInAgrs = false " - + "for j = 2, table.getn(ARGV), 1 do " + + "for j = 2, #ARGV, 1 do " + "if ARGV[j] == element then " + "isInAgrs = true " + "break " @@ -350,7 +350,7 @@ public class RedissonSetMultimapValues extends RedissonExpirable implements R + "end; " + "local v = 0 " + - "for i = 2, table.getn(ARGV), 1 do " + "for i = 2, #ARGV, 1 do " + "if redis.call('srem', KEYS[2], ARGV[i]) == 1 " + "then v = 1 end " +"end " diff --git a/src/main/java/org/redisson/RedissonSubList.java b/src/main/java/org/redisson/RedissonSubList.java index 3306d83c3..759200eef 100644 --- a/src/main/java/org/redisson/RedissonSubList.java +++ b/src/main/java/org/redisson/RedissonSubList.java @@ -104,13 +104,13 @@ public class RedissonSubList extends RedissonList implements RList { "local toIndex = table.remove(ARGV, 2);" + "local items = redis.call('lrange', KEYS[1], tonumber(fromIndex), tonumber(toIndex)) " + "for i=1, #items do " + - "for j = 0, #ARGV, 1 do " + + "for j = 1, #ARGV, 1 do " + "if items[i] == ARGV[j] then " + "table.remove(ARGV, j) " + "end " + "end " + "end " + - "return table.getn(ARGV) == 0 and 1 or 0", + "return #ARGV == 0 and 1 or 0", Collections.singletonList(getName()), params.toArray()); } @@ -203,11 +203,10 @@ public class RedissonSubList extends RedissonList implements RList { "local toIndex = table.remove(ARGV, 2);" + "local items = redis.call('lrange', KEYS[1], fromIndex, toIndex) " + "local i = 1 " - + "local s = table.getn(items) " - + "while i <= s do " + + "while i <= #items do " + "local element = items[i] " + "local isInAgrs = false " - + "for j = 0, table.getn(ARGV), 1 do " + + "for j = 1, #ARGV, 1 do " + "if ARGV[j] == element then " + "isInAgrs = true " + "break " From e45d370404cbd000e932f26c701bae40c963eabf Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 12 Apr 2016 14:05:11 +0300 Subject: [PATCH 055/120] MultiDecoder empty result handling fixed --- src/main/java/org/redisson/RedissonGeo.java | 21 +++++++++++-------- .../decoder/FlatNestedMultiDecoder.java | 4 ++++ .../protocol/decoder/NestedMultiDecoder.java | 16 ++++++++++---- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/redisson/RedissonGeo.java b/src/main/java/org/redisson/RedissonGeo.java index 7a62e165c..527892d25 100644 --- a/src/main/java/org/redisson/RedissonGeo.java +++ b/src/main/java/org/redisson/RedissonGeo.java @@ -45,12 +45,19 @@ import io.netty.util.concurrent.Future; public class RedissonGeo extends RedissonExpirable implements RGeo { + MultiDecoder> postitionDecoder; + MultiDecoder> distanceDecoder; + public RedissonGeo(CommandAsyncExecutor connectionManager, String name) { super(connectionManager, name); + postitionDecoder = new NestedMultiDecoder(new GeoPositionDecoder(), new GeoDistanceDecoder(codec), new GeoMapReplayDecoder(), true); + distanceDecoder = new FlatNestedMultiDecoder(new GeoDistanceDecoder(codec), new GeoMapReplayDecoder(), true); } public RedissonGeo(Codec codec, CommandAsyncExecutor connectionManager, String name) { super(codec, connectionManager, name); + postitionDecoder = new NestedMultiDecoder(new GeoPositionDecoder(), new GeoDistanceDecoder(codec), new GeoMapReplayDecoder(), true); + distanceDecoder = new FlatNestedMultiDecoder(new GeoDistanceDecoder(codec), new GeoMapReplayDecoder(), true); } @Override @@ -119,7 +126,7 @@ public class RedissonGeo extends RedissonExpirable implements RGeo { params.add(getName()); params.addAll(Arrays.asList(members)); - MultiDecoder> decoder = new NestedMultiDecoder(new GeoPositionDecoder(), new GeoPositionMapDecoder(params)); + MultiDecoder> decoder = new NestedMultiDecoder(new GeoPositionDecoder(), new GeoPositionMapDecoder(params), true); RedisCommand> command = new RedisCommand>("GEOPOS", decoder, 2, ValueType.OBJECTS); return commandExecutor.readAsync(getName(), new ScoredCodec(codec), command, params.toArray()); } @@ -141,8 +148,7 @@ public class RedissonGeo extends RedissonExpirable implements RGeo { @Override public Future> radiusWithDistanceAsync(double longitude, double latitude, double radius, GeoUnit geoUnit) { - MultiDecoder> decoder = new FlatNestedMultiDecoder(new GeoDistanceDecoder(codec), new GeoMapReplayDecoder()); - RedisCommand> command = new RedisCommand>("GEORADIUS", decoder); + RedisCommand> command = new RedisCommand>("GEORADIUS", distanceDecoder); return commandExecutor.readAsync(getName(), codec, command, getName(), convert(longitude), convert(latitude), radius, geoUnit, "WITHDIST"); } @@ -153,8 +159,7 @@ public class RedissonGeo extends RedissonExpirable implements RGeo { @Override public Future> radiusWithPositionAsync(double longitude, double latitude, double radius, GeoUnit geoUnit) { - MultiDecoder> decoder = new NestedMultiDecoder(new GeoPositionDecoder(), new GeoDistanceDecoder(codec), new GeoMapReplayDecoder()); - RedisCommand> command = new RedisCommand>("GEORADIUS", decoder); + RedisCommand> command = new RedisCommand>("GEORADIUS", postitionDecoder); return commandExecutor.readAsync(getName(), codec, command, getName(), convert(longitude), convert(latitude), radius, geoUnit, "WITHCOORD"); } @@ -175,8 +180,7 @@ public class RedissonGeo extends RedissonExpirable implements RGeo { @Override public Future> radiusWithDistanceAsync(V member, double radius, GeoUnit geoUnit) { - MultiDecoder> decoder = new FlatNestedMultiDecoder(new GeoDistanceDecoder(codec), new GeoMapReplayDecoder()); - RedisCommand command = new RedisCommand("GEORADIUSBYMEMBER", decoder, 2); + RedisCommand command = new RedisCommand("GEORADIUSBYMEMBER", distanceDecoder, 2); return commandExecutor.readAsync(getName(), codec, command, getName(), member, radius, geoUnit, "WITHDIST"); } @@ -187,8 +191,7 @@ public class RedissonGeo extends RedissonExpirable implements RGeo { @Override public Future> radiusWithPositionAsync(V member, double radius, GeoUnit geoUnit) { - MultiDecoder> decoder = new NestedMultiDecoder(new GeoPositionDecoder(), new GeoDistanceDecoder(codec), new GeoMapReplayDecoder()); - RedisCommand> command = new RedisCommand>("GEORADIUSBYMEMBER", decoder, 2); + RedisCommand> command = new RedisCommand>("GEORADIUSBYMEMBER", postitionDecoder, 2); return commandExecutor.readAsync(getName(), codec, command, getName(), member, radius, geoUnit, "WITHCOORD"); } } diff --git a/src/main/java/org/redisson/client/protocol/decoder/FlatNestedMultiDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/FlatNestedMultiDecoder.java index 413694b33..4bd04c8ee 100644 --- a/src/main/java/org/redisson/client/protocol/decoder/FlatNestedMultiDecoder.java +++ b/src/main/java/org/redisson/client/protocol/decoder/FlatNestedMultiDecoder.java @@ -27,6 +27,10 @@ public class FlatNestedMultiDecoder extends NestedMultiDecoder { super(firstDecoder, secondDecoder); } + public FlatNestedMultiDecoder(MultiDecoder firstDecoder, MultiDecoder secondDecoder, boolean handleEmpty) { + super(firstDecoder, secondDecoder, handleEmpty); + } + @Override public Object decode(ByteBuf buf, State state) throws IOException { return firstDecoder.decode(buf, state); diff --git a/src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder.java index c2a5f5fdd..43b5266ac 100644 --- a/src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder.java +++ b/src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder.java @@ -58,19 +58,27 @@ public class NestedMultiDecoder implements MultiDecoder { protected final MultiDecoder firstDecoder; protected final MultiDecoder secondDecoder; private MultiDecoder thirdDecoder; + private boolean handleEmpty; public NestedMultiDecoder(MultiDecoder firstDecoder, MultiDecoder secondDecoder) { - this.firstDecoder = firstDecoder; - this.secondDecoder = secondDecoder; + this(firstDecoder, secondDecoder, false); + } + + public NestedMultiDecoder(MultiDecoder firstDecoder, MultiDecoder secondDecoder, boolean handleEmpty) { + this(firstDecoder, secondDecoder, null, handleEmpty); } public NestedMultiDecoder(MultiDecoder firstDecoder, MultiDecoder secondDecoder, MultiDecoder thirdDecoder) { + this(firstDecoder, secondDecoder, thirdDecoder, false); + } + + public NestedMultiDecoder(MultiDecoder firstDecoder, MultiDecoder secondDecoder, MultiDecoder thirdDecoder, boolean handleEmpty) { this.firstDecoder = firstDecoder; this.secondDecoder = secondDecoder; this.thirdDecoder = thirdDecoder; + this.handleEmpty = handleEmpty; } - @Override public Object decode(ByteBuf buf, State state) throws IOException { DecoderState ds = getDecoder(state); @@ -121,7 +129,7 @@ public class NestedMultiDecoder implements MultiDecoder { @Override public Object decode(List parts, State state) { - if (parts.isEmpty() && state.getDecoderState() == null) { + if (parts.isEmpty() && state.getDecoderState() == null && handleEmpty) { MultiDecoder decoder = secondDecoder; if (thirdDecoder != null) { decoder = thirdDecoder; From 11eaf5d62e7996de8a26d1326c2fbb166d75c19f Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 12 Apr 2016 14:12:13 +0300 Subject: [PATCH 056/120] License header fixed --- src/main/java/org/redisson/client/handler/CommandDecoder.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/redisson/client/handler/CommandDecoder.java b/src/main/java/org/redisson/client/handler/CommandDecoder.java index 6e4849a00..64113ad5d 100644 --- a/src/main/java/org/redisson/client/handler/CommandDecoder.java +++ b/src/main/java/org/redisson/client/handler/CommandDecoder.java @@ -1,4 +1,5 @@ /** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From de27fd005e9fa801e8265f56b0d2d41faad26b69 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Tue, 12 Apr 2016 21:32:20 +0100 Subject: [PATCH 057/120] A few more LUA array index started at 0 --- src/main/java/org/redisson/RedissonListMultimapCache.java | 2 +- src/main/java/org/redisson/RedissonSubList.java | 2 +- .../reactive/RedissonScoredSortedSetReactive.java | 8 ++++---- .../org/redisson/reactive/RedissonSetCacheReactive.java | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/redisson/RedissonListMultimapCache.java b/src/main/java/org/redisson/RedissonListMultimapCache.java index 2ba95e527..828ff6e14 100644 --- a/src/main/java/org/redisson/RedissonListMultimapCache.java +++ b/src/main/java/org/redisson/RedissonListMultimapCache.java @@ -131,7 +131,7 @@ public class RedissonListMultimapCache extends RedissonListMultimap + "end; " + "if expireDate > tonumber(ARGV[1]) then " + "local items = redis.call('lrange', KEYS[1], 0, -1); " + - "for i=0, #items do " + + "for i = 1, #items do " + "if items[i] == ARGV[3] then " + "return 1; " + "end; " + diff --git a/src/main/java/org/redisson/RedissonSubList.java b/src/main/java/org/redisson/RedissonSubList.java index 759200eef..4e474bb7d 100644 --- a/src/main/java/org/redisson/RedissonSubList.java +++ b/src/main/java/org/redisson/RedissonSubList.java @@ -179,7 +179,7 @@ public class RedissonSubList extends RedissonList implements RList { "local items = redis.call('lrange', KEYS[1], fromIndex, toIndex); " + "for i=1, #items do " + - "for j = 0, #ARGV, 1 do " + + "for j = 1, #ARGV, 1 do " + "if items[i] == ARGV[j] then " + "redis.call('lrem', KEYS[1], count, ARGV[i]); " + "v = 1; " + diff --git a/src/main/java/org/redisson/reactive/RedissonScoredSortedSetReactive.java b/src/main/java/org/redisson/reactive/RedissonScoredSortedSetReactive.java index 50af3b9a4..f72152841 100644 --- a/src/main/java/org/redisson/reactive/RedissonScoredSortedSetReactive.java +++ b/src/main/java/org/redisson/reactive/RedissonScoredSortedSetReactive.java @@ -140,8 +140,8 @@ public class RedissonScoredSortedSetReactive extends RedissonExpirableReactiv public Publisher containsAll(Collection c) { return commandExecutor.evalReadReactive(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 4, ValueType.OBJECTS), "local s = redis.call('zrange', KEYS[1], 0, -1);" + - "for i = 0, table.getn(s), 1 do " + - "for j = 0, table.getn(ARGV), 1 do " + "for i = 1, table.getn(s), 1 do " + + "for j = 1, table.getn(ARGV), 1 do " + "if ARGV[j] == s[i] " + "then table.remove(ARGV, j) end " + "end; " @@ -154,7 +154,7 @@ public class RedissonScoredSortedSetReactive extends RedissonExpirableReactiv public Publisher removeAll(Collection c) { return commandExecutor.evalWriteReactive(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 4, ValueType.OBJECTS), "local v = 0 " + - "for i = 0, table.getn(ARGV), 1 do " + "for i = 1, table.getn(ARGV), 1 do " + "if redis.call('zrem', KEYS[1], ARGV[i]) == 1 " + "then v = 1 end " +"end " @@ -171,7 +171,7 @@ public class RedissonScoredSortedSetReactive extends RedissonExpirableReactiv + "while i <= table.getn(s) do " + "local element = s[i] " + "local isInAgrs = false " - + "for j = 0, table.getn(ARGV), 1 do " + + "for j = 1, table.getn(ARGV), 1 do " + "if ARGV[j] == element then " + "isInAgrs = true " + "break " diff --git a/src/main/java/org/redisson/reactive/RedissonSetCacheReactive.java b/src/main/java/org/redisson/reactive/RedissonSetCacheReactive.java index bce238a28..dc768e88a 100644 --- a/src/main/java/org/redisson/reactive/RedissonSetCacheReactive.java +++ b/src/main/java/org/redisson/reactive/RedissonSetCacheReactive.java @@ -224,8 +224,8 @@ public class RedissonSetCacheReactive extends RedissonExpirableReactive imple public Publisher containsAll(Collection c) { return commandExecutor.evalReadReactive(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, "local s = redis.call('hvals', KEYS[1]);" + - "for i = 0, table.getn(s), 1 do " + - "for j = 0, table.getn(ARGV), 1 do " + "for i = 1, table.getn(s), 1 do " + + "for j = 1, table.getn(ARGV), 1 do " + "if ARGV[j] == s[i] then " + "table.remove(ARGV, j) " + "end " From 5274e762b59811997147b3b42a11fb20418c1306 Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 13 Apr 2016 14:40:37 +0300 Subject: [PATCH 058/120] RedissonMultiLock deadlock fixed. #467 --- src/main/java/org/redisson/RedissonLock.java | 29 +++++-- .../org/redisson/core/RedissonMultiLock.java | 85 +++++++++++-------- .../org/redisson/RedissonMultiLockTest.java | 35 ++++++++ 3 files changed, 110 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/redisson/RedissonLock.java b/src/main/java/org/redisson/RedissonLock.java index eaf060496..96ad9ac74 100644 --- a/src/main/java/org/redisson/RedissonLock.java +++ b/src/main/java/org/redisson/RedissonLock.java @@ -377,6 +377,11 @@ public class RedissonLock extends RedissonExpirable implements RLock { } public Future unlockAsync() { + long threadId = Thread.currentThread().getId(); + return unlockAsync(threadId); + } + + public Future unlockAsync(final long threadId) { final Promise result = newPromise(); Future future = commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, "if (redis.call('exists', KEYS[1]) == 0) then " + @@ -396,7 +401,7 @@ public class RedissonLock extends RedissonExpirable implements RLock { "return 1; "+ "end; " + "return nil;", - Arrays.asList(getName(), getChannelName()), LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(Thread.currentThread().getId())); + Arrays.asList(getName(), getChannelName()), LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(threadId)); future.addListener(new FutureListener() { @Override @@ -409,7 +414,7 @@ public class RedissonLock extends RedissonExpirable implements RLock { Boolean opStatus = future.getNow(); if (opStatus == null) { IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: " - + id + " thread-id: " + Thread.currentThread().getId()); + + id + " thread-id: " + threadId); result.setFailure(cause); return; } @@ -428,8 +433,12 @@ public class RedissonLock extends RedissonExpirable implements RLock { } public Future lockAsync(final long leaseTime, final TimeUnit unit) { - final Promise result = newPromise(); final long currentThreadId = Thread.currentThread().getId(); + return lockAsync(leaseTime, unit, currentThreadId); + } + + public Future lockAsync(final long leaseTime, final TimeUnit unit, final long currentThreadId) { + final Promise result = newPromise(); Future ttlFuture = tryAcquireAsync(leaseTime, unit, currentThreadId); ttlFuture.addListener(new FutureListener() { @Override @@ -525,8 +534,13 @@ public class RedissonLock extends RedissonExpirable implements RLock { } public Future tryLockAsync() { + long threadId = Thread.currentThread().getId(); + return tryLockAsync(threadId); + } + + public Future tryLockAsync(long threadId) { final Promise result = newPromise(); - Future future = tryLockInnerAsync(Thread.currentThread().getId()); + Future future = tryLockInnerAsync(threadId); future.addListener(new FutureListener() { @Override public void operationComplete(Future future) throws Exception { @@ -546,10 +560,15 @@ public class RedissonLock extends RedissonExpirable implements RLock { } public Future tryLockAsync(final long waitTime, final long leaseTime, final TimeUnit unit) { + final long currentThreadId = Thread.currentThread().getId(); + return tryLockAsync(waitTime, leaseTime, unit, currentThreadId); + } + + public Future tryLockAsync(final long waitTime, final long leaseTime, final TimeUnit unit, + final long currentThreadId) { final Promise result = newPromise(); final AtomicLong time = new AtomicLong(unit.toMillis(waitTime)); - final long currentThreadId = Thread.currentThread().getId(); Future ttlFuture = tryAcquireAsync(leaseTime, unit, currentThreadId); ttlFuture.addListener(new FutureListener() { @Override diff --git a/src/main/java/org/redisson/core/RedissonMultiLock.java b/src/main/java/org/redisson/core/RedissonMultiLock.java index 83ee1a785..9da348738 100644 --- a/src/main/java/org/redisson/core/RedissonMultiLock.java +++ b/src/main/java/org/redisson/core/RedissonMultiLock.java @@ -21,11 +21,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; +import org.redisson.RedissonLock; + import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.ImmediateEventExecutor; @@ -74,7 +76,8 @@ public class RedissonMultiLock implements Lock { public void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException { Promise promise = ImmediateEventExecutor.INSTANCE.newPromise(); - lock(promise, 0, leaseTime, unit); + long currentThreadId = Thread.currentThread().getId(); + lock(promise, 0, leaseTime, unit, locks, currentThreadId); promise.sync(); } @@ -84,50 +87,62 @@ public class RedissonMultiLock implements Lock { lockInterruptibly(-1, null); } - private void lock(final Promise promise, final long waitTime, final long leaseTime, final TimeUnit unit) throws InterruptedException { + private void lock(final Promise promise, final long waitTime, final long leaseTime, final TimeUnit unit, final List locks, final long currentThreadId) throws InterruptedException { final AtomicInteger tryLockRequestsAmount = new AtomicInteger(); final Map, RLock> tryLockFutures = new HashMap, RLock>(locks.size()); FutureListener listener = new FutureListener() { - AtomicBoolean unlock = new AtomicBoolean(); + AtomicReference lockedLockHolder = new AtomicReference(); + AtomicReference failed = new AtomicReference(); @Override - public void operationComplete(Future future) throws Exception { + public void operationComplete(final Future future) throws Exception { if (!future.isSuccess()) { - // unlock once - if (unlock.compareAndSet(false, true)) { - for (RLock lock : locks) { - lock.unlockAsync(); - } - - promise.setFailure(future.cause()); - } - return; + failed.compareAndSet(null, future.cause()); } Boolean res = future.getNow(); - // unlock once - if (!res && unlock.compareAndSet(false, true)) { - for (RLock lock : locks) { - lock.unlockAsync(); + if (res != null && !res) { + RLock lock = tryLockFutures.get(future); + lockedLockHolder.compareAndSet(null, lock); + } + + if (tryLockRequestsAmount.decrementAndGet() == 0) { + if (lockedLockHolder.get() == null && failed.get() == null) { + promise.setSuccess(null); + return; } - RLock lock = tryLockFutures.get(future); - lock.lockAsync().addListener(new FutureListener() { - @Override - public void operationComplete(Future future) throws Exception { - if (!future.isSuccess()) { - promise.setFailure(future.cause()); - return; + tryLockRequestsAmount.set(tryLockFutures.size()); + for (RLock lock : tryLockFutures.values()) { + lock.unlockAsync().addListener(new FutureListener() { + @Override + public void operationComplete(Future future) throws Exception { + if (tryLockRequestsAmount.decrementAndGet() == 0) { + if (failed.get() != null) { + promise.setFailure(failed.get()); + } + if (lockedLockHolder.get() != null) { + final RedissonLock lockedLock = (RedissonLock) lockedLockHolder.get(); + lockedLock.lockAsync(leaseTime, unit, currentThreadId).addListener(new FutureListener() { + @Override + public void operationComplete(Future future) throws Exception { + if (!future.isSuccess()) { + promise.setFailure(future.cause()); + return; + } + + List newLocks = new ArrayList(tryLockFutures.values()); + newLocks.remove(lockedLock); + lock(promise, waitTime, leaseTime, unit, newLocks, currentThreadId); + } + }); + } + } } - - lock(promise, waitTime, leaseTime, unit); - } - }); - } - if (!unlock.get() && tryLockRequestsAmount.decrementAndGet() == 0) { - promise.setSuccess(null); + }); + } } } }; @@ -138,11 +153,13 @@ public class RedissonMultiLock implements Lock { } tryLockRequestsAmount.incrementAndGet(); + Future future; if (waitTime > 0 || leaseTime > 0) { - tryLockFutures.put(lock.tryLockAsync(waitTime, leaseTime, unit), lock); + future = ((RedissonLock)lock).tryLockAsync(waitTime, leaseTime, unit, currentThreadId); } else { - tryLockFutures.put(lock.tryLockAsync(), lock); + future = ((RedissonLock)lock).tryLockAsync(currentThreadId); } + tryLockFutures.put(future, lock); } for (Future future : tryLockFutures.keySet()) { diff --git a/src/test/java/org/redisson/RedissonMultiLockTest.java b/src/test/java/org/redisson/RedissonMultiLockTest.java index 9b5446a80..9d8c1399a 100644 --- a/src/test/java/org/redisson/RedissonMultiLockTest.java +++ b/src/test/java/org/redisson/RedissonMultiLockTest.java @@ -16,6 +16,41 @@ import org.redisson.RedisRunner.RedisProcess; public class RedissonMultiLockTest { + @Test + public void testMultiThreads() throws IOException, InterruptedException { + RedisProcess redis1 = redisTestMultilockInstance1(); + + Config config1 = new Config(); + config1.useSingleServer().setAddress("127.0.0.1:6320"); + RedissonClient client = Redisson.create(config1); + + RLock lock1 = client.getLock("lock1"); + RLock lock2 = client.getLock("lock2"); + RLock lock3 = client.getLock("lock3"); + + Thread t = new Thread() { + public void run() { + RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3); + lock.lock(); + + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + } + + lock.unlock(); + }; + }; + t.start(); + t.join(1000); + + RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3); + lock.lock(); + lock.unlock(); + + assertThat(redis1.stop()).isEqualTo(0); + } + @Test public void test() throws IOException, InterruptedException { RedisProcess redis1 = redisTestMultilockInstance1(); From 1fc619fdfd81f3345bbd1705f47c896f7221fb5b Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 13 Apr 2016 14:42:29 +0300 Subject: [PATCH 059/120] minor fix --- src/main/java/org/redisson/core/RedissonMultiLock.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/redisson/core/RedissonMultiLock.java b/src/main/java/org/redisson/core/RedissonMultiLock.java index 9da348738..ad1465dd8 100644 --- a/src/main/java/org/redisson/core/RedissonMultiLock.java +++ b/src/main/java/org/redisson/core/RedissonMultiLock.java @@ -122,8 +122,7 @@ public class RedissonMultiLock implements Lock { if (tryLockRequestsAmount.decrementAndGet() == 0) { if (failed.get() != null) { promise.setFailure(failed.get()); - } - if (lockedLockHolder.get() != null) { + } else if (lockedLockHolder.get() != null) { final RedissonLock lockedLock = (RedissonLock) lockedLockHolder.get(); lockedLock.lockAsync(leaseTime, unit, currentThreadId).addListener(new FutureListener() { @Override From 1df07fa9da9668dbdd8bebbda7f37e261138aed0 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Wed, 13 Apr 2016 21:46:41 +0100 Subject: [PATCH 060/120] one more index start fix --- .../org/redisson/reactive/RedissonScoredSortedSetReactive.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/redisson/reactive/RedissonScoredSortedSetReactive.java b/src/main/java/org/redisson/reactive/RedissonScoredSortedSetReactive.java index f72152841..26a935c50 100644 --- a/src/main/java/org/redisson/reactive/RedissonScoredSortedSetReactive.java +++ b/src/main/java/org/redisson/reactive/RedissonScoredSortedSetReactive.java @@ -167,7 +167,7 @@ public class RedissonScoredSortedSetReactive extends RedissonExpirableReactiv return commandExecutor.evalWriteReactive(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 4, ValueType.OBJECTS), "local changed = 0 " + "local s = redis.call('zrange', KEYS[1], 0, -1) " - + "local i = 0 " + + "local i = 1 " + "while i <= table.getn(s) do " + "local element = s[i] " + "local isInAgrs = false " From abca09e5b7a5f71cb0618deb9fe3598bee870699 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 15 Apr 2016 16:48:16 +0300 Subject: [PATCH 061/120] Replaying phase handling in CommandDecoder --- .../client/handler/CommandDecoder.java | 188 +++++++++++------- .../org/redisson/client/handler/State.java | 80 +++++--- .../redisson/client/handler/StateLevel.java | 29 +++ .../client/protocol/decoder/DecoderState.java | 7 + .../decoder/FlatNestedMultiDecoder.java | 2 +- .../protocol/decoder/NestedMultiDecoder.java | 32 ++- .../decoder/CacheGetAllDecoder.java | 4 +- .../java/org/redisson/RedissonGeoTest.java | 56 ++++++ .../org/redisson/RedissonSetCacheTest.java | 17 +- 9 files changed, 306 insertions(+), 109 deletions(-) create mode 100644 src/main/java/org/redisson/client/handler/StateLevel.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/DecoderState.java diff --git a/src/main/java/org/redisson/client/handler/CommandDecoder.java b/src/main/java/org/redisson/client/handler/CommandDecoder.java index 64113ad5d..d9eb1c1d2 100644 --- a/src/main/java/org/redisson/client/handler/CommandDecoder.java +++ b/src/main/java/org/redisson/client/handler/CommandDecoder.java @@ -36,6 +36,7 @@ import org.redisson.client.protocol.Decoder; import org.redisson.client.protocol.QueueCommand; import org.redisson.client.protocol.RedisCommand.ValueType; import org.redisson.client.protocol.decoder.MultiDecoder; +import org.redisson.client.protocol.decoder.NestedMultiDecoder; import org.redisson.client.protocol.pubsub.Message; import org.redisson.client.protocol.pubsub.PubSubMessage; import org.redisson.client.protocol.pubsub.PubSubPatternMessage; @@ -79,61 +80,99 @@ public class CommandDecoder extends ReplayingDecoder { protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { QueueCommand data = ctx.channel().attr(CommandsQueue.CURRENT_COMMAND).get(); - Decoder currentDecoder = null; - if (data == null) { - currentDecoder = StringCodec.INSTANCE.getValueDecoder(); + if (log.isTraceEnabled()) { + log.trace("channel: {} message: {}", ctx.channel(), in.toString(0, in.writerIndex(), CharsetUtil.UTF_8)); } - if (state() == null) { - state(new State()); - - if (log.isTraceEnabled()) { - log.trace("channel: {} message: {}", ctx.channel(), in.toString(0, in.writerIndex(), CharsetUtil.UTF_8)); + boolean makeCheckpoint = data != null; + if (data != null) { + if (data instanceof CommandsData) { + makeCheckpoint = false; + } else { + CommandData cmd = (CommandData)data; + if (cmd.getCommand().getReplayMultiDecoder() != null && NestedMultiDecoder.class.isAssignableFrom(cmd.getCommand().getReplayMultiDecoder().getClass())) { + makeCheckpoint = false; + } + } } + state(new State(makeCheckpoint)); } + state().setDecoderState(null); if (data == null) { - decode(in, null, null, ctx.channel(), currentDecoder); + decode(in, null, null, ctx.channel()); } else if (data instanceof CommandData) { CommandData cmd = (CommandData)data; try { -// if (state().getSize() > 0) { -// List respParts = new ArrayList(); -// if (state().getRespParts() != null) { -// respParts = state().getRespParts(); -// } -// decodeMulti(in, cmd, null, ctx.channel(), currentDecoder, state().getSize(), respParts, true); -// } else { - decode(in, cmd, null, ctx.channel(), currentDecoder); -// } + if (state().getLevels().size() > 0) { + decodeFromCheckpoint(ctx, in, data, cmd); + } else { + decode(in, cmd, null, ctx.channel()); + } } catch (IOException e) { cmd.getPromise().tryFailure(e); } } else if (data instanceof CommandsData) { CommandsData commands = (CommandsData)data; - handleCommandsDataResponse(ctx, in, data, currentDecoder, commands); + decodeCommandBatch(ctx, in, data, commands); return; } - + ctx.pipeline().get(CommandsQueue.class).sendNextCommand(ctx.channel()); state(null); } - private void handleCommandsDataResponse(ChannelHandlerContext ctx, ByteBuf in, QueueCommand data, - Decoder currentDecoder, CommandsData commands) { - int i = state().getIndex(); + private void decodeFromCheckpoint(ChannelHandlerContext ctx, ByteBuf in, QueueCommand data, + CommandData cmd) throws IOException { + if (state().getLevels().size() == 2) { + StateLevel secondLevel = state().getLevels().get(1); + + if (secondLevel.getParts().isEmpty()) { + state().getLevels().remove(1); + } + } + + if (state().getLevels().size() == 2) { + StateLevel firstLevel = state().getLevels().get(0); + StateLevel secondLevel = state().getLevels().get(1); + + decodeMulti(in, cmd, firstLevel.getParts(), ctx.channel(), secondLevel.getSize(), secondLevel.getParts()); + + Channel channel = ctx.channel(); + MultiDecoder decoder = messageDecoder(cmd, firstLevel.getParts(), channel); + if (decoder != null) { + Object result = decoder.decode(firstLevel.getParts(), state()); + if (data != null) { + handleResult(cmd, null, result, true, channel); + } + } + } + if (state().getLevels().size() == 1) { + StateLevel firstLevel = state().getLevels().get(0); + if (firstLevel.getParts().isEmpty()) { + state().resetLevel(); + decode(in, cmd, null, ctx.channel()); + } else { + decodeMulti(in, cmd, null, ctx.channel(), firstLevel.getSize(), firstLevel.getParts()); + } + } + } + + private void decodeCommandBatch(ChannelHandlerContext ctx, ByteBuf in, QueueCommand data, + CommandsData commandBatch) { + int i = state().getBatchIndex(); RedisException error = null; while (in.writerIndex() > in.readerIndex()) { CommandData cmd = null; try { checkpoint(); - state().setIndex(i); - cmd = (CommandData) commands.getCommands().get(i); - decode(in, cmd, null, ctx.channel(), currentDecoder); + state().setBatchIndex(i); + cmd = (CommandData) commandBatch.getCommands().get(i); + decode(in, cmd, null, ctx.channel()); i++; } catch (IOException e) { cmd.getPromise().tryFailure(e); @@ -147,8 +186,8 @@ public class CommandDecoder extends ReplayingDecoder { } } - if (i == commands.getCommands().size()) { - Promise promise = commands.getPromise(); + if (i == commandBatch.getCommands().size()) { + Promise promise = commandBatch.getPromise(); if (error != null) { if (!promise.tryFailure(error) && promise.cause() instanceof RedisTimeoutException) { log.warn("response has been skipped due to timeout! channel: {}, command: {}", ctx.channel(), data); @@ -164,11 +203,11 @@ public class CommandDecoder extends ReplayingDecoder { state(null); } else { checkpoint(); - state().setIndex(i); + state().setBatchIndex(i); } } - private void decode(ByteBuf in, CommandData data, List parts, Channel channel, Decoder currentDecoder) throws IOException { + private void decode(ByteBuf in, CommandData data, List parts, Channel channel) throws IOException { int code = in.readByte(); if (code == '+') { String result = in.readBytes(in.bytesBefore((byte) '\r')).toString(CharsetUtil.UTF_8); @@ -212,32 +251,40 @@ public class CommandDecoder extends ReplayingDecoder { ByteBuf buf = readBytes(in); Object result = null; if (buf != null) { - result = decoder(data, parts, currentDecoder).decode(buf, state()); + Decoder decoder = selectDecoder(data, parts); + result = decoder.decode(buf, state()); } handleResult(data, parts, result, false, channel); } else if (code == '*') { + int level = state().incLevel(); + long size = readLong(in); - List respParts = new ArrayList(); - boolean top = false; -// if (state().trySetSize(size)) { -// state().setRespParts(respParts); -// top = true; -// } - - decodeMulti(in, data, parts, channel, currentDecoder, size, respParts, top); + List respParts; + if (state().getLevels().size()-1 >= level) { + StateLevel stateLevel = state().getLevels().get(level); + respParts = stateLevel.getParts(); + size = stateLevel.getSize(); + } else { + respParts = new ArrayList(); + if (state().isMakeCheckpoint()) { + state().addLevel(new StateLevel(size, respParts)); + } + } + + decodeMulti(in, data, parts, channel, size, respParts); } else { throw new IllegalStateException("Can't decode replay " + (char)code); } } private void decodeMulti(ByteBuf in, CommandData data, List parts, - Channel channel, Decoder currentDecoder, long size, List respParts, boolean top) + Channel channel, long size, List respParts) throws IOException { for (int i = respParts.size(); i < size; i++) { - decode(in, data, respParts, channel, currentDecoder); -// if (top) { -// checkpoint(); -// } + decode(in, data, respParts, channel); + if (state().isMakeCheckpoint()) { + checkpoint(); + } } MultiDecoder decoder = messageDecoder(data, respParts, channel); @@ -246,7 +293,10 @@ public class CommandDecoder extends ReplayingDecoder { } Object result = decoder.decode(respParts, state()); - + if (data != null) { + handleResult(data, parts, result, true, channel); + return; + } if (result instanceof Message) { // store current message index @@ -255,40 +305,34 @@ public class CommandDecoder extends ReplayingDecoder { handleMultiResult(data, null, channel, result); // has next messages? if (in.writerIndex() > in.readerIndex()) { - decode(in, data, null, channel, currentDecoder); + decode(in, data, null, channel); } - } else { - handleMultiResult(data, parts, channel, result); } } private void handleMultiResult(CommandData data, List parts, Channel channel, Object result) { - if (data != null) { - handleResult(data, parts, result, true, channel); - } else { - if (result instanceof PubSubStatusMessage) { - String channelName = ((PubSubStatusMessage) result).getChannel(); - CommandData d = pubSubChannels.get(channelName); - if (Arrays.asList("PSUBSCRIBE", "SUBSCRIBE").contains(d.getCommand().getName())) { - pubSubChannels.remove(channelName); - pubSubMessageDecoders.put(channelName, d.getMessageDecoder()); - } - if (Arrays.asList("PUNSUBSCRIBE", "UNSUBSCRIBE").contains(d.getCommand().getName())) { - pubSubChannels.remove(channelName); - pubSubMessageDecoders.remove(channelName); - } + if (result instanceof PubSubStatusMessage) { + String channelName = ((PubSubStatusMessage) result).getChannel(); + CommandData d = pubSubChannels.get(channelName); + if (Arrays.asList("PSUBSCRIBE", "SUBSCRIBE").contains(d.getCommand().getName())) { + pubSubChannels.remove(channelName); + pubSubMessageDecoders.put(channelName, d.getMessageDecoder()); } - - RedisPubSubConnection pubSubConnection = RedisPubSubConnection.getFrom(channel); - if (result instanceof PubSubStatusMessage) { - pubSubConnection.onMessage((PubSubStatusMessage) result); - } else if (result instanceof PubSubMessage) { - pubSubConnection.onMessage((PubSubMessage) result); - } else { - pubSubConnection.onMessage((PubSubPatternMessage) result); + if (Arrays.asList("PUNSUBSCRIBE", "UNSUBSCRIBE").contains(d.getCommand().getName())) { + pubSubChannels.remove(channelName); + pubSubMessageDecoders.remove(channelName); } } + + RedisPubSubConnection pubSubConnection = RedisPubSubConnection.getFrom(channel); + if (result instanceof PubSubStatusMessage) { + pubSubConnection.onMessage((PubSubStatusMessage) result); + } else if (result instanceof PubSubMessage) { + pubSubConnection.onMessage((PubSubMessage) result); + } else { + pubSubConnection.onMessage((PubSubPatternMessage) result); + } } private void handleResult(CommandData data, List parts, Object result, boolean multiResult, Channel channel) { @@ -329,7 +373,7 @@ public class CommandDecoder extends ReplayingDecoder { return data.getCommand().getReplayMultiDecoder(); } - private Decoder decoder(CommandData data, List parts, Decoder currentDecoder) { + private Decoder selectDecoder(CommandData data, List parts) { if (data == null) { if (parts.size() == 2 && parts.get(0).equals("message")) { String channelName = (String) parts.get(1); @@ -339,7 +383,7 @@ public class CommandDecoder extends ReplayingDecoder { String patternName = (String) parts.get(1); return pubSubMessageDecoders.get(patternName); } - return currentDecoder; + return StringCodec.INSTANCE.getValueDecoder(); } Decoder decoder = data.getCommand().getReplayDecoder(); diff --git a/src/main/java/org/redisson/client/handler/State.java b/src/main/java/org/redisson/client/handler/State.java index d9d1ba579..0c8f35206 100644 --- a/src/main/java/org/redisson/client/handler/State.java +++ b/src/main/java/org/redisson/client/handler/State.java @@ -15,50 +15,80 @@ */ package org.redisson.client.handler; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import org.redisson.client.protocol.decoder.DecoderState; + public class State { - private int index; - private Object decoderState; + private int batchIndex; + private DecoderState decoderState; + + private int level = -1; + private List levels; + private DecoderState decoderStateCopy; + private final boolean makeCheckpoint; - private long size; - private List respParts; + public State(boolean makeCheckpoint) { + this.makeCheckpoint = makeCheckpoint; + } - public State() { - super(); + public boolean isMakeCheckpoint() { + return makeCheckpoint; } - public boolean trySetSize(long size) { - if (this.size != 0) { - return false; + public void resetLevel() { + level = -1; + } + public int decLevel() { + return --level; + } + public int incLevel() { + return ++level; + } + + public void addLevel(StateLevel stateLevel) { + if (levels == null) { + levels = new ArrayList(2); } - this.size = size; - return true; + levels.add(stateLevel); } - public long getSize() { - return size; + public List getLevels() { + if (levels == null) { + return Collections.emptyList(); + } + return levels; } - public void setRespParts(List respParts) { - this.respParts = respParts; + public void setBatchIndex(int index) { + this.batchIndex = index; } - public List getRespParts() { - return respParts; + public int getBatchIndex() { + return batchIndex; } - public void setIndex(int index) { - this.index = index; + public T getDecoderState() { + return (T) decoderState; } - public int getIndex() { - return index; + public void setDecoderState(DecoderState decoderState) { + this.decoderState = decoderState; } - public T getDecoderState() { - return (T)decoderState; + public DecoderState getDecoderStateCopy() { + return decoderStateCopy; } - public void setDecoderState(Object decoderState) { - this.decoderState = decoderState; + public void setDecoderStateCopy(DecoderState decoderStateCopy) { + this.decoderStateCopy = decoderStateCopy; + } + + @Override + public String toString() { + return "State [batchIndex=" + batchIndex + ", decoderState=" + decoderState + ", level=" + level + ", levels=" + + levels + ", decoderStateCopy=" + decoderStateCopy + "]"; } + + } diff --git a/src/main/java/org/redisson/client/handler/StateLevel.java b/src/main/java/org/redisson/client/handler/StateLevel.java new file mode 100644 index 000000000..37f2fbc4a --- /dev/null +++ b/src/main/java/org/redisson/client/handler/StateLevel.java @@ -0,0 +1,29 @@ +package org.redisson.client.handler; + +import java.util.List; + +public class StateLevel { + + private long size; + private List parts; + + public StateLevel(long size, List parts) { + super(); + this.size = size; + this.parts = parts; + } + + public long getSize() { + return size; + } + + public List getParts() { + return parts; + } + + @Override + public String toString() { + return "StateLevel [size=" + size + ", parts=" + parts + "]"; + } + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/DecoderState.java b/src/main/java/org/redisson/client/protocol/decoder/DecoderState.java new file mode 100644 index 000000000..c3abf7502 --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/DecoderState.java @@ -0,0 +1,7 @@ +package org.redisson.client.protocol.decoder; + +public interface DecoderState { + + DecoderState copy(); + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/FlatNestedMultiDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/FlatNestedMultiDecoder.java index 4bd04c8ee..72f5cfc1c 100644 --- a/src/main/java/org/redisson/client/protocol/decoder/FlatNestedMultiDecoder.java +++ b/src/main/java/org/redisson/client/protocol/decoder/FlatNestedMultiDecoder.java @@ -38,7 +38,7 @@ public class FlatNestedMultiDecoder extends NestedMultiDecoder { @Override public boolean isApplicable(int paramNum, State state) { - DecoderState ds = getDecoder(state); + NestedDecoderState ds = getDecoder(state); if (paramNum == 0) { ds.resetDecoderIndex(); } diff --git a/src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder.java index 43b5266ac..2fcd87c4e 100644 --- a/src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder.java +++ b/src/main/java/org/redisson/client/protocol/decoder/NestedMultiDecoder.java @@ -24,13 +24,19 @@ import io.netty.buffer.ByteBuf; public class NestedMultiDecoder implements MultiDecoder { - public static class DecoderState { + public static class NestedDecoderState implements DecoderState { int decoderIndex; int flipDecoderIndex; - public DecoderState() { + public NestedDecoderState() { + } + + public NestedDecoderState(int decoderIndex, int flipDecoderIndex) { + super(); + this.decoderIndex = decoderIndex; + this.flipDecoderIndex = flipDecoderIndex; } public int getDecoderIndex() { @@ -53,6 +59,16 @@ public class NestedMultiDecoder implements MultiDecoder { flipDecoderIndex++; } + @Override + public DecoderState copy() { + return new NestedDecoderState(decoderIndex, flipDecoderIndex); + } + + @Override + public String toString() { + return "NestedDecoderState [decoderIndex=" + decoderIndex + ", flipDecoderIndex=" + flipDecoderIndex + "]"; + } + } protected final MultiDecoder firstDecoder; @@ -81,7 +97,7 @@ public class NestedMultiDecoder implements MultiDecoder { @Override public Object decode(ByteBuf buf, State state) throws IOException { - DecoderState ds = getDecoder(state); + NestedDecoderState ds = getDecoder(state); MultiDecoder decoder = null; if (ds.getFlipDecoderIndex() == 2) { @@ -96,7 +112,7 @@ public class NestedMultiDecoder implements MultiDecoder { @Override public boolean isApplicable(int paramNum, State state) { - DecoderState ds = getDecoder(state); + NestedDecoderState ds = getDecoder(state); if (paramNum == 0) { ds.incFlipDecoderIndex(); ds.resetDecoderIndex(); @@ -118,10 +134,10 @@ public class NestedMultiDecoder implements MultiDecoder { return decoder.isApplicable(paramNum, state); } - protected final DecoderState getDecoder(State state) { - DecoderState ds = state.getDecoderState(); + protected final NestedDecoderState getDecoder(State state) { + NestedDecoderState ds = state.getDecoderState(); if (ds == null) { - ds = new DecoderState(); + ds = new NestedDecoderState(); state.setDecoderState(ds); } return ds; @@ -137,7 +153,7 @@ public class NestedMultiDecoder implements MultiDecoder { return decoder.decode(parts, state); } - DecoderState ds = getDecoder(state); + NestedDecoderState ds = getDecoder(state); if (parts.isEmpty()) { ds.resetDecoderIndex(); } diff --git a/src/main/java/org/redisson/connection/decoder/CacheGetAllDecoder.java b/src/main/java/org/redisson/connection/decoder/CacheGetAllDecoder.java index 8e5fe597b..0d14eff2d 100644 --- a/src/main/java/org/redisson/connection/decoder/CacheGetAllDecoder.java +++ b/src/main/java/org/redisson/connection/decoder/CacheGetAllDecoder.java @@ -21,11 +21,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.redisson.client.codec.LongCodec; import org.redisson.client.handler.State; import org.redisson.client.protocol.decoder.MultiDecoder; import io.netty.buffer.ByteBuf; -import io.netty.util.CharsetUtil; public class CacheGetAllDecoder implements MultiDecoder> { @@ -37,7 +37,7 @@ public class CacheGetAllDecoder implements MultiDecoder> { @Override public Object decode(ByteBuf buf, State state) throws IOException { - return Long.valueOf(buf.toString(CharsetUtil.UTF_8)); + return LongCodec.INSTANCE.getValueDecoder().decode(buf, state); } @Override diff --git a/src/test/java/org/redisson/RedissonGeoTest.java b/src/test/java/org/redisson/RedissonGeoTest.java index fc689e006..4b6baf7ee 100644 --- a/src/test/java/org/redisson/RedissonGeoTest.java +++ b/src/test/java/org/redisson/RedissonGeoTest.java @@ -5,6 +5,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Map.Entry; import org.junit.Test; import org.redisson.core.GeoEntry; @@ -103,6 +104,61 @@ public class RedissonGeoTest extends BaseTest { expected.put("Catania", 56.4413); assertThat(geo.radiusWithDistance(15, 37, 200, GeoUnit.KILOMETERS)).isEqualTo(expected); } + + @Test + public void testRadiusWithDistanceHugeAmount() { + RGeo geo = redisson.getGeo("test"); + + for (int i = 0; i < 10000; i++) { + geo.add(10 + 0.000001*i, 11 + 0.000001*i, "" + i); + } + + Map res = geo.radiusWithDistance(10, 11, 200, GeoUnit.KILOMETERS); + assertThat(res).hasSize(10000); + } + + @Test + public void testRadiusWithPositionHugeAmount() { + RGeo geo = redisson.getGeo("test"); + + for (int i = 0; i < 10000; i++) { + geo.add(10 + 0.000001*i, 11 + 0.000001*i, "" + i); + } + + Map res = geo.radiusWithPosition(10, 11, 200, GeoUnit.KILOMETERS); + assertThat(res).hasSize(10000); + } + + + @Test + public void testRadiusWithDistanceBigObject() { + RGeo> geo = redisson.getGeo("test"); + + Map map = new HashMap(); + for (int i = 0; i < 150; i++) { + map.put("" + i, "" + i); + } + + geo.add(new GeoEntry(13.361389, 38.115556, map)); + + Map map1 = new HashMap(map); + map1.remove("100"); + geo.add(new GeoEntry(15.087269, 37.502669, map1)); + + Map map2 = new HashMap(map); + map2.remove("0"); + geo.add(new GeoEntry(15.081269, 37.502169, map2)); + + Map, Double> expected = new HashMap, Double>(); + expected.put(map, 190.4424); + expected.put(map1, 56.4413); + expected.put(map2, 56.3159); + + Map, Double> res = geo.radiusWithDistance(15, 37, 200, GeoUnit.KILOMETERS); + assertThat(res.keySet()).containsOnlyElementsOf(expected.keySet()); + assertThat(res.values()).containsOnlyElementsOf(expected.values()); + } + @Test public void testRadiusWithDistanceEmpty() { diff --git a/src/test/java/org/redisson/RedissonSetCacheTest.java b/src/test/java/org/redisson/RedissonSetCacheTest.java index 64e4ddc23..4698435b4 100644 --- a/src/test/java/org/redisson/RedissonSetCacheTest.java +++ b/src/test/java/org/redisson/RedissonSetCacheTest.java @@ -5,8 +5,10 @@ import static org.assertj.core.api.Assertions.assertThat; import java.io.Serializable; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -46,7 +48,20 @@ public class RedissonSetCacheTest extends BaseTest { RSetCache set = redisson.getSetCache("set"); assertThat(set.readAll()).isEmpty(); } - + + @Test + public void testAddBigBean() { + RSetCache> set = redisson.getSetCache("simple"); + Map map = new HashMap(); + for (int i = 0; i < 150; i++) { + map.put(i, i); + } + set.add(map); + map.remove(0); + set.add(map); + set.iterator().next(); + } + @Test public void testAddBean() throws InterruptedException, ExecutionException { SimpleBean sb = new SimpleBean(); From 4f8663f21ac0a3c2fa87280bdc5c914f6c6ee3f9 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 15 Apr 2016 16:57:38 +0300 Subject: [PATCH 062/120] Command encoder handlers now are singletons --- .../java/org/redisson/client/RedisClient.java | 6 +++--- ...sListEncoder.java => CommandBatchEncoder.java} | 9 +++++++-- .../redisson/client/handler/CommandEncoder.java | 15 +++++++++------ 3 files changed, 19 insertions(+), 11 deletions(-) rename src/main/java/org/redisson/client/handler/{CommandsListEncoder.java => CommandBatchEncoder.java} (77%) diff --git a/src/main/java/org/redisson/client/RedisClient.java b/src/main/java/org/redisson/client/RedisClient.java index 2aa645755..32e5445aa 100644 --- a/src/main/java/org/redisson/client/RedisClient.java +++ b/src/main/java/org/redisson/client/RedisClient.java @@ -19,7 +19,7 @@ import java.net.InetSocketAddress; import org.redisson.client.handler.CommandDecoder; import org.redisson.client.handler.CommandEncoder; -import org.redisson.client.handler.CommandsListEncoder; +import org.redisson.client.handler.CommandBatchEncoder; import org.redisson.client.handler.CommandsQueue; import org.redisson.client.handler.ConnectionWatchdog; @@ -62,8 +62,8 @@ public class RedisClient { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addFirst(new ConnectionWatchdog(bootstrap, channels), - new CommandEncoder(), - new CommandsListEncoder(), + CommandEncoder.INSTANCE, + CommandBatchEncoder.INSTANCE, new CommandsQueue(), new CommandDecoder()); } diff --git a/src/main/java/org/redisson/client/handler/CommandsListEncoder.java b/src/main/java/org/redisson/client/handler/CommandBatchEncoder.java similarity index 77% rename from src/main/java/org/redisson/client/handler/CommandsListEncoder.java rename to src/main/java/org/redisson/client/handler/CommandBatchEncoder.java index a4c4ffef9..9412b5ab4 100644 --- a/src/main/java/org/redisson/client/handler/CommandsListEncoder.java +++ b/src/main/java/org/redisson/client/handler/CommandBatchEncoder.java @@ -20,6 +20,7 @@ import org.redisson.client.protocol.CommandsData; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelHandler.Sharable; import io.netty.handler.codec.MessageToByteEncoder; /** @@ -27,12 +28,16 @@ import io.netty.handler.codec.MessageToByteEncoder; * @author Nikita Koksharov * */ -public class CommandsListEncoder extends MessageToByteEncoder { +@Sharable +public class CommandBatchEncoder extends MessageToByteEncoder { + public static final CommandBatchEncoder INSTANCE = new CommandBatchEncoder(); + @Override protected void encode(ChannelHandlerContext ctx, CommandsData msg, ByteBuf out) throws Exception { + CommandEncoder encoder = ctx.pipeline().get(CommandEncoder.class); for (CommandData commandData : msg.getCommands()) { - ctx.pipeline().get(CommandEncoder.class).encode(ctx, (CommandData)commandData, out); + encoder.encode(ctx, commandData, out); } } diff --git a/src/main/java/org/redisson/client/handler/CommandEncoder.java b/src/main/java/org/redisson/client/handler/CommandEncoder.java index 2a8b0af2c..ed245e211 100644 --- a/src/main/java/org/redisson/client/handler/CommandEncoder.java +++ b/src/main/java/org/redisson/client/handler/CommandEncoder.java @@ -17,17 +17,17 @@ package org.redisson.client.handler; import java.util.List; -import org.redisson.client.codec.ByteArrayCodec; import org.redisson.client.codec.StringCodec; import org.redisson.client.protocol.CommandData; -import org.redisson.client.protocol.Encoder; import org.redisson.client.protocol.DefaultParamsEncoder; +import org.redisson.client.protocol.Encoder; +import org.redisson.client.protocol.RedisCommand.ValueType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.redisson.client.protocol.RedisCommand.ValueType; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelHandler.Sharable; import io.netty.handler.codec.MessageToByteEncoder; import io.netty.util.CharsetUtil; @@ -39,8 +39,11 @@ import io.netty.util.CharsetUtil; * @author Nikita Koksharov * */ -public class CommandEncoder extends MessageToByteEncoder> { +@Sharable +public class CommandEncoder extends MessageToByteEncoder> { + public static final CommandEncoder INSTANCE = new CommandEncoder(); + private final Logger log = LoggerFactory.getLogger(getClass()); private final Encoder paramsEncoder = new DefaultParamsEncoder(); @@ -50,7 +53,7 @@ public class CommandEncoder extends MessageToByteEncoder msg, ByteBuf out) throws Exception { + protected void encode(ChannelHandlerContext ctx, CommandData msg, ByteBuf out) throws Exception { out.writeByte(ARGS_PREFIX); int len = 1 + msg.getParams().length; if (msg.getCommand().getSubName() != null) { @@ -91,7 +94,7 @@ public class CommandEncoder extends MessageToByteEncoder msg, int param) { + private Encoder selectEncoder(CommandData msg, int param) { int typeIndex = 0; List inParamType = msg.getCommand().getInParamType(); if (inParamType.size() > 1) { From 64cf095553eb49b7a40b696aa80fdb9e3bfd7d9a Mon Sep 17 00:00:00 2001 From: jackygurui Date: Fri, 15 Apr 2016 15:04:23 +0100 Subject: [PATCH 063/120] Test case added for #471 --- .../java/org/redisson/RedissonMapCacheTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/test/java/org/redisson/RedissonMapCacheTest.java b/src/test/java/org/redisson/RedissonMapCacheTest.java index 865bf7852..e1475d4e7 100644 --- a/src/test/java/org/redisson/RedissonMapCacheTest.java +++ b/src/test/java/org/redisson/RedissonMapCacheTest.java @@ -766,6 +766,21 @@ public class RedissonMapCacheTest extends BaseTest { } } + + @Test + public void testRMapCacheValues() { + final RMapCache map = redisson.getMapCache("testRMapCacheValues"); + map.put("1234", "5678", 1, TimeUnit.MINUTES, 60, TimeUnit.MINUTES); + assertThat(map.values()).containsOnly("5678"); + } + + @Test + public void testRMapCacheAllValues() { + final RMapCache map = redisson.getMapCache("testRMapCacheAllValues"); + map.put("1234", "5678", 1, TimeUnit.MINUTES, 60, TimeUnit.MINUTES); + assertThat(map.readAllValues()).containsOnly("5678"); + } + public static class SimpleObjectWithoutDefaultConstructor { private String testField; From b60b170d4574f0121ecacff6b351b851b010920e Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 15 Apr 2016 21:06:38 +0300 Subject: [PATCH 064/120] RedissonMultimap.fastRemove method cluster compatibility fixed --- .../java/org/redisson/RedissonMultimap.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/redisson/RedissonMultimap.java b/src/main/java/org/redisson/RedissonMultimap.java index a072520a5..1075b2e33 100644 --- a/src/main/java/org/redisson/RedissonMultimap.java +++ b/src/main/java/org/redisson/RedissonMultimap.java @@ -22,13 +22,12 @@ import java.util.AbstractSet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.concurrent.TimeUnit; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.redisson.client.codec.Codec; import org.redisson.client.codec.LongCodec; @@ -167,24 +166,24 @@ public abstract class RedissonMultimap extends RedissonExpirable implement } try { - List args = new ArrayList(keys.length*2); - List hashes = new ArrayList(); + List mapKeys = new ArrayList(keys.length); + List listKeys = new ArrayList(keys.length + 1); + listKeys.add(getName()); for (K key : keys) { byte[] keyState = codec.getMapKeyEncoder().encode(key); - args.add(keyState); + mapKeys.add(keyState); String keyHash = hash(keyState); String name = getValuesName(keyHash); - hashes.add(name); + listKeys.add(name); } - args.addAll(hashes); return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_LONG, - "local res = redis.call('hdel', KEYS[1], unpack(ARGV, 1, #ARGV/2)); " + + "local res = redis.call('hdel', KEYS[1], unpack(ARGV)); " + "if res > 0 then " + - "redis.call('del', unpack(ARGV, #ARGV/2, #ARGV)); " + + "redis.call('del', unpack(KEYS, 2, #KEYS)); " + "end; " + "return res; ", - Collections.singletonList(getName()), args.toArray()); + listKeys, mapKeys.toArray()); } catch (IOException e) { throw new RuntimeException(e); } From 0d59eaea3c15d1ecdf1f23ea8a75f4d755c300b6 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 15 Apr 2016 21:07:45 +0300 Subject: [PATCH 065/120] Command batches cluster redirect handling fixed --- .../client/RedisRedirectException.java | 2 +- .../client/handler/CommandDecoder.java | 28 ++++----- .../redisson/client/handler/StateLevel.java | 15 +++++ .../client/protocol/BatchCommandData.java | 62 +++++++++++++++++++ .../redisson/client/protocol/CommandData.java | 12 ++++ .../client/protocol/decoder/DecoderState.java | 15 +++++ .../redisson/command/CommandBatchService.java | 24 +++++-- 7 files changed, 135 insertions(+), 23 deletions(-) create mode 100644 src/main/java/org/redisson/client/protocol/BatchCommandData.java diff --git a/src/main/java/org/redisson/client/RedisRedirectException.java b/src/main/java/org/redisson/client/RedisRedirectException.java index e2c30b761..9c15b87e5 100644 --- a/src/main/java/org/redisson/client/RedisRedirectException.java +++ b/src/main/java/org/redisson/client/RedisRedirectException.java @@ -18,7 +18,7 @@ package org.redisson.client; import java.net.InetSocketAddress; import java.net.URI; -class RedisRedirectException extends RedisException { +public class RedisRedirectException extends RedisException { private static final long serialVersionUID = 181505625075250011L; diff --git a/src/main/java/org/redisson/client/handler/CommandDecoder.java b/src/main/java/org/redisson/client/handler/CommandDecoder.java index d9eb1c1d2..260c21fb7 100644 --- a/src/main/java/org/redisson/client/handler/CommandDecoder.java +++ b/src/main/java/org/redisson/client/handler/CommandDecoder.java @@ -111,7 +111,7 @@ public class CommandDecoder extends ReplayingDecoder { decode(in, cmd, null, ctx.channel()); } } catch (IOException e) { - cmd.getPromise().tryFailure(e); + cmd.tryFailure(e); } } else if (data instanceof CommandsData) { CommandsData commands = (CommandsData)data; @@ -175,14 +175,10 @@ public class CommandDecoder extends ReplayingDecoder { decode(in, cmd, null, ctx.channel()); i++; } catch (IOException e) { - cmd.getPromise().tryFailure(e); + cmd.tryFailure(e); } - if (!cmd.getPromise().isSuccess()) { - if (!(cmd.getPromise().cause() instanceof RedisMovedException - || cmd.getPromise().cause() instanceof RedisAskException - || cmd.getPromise().cause() instanceof RedisLoadingException)) { - error = (RedisException) cmd.getPromise().cause(); - } + if (!cmd.isSuccess()) { + error = (RedisException) cmd.cause(); } } @@ -197,7 +193,7 @@ public class CommandDecoder extends ReplayingDecoder { log.warn("response has been skipped due to timeout! channel: {}, command: {}", ctx.channel(), data); } } - + ctx.pipeline().get(CommandsQueue.class).sendNextCommand(ctx.channel()); state(null); @@ -222,24 +218,24 @@ public class CommandDecoder extends ReplayingDecoder { String[] errorParts = error.split(" "); int slot = Integer.valueOf(errorParts[1]); String addr = errorParts[2]; - data.getPromise().tryFailure(new RedisMovedException(slot, addr)); + data.tryFailure(new RedisMovedException(slot, addr)); } else if (error.startsWith("ASK")) { String[] errorParts = error.split(" "); int slot = Integer.valueOf(errorParts[1]); String addr = errorParts[2]; - data.getPromise().tryFailure(new RedisAskException(slot, addr)); + data.tryFailure(new RedisAskException(slot, addr)); } else if (error.startsWith("LOADING")) { - data.getPromise().tryFailure(new RedisLoadingException(error + data.tryFailure(new RedisLoadingException(error + ". channel: " + channel + " data: " + data)); } else if (error.startsWith("OOM")) { - data.getPromise().tryFailure(new RedisOutOfMemoryException(error.split("OOM ")[1] + data.tryFailure(new RedisOutOfMemoryException(error.split("OOM ")[1] + ". channel: " + channel + " data: " + data)); } else if (error.contains("-OOM ")) { - data.getPromise().tryFailure(new RedisOutOfMemoryException(error.split("-OOM ")[1] + data.tryFailure(new RedisOutOfMemoryException(error.split("-OOM ")[1] + ". channel: " + channel + " data: " + data)); } else { if (data != null) { - data.getPromise().tryFailure(new RedisException(error + ". channel: " + channel + " command: " + data)); + data.tryFailure(new RedisException(error + ". channel: " + channel + " command: " + data)); } else { log.error("Error: {} channel: {} data: {}", error, channel, data); } @@ -346,7 +342,7 @@ public class CommandDecoder extends ReplayingDecoder { if (parts != null) { parts.add(result); } else { - if (!data.getPromise().trySuccess(result) && data.getPromise().cause() instanceof RedisTimeoutException) { + if (!data.getPromise().trySuccess(result) && data.cause() instanceof RedisTimeoutException) { log.warn("response has been skipped due to timeout! channel: {}, command: {}, result: {}", channel, data, result); } } diff --git a/src/main/java/org/redisson/client/handler/StateLevel.java b/src/main/java/org/redisson/client/handler/StateLevel.java index 37f2fbc4a..64b0d9186 100644 --- a/src/main/java/org/redisson/client/handler/StateLevel.java +++ b/src/main/java/org/redisson/client/handler/StateLevel.java @@ -1,3 +1,18 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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.handler; import java.util.List; diff --git a/src/main/java/org/redisson/client/protocol/BatchCommandData.java b/src/main/java/org/redisson/client/protocol/BatchCommandData.java new file mode 100644 index 000000000..70ce32f84 --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/BatchCommandData.java @@ -0,0 +1,62 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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; + +import java.util.concurrent.atomic.AtomicReference; + +import org.redisson.client.RedisRedirectException; +import org.redisson.client.codec.Codec; + +import io.netty.util.concurrent.Promise; + +public class BatchCommandData extends CommandData { + + private final AtomicReference redirectError = new AtomicReference(); + + public BatchCommandData(Promise promise, Codec codec, RedisCommand command, Object[] params) { + super(promise, codec, command, params); + } + + @Override + public boolean tryFailure(Throwable cause) { + if (redirectError.get() != null) { + return false; + } + if (cause instanceof RedisRedirectException) { + return redirectError.compareAndSet(null, (RedisRedirectException) cause); + } + + return super.tryFailure(cause); + } + + @Override + public boolean isSuccess() { + return redirectError.get() == null && super.isSuccess(); + } + + @Override + public Throwable cause() { + if (redirectError.get() != null) { + return redirectError.get(); + } + return super.cause(); + } + + public void clearError() { + redirectError.set(null); + } + +} diff --git a/src/main/java/org/redisson/client/protocol/CommandData.java b/src/main/java/org/redisson/client/protocol/CommandData.java index a47227c9e..7cafdedab 100644 --- a/src/main/java/org/redisson/client/protocol/CommandData.java +++ b/src/main/java/org/redisson/client/protocol/CommandData.java @@ -59,6 +59,18 @@ public class CommandData implements QueueCommand { public Promise getPromise() { return promise; } + + public Throwable cause() { + return promise.cause(); + } + + public boolean isSuccess() { + return promise.isSuccess(); + } + + public boolean tryFailure(Throwable cause) { + return promise.tryFailure(cause); + } public Codec getCodec() { return codec; diff --git a/src/main/java/org/redisson/client/protocol/decoder/DecoderState.java b/src/main/java/org/redisson/client/protocol/decoder/DecoderState.java index c3abf7502..1c9c78afa 100644 --- a/src/main/java/org/redisson/client/protocol/decoder/DecoderState.java +++ b/src/main/java/org/redisson/client/protocol/decoder/DecoderState.java @@ -1,3 +1,18 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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; public interface DecoderState { diff --git a/src/main/java/org/redisson/command/CommandBatchService.java b/src/main/java/org/redisson/command/CommandBatchService.java index 6bf4083cb..3939f1190 100644 --- a/src/main/java/org/redisson/command/CommandBatchService.java +++ b/src/main/java/org/redisson/command/CommandBatchService.java @@ -31,6 +31,7 @@ import org.redisson.client.RedisTimeoutException; import org.redisson.client.WriteRedisConnectionException; import org.redisson.client.codec.Codec; import org.redisson.client.codec.StringCodec; +import org.redisson.client.protocol.BatchCommandData; import org.redisson.client.protocol.CommandData; import org.redisson.client.protocol.CommandsData; import org.redisson.client.protocol.RedisCommand; @@ -52,16 +53,16 @@ public class CommandBatchService extends CommandReactiveService { public static class CommandEntry implements Comparable { - final CommandData command; + final BatchCommandData command; final int index; - public CommandEntry(CommandData command, int index) { + public CommandEntry(BatchCommandData command, int index) { super(); this.command = command; this.index = index; } - public CommandData getCommand() { + public BatchCommandData getCommand() { return command; } @@ -89,6 +90,12 @@ public class CommandBatchService extends CommandReactiveService { public boolean isReadOnlyMode() { return readOnlyMode; } + + public void clearErrors() { + for (CommandEntry commandEntry : commands) { + commandEntry.getCommand().clearError(); + } + } } @@ -96,7 +103,7 @@ public class CommandBatchService extends CommandReactiveService { private ConcurrentMap commands = PlatformDependent.newConcurrentHashMap(); - private boolean executed; + private volatile boolean executed; public CommandBatchService(ConnectionManager connectionManager) { super(connectionManager); @@ -106,7 +113,7 @@ public class CommandBatchService extends CommandReactiveService { protected void async(boolean readOnlyMode, NodeSource nodeSource, Codec codec, RedisCommand command, Object[] params, Promise mainPromise, int attempt) { if (executed) { - throw new IllegalStateException("Batch already executed!"); + throw new IllegalStateException("Batch already has been executed!"); } Entry entry = commands.get(nodeSource.getSlot()); if (entry == null) { @@ -120,7 +127,9 @@ public class CommandBatchService extends CommandReactiveService { if (!readOnlyMode) { entry.setReadOnlyMode(false); } - entry.getCommands().add(new CommandEntry(new CommandData(mainPromise, codec, command, params), index.incrementAndGet())); + + BatchCommandData commandData = new BatchCommandData(mainPromise, codec, command, params); + entry.getCommands().add(new CommandEntry(commandData, index.incrementAndGet())); } public List execute() { @@ -278,15 +287,18 @@ public class CommandBatchService extends CommandReactiveService { if (future.cause() instanceof RedisMovedException) { RedisMovedException ex = (RedisMovedException)future.cause(); + entry.clearErrors(); execute(entry, new NodeSource(ex.getSlot(), ex.getAddr(), Redirect.MOVED), mainPromise, slots, attempt); return; } if (future.cause() instanceof RedisAskException) { RedisAskException ex = (RedisAskException)future.cause(); + entry.clearErrors(); execute(entry, new NodeSource(ex.getSlot(), ex.getAddr(), Redirect.ASK), mainPromise, slots, attempt); return; } if (future.cause() instanceof RedisLoadingException) { + entry.clearErrors(); execute(entry, source, mainPromise, slots, attempt); return; } From 3a848c0c1d28df9d6c112a9348201b70cfc34cd5 Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 18 Apr 2016 13:16:12 +0300 Subject: [PATCH 066/120] Fixed unnecessary slots migration in cluster mode. #475 --- .../org/redisson/cluster/ClusterConnectionManager.java | 6 ++++-- src/main/java/org/redisson/cluster/ClusterPartition.java | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/redisson/cluster/ClusterConnectionManager.java b/src/main/java/org/redisson/cluster/ClusterConnectionManager.java index 7ac9a601e..12b8518d6 100644 --- a/src/main/java/org/redisson/cluster/ClusterConnectionManager.java +++ b/src/main/java/org/redisson/cluster/ClusterConnectionManager.java @@ -485,10 +485,12 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { List currentPartitions = new ArrayList(lastPartitions.values()); for (ClusterPartition currentPartition : currentPartitions) { for (ClusterPartition newPartition : newPartitions) { - if (!currentPartition.getNodeId().equals(newPartition.getNodeId())) { + if (!currentPartition.getNodeId().equals(newPartition.getNodeId()) + // skip master change case + || !currentPartition.getMasterAddr().equals(newPartition.getMasterAddr())) { continue; } - + Set addedSlots = new HashSet(newPartition.getSlotRanges()); addedSlots.removeAll(currentPartition.getSlotRanges()); MasterSlaveEntry entry = getEntry(currentPartition.getSlotRanges().iterator().next()); diff --git a/src/main/java/org/redisson/cluster/ClusterPartition.java b/src/main/java/org/redisson/cluster/ClusterPartition.java index f6486bd9f..ace54a42d 100644 --- a/src/main/java/org/redisson/cluster/ClusterPartition.java +++ b/src/main/java/org/redisson/cluster/ClusterPartition.java @@ -93,4 +93,11 @@ public class ClusterPartition { failedSlaves.remove(uri); } + @Override + public String toString() { + return "ClusterPartition [nodeId=" + nodeId + ", masterFail=" + masterFail + ", masterAddress=" + masterAddress + + ", slaveAddresses=" + slaveAddresses + ", failedSlaves=" + failedSlaves + ", slotRanges=" + slotRanges + + "]"; + } + } From 904d58d8e94814c5271ef74dac9e7a1bc925557a Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 19 Apr 2016 11:54:36 +0300 Subject: [PATCH 067/120] RMapCache iterator fixed. readAll methods fixed. #471 --- .../java/org/redisson/RedissonMapCache.java | 153 +++++++++++++++--- .../client/handler/CommandDecoder.java | 13 +- .../client/protocol/RedisCommands.java | 1 + .../protocol/decoder/ListMultiDecoder.java | 115 +++++++++++++ .../protocol/decoder/LongMultiDecoder.java | 43 +++++ .../protocol/decoder/MapCacheScanResult.java | 34 ++++ .../MapCacheScanResultReplayDecoder.java | 46 ++++++ .../protocol/decoder/ObjectListDecoder.java | 50 ++++++ .../protocol/decoder/ObjectMapDecoder.java | 63 ++++++++ .../org/redisson/RedissonMapCacheTest.java | 78 ++++----- 10 files changed, 523 insertions(+), 73 deletions(-) create mode 100644 src/main/java/org/redisson/client/protocol/decoder/ListMultiDecoder.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/LongMultiDecoder.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/MapCacheScanResult.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/MapCacheScanResultReplayDecoder.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/ObjectListDecoder.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/ObjectMapDecoder.java diff --git a/src/main/java/org/redisson/RedissonMapCache.java b/src/main/java/org/redisson/RedissonMapCache.java index 0167015c8..0b5e495f6 100644 --- a/src/main/java/org/redisson/RedissonMapCache.java +++ b/src/main/java/org/redisson/RedissonMapCache.java @@ -18,6 +18,7 @@ package org.redisson; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -33,9 +34,14 @@ import org.redisson.client.protocol.RedisCommands; import org.redisson.client.protocol.convertor.BooleanReplayConvertor; import org.redisson.client.protocol.convertor.LongReplayConvertor; import org.redisson.client.protocol.convertor.VoidReplayConvertor; +import org.redisson.client.protocol.decoder.ListMultiDecoder; +import org.redisson.client.protocol.decoder.LongMultiDecoder; +import org.redisson.client.protocol.decoder.MapCacheScanResult; +import org.redisson.client.protocol.decoder.MapCacheScanResultReplayDecoder; import org.redisson.client.protocol.decoder.MapScanResult; -import org.redisson.client.protocol.decoder.MapScanResultReplayDecoder; -import org.redisson.client.protocol.decoder.NestedMultiDecoder; +import org.redisson.client.protocol.decoder.ObjectListDecoder; +import org.redisson.client.protocol.decoder.ObjectListReplayDecoder; +import org.redisson.client.protocol.decoder.ObjectMapDecoder; import org.redisson.client.protocol.decoder.ObjectMapReplayDecoder; import org.redisson.client.protocol.decoder.ScanObjectEntry; import org.redisson.command.CommandAsyncExecutor; @@ -43,6 +49,7 @@ import org.redisson.connection.decoder.MapGetAllDecoder; import org.redisson.core.RMapCache; import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.FutureListener; /** *

Map-based cache with ability to set TTL for each entry via @@ -69,7 +76,7 @@ public class RedissonMapCache extends RedissonMap implements RMapCac static final RedisCommand EVAL_REPLACE = new RedisCommand("EVAL", 6, ValueType.MAP, ValueType.MAP_VALUE); static final RedisCommand EVAL_REPLACE_VALUE = new RedisCommand("EVAL", new BooleanReplayConvertor(), 7, Arrays.asList(ValueType.MAP_KEY, ValueType.MAP_VALUE, ValueType.MAP_VALUE)); private static final RedisCommand EVAL_HMSET = new RedisCommand("EVAL", new VoidReplayConvertor(), 4, ValueType.MAP); - private static final RedisCommand> EVAL_HSCAN = new RedisCommand>("EVAL", new NestedMultiDecoder(new ObjectMapReplayDecoder(), new MapScanResultReplayDecoder()), ValueType.MAP); + private static final RedisCommand> EVAL_HSCAN = new RedisCommand>("EVAL", new ListMultiDecoder(new LongMultiDecoder(), new ObjectMapReplayDecoder(), new ObjectListReplayDecoder()), ValueType.MAP); private static final RedisCommand EVAL_REMOVE = new RedisCommand("EVAL", 4, ValueType.MAP_KEY, ValueType.MAP_VALUE); private static final RedisCommand EVAL_REMOVE_VALUE = new RedisCommand("EVAL", new LongReplayConvertor(), 5, ValueType.MAP); private static final RedisCommand EVAL_PUT_TTL = new RedisCommand("EVAL", 9, ValueType.MAP, ValueType.MAP_VALUE); @@ -168,10 +175,8 @@ public class RedissonMapCache extends RedissonMap implements RMapCac return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand>("EVAL", new MapGetAllDecoder(args, 1), 7, ValueType.MAP_KEY, ValueType.MAP_VALUE), "local expireHead = redis.call('zrange', KEYS[2], 0, 0, 'withscores');" + - "local expireIdleHead = redis.call('zrange', KEYS[3], 0, 0, 'withscores');" + - "local maxDate = table.remove(ARGV, 1); " // index is the first parameter - + "local hasExpire = #expireHead == 2 and tonumber(expireHead[2]) <= tonumber(maxDate); " - + "local hasExpireIdle = #expireIdleHead == 2 and tonumber(expireIdleHead[2]) <= tonumber(maxDate); " + "local currentTime = tonumber(table.remove(ARGV, 1)); " // index is the first parameter + + "local hasExpire = #expireHead == 2 and tonumber(expireHead[2]) <= currentTime; " + "local map = redis.call('hmget', KEYS[1], unpack(ARGV)); " + "for i = #map, 1, -1 do " + "local value = map[i]; " @@ -182,18 +187,18 @@ public class RedissonMapCache extends RedissonMap implements RMapCac + "if hasExpire then " + "local expireDate = redis.call('zscore', KEYS[2], key); " - + "if expireDate ~= false and tonumber(expireDate) <= tonumber(maxDate) then " + + "if expireDate ~= false and tonumber(expireDate) <= currentTime then " + "map[i] = false; " + "end; " + "end; " - + "if hasExpireIdle and t ~= 0 then " + + "if t ~= 0 then " + "local expireIdle = redis.call('zscore', KEYS[3], key); " + "if expireIdle ~= false then " - + "if tonumber(expireIdle) > tonumber(ARGV[1]) then " + + "if tonumber(expireIdle) > currentTime then " + "local value = struct.pack('dLc0', t, string.len(val), val); " + "redis.call('hset', KEYS[1], key, value); " - + "redis.call('zadd', KEYS[3], t + tonumber(ARGV[1]), key); " + + "redis.call('zadd', KEYS[3], t + currentTime, key); " + "else " + "map[i] = false; " + "end; " @@ -526,9 +531,13 @@ public class RedissonMapCache extends RedissonMap implements RMapCac @Override MapScanResult scanIterator(InetSocketAddress client, long startPos) { - Future> f = commandExecutor.evalReadAsync(client, getName(), new ScanCodec(codec), EVAL_HSCAN, + RedisCommand> EVAL_HSCAN = new RedisCommand>("EVAL", + new ListMultiDecoder(new LongMultiDecoder(), new ObjectMapDecoder(new ScanCodec(codec)), new ObjectListDecoder(codec), new MapCacheScanResultReplayDecoder()), ValueType.MAP); + Future> f = commandExecutor.evalReadAsync(client, getName(), codec, EVAL_HSCAN, "local result = {}; " + + "local idleKeys = {}; " + "local res = redis.call('hscan', KEYS[1], ARGV[2]); " + + "local currentTime = tonumber(ARGV[1]); " + "for i, value in ipairs(res[2]) do " + "if i % 2 == 0 then " + "local key = res[2][i-1]; " + @@ -542,22 +551,61 @@ public class RedissonMapCache extends RedissonMap implements RMapCac + "if t ~= 0 then " + "local expireIdle = redis.call('zscore', KEYS[3], key); " + "if expireIdle ~= false then " - + "if tonumber(expireIdle) > tonumber(ARGV[1]) then " - + "local value = struct.pack('dLc0', t, string.len(val), val); " - + "redis.call('hset', KEYS[1], key, value); " - + "redis.call('zadd', KEYS[3], t + tonumber(ARGV[1]), key); " + + "if tonumber(expireIdle) > currentTime and expireDate > currentTime then " + + "table.insert(idleKeys, key); " + "end; " + "expireDate = math.min(expireDate, tonumber(expireIdle)) " + "end; " + "end; " - + "if expireDate > tonumber(ARGV[1]) then " + + "if expireDate > currentTime then " + "table.insert(result, key); " + "table.insert(result, val); " + "end; " + "end; " + "end;" - + "return {res[1], result};", Arrays.asList(getName(), getTimeoutSetName(), getIdleSetName()), System.currentTimeMillis(), startPos); + + "return {res[1], result, idleKeys};", Arrays.asList(getName(), getTimeoutSetName(), getIdleSetName()), System.currentTimeMillis(), startPos); + + f.addListener(new FutureListener>() { + @Override + public void operationComplete(Future> future) + throws Exception { + if (future.isSuccess()) { + MapCacheScanResult res = future.getNow(); + if (res.getIdleKeys().isEmpty()) { + return; + } + + List args = new ArrayList(res.getIdleKeys().size() + 1); + args.add(System.currentTimeMillis()); + args.addAll(res.getIdleKeys()); + + commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand>("EVAL", new MapGetAllDecoder(args, 1), 7, ValueType.MAP_KEY, ValueType.MAP_VALUE), + "local currentTime = tonumber(table.remove(ARGV, 1)); " // index is the first parameter + + "local map = redis.call('hmget', KEYS[1], unpack(ARGV)); " + + "for i = #map, 1, -1 do " + + "local value = map[i]; " + + "if value ~= false then " + + "local key = ARGV[i]; " + + "local t, val = struct.unpack('dLc0', value); " + + + "if t ~= 0 then " + + "local expireIdle = redis.call('zscore', KEYS[2], key); " + + "if expireIdle ~= false then " + + "if tonumber(expireIdle) > currentTime then " + + "local value = struct.pack('dLc0', t, string.len(val), val); " + + "redis.call('hset', KEYS[1], key, value); " + + "redis.call('zadd', KEYS[2], t + currentTime, key); " + + "end; " + + "end; " + + "end; " + + "end; " + + "end; ", + Arrays.asList(getName(), getIdleSetName()), args.toArray()); + + } + } + }); return get(f); } @@ -691,4 +739,73 @@ public class RedissonMapCache extends RedissonMap implements RMapCac Arrays.asList(getName(), getTimeoutSetName(), getIdleSetName())); } + @Override + public Future>> readAllEntrySetAsync() { + return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_MAP_ENTRY, + "local s = redis.call('hgetall', KEYS[1]); " + + "local result = {}; " + + "for i, v in ipairs(s) do " + + "if i % 2 == 0 then " + + "local t, val = struct.unpack('dLc0', v); " + + "local key = s[i-1];" + + "local expireDate = 92233720368547758; " + + "local expireDateScore = redis.call('zscore', KEYS[2], key); " + + "if expireDateScore ~= false then " + + "expireDate = tonumber(expireDateScore) " + + "end; " + + "if t ~= 0 then " + + "local expireIdle = redis.call('zscore', KEYS[3], key); " + + "if expireIdle ~= false then " + + "if tonumber(expireIdle) > tonumber(ARGV[1]) then " + + "local value = struct.pack('dLc0', t, string.len(val), val); " + + "redis.call('hset', KEYS[1], key, value); " + + "redis.call('zadd', KEYS[3], t + tonumber(ARGV[1]), key); " + + "end; " + + "expireDate = math.min(expireDate, tonumber(expireIdle)) " + + "end; " + + "end; " + + "if expireDate > tonumber(ARGV[1]) then " + + "table.insert(result, key); " + + "table.insert(result, val); " + + "end; " + + "end; " + + "end;" + + "return result;", + Arrays.asList(getName(), getTimeoutSetName(), getIdleSetName()), System.currentTimeMillis()); + } + + @Override + public Future> readAllValuesAsync() { + return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_MAP_VALUE_LIST, + "local s = redis.call('hgetall', KEYS[1]); " + + "local result = {}; " + + "for i, v in ipairs(s) do " + + "if i % 2 == 0 then " + + "local t, val = struct.unpack('dLc0', v); " + + "local key = s[i-1];" + + "local expireDate = 92233720368547758; " + + "local expireDateScore = redis.call('zscore', KEYS[2], key); " + + "if expireDateScore ~= false then " + + "expireDate = tonumber(expireDateScore) " + + "end; " + + "if t ~= 0 then " + + "local expireIdle = redis.call('zscore', KEYS[3], key); " + + "if expireIdle ~= false then " + + "if tonumber(expireIdle) > tonumber(ARGV[1]) then " + + "local value = struct.pack('dLc0', t, string.len(val), val); " + + "redis.call('hset', KEYS[1], key, value); " + + "redis.call('zadd', KEYS[3], t + tonumber(ARGV[1]), key); " + + "end; " + + "expireDate = math.min(expireDate, tonumber(expireIdle)) " + + "end; " + + "end; " + + "if expireDate > tonumber(ARGV[1]) then " + + "table.insert(result, val); " + + "end; " + + "end; " + + "end;" + + "return result;", + Arrays.asList(getName(), getTimeoutSetName(), getIdleSetName()), System.currentTimeMillis()); + } + } diff --git a/src/main/java/org/redisson/client/handler/CommandDecoder.java b/src/main/java/org/redisson/client/handler/CommandDecoder.java index 260c21fb7..9e07d9818 100644 --- a/src/main/java/org/redisson/client/handler/CommandDecoder.java +++ b/src/main/java/org/redisson/client/handler/CommandDecoder.java @@ -35,6 +35,7 @@ import org.redisson.client.protocol.CommandsData; import org.redisson.client.protocol.Decoder; import org.redisson.client.protocol.QueueCommand; import org.redisson.client.protocol.RedisCommand.ValueType; +import org.redisson.client.protocol.decoder.ListMultiDecoder; import org.redisson.client.protocol.decoder.MultiDecoder; import org.redisson.client.protocol.decoder.NestedMultiDecoder; import org.redisson.client.protocol.pubsub.Message; @@ -90,7 +91,9 @@ public class CommandDecoder extends ReplayingDecoder { makeCheckpoint = false; } else { CommandData cmd = (CommandData)data; - if (cmd.getCommand().getReplayMultiDecoder() != null && NestedMultiDecoder.class.isAssignableFrom(cmd.getCommand().getReplayMultiDecoder().getClass())) { + if (cmd.getCommand().getReplayMultiDecoder() != null + && (NestedMultiDecoder.class.isAssignableFrom(cmd.getCommand().getReplayMultiDecoder().getClass()) + || ListMultiDecoder.class.isAssignableFrom(cmd.getCommand().getReplayMultiDecoder().getClass()))) { makeCheckpoint = false; } } @@ -139,7 +142,7 @@ public class CommandDecoder extends ReplayingDecoder { StateLevel firstLevel = state().getLevels().get(0); StateLevel secondLevel = state().getLevels().get(1); - decodeMulti(in, cmd, firstLevel.getParts(), ctx.channel(), secondLevel.getSize(), secondLevel.getParts()); + decodeList(in, cmd, firstLevel.getParts(), ctx.channel(), secondLevel.getSize(), secondLevel.getParts()); Channel channel = ctx.channel(); MultiDecoder decoder = messageDecoder(cmd, firstLevel.getParts(), channel); @@ -156,7 +159,7 @@ public class CommandDecoder extends ReplayingDecoder { state().resetLevel(); decode(in, cmd, null, ctx.channel()); } else { - decodeMulti(in, cmd, null, ctx.channel(), firstLevel.getSize(), firstLevel.getParts()); + decodeList(in, cmd, null, ctx.channel(), firstLevel.getSize(), firstLevel.getParts()); } } } @@ -267,13 +270,13 @@ public class CommandDecoder extends ReplayingDecoder { } } - decodeMulti(in, data, parts, channel, size, respParts); + decodeList(in, data, parts, channel, size, respParts); } else { throw new IllegalStateException("Can't decode replay " + (char)code); } } - private void decodeMulti(ByteBuf in, CommandData data, List parts, + private void decodeList(ByteBuf in, CommandData data, List parts, Channel channel, long size, List respParts) throws IOException { for (int i = respParts.size(); i < size; i++) { diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index e3737d259..1ee63f244 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -179,6 +179,7 @@ public interface RedisCommands { RedisCommand> EVAL_SET = new RedisCommand>("EVAL", new ObjectSetReplayDecoder()); RedisCommand EVAL_OBJECT = new RedisCommand("EVAL"); RedisCommand EVAL_MAP_VALUE = new RedisCommand("EVAL", ValueType.MAP_VALUE); + RedisCommand>> EVAL_MAP_ENTRY = new RedisCommand>>("EVAL", new ObjectMapEntryReplayDecoder(), ValueType.MAP); RedisCommand> EVAL_MAP_VALUE_LIST = new RedisCommand>("EVAL", new ObjectListReplayDecoder(), ValueType.MAP_VALUE); RedisStrictCommand INCR = new RedisStrictCommand("INCR"); diff --git a/src/main/java/org/redisson/client/protocol/decoder/ListMultiDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/ListMultiDecoder.java new file mode 100644 index 000000000..3b9df4013 --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/ListMultiDecoder.java @@ -0,0 +1,115 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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 java.io.IOException; +import java.util.List; + +import org.redisson.client.handler.State; + +import io.netty.buffer.ByteBuf; + +public class ListMultiDecoder implements MultiDecoder { + + private final MultiDecoder[] decoders; + + public static class NestedDecoderState implements DecoderState { + + int index = -1; + int partsIndex = -1; + + public NestedDecoderState() { + } + + public NestedDecoderState(int index) { + super(); + this.index = index; + } + + public void resetPartsIndex() { + partsIndex = -1; + } + + public int incPartsIndex() { + return ++partsIndex; + } + + public int getPartsIndex() { + return partsIndex; + } + + public int incIndex() { + return ++index; + } + + public int getIndex() { + return index; + } + + @Override + public DecoderState copy() { + return new NestedDecoderState(index); + } + + @Override + public String toString() { + return "NestedDecoderState [index=" + index + "]"; + } + + } + + protected final NestedDecoderState getDecoder(State state) { + NestedDecoderState ds = state.getDecoderState(); + if (ds == null) { + ds = new NestedDecoderState(); + state.setDecoderState(ds); + } + return ds; + } + + public ListMultiDecoder(MultiDecoder ... decoders) { + this.decoders = decoders; + } + + public Object decode(ByteBuf buf, State state) throws IOException { + int index = getDecoder(state).getIndex(); + return decoders[index].decode(buf, state); + } + + @Override + public boolean isApplicable(int paramNum, State state) { + if (paramNum == 0) { + NestedDecoderState s = getDecoder(state); + s.incIndex(); + s.resetPartsIndex(); + } + return true; + } + + @Override + public Object decode(List parts, State state) { + NestedDecoderState s = getDecoder(state); + int index = s.getIndex(); + index += s.incPartsIndex(); + Object res = decoders[index].decode(parts, state); + if (res == null) { + index = s.incIndex() + s.getPartsIndex(); + return decoders[index].decode(parts, state); + } + return res; + } + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/LongMultiDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/LongMultiDecoder.java new file mode 100644 index 000000000..30a2ee66d --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/LongMultiDecoder.java @@ -0,0 +1,43 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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 java.io.IOException; +import java.util.List; + +import org.redisson.client.codec.LongCodec; +import org.redisson.client.handler.State; + +import io.netty.buffer.ByteBuf; + +public class LongMultiDecoder implements MultiDecoder { + + @Override + public Object decode(ByteBuf buf, State state) throws IOException { + return LongCodec.INSTANCE.getValueDecoder().decode(buf, state); + } + + @Override + public boolean isApplicable(int paramNum, State state) { + return false; + } + + @Override + public Object decode(List parts, State state) { + return null; + } + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/MapCacheScanResult.java b/src/main/java/org/redisson/client/protocol/decoder/MapCacheScanResult.java new file mode 100644 index 000000000..0a6e9b2f2 --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/MapCacheScanResult.java @@ -0,0 +1,34 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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 java.util.List; +import java.util.Map; + +public class MapCacheScanResult extends MapScanResult { + + private final List idleKeys; + + public MapCacheScanResult(Long pos, Map values, List idleKeys) { + super(pos, values); + this.idleKeys = idleKeys; + }; + + public List getIdleKeys() { + return idleKeys; + } + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/MapCacheScanResultReplayDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/MapCacheScanResultReplayDecoder.java new file mode 100644 index 000000000..afed8cc14 --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/MapCacheScanResultReplayDecoder.java @@ -0,0 +1,46 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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 java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.redisson.client.handler.State; + +import io.netty.buffer.ByteBuf; + +public class MapCacheScanResultReplayDecoder implements MultiDecoder> { + + @Override + public Object decode(ByteBuf buf, State state) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public MapCacheScanResult decode(List parts, State state) { + Long pos = (Long)parts.get(0); + Map values = (Map)parts.get(1); + List idleKeys = (List) parts.get(2); + return new MapCacheScanResult(pos, values, idleKeys); + } + + @Override + public boolean isApplicable(int paramNum, State state) { + return false; + } + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/ObjectListDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/ObjectListDecoder.java new file mode 100644 index 000000000..9b3ac26ee --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/ObjectListDecoder.java @@ -0,0 +1,50 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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 java.io.IOException; +import java.util.List; + +import org.redisson.client.codec.Codec; +import org.redisson.client.handler.State; + +import io.netty.buffer.ByteBuf; + +public class ObjectListDecoder implements MultiDecoder> { + + private Codec codec; + + public ObjectListDecoder(Codec codec) { + super(); + this.codec = codec; + } + + @Override + public Object decode(ByteBuf buf, State state) throws IOException { + return codec.getMapKeyDecoder().decode(buf, state); + } + + @Override + public List decode(List parts, State state) { + return (List) parts; + } + + @Override + public boolean isApplicable(int paramNum, State state) { + return false; + } + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/ObjectMapDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/ObjectMapDecoder.java new file mode 100644 index 000000000..3b1c1c6bb --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/ObjectMapDecoder.java @@ -0,0 +1,63 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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 java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.redisson.client.codec.Codec; +import org.redisson.client.handler.State; + +import io.netty.buffer.ByteBuf; + +public class ObjectMapDecoder implements MultiDecoder> { + + private Codec codec; + + public ObjectMapDecoder(Codec codec) { + super(); + this.codec = codec; + } + + private int pos; + + @Override + public Object decode(ByteBuf buf, State state) throws IOException { + if (pos++ % 2 == 0) { + return codec.getMapKeyDecoder().decode(buf, state); + } + return codec.getMapValueDecoder().decode(buf, state); + } + + @Override + public Map decode(List parts, State state) { + Map result = new HashMap(parts.size()/2); + for (int i = 0; i < parts.size(); i++) { + if (i % 2 != 0) { + result.put(parts.get(i-1), parts.get(i)); + } + } + return result; + } + + @Override + public boolean isApplicable(int paramNum, State state) { + return true; + } + +} diff --git a/src/test/java/org/redisson/RedissonMapCacheTest.java b/src/test/java/org/redisson/RedissonMapCacheTest.java index e1475d4e7..3edbb2216 100644 --- a/src/test/java/org/redisson/RedissonMapCacheTest.java +++ b/src/test/java/org/redisson/RedissonMapCacheTest.java @@ -14,8 +14,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; import org.redisson.codec.JsonJacksonCodec; @@ -127,13 +125,20 @@ public class RedissonMapCacheTest extends BaseTest { } } + + @Test + public void testCacheValues() { + final RMapCache map = redisson.getMapCache("testRMapCacheValues"); + map.put("1234", "5678", 0, TimeUnit.MINUTES, 60, TimeUnit.MINUTES); + assertThat(map.values()).containsOnly("5678"); + } @Test public void testGetAll() throws InterruptedException { RMapCache map = redisson.getMapCache("getAll"); map.put(1, 100); map.put(2, 200, 1, TimeUnit.SECONDS); - map.put(3, 300, 1, TimeUnit.SECONDS); + map.put(3, 300, 1, TimeUnit.SECONDS, 1, TimeUnit.SECONDS); map.put(4, 400); Map filtered = map.getAll(new HashSet(Arrays.asList(2, 3, 5))); @@ -265,10 +270,12 @@ public class RedissonMapCacheTest extends BaseTest { map.put(2, "33", 1, TimeUnit.SECONDS); map.put(3, "43"); - Assert.assertEquals(3, map.entrySet().size()); - - MatcherAssert.assertThat(map, Matchers.hasEntry(Matchers.equalTo(1), Matchers.equalTo("12"))); - MatcherAssert.assertThat(map, Matchers.hasEntry(Matchers.equalTo(3), Matchers.equalTo("43"))); + Map expected = new HashMap<>(); + map.put(1, "12"); + map.put(3, "43"); + + assertThat(map.entrySet()).containsAll(expected.entrySet()); + assertThat(map).hasSize(3); } @Test @@ -297,7 +304,7 @@ public class RedissonMapCacheTest extends BaseTest { joinMap.put(6, "6"); map.putAll(joinMap); - MatcherAssert.assertThat(map.keySet(), Matchers.containsInAnyOrder(1, 2, 3, 4, 5, 6)); + assertThat(map.keySet()).containsOnly(1, 2, 3, 4, 5, 6); } @Test @@ -579,47 +586,6 @@ public class RedissonMapCacheTest extends BaseTest { Assert.assertEquals(1, map.size()); } -// @Test -// public void testKeyIterator() { -// RMap map = redisson.getCache("simple"); -// map.put(1, 0); -// map.put(3, 5); -// map.put(4, 6); -// map.put(7, 8); -// -// Collection keys = map.keySet(); -// MatcherAssert.assertThat(keys, Matchers.containsInAnyOrder(1, 3, 4, 7)); -// for (Iterator iterator = map.keyIterator(); iterator.hasNext();) { -// Integer value = iterator.next(); -// if (!keys.remove(value)) { -// Assert.fail(); -// } -// } -// -// Assert.assertEquals(0, keys.size()); -// } - -// @Test -// public void testValueIterator() { -// RCache map = redisson.getCache("simple"); -// map.put(1, 0); -// map.put(3, 5); -// map.put(4, 6); -// map.put(7, 8); -// -// Collection values = map.values(); -// MatcherAssert.assertThat(values, Matchers.containsInAnyOrder(0, 5, 6, 8)); -// for (Iterator iterator = map.valueIterator(); iterator.hasNext();) { -// Integer value = iterator.next(); -// if (!values.remove(value)) { -// Assert.fail(); -// } -// } -// -// Assert.assertEquals(0, values.size()); -// } - - @Test public void testFastPutIfAbsent() throws Exception { RMapCache map = redisson.getMapCache("simple"); @@ -767,6 +733,7 @@ public class RedissonMapCacheTest extends BaseTest { } + @Test public void testRMapCacheValues() { final RMapCache map = redisson.getMapCache("testRMapCacheValues"); @@ -775,7 +742,18 @@ public class RedissonMapCacheTest extends BaseTest { } @Test - public void testRMapCacheAllValues() { + public void testReadAllEntrySet() throws InterruptedException { + RMapCache map = redisson.getMapCache("simple12"); + map.put(1, "12"); + map.put(2, "33", 10, TimeUnit.MINUTES, 60, TimeUnit.MINUTES); + map.put(3, "43"); + + assertThat(map.readAllEntrySet()).isEqualTo(map.entrySet()); + } + + + @Test + public void testReadAllValues() { final RMapCache map = redisson.getMapCache("testRMapCacheAllValues"); map.put("1234", "5678", 1, TimeUnit.MINUTES, 60, TimeUnit.MINUTES); assertThat(map.readAllValues()).containsOnly("5678"); From b4bf2c66b29ebdd7263c1a8a7b68cf59fbda442f Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 19 Apr 2016 12:14:38 +0300 Subject: [PATCH 068/120] RGeo.addAsync codec definition fixed --- src/main/java/org/redisson/RedissonGeo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/redisson/RedissonGeo.java b/src/main/java/org/redisson/RedissonGeo.java index 527892d25..20ca9a3d1 100644 --- a/src/main/java/org/redisson/RedissonGeo.java +++ b/src/main/java/org/redisson/RedissonGeo.java @@ -62,7 +62,7 @@ public class RedissonGeo extends RedissonExpirable implements RGeo { @Override public Future addAsync(double longitude, double latitude, V member) { - return commandExecutor.writeAsync(getName(), RedisCommands.GEOADD, getName(), convert(longitude), convert(latitude), member); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.GEOADD, getName(), convert(longitude), convert(latitude), member); } private String convert(double longitude) { From fca186befb1946beae3d9a2d326863453b2ab91c Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 19 Apr 2016 16:38:17 +0300 Subject: [PATCH 069/120] Fixed bug with items removing during RedissonMap iteration --- .../org/redisson/RedissonBaseMapIterator.java | 36 +++++++++++-------- .../org/redisson/RedissonMapCacheTest.java | 22 ++++++++++++ .../java/org/redisson/RedissonMapTest.java | 13 +++++++ 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/redisson/RedissonBaseMapIterator.java b/src/main/java/org/redisson/RedissonBaseMapIterator.java index d493efb6d..140cf747a 100644 --- a/src/main/java/org/redisson/RedissonBaseMapIterator.java +++ b/src/main/java/org/redisson/RedissonBaseMapIterator.java @@ -23,7 +23,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; -import org.redisson.client.protocol.decoder.ListScanResult; import org.redisson.client.protocol.decoder.MapScanResult; import org.redisson.client.protocol.decoder.ScanObjectEntry; @@ -32,7 +31,8 @@ import io.netty.buffer.ByteBuf; abstract class RedissonBaseMapIterator implements Iterator { private Map firstValues; - private Iterator> iter; + private Map lastValues; + private Iterator> lastIter; protected long nextIterPos; protected long startPos = -1; protected InetSocketAddress client; @@ -48,7 +48,7 @@ abstract class RedissonBaseMapIterator implements Iterator { return false; } - if (iter == null || !iter.hasNext()) { + if (lastIter == null || !lastIter.hasNext()) { if (nextIterPos == -1) { return false; } @@ -56,31 +56,38 @@ abstract class RedissonBaseMapIterator implements Iterator { do { prevIterPos = nextIterPos; MapScanResult res = iterator(); + if (lastValues != null) { + free(lastValues); + } + lastValues = convert(res.getMap()); client = res.getRedisClient(); if (startPos == -1) { startPos = res.getPos(); } if (nextIterPos == 0 && firstValues == null) { - firstValues = convert(res.getMap()); + firstValues = lastValues; + lastValues = null; } else { - Map newValues = convert(res.getMap()); - if (firstValues.entrySet().containsAll(newValues.entrySet())) { + if (firstValues.isEmpty()) { + firstValues = lastValues; + lastValues = null; + } else if (lastValues.keySet().removeAll(firstValues.keySet())) { finished = true; free(firstValues); - free(newValues); + free(lastValues); firstValues = null; + lastValues = null; return false; } - free(newValues); } - iter = res.getMap().entrySet().iterator(); + lastIter = res.getMap().entrySet().iterator(); nextIterPos = res.getPos(); - } while (!iter.hasNext() && nextIterPos != prevIterPos); + } while (!lastIter.hasNext() && nextIterPos != prevIterPos); if (prevIterPos == nextIterPos && !removeExecuted) { nextIterPos = -1; } } - return iter.hasNext(); + return lastIter.hasNext(); } @@ -107,7 +114,7 @@ abstract class RedissonBaseMapIterator implements Iterator { throw new NoSuchElementException("No such element at index"); } - entry = iter.next(); + entry = lastIter.next(); currentElementRemoved = false; return getValue(entry); } @@ -129,11 +136,12 @@ abstract class RedissonBaseMapIterator implements Iterator { if (currentElementRemoved) { throw new IllegalStateException("Element been already deleted"); } - if (iter == null) { + if (lastIter == null) { throw new IllegalStateException(); } - iter.remove(); + firstValues.remove(entry.getKey().getBuf()); + lastIter.remove(); removeKey(); currentElementRemoved = true; removeExecuted = true; diff --git a/src/test/java/org/redisson/RedissonMapCacheTest.java b/src/test/java/org/redisson/RedissonMapCacheTest.java index 3edbb2216..c98039f4f 100644 --- a/src/test/java/org/redisson/RedissonMapCacheTest.java +++ b/src/test/java/org/redisson/RedissonMapCacheTest.java @@ -227,6 +227,28 @@ public class RedissonMapCacheTest extends BaseTest { Assert.assertEquals(0, map.size()); } + @Test + public void testIteratorRandomRemoveFirst() throws InterruptedException { + RMapCache map = redisson.getMapCache("simpleMap"); + for (int i = 0; i < 1000; i++) { + map.put(i, i*10); + } + + int cnt = 0; + int removed = 0; + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Entry entry = iterator.next(); + if (cnt < 20) { + iterator.remove(); + removed++; + } + cnt++; + } + Assert.assertEquals(1000, cnt); + assertThat(map.size()).isEqualTo(cnt - removed); + } + @Test public void testIteratorRandomRemoveHighVolume() throws InterruptedException { RMapCache map = redisson.getMapCache("simpleMap"); diff --git a/src/test/java/org/redisson/RedissonMapTest.java b/src/test/java/org/redisson/RedissonMapTest.java index 0a46a6558..8ae9a0196 100644 --- a/src/test/java/org/redisson/RedissonMapTest.java +++ b/src/test/java/org/redisson/RedissonMapTest.java @@ -11,6 +11,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; @@ -408,6 +409,18 @@ public class RedissonMapTest extends BaseTest { Map testMap = new HashMap<>(map); assertThat(map.readAllKeySet()).containsOnlyElementsOf(testMap.keySet()); } + + @Test + public void testReadAllKeySetHighAmount() { + RMap map = redisson.getMap("simple"); + for (int i = 0; i < 1000; i++) { + map.put(new SimpleKey("" + i), new SimpleValue("" + i)); + } + + assertThat(map.readAllKeySet().size()).isEqualTo(1000); + Map testMap = new HashMap<>(map); + assertThat(map.readAllKeySet()).containsOnlyElementsOf(testMap.keySet()); + } @Test public void testReadAllValues() { From 13664c3153300054855a52d80b8899f3c3745b79 Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 20 Apr 2016 11:23:24 +0300 Subject: [PATCH 070/120] Fixed cluster initialization. #476 --- .../cluster/ClusterConnectionManager.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/redisson/cluster/ClusterConnectionManager.java b/src/main/java/org/redisson/cluster/ClusterConnectionManager.java index 12b8518d6..de73bd42b 100644 --- a/src/main/java/org/redisson/cluster/ClusterConnectionManager.java +++ b/src/main/java/org/redisson/cluster/ClusterConnectionManager.java @@ -74,18 +74,31 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { try { RedisConnection connection = connectionFuture.syncUninterruptibly().getNow(); String nodesValue = connection.sync(RedisCommands.CLUSTER_NODES); + + log.debug("cluster nodes state from {} during startup:\n{}", connection.getRedisClient().getAddr(), nodesValue); Collection partitions = parsePartitions(nodesValue); List>>> futures = new ArrayList>>>(); for (ClusterPartition partition : partitions) { + if (partition.isMasterFail()) { + continue; + } Future>> masterFuture = addMasterEntry(partition, cfg); futures.add(masterFuture); } for (Future>> masterFuture : futures) { - masterFuture.syncUninterruptibly(); + masterFuture.awaitUninterruptibly(); + if (!masterFuture.isSuccess()) { + log.error("Can't connect to master node.", masterFuture.cause()); + continue; + } for (Future future : masterFuture.getNow()) { - future.syncUninterruptibly(); + future.awaitUninterruptibly(); + if (!future.isSuccess()) { + log.error("Can't add nodes.", masterFuture.cause()); + continue; + } } } break; @@ -102,7 +115,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { scheduleClusterChangeCheck(cfg); } - + private Future connect(ClusterServersConfig cfg, final URI addr) { RedisConnection connection = nodeConnections.get(addr); if (connection != null) { @@ -181,7 +194,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { @Override public void operationComplete(Future> future) throws Exception { if (!future.isSuccess()) { - log.error("Can't execute CLUSTER_INFO with " + connection.getRedisClient().getAddr(), future.cause()); + log.error("Can't execute CLUSTER_INFO for " + connection.getRedisClient().getAddr(), future.cause()); result.setFailure(future.cause()); return; } From 16c5df230f79a4f018d060933d0630a04698f601 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 21 Apr 2016 13:25:43 +0300 Subject: [PATCH 071/120] Fixed - cluster state update manager can't try next node if current node fails. #476 --- .../org/redisson/client/RedisConnection.java | 2 +- .../cluster/ClusterConnectionManager.java | 55 +++++++++++-------- .../redisson/connection/MasterSlaveEntry.java | 15 ++++- .../balancer/LoadBalancerManager.java | 2 + .../balancer/LoadBalancerManagerImpl.java | 17 +++--- .../connection/pool/ConnectionPool.java | 13 +++-- 6 files changed, 69 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/redisson/client/RedisConnection.java b/src/main/java/org/redisson/client/RedisConnection.java index fc98647fd..72158ff59 100644 --- a/src/main/java/org/redisson/client/RedisConnection.java +++ b/src/main/java/org/redisson/client/RedisConnection.java @@ -222,7 +222,7 @@ public class RedisConnection implements RedisCommands { @Override public String toString() { - return getClass().getSimpleName() + " [redisClient=" + redisClient + ", channel=" + channel + "]"; + return getClass().getSimpleName() + "@" + System.identityHashCode(this) + " [redisClient=" + redisClient + ", channel=" + channel + "]"; } public Future getAcquireFuture() { diff --git a/src/main/java/org/redisson/cluster/ClusterConnectionManager.java b/src/main/java/org/redisson/cluster/ClusterConnectionManager.java index de73bd42b..ce8cddf4b 100644 --- a/src/main/java/org/redisson/cluster/ClusterConnectionManager.java +++ b/src/main/java/org/redisson/cluster/ClusterConnectionManager.java @@ -113,7 +113,13 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { throw new RedisConnectionException("Can't connect to servers!", lastException); } - scheduleClusterChangeCheck(cfg); + scheduleClusterChangeCheck(cfg, null); + } + + private void close(RedisConnection conn) { + if (nodeConnections.values().remove(conn)) { + conn.closeAsync(); + } } private Future connect(ClusterServersConfig cfg, final URI addr) { @@ -188,7 +194,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { } final RedisConnection connection = future.getNow(); - Future> clusterFuture = connection.async(RedisCommands.CLUSTER_INFO); + Future> clusterFuture = connection.asyncWithTimeout(null, RedisCommands.CLUSTER_INFO); clusterFuture.addListener(new FutureListener>() { @Override @@ -256,26 +262,31 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { return result; } - private void scheduleClusterChangeCheck(final ClusterServersConfig cfg) { + private void scheduleClusterChangeCheck(final ClusterServersConfig cfg, final Iterator iterator) { monitorFuture = GlobalEventExecutor.INSTANCE.schedule(new Runnable() { @Override public void run() { - List nodes = new ArrayList(); - List slaves = new ArrayList(); AtomicReference lastException = new AtomicReference(); - for (ClusterPartition partition : lastPartitions.values()) { - if (!partition.isMasterFail()) { - nodes.add(partition.getMasterAddress()); + Iterator nodesIterator = iterator; + if (nodesIterator == null) { + List nodes = new ArrayList(); + List slaves = new ArrayList(); + for (ClusterPartition partition : lastPartitions.values()) { + if (!partition.isMasterFail()) { + nodes.add(partition.getMasterAddress()); + } + + Set partitionSlaves = new HashSet(partition.getSlaveAddresses()); + partitionSlaves.removeAll(partition.getFailedSlaveAddresses()); + slaves.addAll(partitionSlaves); } - - Set partitionSlaves = new HashSet(partition.getSlaveAddresses()); - partitionSlaves.removeAll(partition.getFailedSlaveAddresses()); - slaves.addAll(partitionSlaves); + // master nodes first + nodes.addAll(slaves); + + nodesIterator = nodes.iterator(); } - // master nodes first - nodes.addAll(slaves); - checkClusterState(cfg, nodes.iterator(), lastException); + checkClusterState(cfg, nodesIterator, lastException); } }, cfg.getScanInterval(), TimeUnit.MILLISECONDS); @@ -284,7 +295,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { private void checkClusterState(final ClusterServersConfig cfg, final Iterator iterator, final AtomicReference lastException) { if (!iterator.hasNext()) { log.error("Can't update cluster state", lastException.get()); - scheduleClusterChangeCheck(cfg); + scheduleClusterChangeCheck(cfg, null); return; } URI uri = iterator.next(); @@ -299,19 +310,20 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { } RedisConnection connection = future.getNow(); - updateClusterState(cfg, connection); + updateClusterState(cfg, connection, iterator); } }); } - private void updateClusterState(final ClusterServersConfig cfg, final RedisConnection connection) { - Future future = connection.async(RedisCommands.CLUSTER_NODES); + private void updateClusterState(final ClusterServersConfig cfg, final RedisConnection connection, Iterator iterator) { + Future future = connection.asyncWithTimeout(null, RedisCommands.CLUSTER_NODES); future.addListener(new FutureListener() { @Override public void operationComplete(Future future) throws Exception { if (!future.isSuccess()) { log.error("Can't execute CLUSTER_NODES with " + connection.getRedisClient().getAddr(), future.cause()); - scheduleClusterChangeCheck(cfg); + close(connection); + scheduleClusterChangeCheck(cfg, iterator); return; } @@ -322,7 +334,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { checkMasterNodesChange(newPartitions); checkSlaveNodesChange(newPartitions); checkSlotsChange(cfg, newPartitions); - scheduleClusterChangeCheck(cfg); + scheduleClusterChangeCheck(cfg, null); } }); } @@ -432,7 +444,6 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { URI oldUri = currentPart.getMasterAddress(); changeMaster(currentSlotRange, newUri.getHost(), newUri.getPort()); - slaveDown(currentSlotRange, oldUri.getHost(), oldUri.getPort(), FreezeReason.MANAGER); currentPart.setMasterAddress(newMasterPart.getMasterAddress()); } diff --git a/src/main/java/org/redisson/connection/MasterSlaveEntry.java b/src/main/java/org/redisson/connection/MasterSlaveEntry.java index ce8bea5c6..ff26955be 100644 --- a/src/main/java/org/redisson/connection/MasterSlaveEntry.java +++ b/src/main/java/org/redisson/connection/MasterSlaveEntry.java @@ -96,12 +96,25 @@ public class MasterSlaveEntry { return writeConnectionHolder.add(masterEntry); } + private boolean slaveDown(ClientConnectionsEntry entry, FreezeReason freezeReason) { + ClientConnectionsEntry e = slaveBalancer.freeze(entry, freezeReason); + if (e == null) { + return false; + } + + return slaveDown(e); + } + public boolean slaveDown(String host, int port, FreezeReason freezeReason) { ClientConnectionsEntry entry = slaveBalancer.freeze(host, port, freezeReason); if (entry == null) { return false; } + return slaveDown(entry); + } + + private boolean slaveDown(ClientConnectionsEntry entry) { // add master as slave if no more slaves available if (config.getReadMode() == ReadMode.SLAVE && slaveBalancer.getAvailableClients() == 0) { InetSocketAddress addr = masterEntry.getClient().getAddr(); @@ -305,7 +318,7 @@ public class MasterSlaveEntry { ClientConnectionsEntry oldMaster = masterEntry; setupMasterEntry(host, port); writeConnectionHolder.remove(oldMaster); - oldMaster.freezeMaster(FreezeReason.MANAGER); + slaveDown(oldMaster, FreezeReason.MANAGER); // more than one slave available, so master can be removed from slaves if (config.getReadMode() == ReadMode.SLAVE diff --git a/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java b/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java index edef7d2bb..02d408f29 100644 --- a/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java +++ b/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java @@ -36,6 +36,8 @@ public interface LoadBalancerManager { boolean unfreeze(String host, int port, FreezeReason freezeReason); + ClientConnectionsEntry freeze(ClientConnectionsEntry connectionEntry, FreezeReason freezeReason); + ClientConnectionsEntry freeze(String host, int port, FreezeReason freezeReason); Future add(ClientConnectionsEntry entry); diff --git a/src/main/java/org/redisson/connection/balancer/LoadBalancerManagerImpl.java b/src/main/java/org/redisson/connection/balancer/LoadBalancerManagerImpl.java index 38135c62d..38bf4b781 100644 --- a/src/main/java/org/redisson/connection/balancer/LoadBalancerManagerImpl.java +++ b/src/main/java/org/redisson/connection/balancer/LoadBalancerManagerImpl.java @@ -94,25 +94,28 @@ public class LoadBalancerManagerImpl implements LoadBalancerManager { } return false; } - + public ClientConnectionsEntry freeze(String host, int port, FreezeReason freezeReason) { InetSocketAddress addr = new InetSocketAddress(host, port); ClientConnectionsEntry connectionEntry = addr2Entry.get(addr); + return freeze(connectionEntry, freezeReason); + } + + public ClientConnectionsEntry freeze(ClientConnectionsEntry connectionEntry, FreezeReason freezeReason) { if (connectionEntry == null) { return null; } synchronized (connectionEntry) { - if (connectionEntry.isFreezed()) { - return null; - } - - connectionEntry.setFreezed(true); - // only RECONNECT freeze reason could be replaced if (connectionEntry.getFreezeReason() == null || connectionEntry.getFreezeReason() == FreezeReason.RECONNECT) { + connectionEntry.setFreezed(true); connectionEntry.setFreezeReason(freezeReason); + return connectionEntry; + } + if (connectionEntry.isFreezed()) { + return null; } } diff --git a/src/main/java/org/redisson/connection/pool/ConnectionPool.java b/src/main/java/org/redisson/connection/pool/ConnectionPool.java index 87dc8a594..3c63d632b 100644 --- a/src/main/java/org/redisson/connection/pool/ConnectionPool.java +++ b/src/main/java/org/redisson/connection/pool/ConnectionPool.java @@ -153,10 +153,15 @@ abstract class ConnectionPool { } } - StringBuilder errorMsg = new StringBuilder("Connection pool exhausted! All connections are busy. Increase connection pool size."); -// if (!freezed.isEmpty()) { -// errorMsg.append(" Disconnected hosts: " + freezed); -// } + StringBuilder errorMsg; + if (connectionManager.isClusterMode()) { + errorMsg = new StringBuilder("Connection pool exhausted! for slots: " + masterSlaveEntry.getSlotRanges()); + } else { + errorMsg = new StringBuilder("Connection pool exhausted! "); + } + if (!freezed.isEmpty()) { + errorMsg.append(" Disconnected hosts: " + freezed); + } if (!zeroConnectionsAmount.isEmpty()) { errorMsg.append(" Hosts with fully busy connections: " + zeroConnectionsAmount); } From b870b9cc086cfdbfc02fedde71a7dd66cf30c030 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 21 Apr 2016 13:33:43 +0300 Subject: [PATCH 072/120] compilation fixed --- .../java/org/redisson/cluster/ClusterConnectionManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/redisson/cluster/ClusterConnectionManager.java b/src/main/java/org/redisson/cluster/ClusterConnectionManager.java index ce8cddf4b..0fd5e56b8 100644 --- a/src/main/java/org/redisson/cluster/ClusterConnectionManager.java +++ b/src/main/java/org/redisson/cluster/ClusterConnectionManager.java @@ -315,7 +315,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { }); } - private void updateClusterState(final ClusterServersConfig cfg, final RedisConnection connection, Iterator iterator) { + private void updateClusterState(final ClusterServersConfig cfg, final RedisConnection connection, final Iterator iterator) { Future future = connection.asyncWithTimeout(null, RedisCommands.CLUSTER_NODES); future.addListener(new FutureListener() { @Override From a123525d7fb2275484149b6c772094f1d31f874a Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Thu, 21 Apr 2016 17:18:27 +0300 Subject: [PATCH 073/120] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c21b0127..b3141bd9b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Redisson - distributed and scalable Java objects powered by Redis. Advanced Java Redis client +Redisson - Redis based In-Memory Data Grid for Java. ==== [![Maven Central](https://img.shields.io/maven-central/v/org.redisson/redisson.svg?style=flat-square)](https://maven-badges.herokuapp.com/maven-central/org.redisson/redisson/) From 0e393855e24e68ce40edb82bac4e9c61522c1443 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Thu, 21 Apr 2016 17:20:02 +0300 Subject: [PATCH 074/120] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b3141bd9b..a33569be2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -Redisson - Redis based In-Memory Data Grid for Java. +Redisson. +==== +Redis based In-Memory Data Grid for Java. ==== [![Maven Central](https://img.shields.io/maven-central/v/org.redisson/redisson.svg?style=flat-square)](https://maven-badges.herokuapp.com/maven-central/org.redisson/redisson/) From 16937e37f65b788e7937a2cc3478ab7a7a803b89 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Thu, 21 Apr 2016 17:20:34 +0300 Subject: [PATCH 075/120] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index a33569be2..2fbe92591 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ -Redisson. -==== -Redis based In-Memory Data Grid for Java. +Redisson: Redis based In-Memory Data Grid for Java. ==== [![Maven Central](https://img.shields.io/maven-central/v/org.redisson/redisson.svg?style=flat-square)](https://maven-badges.herokuapp.com/maven-central/org.redisson/redisson/) From 3304e3caefeb38144922e6feea154af9a37220c8 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 21 Apr 2016 17:28:02 +0300 Subject: [PATCH 076/120] refactoring --- .../org/redisson/RedissonBaseMapIterator.java | 53 ++++++++++++++----- src/main/java/org/redisson/RedissonMap.java | 47 ++++++++-------- .../java/org/redisson/RedissonMapCache.java | 2 +- .../org/redisson/RedissonMapIterator.java | 2 +- 4 files changed, 69 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/redisson/RedissonBaseMapIterator.java b/src/main/java/org/redisson/RedissonBaseMapIterator.java index 140cf747a..2af67769b 100644 --- a/src/main/java/org/redisson/RedissonBaseMapIterator.java +++ b/src/main/java/org/redisson/RedissonBaseMapIterator.java @@ -34,7 +34,6 @@ abstract class RedissonBaseMapIterator implements Iterator { private Map lastValues; private Iterator> lastIter; protected long nextIterPos; - protected long startPos = -1; protected InetSocketAddress client; private boolean finished; @@ -44,13 +43,22 @@ abstract class RedissonBaseMapIterator implements Iterator { @Override public boolean hasNext() { - if (finished) { - return false; - } - if (lastIter == null || !lastIter.hasNext()) { - if (nextIterPos == -1) { - return false; + if (finished) { + free(firstValues); + free(lastValues); + + currentElementRemoved = false; + removeExecuted = false; + client = null; + firstValues = null; + lastValues = null; + nextIterPos = 0; + + if (!tryAgain()) { + return false; + } + finished = false; } long prevIterPos; do { @@ -61,22 +69,36 @@ abstract class RedissonBaseMapIterator implements Iterator { } lastValues = convert(res.getMap()); client = res.getRedisClient(); - if (startPos == -1) { - startPos = res.getPos(); - } if (nextIterPos == 0 && firstValues == null) { firstValues = lastValues; lastValues = null; + if (firstValues.isEmpty() && tryAgain()) { + client = null; + firstValues = null; + prevIterPos = -1; + } } else { if (firstValues.isEmpty()) { firstValues = lastValues; lastValues = null; + if (firstValues.isEmpty() && tryAgain()) { + continue; + } } else if (lastValues.keySet().removeAll(firstValues.keySet())) { - finished = true; free(firstValues); free(lastValues); + + currentElementRemoved = false; + removeExecuted = false; + client = null; firstValues = null; lastValues = null; + nextIterPos = 0; + prevIterPos = -1; + if (tryAgain()) { + continue; + } + finished = true; return false; } } @@ -84,16 +106,23 @@ abstract class RedissonBaseMapIterator implements Iterator { nextIterPos = res.getPos(); } while (!lastIter.hasNext() && nextIterPos != prevIterPos); if (prevIterPos == nextIterPos && !removeExecuted) { - nextIterPos = -1; + finished = true; } } return lastIter.hasNext(); } + protected boolean tryAgain() { + return false; + } + protected abstract MapScanResult iterator(); private void free(Map map) { + if (map == null) { + return; + } for (Entry entry : map.entrySet()) { entry.getKey().release(); entry.getValue().release(); diff --git a/src/main/java/org/redisson/RedissonMap.java b/src/main/java/org/redisson/RedissonMap.java index e72d4b074..0480dc52f 100644 --- a/src/main/java/org/redisson/RedissonMap.java +++ b/src/main/java/org/redisson/RedissonMap.java @@ -95,7 +95,7 @@ public class RedissonMap extends RedissonExpirable implements RMap { @Override public Future containsKeyAsync(Object key) { - return commandExecutor.readAsync(getName(), codec, RedisCommands.HEXISTS, getName(), key); + return commandExecutor.readAsync(getName(key), codec, RedisCommands.HEXISTS, getName(key), key); } @Override @@ -226,13 +226,13 @@ public class RedissonMap extends RedissonExpirable implements RMap { @Override public Future putIfAbsentAsync(K key, V value) { - return commandExecutor.evalWriteAsync(getName(), codec, EVAL_PUT, + return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_PUT, "if redis.call('hsetnx', KEYS[1], ARGV[1], ARGV[2]) == 1 then " + "return nil " + "else " + "return redis.call('hget', KEYS[1], ARGV[1]) " + "end", - Collections.singletonList(getName()), key, value); + Collections.singletonList(getName(key)), key, value); } @Override @@ -242,7 +242,7 @@ public class RedissonMap extends RedissonExpirable implements RMap { @Override public Future fastPutIfAbsentAsync(K key, V value) { - return commandExecutor.writeAsync(getName(), codec, RedisCommands.HSETNX, getName(), key, value); + return commandExecutor.writeAsync(getName(key), codec, RedisCommands.HSETNX, getName(key), key, value); } @Override @@ -252,13 +252,13 @@ public class RedissonMap extends RedissonExpirable implements RMap { @Override public Future removeAsync(Object key, Object value) { - return commandExecutor.evalWriteAsync(getName(), codec, EVAL_REMOVE_VALUE, + return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_REMOVE_VALUE, "if redis.call('hget', KEYS[1], ARGV[1]) == ARGV[2] then " + "return redis.call('hdel', KEYS[1], ARGV[1]) " + "else " + "return 0 " + "end", - Collections.singletonList(getName()), key, value); + Collections.singletonList(getName(key)), key, value); } @Override @@ -268,14 +268,14 @@ public class RedissonMap extends RedissonExpirable implements RMap { @Override public Future replaceAsync(K key, V oldValue, V newValue) { - return commandExecutor.evalWriteAsync(getName(), codec, EVAL_REPLACE_VALUE, + return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_REPLACE_VALUE, "if redis.call('hget', KEYS[1], ARGV[1]) == ARGV[2] then " + "redis.call('hset', KEYS[1], ARGV[1], ARGV[3]); " + "return 1; " + "else " + "return 0; " + "end", - Collections.singletonList(getName()), key, oldValue, newValue); + Collections.singletonList(getName(key)), key, oldValue, newValue); } @Override @@ -285,7 +285,7 @@ public class RedissonMap extends RedissonExpirable implements RMap { @Override public Future replaceAsync(K key, V value) { - return commandExecutor.evalWriteAsync(getName(), codec, EVAL_REPLACE, + return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_REPLACE, "if redis.call('hexists', KEYS[1], ARGV[1]) == 1 then " + "local v = redis.call('hget', KEYS[1], ARGV[1]); " + "redis.call('hset', KEYS[1], ARGV[1], ARGV[2]); " @@ -293,36 +293,40 @@ public class RedissonMap extends RedissonExpirable implements RMap { + "else " + "return nil; " + "end", - Collections.singletonList(getName()), key, value); + Collections.singletonList(getName(key)), key, value); } @Override public Future getAsync(K key) { - return commandExecutor.readAsync(getName(), codec, RedisCommands.HGET, getName(), key); + return commandExecutor.readAsync(getName(key), codec, RedisCommands.HGET, getName(key), key); } - + + protected String getName(Object key) { + return getName(); + } + @Override public Future putAsync(K key, V value) { - return commandExecutor.evalWriteAsync(getName(), codec, EVAL_PUT, + return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_PUT, "local v = redis.call('hget', KEYS[1], ARGV[1]); " + "redis.call('hset', KEYS[1], ARGV[1], ARGV[2]); " + "return v", - Collections.singletonList(getName()), key, value); + Collections.singletonList(getName(key)), key, value); } @Override public Future removeAsync(K key) { - return commandExecutor.evalWriteAsync(getName(), codec, EVAL_REMOVE, + return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_REMOVE, "local v = redis.call('hget', KEYS[1], ARGV[1]); " + "redis.call('hdel', KEYS[1], ARGV[1]); " + "return v", - Collections.singletonList(getName()), key); + Collections.singletonList(getName(key)), key); } @Override public Future fastPutAsync(K key, V value) { - return commandExecutor.writeAsync(getName(), codec, RedisCommands.HSET, getName(), key, value); + return commandExecutor.writeAsync(getName(key), codec, RedisCommands.HSET, getName(key), key, value); } @Override @@ -347,8 +351,9 @@ public class RedissonMap extends RedissonExpirable implements RMap { return get(fastRemoveAsync(keys)); } - MapScanResult scanIterator(InetSocketAddress client, long startPos) { - Future> f = commandExecutor.readAsync(client, getName(), new ScanCodec(codec), RedisCommands.HSCAN, getName(), startPos); + MapScanResult scanIterator(String name, InetSocketAddress client, long startPos) { + Future> f + = commandExecutor.readAsync(client, name, new ScanCodec(codec), RedisCommands.HSCAN, name, startPos); return get(f); } @@ -423,9 +428,9 @@ public class RedissonMap extends RedissonExpirable implements RMap { public Future addAndGetAsync(K key, Number value) { try { byte[] keyState = codec.getMapKeyEncoder().encode(key); - return commandExecutor.writeAsync(getName(), StringCodec.INSTANCE, + return commandExecutor.writeAsync(getName(key), StringCodec.INSTANCE, new RedisCommand("HINCRBYFLOAT", new NumberConvertor(value.getClass())), - getName(), keyState, new BigDecimal(value.toString()).toPlainString()); + getName(key), keyState, new BigDecimal(value.toString()).toPlainString()); } catch (IOException e) { throw new IllegalArgumentException(e); } diff --git a/src/main/java/org/redisson/RedissonMapCache.java b/src/main/java/org/redisson/RedissonMapCache.java index 0b5e495f6..5cb409ef0 100644 --- a/src/main/java/org/redisson/RedissonMapCache.java +++ b/src/main/java/org/redisson/RedissonMapCache.java @@ -530,7 +530,7 @@ public class RedissonMapCache extends RedissonMap implements RMapCac } @Override - MapScanResult scanIterator(InetSocketAddress client, long startPos) { + MapScanResult scanIterator(String name, InetSocketAddress client, long startPos) { RedisCommand> EVAL_HSCAN = new RedisCommand>("EVAL", new ListMultiDecoder(new LongMultiDecoder(), new ObjectMapDecoder(new ScanCodec(codec)), new ObjectListDecoder(codec), new MapCacheScanResultReplayDecoder()), ValueType.MAP); Future> f = commandExecutor.evalReadAsync(client, getName(), codec, EVAL_HSCAN, diff --git a/src/main/java/org/redisson/RedissonMapIterator.java b/src/main/java/org/redisson/RedissonMapIterator.java index a16cfa3f5..5b3c84326 100644 --- a/src/main/java/org/redisson/RedissonMapIterator.java +++ b/src/main/java/org/redisson/RedissonMapIterator.java @@ -29,7 +29,7 @@ public class RedissonMapIterator extends RedissonBaseMapIterator iterator() { - return map.scanIterator(client, nextIterPos); + return map.scanIterator(map.getName(), client, nextIterPos); } protected void removeKey() { From aa3be95319b33bc46fd797aee5d493e9ca901d23 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 21 Apr 2016 18:09:52 +0300 Subject: [PATCH 077/120] RedissonSemaphoreTest.testConcurrency_MultiInstance_10_permits fixed --- .../java/org/redisson/RedissonSemaphoreTest.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/redisson/RedissonSemaphoreTest.java b/src/test/java/org/redisson/RedissonSemaphoreTest.java index 4561a8709..9a81d26ff 100644 --- a/src/test/java/org/redisson/RedissonSemaphoreTest.java +++ b/src/test/java/org/redisson/RedissonSemaphoreTest.java @@ -6,6 +6,7 @@ import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import org.junit.Test; import org.redisson.core.RSemaphore; @@ -240,7 +241,8 @@ public class RedissonSemaphoreTest extends BaseConcurrentTest { RSemaphore s = redisson.getSemaphore("test"); s.setPermits(10); - final CyclicBarrier barrier = new CyclicBarrier(10); + final AtomicInteger checkPermits = new AtomicInteger(s.availablePermits()); + final CyclicBarrier barrier = new CyclicBarrier(s.availablePermits()); testMultiInstanceConcurrency(iterations, new RedissonRunnable() { @Override public void run(RedissonClient redisson) { @@ -250,10 +252,12 @@ public class RedissonSemaphoreTest extends BaseConcurrentTest { barrier.await(); - assertThat(s.availablePermits()).isEqualTo(0); - assertThat(s.tryAcquire()).isFalse(); - - Thread.sleep(50); + if (checkPermits.decrementAndGet() > 0) { + assertThat(s.availablePermits()).isEqualTo(0); + assertThat(s.tryAcquire()).isFalse(); + } else { + Thread.sleep(50); + } } catch (InterruptedException e) { // TODO Auto-generated catch block @@ -268,6 +272,8 @@ public class RedissonSemaphoreTest extends BaseConcurrentTest { } }); + System.out.println(lockedCounter.get()); + assertThat(lockedCounter.get()).isLessThan(iterations); } From f14b794391dd7d14de09945e9a458580a3c96bd5 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 21 Apr 2016 18:23:41 +0300 Subject: [PATCH 078/120] RScoredSortedSet rank NPE fixed. #478 --- .../org/redisson/RedissonScoredSortedSet.java | 4 ++-- .../java/org/redisson/core/RLexSortedSet.java | 2 +- .../java/org/redisson/core/RScoredSortedSet.java | 16 ++++++++++++++-- .../redisson/RedissonScoredSortedSetTest.java | 6 ++++-- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index 01875ae2b..d36f31c11 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -228,7 +228,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc } @Override - public int rank(V o) { + public Integer rank(V o) { return get(rankAsync(o)); } @@ -448,7 +448,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc } @Override - public int revRank(V o) { + public Integer revRank(V o) { return get(revRankAsync(o)); } diff --git a/src/main/java/org/redisson/core/RLexSortedSet.java b/src/main/java/org/redisson/core/RLexSortedSet.java index 598df157f..2a56ca448 100644 --- a/src/main/java/org/redisson/core/RLexSortedSet.java +++ b/src/main/java/org/redisson/core/RLexSortedSet.java @@ -116,7 +116,7 @@ public interface RLexSortedSet extends RLexSortedSetAsync, Set, RExpirab @Deprecated int lexCount(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); - int rank(String o); + Integer rank(String o); Collection range(int startIndex, int endIndex); diff --git a/src/main/java/org/redisson/core/RScoredSortedSet.java b/src/main/java/org/redisson/core/RScoredSortedSet.java index ce4e6fccf..27a4c5ea5 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSet.java +++ b/src/main/java/org/redisson/core/RScoredSortedSet.java @@ -36,9 +36,21 @@ public interface RScoredSortedSet extends RScoredSortedSetAsync, Iterable< int removeRangeByRank(int startIndex, int endIndex); - int rank(V o); + /** + * Returns rank of value, with the scores ordered from low to high. + * + * @param o + * @return rank or null if value does not exist + */ + Integer rank(V o); - int revRank(V o); + /** + * Returns rank of value, with the scores ordered from high to low. + * + * @param o + * @return rank or null if value does not exist + */ + Integer revRank(V o); Double getScore(V o); diff --git a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index 3aeab3d72..3b08976af 100644 --- a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -159,7 +159,8 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(0.6, "f"); set.add(0.7, "g"); - Assert.assertEquals(3, (int)set.rank("d")); + assertThat(set.revRank("d")).isEqualTo(3); + assertThat(set.rank("abc")).isNull(); } @Test @@ -173,7 +174,8 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(0.6, "f"); set.add(0.7, "g"); - Assert.assertEquals(1, (int)set.revRank("f")); + assertThat(set.revRank("f")).isEqualTo(1); + assertThat(set.revRank("abc")).isNull(); } From 4c23d3236a6affe7a42d3781144ada6c608a6971 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Thu, 21 Apr 2016 18:29:05 +0300 Subject: [PATCH 079/120] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2fbe92591..2c4d3e6ab 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Redisson: Redis based In-Memory Data Grid for Java. +Redis based In-Memory Data Grid for Java. Redisson. ==== [![Maven Central](https://img.shields.io/maven-central/v/org.redisson/redisson.svg?style=flat-square)](https://maven-badges.herokuapp.com/maven-central/org.redisson/redisson/) From b0ddb04d8e3caa2236e78414bcf17f188160d326 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 22 Apr 2016 08:46:21 +0300 Subject: [PATCH 080/120] [maven-release-plugin] prepare release redisson-2.2.12 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c8e527aae..6dee5c612 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.redisson redisson - 2.2.12-SNAPSHOT + 2.2.12 bundle Redisson @@ -15,7 +15,7 @@ scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git - HEAD + redisson-2.2.12 From f5b46afdf7ebb018c717572cbd313db0641494da Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 22 Apr 2016 08:47:59 +0300 Subject: [PATCH 081/120] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6dee5c612..c7cf883d8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.redisson redisson - 2.2.12 + 2.2.13-SNAPSHOT bundle Redisson @@ -15,7 +15,7 @@ scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git - redisson-2.2.12 + HEAD From 59b9f89f37d305234a9e21ea4e5294194dbe4a92 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 22 Apr 2016 08:50:57 +0300 Subject: [PATCH 082/120] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c7cf883d8..c8e527aae 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.redisson redisson - 2.2.13-SNAPSHOT + 2.2.12-SNAPSHOT bundle Redisson From bca8ae5c76104f29c8db29641412d0db72af0773 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 22 Apr 2016 08:53:15 +0300 Subject: [PATCH 083/120] [maven-release-plugin] prepare release redisson-2.2.12 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c8e527aae..6dee5c612 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.redisson redisson - 2.2.12-SNAPSHOT + 2.2.12 bundle Redisson @@ -15,7 +15,7 @@ scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git - HEAD + redisson-2.2.12 From 5dd49addc889eed55d89ce0b64221ea38a8fa051 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 22 Apr 2016 08:53:21 +0300 Subject: [PATCH 084/120] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6dee5c612..c7cf883d8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.redisson redisson - 2.2.12 + 2.2.13-SNAPSHOT bundle Redisson @@ -15,7 +15,7 @@ scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git - redisson-2.2.12 + HEAD From 52815e077bc69e02f55f2e3d106644cf2d5aed5f Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Sat, 23 Apr 2016 09:05:10 +0300 Subject: [PATCH 085/120] Update CHANGELOG.md --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54c412b17..592bd4795 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ Redisson Releases History ================================ ####Please Note: trunk is current development branch. +####22-Apr-2016 - version 2.2.12 released + +Imporovement - Replaying phase handling in CommandDecoder +Fixed - cluster state update manager can't try next node if current node has failed to response +Fixed - cluster initialization +Fixed - items removing during `RMap` iteration +Fixed - `RGeo.addAsync` codec definition +Fixed - `RMapCache` iterator and readAll methods +Fixed - unnecessary slots migration in cluster mode +Fixed - Command batches redirect in cluster mode +Fixed - cluster mode compatibility for `RedissonMultimap.fastRemove` method +Fixed - `RedissonMultiLock` deadlock +Fixed - MultiDecoder empty result handling +Fixed - array start index in LUA scripts +Fixed - RMap iterator +Fixed - probably thread blocking issues + ####04-Apr-2016 - version 2.2.11 released Since this version Redisson has __perfomance boost up to 43%__ From cd7e6ad8db33b8029a13f3c721894e8b502feba9 Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 25 Apr 2016 11:23:18 +0300 Subject: [PATCH 086/120] RSet.retainAll, containsAll, removeAll methods optimized up to 100x times. RSet refactoring --- src/main/java/org/redisson/RedissonSet.java | 84 +++++++++---------- .../client/protocol/RedisCommands.java | 3 +- 2 files changed, 41 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/redisson/RedissonSet.java b/src/main/java/org/redisson/RedissonSet.java index d2e728195..d6b3fe384 100644 --- a/src/main/java/org/redisson/RedissonSet.java +++ b/src/main/java/org/redisson/RedissonSet.java @@ -19,13 +19,15 @@ import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; import org.redisson.client.codec.Codec; +import org.redisson.client.protocol.RedisCommand; +import org.redisson.client.protocol.RedisCommand.ValueType; import org.redisson.client.protocol.RedisCommands; +import org.redisson.client.protocol.convertor.BooleanReplayConvertor; import org.redisson.client.protocol.decoder.ListScanResult; import org.redisson.command.CommandAsyncExecutor; import org.redisson.core.RSet; @@ -71,7 +73,11 @@ public class RedissonSet extends RedissonExpirable implements RSet { @Override public Future containsAsync(Object o) { - return commandExecutor.readAsync(getName(), codec, RedisCommands.SISMEMBER, getName(), o); + return commandExecutor.readAsync(getName(o), codec, RedisCommands.SISMEMBER, getName(o), o); + } + + protected String getName(Object o) { + return getName(); } private ListScanResult scanIterator(InetSocketAddress client, long startPos) { @@ -125,7 +131,7 @@ public class RedissonSet extends RedissonExpirable implements RSet { @Override public Future addAsync(V e) { - return commandExecutor.writeAsync(getName(), codec, RedisCommands.SADD_SINGLE, getName(), e); + return commandExecutor.writeAsync(getName(e), codec, RedisCommands.SADD_SINGLE, getName(e), e); } @Override @@ -140,7 +146,7 @@ public class RedissonSet extends RedissonExpirable implements RSet { @Override public Future removeAsync(Object o) { - return commandExecutor.writeAsync(getName(), codec, RedisCommands.SREM_SINGLE, getName(), o); + return commandExecutor.writeAsync(getName(o), codec, RedisCommands.SREM_SINGLE, getName(o), o); } @Override @@ -150,7 +156,7 @@ public class RedissonSet extends RedissonExpirable implements RSet { @Override public Future moveAsync(String destination, V member) { - return commandExecutor.writeAsync(getName(), codec, RedisCommands.SMOVE, getName(), destination, member); + return commandExecutor.writeAsync(getName(member), codec, RedisCommands.SMOVE, getName(member), destination, member); } @Override @@ -165,16 +171,16 @@ public class RedissonSet extends RedissonExpirable implements RSet { @Override public Future containsAllAsync(Collection c) { - return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, - "local s = redis.call('smembers', KEYS[1]);" + - "for i = 1, #s, 1 do " + - "for j = 1, #ARGV, 1 do " - + "if ARGV[j] == s[i] " - + "then table.remove(ARGV, j) end " - + "end; " - + "end;" - + "return #ARGV == 0 and 1 or 0; ", - Collections.singletonList(getName()), c.toArray()); + if (c.isEmpty()) { + return newSucceededFuture(true); + } + + return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 5, ValueType.OBJECTS), + "redis.call('sadd', KEYS[2], unpack(ARGV)); " + + "local size = redis.call('sdiff', KEYS[2], KEYS[1]);" + + "redis.call('del', KEYS[2]); " + + "return #size == 0 and 1 or 0; ", + Arrays.asList(getName(), "redisson_temp__{" + getName() + "}"), c.toArray()); } @Override @@ -188,6 +194,10 @@ public class RedissonSet extends RedissonExpirable implements RSet { @Override public Future addAllAsync(Collection c) { + if (c.isEmpty()) { + return newSucceededFuture(false); + } + List args = new ArrayList(c.size() + 1); args.add(getName()); args.addAll(c); @@ -201,39 +211,25 @@ public class RedissonSet extends RedissonExpirable implements RSet { @Override public Future retainAllAsync(Collection c) { - return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, - "local changed = 0 " + - "local s = redis.call('smembers', KEYS[1]) " - + "local i = 1 " - + "while i <= #s do " - + "local element = s[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('SREM', KEYS[1], element) " - + "changed = 1 " - + "end " - + "i = i + 1 " - + "end " - + "return changed ", - Collections.singletonList(getName()), c.toArray()); + if (c.isEmpty()) { + return deleteAsync(); + } + return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 5, ValueType.OBJECTS), + "redis.call('sadd', KEYS[2], unpack(ARGV)); " + + "local prevSize = redis.call('scard', KEYS[1]); " + + "local size = redis.call('sinterstore', KEYS[1], KEYS[1], KEYS[2]);" + + "redis.call('del', KEYS[2]); " + + "return size ~= prevSize and 1 or 0; ", + Arrays.asList(getName(), "redisson_temp__{" + getName() + "}"), c.toArray()); } @Override public Future removeAllAsync(Collection c) { - return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, - "local v = 0 " + - "for i = 1, #ARGV, 1 do " - + "if redis.call('srem', KEYS[1], ARGV[i]) == 1 " - + "then v = 1 end " - +"end " - + "return v ", - Collections.singletonList(getName()), c.toArray()); + if (c.isEmpty()) { + return newSucceededFuture(false); + } + + return commandExecutor.writeAsync(getName(), codec, RedisCommands.SREM_SINGLE, getName(), c.toArray()); } @Override diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index 1ee63f244..0b4f86307 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -114,12 +114,11 @@ public interface RedisCommands { RedisStrictCommand MULTI = new RedisStrictCommand("MULTI", new VoidReplayConvertor()); RedisCommand> EXEC = new RedisCommand>("EXEC", new ObjectListReplayDecoder()); - RedisCommand SREM = new RedisCommand("SREM", 2, ValueType.OBJECTS); RedisCommand SADD_BOOL = new RedisCommand("SADD", new BooleanAmountReplayConvertor(), 2, ValueType.OBJECTS); RedisStrictCommand SADD = new RedisStrictCommand("SADD", 2, ValueType.OBJECTS); RedisCommand SPOP_SINGLE = new RedisCommand("SPOP"); RedisCommand SADD_SINGLE = new RedisCommand("SADD", new BooleanReplayConvertor(), 2); - RedisCommand SREM_SINGLE = new RedisCommand("SREM", new BooleanReplayConvertor(), 2); + RedisCommand SREM_SINGLE = new RedisCommand("SREM", new BooleanAmountReplayConvertor(), 2, ValueType.OBJECTS); RedisCommand SMOVE = new RedisCommand("SMOVE", new BooleanReplayConvertor(), 3); RedisCommand> SMEMBERS = new RedisCommand>("SMEMBERS", new ObjectSetReplayDecoder()); RedisCommand> SSCAN = new RedisCommand>("SSCAN", new NestedMultiDecoder(new ObjectListReplayDecoder(), new ListScanResultReplayDecoder()), ValueType.OBJECT); From 4a1f2e57b6bf591f5654daa3a3d7d22ea35be942 Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 25 Apr 2016 11:26:43 +0300 Subject: [PATCH 087/120] Compilation fixed --- src/main/java/org/redisson/RedissonSet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/redisson/RedissonSet.java b/src/main/java/org/redisson/RedissonSet.java index d6b3fe384..a7466e24b 100644 --- a/src/main/java/org/redisson/RedissonSet.java +++ b/src/main/java/org/redisson/RedissonSet.java @@ -180,7 +180,7 @@ public class RedissonSet extends RedissonExpirable implements RSet { + "local size = redis.call('sdiff', KEYS[2], KEYS[1]);" + "redis.call('del', KEYS[2]); " + "return #size == 0 and 1 or 0; ", - Arrays.asList(getName(), "redisson_temp__{" + getName() + "}"), c.toArray()); + Arrays.asList(getName(), "redisson_temp__{" + getName() + "}"), c.toArray()); } @Override @@ -220,7 +220,7 @@ public class RedissonSet extends RedissonExpirable implements RSet { + "local size = redis.call('sinterstore', KEYS[1], KEYS[1], KEYS[2]);" + "redis.call('del', KEYS[2]); " + "return size ~= prevSize and 1 or 0; ", - Arrays.asList(getName(), "redisson_temp__{" + getName() + "}"), c.toArray()); + Arrays.asList(getName(), "redisson_temp__{" + getName() + "}"), c.toArray()); } @Override From c9ac67ea7ecebc69128196ca746cd42d5f98a9b2 Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 25 Apr 2016 17:12:55 +0300 Subject: [PATCH 088/120] Few iterator improvements --- .../org/redisson/RedissonBaseIterator.java | 87 ++++++++++++++----- .../org/redisson/RedissonBaseMapIterator.java | 5 ++ src/main/java/org/redisson/RedissonSet.java | 33 +++++-- .../MasterSlaveConnectionManager.java | 4 +- 4 files changed, 99 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/redisson/RedissonBaseIterator.java b/src/main/java/org/redisson/RedissonBaseIterator.java index ab1eaa044..3f6c62c7a 100644 --- a/src/main/java/org/redisson/RedissonBaseIterator.java +++ b/src/main/java/org/redisson/RedissonBaseIterator.java @@ -16,6 +16,7 @@ package org.redisson; import java.net.InetSocketAddress; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; @@ -25,42 +26,87 @@ import org.redisson.client.protocol.decoder.ListScanResult; abstract class RedissonBaseIterator implements Iterator { private List firstValues; - private Iterator iter; - private InetSocketAddress client; - private long nextIterPos; - private long startPos = -1; + private List lastValues; + private Iterator lastIter; + protected long nextIterPos; + protected InetSocketAddress client; + private boolean finished; private boolean currentElementRemoved; private boolean removeExecuted; private V value; @Override public boolean hasNext() { - if (iter == null || !iter.hasNext()) { - if (nextIterPos == -1) { - return false; + if (lastIter == null || !lastIter.hasNext()) { + if (finished) { + + currentElementRemoved = false; + removeExecuted = false; + client = null; + firstValues = null; + lastValues = null; + nextIterPos = 0; + + if (!tryAgain()) { + return false; + } + finished = false; } long prevIterPos; do { prevIterPos = nextIterPos; ListScanResult res = iterator(client, nextIterPos); + lastValues = new ArrayList(res.getValues()); client = res.getRedisClient(); - if (startPos == -1) { - startPos = res.getPos(); - } + if (nextIterPos == 0 && firstValues == null) { - firstValues = res.getValues(); - } else if (res.getValues().equals(firstValues) && res.getPos() == startPos) { - return false; + firstValues = lastValues; + lastValues = null; + if (firstValues.isEmpty() && tryAgain()) { + client = null; + firstValues = null; + nextIterPos = 0; + prevIterPos = -1; + } + } else { + if (firstValues.isEmpty()) { + firstValues = lastValues; + lastValues = null; + if (firstValues.isEmpty() && tryAgain()) { + client = null; + firstValues = null; + nextIterPos = 0; + prevIterPos = -1; + continue; + } + } else if (lastValues.removeAll(firstValues)) { + currentElementRemoved = false; + removeExecuted = false; + client = null; + firstValues = null; + lastValues = null; + nextIterPos = 0; + prevIterPos = -1; + if (tryAgain()) { + continue; + } + finished = true; + return false; + } } - iter = res.getValues().iterator(); + lastIter = res.getValues().iterator(); nextIterPos = res.getPos(); - } while (!iter.hasNext() && nextIterPos != prevIterPos); + } while (!lastIter.hasNext() && nextIterPos != prevIterPos); if (prevIterPos == nextIterPos && !removeExecuted) { - nextIterPos = -1; + finished = true; } } - return iter.hasNext(); + return lastIter.hasNext(); + } + + protected boolean tryAgain() { + return false; } abstract ListScanResult iterator(InetSocketAddress client, long nextIterPos); @@ -71,7 +117,7 @@ abstract class RedissonBaseIterator implements Iterator { throw new NoSuchElementException("No such element"); } - value = iter.next(); + value = lastIter.next(); currentElementRemoved = false; return value; } @@ -81,11 +127,12 @@ abstract class RedissonBaseIterator implements Iterator { if (currentElementRemoved) { throw new IllegalStateException("Element been already deleted"); } - if (iter == null) { + if (lastIter == null) { throw new IllegalStateException(); } - iter.remove(); + firstValues.remove(value); + lastIter.remove(); remove(value); currentElementRemoved = true; removeExecuted = true; diff --git a/src/main/java/org/redisson/RedissonBaseMapIterator.java b/src/main/java/org/redisson/RedissonBaseMapIterator.java index 2af67769b..b31246795 100644 --- a/src/main/java/org/redisson/RedissonBaseMapIterator.java +++ b/src/main/java/org/redisson/RedissonBaseMapIterator.java @@ -75,6 +75,7 @@ abstract class RedissonBaseMapIterator implements Iterator { if (firstValues.isEmpty() && tryAgain()) { client = null; firstValues = null; + nextIterPos = 0; prevIterPos = -1; } } else { @@ -82,6 +83,10 @@ abstract class RedissonBaseMapIterator implements Iterator { firstValues = lastValues; lastValues = null; if (firstValues.isEmpty() && tryAgain()) { + client = null; + firstValues = null; + nextIterPos = 0; + prevIterPos = -1; continue; } } else if (lastValues.keySet().removeAll(firstValues.keySet())) { diff --git a/src/main/java/org/redisson/RedissonSet.java b/src/main/java/org/redisson/RedissonSet.java index a7466e24b..c0db7dea7 100644 --- a/src/main/java/org/redisson/RedissonSet.java +++ b/src/main/java/org/redisson/RedissonSet.java @@ -80,8 +80,8 @@ public class RedissonSet extends RedissonExpirable implements RSet { return getName(); } - private ListScanResult scanIterator(InetSocketAddress client, long startPos) { - Future> f = commandExecutor.readAsync(client, getName(), codec, RedisCommands.SSCAN, getName(), startPos); + ListScanResult scanIterator(String name, InetSocketAddress client, long startPos) { + Future> f = commandExecutor.readAsync(client, name, codec, RedisCommands.SSCAN, name, startPos); return get(f); } @@ -91,7 +91,7 @@ public class RedissonSet extends RedissonExpirable implements RSet { @Override ListScanResult iterator(InetSocketAddress client, long nextIterPos) { - return scanIterator(client, nextIterPos); + return scanIterator(getName(), client, nextIterPos); } @Override @@ -185,10 +185,6 @@ public class RedissonSet extends RedissonExpirable implements RSet { @Override public boolean addAll(Collection c) { - if (c.isEmpty()) { - return false; - } - return get(addAllAsync(c)); } @@ -229,7 +225,11 @@ public class RedissonSet extends RedissonExpirable implements RSet { return newSucceededFuture(false); } - return commandExecutor.writeAsync(getName(), codec, RedisCommands.SREM_SINGLE, getName(), c.toArray()); + List args = new ArrayList(c.size() + 1); + args.add(getName()); + args.addAll(c); + + return commandExecutor.writeAsync(getName(), codec, RedisCommands.SREM_SINGLE, c.toArray()); } @Override @@ -268,4 +268,21 @@ public class RedissonSet extends RedissonExpirable implements RSet { delete(); } + @Override + 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(' '); + } + } + } diff --git a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java index 63f8e175f..abdb48f98 100644 --- a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java +++ b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java @@ -99,7 +99,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager { } }; - protected static final int MAX_SLOT = 16384; + public static final int MAX_SLOT = 16384; protected final ClusterSlotRange singleSlotRange = new ClusterSlotRange(0, MAX_SLOT); @@ -121,7 +121,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager { protected boolean isClusterMode; - protected final Map entries = PlatformDependent.newConcurrentHashMap(); + private final Map entries = PlatformDependent.newConcurrentHashMap(); private final Promise shutdownPromise; From 5675806bfbf2443c7dca2eaf16725a2330fad588 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 26 Apr 2016 20:35:44 +0300 Subject: [PATCH 089/120] Error in RSetCache.readAll fixed. Expiration override in RSetCache.add fixed. #481 --- .../java/org/redisson/RedissonSetCache.java | 19 +++++++++++-------- .../org/redisson/RedissonSetCacheTest.java | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/redisson/RedissonSetCache.java b/src/main/java/org/redisson/RedissonSetCache.java index a4c1e13b6..2c9a6b048 100644 --- a/src/main/java/org/redisson/RedissonSetCache.java +++ b/src/main/java/org/redisson/RedissonSetCache.java @@ -184,22 +184,25 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< public Future> readAllAsync() { return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_SET, "local expireHead = redis.call('zrange', KEYS[2], 0, 0, 'withscores');" + - "local keys = redis.call('hkeys', KEYS[1]); " - + "if #keys == 0 then " - + "return {}; " - + "end; " + - "local maxDate = ARGV[1]; " + + "local keys = redis.call('hkeys', KEYS[1]); " + + "if #keys == 0 then " + + "return {}; " + + "end; " + + "local maxDate = tonumber(ARGV[1]); " + "local minExpireDate = 92233720368547758;" + - "if #expireHead == 2 and tonumber(expireHead[2]) <= tonumber(maxDate) then " + + "if #expireHead == 2 and tonumber(expireHead[2]) <= maxDate then " + "for i = #keys, 1, -1 do " + "local key = keys[i]; " + "local expireDate = redis.call('zscore', KEYS[2], key); " + - "if expireDate ~= false and tonumber(expireDate) <= tonumber(maxDate) then " + + "if expireDate ~= false and tonumber(expireDate) <= maxDate then " + "minExpireDate = math.min(tonumber(expireDate), minExpireDate); " + "table.remove(keys, i); " + "end;" + "end;" + "end; " + + "if #keys == 0 then " + + "return {}; " + + "end; " + "return redis.call('hmget', KEYS[1], unpack(keys));", Arrays.asList(getName(), getTimeoutSetName()), System.currentTimeMillis()); } @@ -272,7 +275,7 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< "if value == 1 then " + "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[3]); " + "if expireDateScore ~= false and tonumber(expireDateScore) <= tonumber(ARGV[1]) then " - + "redis.call('zadd', KEYS[2], ARGV[2], ARGV[2]); " + + "redis.call('zadd', KEYS[2], ARGV[2], ARGV[3]); " + "return 1;" + "else " + "return 0;" diff --git a/src/test/java/org/redisson/RedissonSetCacheTest.java b/src/test/java/org/redisson/RedissonSetCacheTest.java index 4698435b4..d78c6caef 100644 --- a/src/test/java/org/redisson/RedissonSetCacheTest.java +++ b/src/test/java/org/redisson/RedissonSetCacheTest.java @@ -86,6 +86,16 @@ public class RedissonSetCacheTest extends BaseTest { } + @Test + public void testAddOverrideExpiration() throws InterruptedException { + RSetCache set = redisson.getSetCache("simple31"); + assertThat(set.add("123", 500, TimeUnit.MILLISECONDS)).isTrue(); + Thread.sleep(500); + assertThat(set.add("123", 3, TimeUnit.SECONDS)).isTrue(); + Thread.sleep(2000); + assertThat(set.contains("123")).isTrue(); + } + @Test public void testAddExpireTwise() throws InterruptedException, ExecutionException { RSetCache set = redisson.getSetCache("simple31"); @@ -319,6 +329,14 @@ public class RedissonSetCacheTest extends BaseTest { Assert.assertEquals(5, set.size()); } + @Test + public void testReadAllExpired() throws InterruptedException { + RSetCache set = redisson.getSetCache("set"); + set.add(1, 1, TimeUnit.SECONDS); + Thread.sleep(1000); + assertThat(set.readAll()).isEmpty(); + } + @Test public void testReadAll() { RSetCache set = redisson.getSetCache("set"); From d82678b10336873c668d9022d8f15fd837544a57 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Tue, 26 Apr 2016 20:44:34 +0300 Subject: [PATCH 090/120] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2c4d3e6ab..fdce687bb 100644 --- a/README.md +++ b/README.md @@ -82,12 +82,12 @@ Include the following to your dependency list: org.redisson redisson - 2.2.11 + 2.2.12 ### Gradle - compile 'org.redisson:redisson:2.2.11' + compile 'org.redisson:redisson:2.2.12' ### Supported by From 9a01cec004b8f482ad51ac5b48826b3ffc609725 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 26 Apr 2016 20:58:33 +0300 Subject: [PATCH 091/120] RSetCache few improvements --- .../java/org/redisson/RedissonSetCache.java | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/redisson/RedissonSetCache.java b/src/main/java/org/redisson/RedissonSetCache.java index 2c9a6b048..9487cb97e 100644 --- a/src/main/java/org/redisson/RedissonSetCache.java +++ b/src/main/java/org/redisson/RedissonSetCache.java @@ -182,7 +182,11 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< @Override public Future> readAllAsync() { - return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_SET, + return (Future>)readAllAsync(RedisCommands.EVAL_SET); + } + + private Future readAllAsync(RedisCommand> command) { + return commandExecutor.evalReadAsync(getName(), codec, command, "local expireHead = redis.call('zrange', KEYS[2], 0, 0, 'withscores');" + "local keys = redis.call('hkeys', KEYS[1]); " + "if #keys == 0 then " + @@ -207,27 +211,9 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< Arrays.asList(getName(), getTimeoutSetName()), System.currentTimeMillis()); } + private Future> readAllasListAsync() { - return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_LIST, - "local expireHead = redis.call('zrange', KEYS[2], 0, 0, 'withscores');" + - "local keys = redis.call('hkeys', KEYS[1]); " + - "if #keys == 0 then " - + "return {}; " + - "end; " + - "local maxDate = ARGV[1]; " + - "local minExpireDate = 92233720368547758;" + - "if #expireHead == 2 and tonumber(expireHead[2]) <= tonumber(maxDate) then " + - "for i = #keys, 1, -1 do " + - "local key = keys[i]; " + - "local expireDate = redis.call('zscore', KEYS[2], key); " + - "if expireDate ~= false and tonumber(expireDate) <= tonumber(maxDate) then " + - "minExpireDate = math.min(tonumber(expireDate), minExpireDate); " + - "table.remove(keys, i); " + - "end;" + - "end;" + - "end; " + - "return redis.call('hmget', KEYS[1], unpack(keys));", - Arrays.asList(getName(), getTimeoutSetName()), System.currentTimeMillis()); + return (Future>)readAllAsync(RedisCommands.EVAL_LIST); } @Override @@ -336,6 +322,10 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< @Override public Future containsAllAsync(Collection c) { + if (c.isEmpty()) { + return newSucceededFuture(true); + } + return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, "local s = redis.call('hvals', KEYS[1]);" + "for i = 1, #s, 1 do " + @@ -383,6 +373,10 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< @Override public Future retainAllAsync(Collection c) { + if (c.isEmpty()) { + return deleteAsync(); + } + List params = new ArrayList(c.size()); for (Object object : c) { params.add(hash(object)); @@ -418,6 +412,10 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< @Override public Future removeAllAsync(Collection c) { + if (c.isEmpty()) { + return newSucceededFuture(false); + } + List params = new ArrayList(c.size()+1); params.add(getName()); for (Object object : c) { From 747647b5540aee20e795dfe422a967ce32fdcfbd Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 27 Apr 2016 11:59:38 +0300 Subject: [PATCH 092/120] Big optimization of RSetCache! --- .../java/org/redisson/EvictionScheduler.java | 7 + .../redisson/RedissonListMultimapValues.java | 2 +- .../org/redisson/RedissonScoredSortedSet.java | 4 +- .../java/org/redisson/RedissonSetCache.java | 302 +++++------------- .../redisson/RedissonSetMultimapValues.java | 2 +- .../client/protocol/RedisCommands.java | 12 +- .../decoder/ObjectSetReplayDecoder.java | 6 +- .../RedissonScoredSortedSetReactive.java | 4 +- .../reactive/RedissonSetCacheReactive.java | 224 ++----------- .../RedissonSetCacheReactiveTest.java | 16 +- .../org/redisson/RedissonSetCacheTest.java | 16 +- 11 files changed, 157 insertions(+), 438 deletions(-) diff --git a/src/main/java/org/redisson/EvictionScheduler.java b/src/main/java/org/redisson/EvictionScheduler.java index d73f861cc..245374cf6 100644 --- a/src/main/java/org/redisson/EvictionScheduler.java +++ b/src/main/java/org/redisson/EvictionScheduler.java @@ -141,6 +141,9 @@ public class EvictionScheduler { } } + public void schedule(String name) { + schedule(name, null); + } public void schedule(String name, String timeoutSetName, String maxIdleSetName) { RedissonCacheTask task = new RedissonCacheTask(name, timeoutSetName, maxIdleSetName, false); @@ -225,6 +228,10 @@ public class EvictionScheduler { Arrays.asList(name, timeoutSetName, maxIdleSetName), System.currentTimeMillis(), keysLimit); } + if (timeoutSetName == null) { + return executor.writeAsync(name, LongCodec.INSTANCE, RedisCommands.ZREMRANGEBYSCORE, name, 0, System.currentTimeMillis()); + } + return executor.evalWriteAsync(name, LongCodec.INSTANCE, RedisCommands.EVAL_INTEGER, "local expiredKeys = redis.call('zrangebyscore', KEYS[2], 0, ARGV[1], 'limit', 0, ARGV[2]); " + "if #expiredKeys > 0 then " diff --git a/src/main/java/org/redisson/RedissonListMultimapValues.java b/src/main/java/org/redisson/RedissonListMultimapValues.java index abffc6e61..044985a2e 100644 --- a/src/main/java/org/redisson/RedissonListMultimapValues.java +++ b/src/main/java/org/redisson/RedissonListMultimapValues.java @@ -57,7 +57,7 @@ public class RedissonListMultimapValues extends RedissonExpirable implements private static final RedisCommand LAST_INDEX = new RedisCommand("EVAL", new IntegerReplayConvertor(), 4, Arrays.asList(ValueType.MAP_KEY, ValueType.MAP_VALUE)); private static final RedisCommand EVAL_SIZE = new RedisCommand("EVAL", new IntegerReplayConvertor(), 6, ValueType.MAP_KEY); private static final RedisCommand EVAL_GET = new RedisCommand("EVAL", 7, ValueType.MAP_KEY); - private static final RedisCommand> EVAL_READALL = new RedisCommand>("EVAL", new ObjectSetReplayDecoder(), 6, ValueType.MAP_KEY); + private static final RedisCommand> EVAL_READALL = new RedisCommand>("EVAL", new ObjectSetReplayDecoder(), 6, ValueType.MAP_KEY); private static final RedisCommand EVAL_CONTAINS_VALUE = new RedisCommand("EVAL", new BooleanReplayConvertor(), 7, Arrays.asList(ValueType.MAP_KEY, ValueType.MAP_VALUE)); private static final RedisCommand EVAL_CONTAINS_ALL_WITH_VALUES = new RedisCommand("EVAL", new BooleanReplayConvertor(), 7, ValueType.OBJECTS); diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index d36f31c11..d4f714fb1 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -376,7 +376,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc public Future> valueRangeAsync(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive) { String startValue = value(startScore, startScoreInclusive); String endValue = value(endScore, endScoreInclusive); - return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANGEBYSCORE, getName(), startValue, endValue); + return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANGEBYSCORE_LIST, getName(), startValue, endValue); } @Override @@ -415,7 +415,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc public Future> valueRangeAsync(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive, int offset, int count) { String startValue = value(startScore, startScoreInclusive); String endValue = value(endScore, endScoreInclusive); - return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANGEBYSCORE, getName(), startValue, endValue, "LIMIT", offset, count); + return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANGEBYSCORE_LIST, getName(), startValue, endValue, "LIMIT", offset, count); } @Override diff --git a/src/main/java/org/redisson/RedissonSetCache.java b/src/main/java/org/redisson/RedissonSetCache.java index 9487cb97e..2eb59382e 100644 --- a/src/main/java/org/redisson/RedissonSetCache.java +++ b/src/main/java/org/redisson/RedissonSetCache.java @@ -27,20 +27,16 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import org.redisson.client.codec.Codec; -import org.redisson.client.codec.LongCodec; import org.redisson.client.protocol.RedisCommand; +import org.redisson.client.protocol.RedisCommand.ValueType; import org.redisson.client.protocol.RedisCommands; import org.redisson.client.protocol.RedisStrictCommand; import org.redisson.client.protocol.convertor.BooleanReplayConvertor; -import org.redisson.client.protocol.convertor.VoidReplayConvertor; import org.redisson.client.protocol.decoder.ListScanResult; import org.redisson.command.CommandAsyncExecutor; import org.redisson.core.RSetCache; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.netty.util.concurrent.Future; -import net.openhft.hashing.LongHashFunction; /** *

Set-based cache with ability to set TTL for each entry via @@ -65,17 +61,14 @@ import net.openhft.hashing.LongHashFunction; */ public class RedissonSetCache extends RedissonExpirable implements RSetCache { - private static final RedisCommand ADD_ALL = new RedisCommand("HMSET", new VoidReplayConvertor()); - private static final RedisStrictCommand HDEL = new RedisStrictCommand("HDEL", new BooleanReplayConvertor()); - - protected RedissonSetCache(EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name) { + public RedissonSetCache(EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name) { super(commandExecutor, name); - evictionScheduler.schedule(getName(), getTimeoutSetName()); + evictionScheduler.schedule(getName()); } - protected RedissonSetCache(Codec codec, EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name) { + public RedissonSetCache(Codec codec, EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name) { super(codec, commandExecutor, name); - evictionScheduler.schedule(getName(), getTimeoutSetName()); + evictionScheduler.schedule(getName()); } @Override @@ -85,7 +78,7 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< @Override public Future sizeAsync() { - return commandExecutor.readAsync(getName(), codec, RedisCommands.HLEN, getName()); + return commandExecutor.readAsync(getName(), codec, RedisCommands.ZCARD_INT, getName()); } @Override @@ -98,64 +91,40 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< return get(containsAsync(o)); } - private byte[] hash(Object o) { - if (o == null) { - throw new NullPointerException("Value can't be null"); - } - try { - byte[] objectState = codec.getValueEncoder().encode(o); - return hash(objectState); - } catch (IOException e) { - throw new IllegalArgumentException(e); - } - } - - private byte[] hash(byte[] objectState) { - long h1 = LongHashFunction.farmUo().hashBytes(objectState); - long h2 = LongHashFunction.xx_r39().hashBytes(objectState); - - ByteBuf buf = Unpooled.buffer((2 * Long.SIZE) / Byte.SIZE).writeLong(h1).writeLong(h2); - try { - return buf.array(); - } finally { - buf.release(); - } - } - - String getTimeoutSetName() { - return "redisson__timeout__set__{" + getName() + "}"; - } - @Override public Future containsAsync(Object o) { - byte[] key = hash(o); - return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN, - "local value = redis.call('hexists', KEYS[1], ARGV[2]); " + - "if value == 1 then " + - "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[2]); " - + "if expireDateScore ~= false and tonumber(expireDateScore) <= tonumber(ARGV[1]) then " - + "return 0;" - + "end; " + - "end;" + - "return value; ", - Arrays.asList(getName(), getTimeoutSetName()), System.currentTimeMillis(), key); + return commandExecutor.evalReadAsync(getName(), codec, new RedisStrictCommand("EVAL", new BooleanReplayConvertor(), 5), + "local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); " + + "if expireDateScore ~= false then " + + "if tonumber(expireDateScore) <= tonumber(ARGV[1]) then " + + "return 0;" + + "else " + + "return 1;" + + "end;" + + "else " + + "return 0;" + + "end; ", + Arrays.asList(getName()), System.currentTimeMillis(), o); } ListScanResult scanIterator(InetSocketAddress client, long startPos) { - Future> f = commandExecutor.evalReadAsync(client, getName(), codec, RedisCommands.EVAL_SSCAN, + Future> f = scanIteratorAsync(client, startPos); + return get(f); + } + + public Future> scanIteratorAsync(InetSocketAddress client, long startPos) { + return commandExecutor.evalReadAsync(client, getName(), codec, RedisCommands.EVAL_ZSCAN, "local result = {}; " - + "local res = redis.call('hscan', KEYS[1], ARGV[1]); " + + "local res = redis.call('zscan', KEYS[1], ARGV[1]); " + "for i, value in ipairs(res[2]) do " + "if i % 2 == 0 then " - + "local key = res[2][i-1]; " - + "local expireDate = redis.call('zscore', KEYS[2], key); " - + "if (expireDate == false) or (expireDate ~= false and tonumber(expireDate) > tonumber(ARGV[2])) then " - + "table.insert(result, value); " + + "local expireDate = value; " + + "if tonumber(expireDate) > tonumber(ARGV[2]) then " + + "table.insert(result, res[2][i-1]); " + "end; " - + "end; " + + "end;" + "end;" - + "return {res[1], result};", Arrays.asList(getName(), getTimeoutSetName()), startPos, System.currentTimeMillis()); - return get(f); + + "return {res[1], result};", Arrays.asList(getName()), startPos, System.currentTimeMillis()); } @Override @@ -182,38 +151,16 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< @Override public Future> readAllAsync() { - return (Future>)readAllAsync(RedisCommands.EVAL_SET); + return (Future>)readAllAsync(RedisCommands.ZRANGEBYSCORE); } private Future readAllAsync(RedisCommand> command) { - return commandExecutor.evalReadAsync(getName(), codec, command, - "local expireHead = redis.call('zrange', KEYS[2], 0, 0, 'withscores');" + - "local keys = redis.call('hkeys', KEYS[1]); " + - "if #keys == 0 then " + - "return {}; " + - "end; " + - "local maxDate = tonumber(ARGV[1]); " + - "local minExpireDate = 92233720368547758;" + - "if #expireHead == 2 and tonumber(expireHead[2]) <= maxDate then " + - "for i = #keys, 1, -1 do " + - "local key = keys[i]; " + - "local expireDate = redis.call('zscore', KEYS[2], key); " + - "if expireDate ~= false and tonumber(expireDate) <= maxDate then " + - "minExpireDate = math.min(tonumber(expireDate), minExpireDate); " + - "table.remove(keys, i); " + - "end;" + - "end;" + - "end; " + - "if #keys == 0 then " + - "return {}; " + - "end; " + - "return redis.call('hmget', KEYS[1], unpack(keys));", - Arrays.asList(getName(), getTimeoutSetName()), System.currentTimeMillis()); + return commandExecutor.readAsync(getName(), codec, command, getName(), System.currentTimeMillis(), 92233720368547758L); } private Future> readAllasListAsync() { - return (Future>)readAllAsync(RedisCommands.EVAL_LIST); + return (Future>)readAllAsync(RedisCommands.ZRANGEBYSCORE_LIST); } @Override @@ -251,63 +198,35 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< throw new NullPointerException("TimeUnit param can't be null"); } + byte[] objectState = encode(value); + + long timeoutDate = System.currentTimeMillis() + unit.toMillis(ttl); + return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN, + "local expireDateScore = redis.call('zscore', KEYS[1], ARGV[3]); " + + "if expireDateScore ~= false and tonumber(expireDateScore) > tonumber(ARGV[1]) then " + + "return 0;" + + "end; " + + "redis.call('zadd', KEYS[1], ARGV[2], ARGV[3]); " + + "return 1; ", + Arrays.asList(getName()), System.currentTimeMillis(), timeoutDate, objectState); + } + + private byte[] encode(V value) { try { - byte[] objectState = encode(value); - byte[] key = hash(objectState); - - long timeoutDate = System.currentTimeMillis() + unit.toMillis(ttl); - return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN, - "local value = redis.call('hexists', KEYS[1], ARGV[3]); " + - "if value == 1 then " + - "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[3]); " - + "if expireDateScore ~= false and tonumber(expireDateScore) <= tonumber(ARGV[1]) then " - + "redis.call('zadd', KEYS[2], ARGV[2], ARGV[3]); " - + "return 1;" - + "else " - + "return 0;" - + "end; " + - "end;" + - "redis.call('zadd', KEYS[2], ARGV[2], ARGV[3]); " + - "redis.call('hset', KEYS[1], ARGV[3], ARGV[4]); " + - "return 1; ", - Arrays.asList(getName(), getTimeoutSetName()), System.currentTimeMillis(), timeoutDate, key, objectState); + return codec.getValueEncoder().encode(value); } catch (IOException e) { - throw new RuntimeException(e); + throw new IllegalArgumentException(e); } } - private byte[] encode(V value) throws IOException { - return codec.getValueEncoder().encode(value); - } - @Override public Future addAsync(V value) { - try { - byte[] objectState = encode(value); - byte[] key = hash(objectState); - return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN, - "local value = redis.call('hexists', KEYS[1], ARGV[2]); " + - "if value == 1 then " + - "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[2]); " - + "if expireDateScore ~= false and tonumber(expireDateScore) <= tonumber(ARGV[1]) then " - + "redis.call('zrem', KEYS[2], ARGV[2]); " - + "return 1;" - + "else " - + "return 0;" - + "end; " + - "end;" + - "redis.call('hset', KEYS[1], ARGV[2], ARGV[3]); " + - "return 1; ", - Arrays.asList(getName(), getTimeoutSetName()), System.currentTimeMillis(), key, objectState); - } catch (IOException e) { - throw new RuntimeException(e); - } + return addAsync(value, 92233720368547758L - System.currentTimeMillis(), TimeUnit.MILLISECONDS); } @Override public Future removeAsync(Object o) { - byte[] key = hash(o); - return commandExecutor.writeAsync(getName(), codec, HDEL, getName(), key); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.ZREM, getName(), o); } @Override @@ -326,17 +245,23 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< return newSucceededFuture(true); } - return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, - "local s = redis.call('hvals', KEYS[1]);" + - "for i = 1, #s, 1 do " + - "for j = 1, #ARGV, 1 do " - + "if ARGV[j] == s[i] then " - + "table.remove(ARGV, j) " - + "end " + List params = new ArrayList(c.size() + 1); + params.add(System.currentTimeMillis()); + params.addAll(c); + + return commandExecutor.evalReadAsync(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 5, ValueType.OBJECTS), + "for j = 2, #ARGV, 1 do " + + "local expireDateScore = redis.call('zscore', KEYS[1], ARGV[j]) " + + "if expireDateScore ~= false then " + + "if tonumber(expireDateScore) <= tonumber(ARGV[1]) then " + + "return 0;" + + "end; " + + "else " + + "return 0;" + + "end; " + "end; " - + "end;" - + "return #ARGV == 0 and 1 or 0; ", - Collections.singletonList(getName()), c.toArray()); + + "return 1; ", + Collections.singletonList(getName()), params.toArray()); } @Override @@ -350,20 +275,16 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< return newSucceededFuture(false); } + long score = 92233720368547758L - System.currentTimeMillis(); List params = new ArrayList(c.size()*2 + 1); params.add(getName()); - try { - for (V value : c) { - byte[] objectState = encode(value); - byte[] key = hash(objectState); - params.add(key); - params.add(objectState); - } - } catch (IOException e) { - throw new RuntimeException(e); + for (V value : c) { + byte[] objectState = encode(value); + params.add(score); + params.add(objectState); } - return commandExecutor.writeAsync(getName(), codec, ADD_ALL, params.toArray()); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.ZADD_BOOL_RAW, params.toArray()); } @Override @@ -377,37 +298,20 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< return deleteAsync(); } - List params = new ArrayList(c.size()); + long score = 92233720368547758L - System.currentTimeMillis(); + List params = new ArrayList(c.size()*2); for (Object object : c) { - params.add(hash(object)); + params.add(score); + params.add(encode((V)object)); } + return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN, - "local keys = redis.call('hkeys', KEYS[1]); " + - "local i=1;" + - "while i <= #keys do " - + "local changed = false;" - + "local element = keys[i];" - + "for j, argElement in pairs(ARGV) do " - + "if argElement == element then " - + "changed = true;" - + "table.remove(keys, i); " - + "table.remove(ARGV, j); " - + "break; " - + "end; " - + "end; " + - "if changed == false then " + - "i = i + 1 " + - "end " + - "end " + - "if #keys > 0 then " - + "for i=1, #keys,5000 do " - + "redis.call('hdel', KEYS[1], unpack(keys, i, math.min(i+4999, #keys))); " - + "redis.call('zrem', KEYS[2], unpack(keys, i, math.min(i+4999, #keys))); " - + "end " - + "return 1;" - + "end; " - + "return 0; ", - Arrays.asList(getName(), getTimeoutSetName()), params.toArray()); + "redis.call('zadd', KEYS[2], unpack(ARGV)); " + + "local prevSize = redis.call('zcard', KEYS[1]); " + + "local size = redis.call('zinterstore', KEYS[1], #ARGV/2, KEYS[1], KEYS[2], 'aggregate', 'min');" + + "redis.call('del', KEYS[2]); " + + "return size ~= prevSize and 1 or 0; ", + Arrays.asList(getName(), "redisson_temp__{" + getName() + "}"), params.toArray()); } @Override @@ -418,11 +322,9 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< List params = new ArrayList(c.size()+1); params.add(getName()); - for (Object object : c) { - params.add(hash(object)); - } + params.addAll(c); - return commandExecutor.writeAsync(getName(), codec, HDEL, params.toArray()); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.ZREM, params.toArray()); } @Override @@ -435,36 +337,4 @@ public class RedissonSetCache extends RedissonExpirable implements RSetCache< delete(); } - @Override - public Future deleteAsync() { - return commandExecutor.writeAsync(getName(), RedisCommands.DEL_OBJECTS, getName(), getTimeoutSetName()); - } - - @Override - public Future expireAsync(long timeToLive, TimeUnit timeUnit) { - return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, - "redis.call('zadd', KEYS[2], 92233720368547758, 'redisson__expiretag');" + - "redis.call('pexpire', KEYS[2], ARGV[1]); " - + "return redis.call('pexpire', KEYS[1], ARGV[1]); ", - Arrays.asList(getName(), getTimeoutSetName()), timeUnit.toMillis(timeToLive)); - } - - @Override - public Future expireAtAsync(long timestamp) { - return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, - "redis.call('zadd', KEYS[2], 92233720368547758, 'redisson__expiretag');" + - "redis.call('pexpireat', KEYS[2], ARGV[1]); " - + "return redis.call('pexpireat', KEYS[1], ARGV[1]); ", - Arrays.asList(getName(), getTimeoutSetName()), timestamp); - } - - @Override - public Future clearExpireAsync() { - return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, - "redis.call('zrem', KEYS[2], 'redisson__expiretag'); " + - "redis.call('persist', KEYS[2]); " - + "return redis.call('persist', KEYS[1]); ", - Arrays.asList(getName(), getTimeoutSetName())); - } - } diff --git a/src/main/java/org/redisson/RedissonSetMultimapValues.java b/src/main/java/org/redisson/RedissonSetMultimapValues.java index c3546e05e..a4cd1513c 100644 --- a/src/main/java/org/redisson/RedissonSetMultimapValues.java +++ b/src/main/java/org/redisson/RedissonSetMultimapValues.java @@ -51,7 +51,7 @@ public class RedissonSetMultimapValues extends RedissonExpirable implements R private static final RedisCommand> EVAL_SSCAN = new RedisCommand>("EVAL", new NestedMultiDecoder(new ObjectListReplayDecoder(), new ListScanResultReplayDecoder()), 7, ValueType.MAP_KEY, ValueType.OBJECT); private static final RedisCommand EVAL_SIZE = new RedisCommand("EVAL", new IntegerReplayConvertor(), 6, ValueType.MAP_KEY); - private static final RedisCommand> EVAL_READALL = new RedisCommand>("EVAL", new ObjectSetReplayDecoder(), 6, ValueType.MAP_KEY); + private static final RedisCommand> EVAL_READALL = new RedisCommand>("EVAL", new ObjectSetReplayDecoder(), 6, ValueType.MAP_KEY); private static final RedisCommand EVAL_CONTAINS_VALUE = new RedisCommand("EVAL", new BooleanReplayConvertor(), 6, Arrays.asList(ValueType.MAP_KEY, ValueType.MAP_VALUE)); private static final RedisCommand EVAL_CONTAINS_ALL_WITH_VALUES = new RedisCommand("EVAL", new BooleanReplayConvertor(), 7, ValueType.OBJECTS); diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index 0b4f86307..490914c6c 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -83,7 +83,7 @@ public interface RedisCommands { RedisCommand ZADD_BOOL_RAW = new RedisCommand("ZADD", new BooleanAmountReplayConvertor()); RedisCommand ZADD_RAW = new RedisCommand("ZADD"); RedisCommand ZADD = new RedisCommand("ZADD"); - RedisCommand ZREM = new RedisCommand("ZREM", new BooleanAmountReplayConvertor(), 2); + RedisCommand ZREM = new RedisCommand("ZREM", new BooleanAmountReplayConvertor(), 2, ValueType.OBJECTS); RedisStrictCommand ZCARD_INT = new RedisStrictCommand("ZCARD", new IntegerReplayConvertor()); RedisStrictCommand ZCARD = new RedisStrictCommand("ZCARD"); RedisStrictCommand ZLEXCOUNT = new RedisStrictCommand("ZLEXCOUNT", new IntegerReplayConvertor()); @@ -98,7 +98,8 @@ public interface RedisCommands { RedisStrictCommand ZREMRANGEBYSCORE = new RedisStrictCommand("ZREMRANGEBYSCORE", new IntegerReplayConvertor()); RedisStrictCommand ZREMRANGEBYLEX = new RedisStrictCommand("ZREMRANGEBYLEX", new IntegerReplayConvertor()); RedisCommand> ZRANGEBYLEX = new RedisCommand>("ZRANGEBYLEX", new ObjectListReplayDecoder()); - RedisCommand> ZRANGEBYSCORE = new RedisCommand>("ZRANGEBYSCORE", new ObjectListReplayDecoder()); + RedisCommand> ZRANGEBYSCORE = new RedisCommand>("ZRANGEBYSCORE", new ObjectSetReplayDecoder()); + RedisCommand> ZRANGEBYSCORE_LIST = new RedisCommand>("ZRANGEBYSCORE", new ObjectListReplayDecoder()); RedisCommand> ZREVRANGEBYSCORE = new RedisCommand>("ZREVRANGEBYSCORE", new ObjectListReplayDecoder()); RedisCommand>> ZRANGE_ENTRY = new RedisCommand>>("ZRANGE", new ScoredSortedSetReplayDecoder()); RedisCommand>> ZRANGEBYSCORE_ENTRY = new RedisCommand>>("ZRANGEBYSCORE", new ScoredSortedSetReplayDecoder()); @@ -120,15 +121,16 @@ public interface RedisCommands { RedisCommand SADD_SINGLE = new RedisCommand("SADD", new BooleanReplayConvertor(), 2); RedisCommand SREM_SINGLE = new RedisCommand("SREM", new BooleanAmountReplayConvertor(), 2, ValueType.OBJECTS); RedisCommand SMOVE = new RedisCommand("SMOVE", new BooleanReplayConvertor(), 3); - RedisCommand> SMEMBERS = new RedisCommand>("SMEMBERS", new ObjectSetReplayDecoder()); + RedisCommand> SMEMBERS = new RedisCommand>("SMEMBERS", new ObjectSetReplayDecoder()); RedisCommand> SSCAN = new RedisCommand>("SSCAN", new NestedMultiDecoder(new ObjectListReplayDecoder(), new ListScanResultReplayDecoder()), ValueType.OBJECT); RedisCommand> EVAL_SSCAN = new RedisCommand>("EVAL", new NestedMultiDecoder(new ObjectListReplayDecoder(), new ListScanResultReplayDecoder()), ValueType.OBJECT); + RedisCommand> EVAL_ZSCAN = new RedisCommand>("EVAL", new NestedMultiDecoder(new ObjectListReplayDecoder(), new ListScanResultReplayDecoder()), ValueType.OBJECT); RedisCommand SISMEMBER = new RedisCommand("SISMEMBER", new BooleanReplayConvertor(), 2); RedisStrictCommand SCARD_INT = new RedisStrictCommand("SCARD", new IntegerReplayConvertor()); RedisStrictCommand SCARD = new RedisStrictCommand("SCARD"); RedisStrictCommand SUNIONSTORE_INT = new RedisStrictCommand("SUNIONSTORE", new IntegerReplayConvertor()); RedisStrictCommand SUNIONSTORE = new RedisStrictCommand("SUNIONSTORE"); - RedisCommand> SUNION = new RedisCommand>("SUNION", new ObjectSetReplayDecoder()); + RedisCommand> SUNION = new RedisCommand>("SUNION", new ObjectSetReplayDecoder()); RedisCommand LSET = new RedisCommand("LSET", new VoidReplayConvertor(), 3); RedisCommand LPOP = new RedisCommand("LPOP"); @@ -175,7 +177,7 @@ public interface RedisCommands { RedisStrictCommand EVAL_LONG = new RedisStrictCommand("EVAL"); RedisStrictCommand EVAL_VOID = new RedisStrictCommand("EVAL", new VoidReplayConvertor()); RedisCommand> EVAL_LIST = new RedisCommand>("EVAL", new ObjectListReplayDecoder()); - RedisCommand> EVAL_SET = new RedisCommand>("EVAL", new ObjectSetReplayDecoder()); + RedisCommand> EVAL_SET = new RedisCommand>("EVAL", new ObjectSetReplayDecoder()); RedisCommand EVAL_OBJECT = new RedisCommand("EVAL"); RedisCommand EVAL_MAP_VALUE = new RedisCommand("EVAL", ValueType.MAP_VALUE); RedisCommand>> EVAL_MAP_ENTRY = new RedisCommand>>("EVAL", new ObjectMapEntryReplayDecoder(), ValueType.MAP); diff --git a/src/main/java/org/redisson/client/protocol/decoder/ObjectSetReplayDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/ObjectSetReplayDecoder.java index e6b17b62b..ac5df9c37 100644 --- a/src/main/java/org/redisson/client/protocol/decoder/ObjectSetReplayDecoder.java +++ b/src/main/java/org/redisson/client/protocol/decoder/ObjectSetReplayDecoder.java @@ -23,7 +23,7 @@ import org.redisson.client.handler.State; import io.netty.buffer.ByteBuf; -public class ObjectSetReplayDecoder implements MultiDecoder> { +public class ObjectSetReplayDecoder implements MultiDecoder> { @Override public Object decode(ByteBuf buf, State state) { @@ -31,8 +31,8 @@ public class ObjectSetReplayDecoder implements MultiDecoder> { } @Override - public Set decode(List parts, State state) { - return new HashSet(parts); + public Set decode(List parts, State state) { + return new HashSet(parts); } @Override diff --git a/src/main/java/org/redisson/reactive/RedissonScoredSortedSetReactive.java b/src/main/java/org/redisson/reactive/RedissonScoredSortedSetReactive.java index 26a935c50..fec4044b4 100644 --- a/src/main/java/org/redisson/reactive/RedissonScoredSortedSetReactive.java +++ b/src/main/java/org/redisson/reactive/RedissonScoredSortedSetReactive.java @@ -207,7 +207,7 @@ public class RedissonScoredSortedSetReactive extends RedissonExpirableReactiv public Publisher> valueRange(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive) { String startValue = value(BigDecimal.valueOf(startScore).toPlainString(), startScoreInclusive); String endValue = value(BigDecimal.valueOf(endScore).toPlainString(), endScoreInclusive); - return commandExecutor.readReactive(getName(), codec, RedisCommands.ZRANGEBYSCORE, getName(), startValue, endValue); + return commandExecutor.readReactive(getName(), codec, RedisCommands.ZRANGEBYSCORE_LIST, getName(), startValue, endValue); } @Override @@ -221,7 +221,7 @@ public class RedissonScoredSortedSetReactive extends RedissonExpirableReactiv public Publisher> valueRange(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive, int offset, int count) { String startValue = value(BigDecimal.valueOf(startScore).toPlainString(), startScoreInclusive); String endValue = value(BigDecimal.valueOf(endScore).toPlainString(), endScoreInclusive); - return commandExecutor.readReactive(getName(), codec, RedisCommands.ZRANGEBYSCORE, getName(), startValue, endValue, "LIMIT", offset, count); + return commandExecutor.readReactive(getName(), codec, RedisCommands.ZRANGEBYSCORE_LIST, getName(), startValue, endValue, "LIMIT", offset, count); } @Override diff --git a/src/main/java/org/redisson/reactive/RedissonSetCacheReactive.java b/src/main/java/org/redisson/reactive/RedissonSetCacheReactive.java index dc768e88a..52c90959f 100644 --- a/src/main/java/org/redisson/reactive/RedissonSetCacheReactive.java +++ b/src/main/java/org/redisson/reactive/RedissonSetCacheReactive.java @@ -17,30 +17,21 @@ package org.redisson.reactive; import java.io.IOException; import java.net.InetSocketAddress; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import org.reactivestreams.Publisher; import org.redisson.EvictionScheduler; +import org.redisson.RedissonSetCache; import org.redisson.api.RSetCacheReactive; import org.redisson.client.codec.Codec; -import org.redisson.client.codec.LongCodec; -import org.redisson.client.protocol.RedisCommand; import org.redisson.client.protocol.RedisCommands; -import org.redisson.client.protocol.RedisStrictCommand; -import org.redisson.client.protocol.convertor.BooleanReplayConvertor; -import org.redisson.client.protocol.convertor.VoidReplayConvertor; import org.redisson.client.protocol.decoder.ListScanResult; -import org.redisson.client.protocol.decoder.ObjectListReplayDecoder; import org.redisson.command.CommandReactiveExecutor; -import net.openhft.hashing.LongHashFunction; - /** *

Set-based cache with ability to set TTL for each entry via * {@link #put(Object, Object, long, TimeUnit)} method. @@ -64,80 +55,30 @@ import net.openhft.hashing.LongHashFunction; */ public class RedissonSetCacheReactive extends RedissonExpirableReactive implements RSetCacheReactive { - private static final RedisCommand ADD_ALL = new RedisCommand("HMSET", new VoidReplayConvertor()); - private static final RedisStrictCommand HDEL = new RedisStrictCommand("HDEL", new BooleanReplayConvertor()); - + private final RedissonSetCache instance; + public RedissonSetCacheReactive(EvictionScheduler evictionScheduler, CommandReactiveExecutor commandExecutor, String name) { super(commandExecutor, name); - evictionScheduler.schedule(getName(), getTimeoutSetName()); + instance = new RedissonSetCache(evictionScheduler, commandExecutor, name); } public RedissonSetCacheReactive(Codec codec, EvictionScheduler evictionScheduler, CommandReactiveExecutor commandExecutor, String name) { super(codec, commandExecutor, name); - evictionScheduler.schedule(getName(), getTimeoutSetName()); + instance = new RedissonSetCache(codec, evictionScheduler, commandExecutor, name); } @Override public Publisher size() { - return commandExecutor.readReactive(getName(), codec, RedisCommands.HLEN_LONG, getName()); - } - - private byte[] hash(Object o) { - if (o == null) { - throw new NullPointerException("Value can't be null"); - } - try { - byte[] objectState = codec.getValueEncoder().encode(o); - return hash(objectState); - } catch (IOException e) { - throw new IllegalArgumentException(e); - } - } - - private byte[] hash(byte[] objectState) { - long h1 = LongHashFunction.farmUo().hashBytes(objectState); - long h2 = LongHashFunction.xx_r39().hashBytes(objectState); - - return ByteBuffer.allocate((2 * Long.SIZE) / Byte.SIZE) - .putLong(h1) - .putLong(h2) - .array(); - } - - String getTimeoutSetName() { - return "redisson__timeout__set__{" + getName() + "}"; + return commandExecutor.readReactive(getName(), codec, RedisCommands.ZCARD, getName()); } @Override public Publisher contains(Object o) { - byte[] key = hash(o); - - return commandExecutor.evalReadReactive(getName(), codec, RedisCommands.EVAL_BOOLEAN, - "local value = redis.call('hexists', KEYS[1], ARGV[2]); " + - "if value == 1 then " + - "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[2]); " - + "if expireDateScore ~= false and tonumber(expireDateScore) <= tonumber(ARGV[1]) then " - + "return 0;" - + "end; " + - "end;" + - "return value; ", - Arrays.asList(getName(), getTimeoutSetName()), System.currentTimeMillis(), key); + return reactive(instance.containsAsync(o)); } Publisher> scanIterator(InetSocketAddress client, long startPos) { - return commandExecutor.evalReadReactive(client, getName(), codec, RedisCommands.EVAL_SSCAN, - "local result = {}; " - + "local res = redis.call('hscan', KEYS[1], ARGV[1]); " - + "for i, value in ipairs(res[2]) do " - + "if i % 2 == 0 then " - + "local key = res[2][i-1]; " - + "local expireDate = redis.call('zscore', KEYS[2], key); " - + "if (expireDate == false) or (expireDate ~= false and tonumber(expireDate) > tonumber(ARGV[2])) then " - + "table.insert(result, value); " - + "end; " - + "end; " - + "end;" - + "return {res[1], result};", Arrays.asList(getName(), getTimeoutSetName()), startPos, System.currentTimeMillis()); + return reactive(instance.scanIteratorAsync(client, startPos)); } @Override @@ -152,45 +93,7 @@ public class RedissonSetCacheReactive extends RedissonExpirableReactive imple @Override public Publisher add(V value, long ttl, TimeUnit unit) { - if (ttl < 0) { - throw new IllegalArgumentException("TTL can't be negative"); - } - if (ttl == 0) { - try { - byte[] objectState = encode(value); - byte[] key = hash(objectState); - return commandExecutor.evalWriteReactive(getName(), codec, RedisCommands.EVAL_BOOLEAN, - "if redis.call('hexists', KEYS[1], ARGV[1]) == 0 then " + - "redis.call('hset', KEYS[1], ARGV[1], ARGV[2]); " + - "return 1; " + - "end; " + - "return 0; ", - Arrays.asList(getName()), key, objectState); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - if (unit == null) { - throw new NullPointerException("TimeUnit param can't be null"); - } - - try { - byte[] objectState = encode(value); - byte[] key = hash(objectState); - - long timeoutDate = System.currentTimeMillis() + unit.toMillis(ttl); - return commandExecutor.evalWriteReactive(getName(), codec, RedisCommands.EVAL_BOOLEAN, - "redis.call('zadd', KEYS[2], ARGV[1], ARGV[3]); " + - "if redis.call('hexists', KEYS[1], ARGV[3]) == 0 then " + - "redis.call('hset', KEYS[1], ARGV[3], ARGV[2]); " + - "return 1; " + - "end;" + - "return 0; ", - Arrays.asList(getName(), getTimeoutSetName()), timeoutDate, objectState, key); - } catch (IOException e) { - throw new RuntimeException(e); - } + return reactive(instance.addAsync(value, ttl, unit)); } private byte[] encode(V value) throws IOException { @@ -201,14 +104,16 @@ public class RedissonSetCacheReactive extends RedissonExpirableReactive imple public Publisher add(V value) { try { byte[] objectState = encode(value); - byte[] key = hash(objectState); + + long timeoutDate = 92233720368547758L; return commandExecutor.evalWriteReactive(getName(), codec, RedisCommands.EVAL_LONG, - "if redis.call('hexists', KEYS[1], ARGV[1]) == 0 then " + - "redis.call('hset', KEYS[1], ARGV[1], ARGV[2]); " + - "return 1; " + - "end; " + - "return 0; ", - Arrays.asList(getName()), key, objectState); + "local expireDateScore = redis.call('zscore', KEYS[1], ARGV[3]); " + + "if expireDateScore ~= false and tonumber(expireDateScore) > tonumber(ARGV[1]) then " + + "return 0;" + + "end; " + + "redis.call('zadd', KEYS[1], ARGV[2], ARGV[3]); " + + "return 1; ", + Arrays.asList(getName()), System.currentTimeMillis(), timeoutDate, objectState); } catch (IOException e) { throw new RuntimeException(e); } @@ -216,23 +121,12 @@ public class RedissonSetCacheReactive extends RedissonExpirableReactive imple @Override public Publisher remove(Object o) { - byte[] key = hash(o); - return commandExecutor.writeReactive(getName(), codec, HDEL, getName(), key); + return reactive(instance.removeAsync(o)); } @Override public Publisher containsAll(Collection c) { - return commandExecutor.evalReadReactive(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, - "local s = redis.call('hvals', KEYS[1]);" + - "for i = 1, table.getn(s), 1 do " + - "for j = 1, table.getn(ARGV), 1 do " - + "if ARGV[j] == s[i] then " - + "table.remove(ARGV, j) " - + "end " - + "end; " - + "end;" - + "return table.getn(ARGV) == 0 and 1 or 0; ", - Collections.singletonList(getName()), c.toArray()); + return reactive(instance.containsAllAsync(c)); } @Override @@ -241,98 +135,30 @@ public class RedissonSetCacheReactive extends RedissonExpirableReactive imple return newSucceeded(0L); } + long score = 92233720368547758L - System.currentTimeMillis(); List params = new ArrayList(c.size()*2 + 1); params.add(getName()); try { for (V value : c) { byte[] objectState = encode(value); - byte[] key = hash(objectState); - params.add(key); + params.add(score); params.add(objectState); } } catch (IOException e) { throw new RuntimeException(e); } - return commandExecutor.writeReactive(getName(), codec, ADD_ALL, params.toArray()); + return commandExecutor.writeReactive(getName(), codec, RedisCommands.ZADD_RAW, params.toArray()); } @Override public Publisher retainAll(Collection c) { - List params = new ArrayList(c.size()); - for (Object object : c) { - params.add(hash(object)); - } - return commandExecutor.evalWriteReactive(getName(), codec, RedisCommands.EVAL_BOOLEAN, - "local keys = redis.call('hkeys', KEYS[1]); " + - "local i=1;" + - "while i <= #keys do " - + "local changed = false;" - + "local element = keys[i];" - + "for j, argElement in pairs(ARGV) do " - + "if argElement == element then " - + "changed = true;" - + "table.remove(keys, i); " - + "table.remove(ARGV, j); " - + "break; " - + "end; " - + "end; " + - "if changed == false then " + - "i = i + 1 " + - "end " + - "end " + - "if #keys > 0 then " - + "for i=1, #keys,5000 do " - + "redis.call('hdel', KEYS[1], unpack(keys, i, math.min(i+4999, #keys))); " - + "redis.call('zrem', KEYS[2], unpack(keys, i, math.min(i+4999, #keys))); " - + "end " - + "return 1;" - + "end; " - + "return 0; ", - Arrays.asList(getName(), getTimeoutSetName()), params.toArray()); + return reactive(instance.retainAllAsync(c)); } @Override public Publisher removeAll(Collection c) { - List params = new ArrayList(c.size()+1); - params.add(getName()); - for (Object object : c) { - params.add(hash(object)); - } - - return commandExecutor.writeReactive(getName(), codec, HDEL, params.toArray()); - } - - @Override - public Publisher delete() { - return commandExecutor.writeReactive(getName(), RedisCommands.DEL_OBJECTS, getName(), getTimeoutSetName()); - } - - @Override - public Publisher expire(long timeToLive, TimeUnit timeUnit) { - return commandExecutor.evalWriteReactive(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, - "redis.call('zadd', KEYS[2], 92233720368547758, 'redisson__expiretag');" + - "redis.call('pexpire', KEYS[2], ARGV[1]); " - + "return redis.call('pexpire', KEYS[1], ARGV[1]); ", - Arrays.asList(getName(), getTimeoutSetName()), timeUnit.toMillis(timeToLive)); - } - - @Override - public Publisher expireAt(long timestamp) { - return commandExecutor.evalWriteReactive(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, - "redis.call('zadd', KEYS[2], 92233720368547758, 'redisson__expiretag');" + - "redis.call('pexpireat', KEYS[2], ARGV[1]); " - + "return redis.call('pexpireat', KEYS[1], ARGV[1]); ", - Arrays.asList(getName(), getTimeoutSetName()), timestamp); - } - - @Override - public Publisher clearExpire() { - return commandExecutor.evalWriteReactive(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, - "redis.call('zrem', KEYS[2], 'redisson__expiretag'); " + - "redis.call('persist', KEYS[2]); " - + "return redis.call('persist', KEYS[1]); ", - Arrays.asList(getName(), getTimeoutSetName())); + return reactive(instance.removeAllAsync(c)); } @Override diff --git a/src/test/java/org/redisson/RedissonSetCacheReactiveTest.java b/src/test/java/org/redisson/RedissonSetCacheReactiveTest.java index 5ea9204d2..bbd1bb185 100644 --- a/src/test/java/org/redisson/RedissonSetCacheReactiveTest.java +++ b/src/test/java/org/redisson/RedissonSetCacheReactiveTest.java @@ -1,5 +1,7 @@ package org.redisson; +import static org.assertj.core.api.Assertions.assertThat; + import java.io.Serializable; import java.util.Arrays; import java.util.Collections; @@ -73,18 +75,18 @@ public class RedissonSetCacheReactiveTest extends BaseReactiveTest { @Test public void testExpireOverwrite() throws InterruptedException, ExecutionException { RSetCacheReactive set = redisson.getSetCache("simple"); - set.add("123", 1, TimeUnit.SECONDS); + assertThat(sync(set.add("123", 1, TimeUnit.SECONDS))).isTrue(); Thread.sleep(800); - set.add("123", 1, TimeUnit.SECONDS); + assertThat(sync(set.add("123", 1, TimeUnit.SECONDS))).isFalse(); - Thread.sleep(800); - Assert.assertTrue(sync(set.contains("123"))); + Thread.sleep(50); + assertThat(sync(set.contains("123"))).isTrue(); - Thread.sleep(200); + Thread.sleep(150); - Assert.assertFalse(sync(set.contains("123"))); + assertThat(sync(set.contains("123"))).isFalse(); } @Test @@ -238,7 +240,7 @@ public class RedissonSetCacheReactiveTest extends BaseReactiveTest { Thread.sleep(1000); - MatcherAssert.assertThat(sync(cache), Matchers.contains("0", "2", "3")); + assertThat(sync(cache)).contains("0", "2", "3"); } @Test diff --git a/src/test/java/org/redisson/RedissonSetCacheTest.java b/src/test/java/org/redisson/RedissonSetCacheTest.java index d78c6caef..65fce35e6 100644 --- a/src/test/java/org/redisson/RedissonSetCacheTest.java +++ b/src/test/java/org/redisson/RedissonSetCacheTest.java @@ -34,6 +34,18 @@ public class RedissonSetCacheTest extends BaseTest { } + @Test + public void testRemoveAll() { + RSetCache set = redisson.getSetCache("set"); + set.add(1); + set.add(2, 10, TimeUnit.SECONDS); + set.add(3); + + assertThat(set.removeAll(Arrays.asList(1, 3))).isTrue(); + assertThat(set.removeAll(Arrays.asList(1, 3))).isFalse(); + assertThat(set).containsOnly(2); + } + @Test public void testDelete() { RSetCache set = redisson.getSetCache("set"); @@ -67,7 +79,7 @@ public class RedissonSetCacheTest extends BaseTest { SimpleBean sb = new SimpleBean(); sb.setLng(1L); RSetCache set = redisson.getSetCache("simple"); - set.add(sb); + assertThat(set.add(sb)).isTrue(); Assert.assertEquals(sb.getLng(), set.iterator().next().getLng()); } @@ -333,7 +345,7 @@ public class RedissonSetCacheTest extends BaseTest { public void testReadAllExpired() throws InterruptedException { RSetCache set = redisson.getSetCache("set"); set.add(1, 1, TimeUnit.SECONDS); - Thread.sleep(1000); + Thread.sleep(1005); assertThat(set.readAll()).isEmpty(); } From f8318a205f9f47c535c1df2f4bc27321af9753e3 Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 27 Apr 2016 12:45:02 +0300 Subject: [PATCH 093/120] Possible infinity RLock expiration renewal fixed --- src/main/java/org/redisson/RedissonLock.java | 29 +++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/redisson/RedissonLock.java b/src/main/java/org/redisson/RedissonLock.java index 96ad9ac74..15d2541e9 100644 --- a/src/main/java/org/redisson/RedissonLock.java +++ b/src/main/java/org/redisson/RedissonLock.java @@ -30,6 +30,8 @@ import org.redisson.client.protocol.RedisCommands; import org.redisson.command.CommandExecutor; import org.redisson.core.RLock; import org.redisson.pubsub.LockPubSub; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import io.netty.util.Timeout; import io.netty.util.TimerTask; @@ -49,6 +51,8 @@ import io.netty.util.internal.PlatformDependent; */ public class RedissonLock extends RedissonExpirable implements RLock { + private final Logger log = LoggerFactory.getLogger(RedissonLock.class); + public static final long LOCK_EXPIRATION_INTERVAL_SECONDS = 30; private static final ConcurrentMap expirationRenewalMap = PlatformDependent.newConcurrentHashMap(); protected long internalLockLeaseTime = TimeUnit.SECONDS.toMillis(LOCK_EXPIRATION_INTERVAL_SECONDS); @@ -155,7 +159,7 @@ public class RedissonLock extends RedissonExpirable implements RLock { } - private Future tryLockInnerAsync(long threadId) { + private Future tryLockInnerAsync(final long threadId) { Future ttlRemaining = tryLockInnerAsync(LOCK_EXPIRATION_INTERVAL_SECONDS, TimeUnit.SECONDS, threadId); ttlRemaining.addListener(new FutureListener() { @Override @@ -175,26 +179,37 @@ public class RedissonLock extends RedissonExpirable implements RLock { } private void scheduleExpirationRenewal() { - if (expirationRenewalMap.containsKey(getName())) { + if (expirationRenewalMap.containsKey(getEntryName())) { return; } Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() { @Override public void run(Timeout timeout) throws Exception { - expireAsync(internalLockLeaseTime, TimeUnit.MILLISECONDS); - expirationRenewalMap.remove(getName()); - scheduleExpirationRenewal(); // reschedule itself + Future future = expireAsync(internalLockLeaseTime, TimeUnit.MILLISECONDS); + future.addListener(new FutureListener() { + @Override + public void operationComplete(Future future) throws Exception { + expirationRenewalMap.remove(getEntryName()); + if (!future.isSuccess()) { + log.error("Can't update lock " + getName() + " expiration", future.cause()); + return; + } + + // reschedule itself + scheduleExpirationRenewal(); + } + }); } }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS); - if (expirationRenewalMap.putIfAbsent(getName(), task) != null) { + if (expirationRenewalMap.putIfAbsent(getEntryName(), task) != null) { task.cancel(); } } void cancelExpirationRenewal() { - Timeout task = expirationRenewalMap.remove(getName()); + Timeout task = expirationRenewalMap.remove(getEntryName()); if (task != null) { task.cancel(); } From 7ddbc8cd033a10dc5e41ff228a8950ab1079a9fb Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 27 Apr 2016 12:47:07 +0300 Subject: [PATCH 094/120] Minor changes --- src/main/java/org/redisson/RedissonLock.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/redisson/RedissonLock.java b/src/main/java/org/redisson/RedissonLock.java index 15d2541e9..6f26229da 100644 --- a/src/main/java/org/redisson/RedissonLock.java +++ b/src/main/java/org/redisson/RedissonLock.java @@ -196,8 +196,10 @@ public class RedissonLock extends RedissonExpirable implements RLock { return; } - // reschedule itself - scheduleExpirationRenewal(); + if (future.getNow()) { + // reschedule itself + scheduleExpirationRenewal(); + } } }); } From 2718f70b4ac85e7a343d9f38df1c1c1dbd844c3e Mon Sep 17 00:00:00 2001 From: jackygurui Date: Wed, 27 Apr 2016 22:55:41 +0100 Subject: [PATCH 095/120] added redisson client shutdown options Overloaded shutdown method to allow specifying the timeout and the quiet period. --- src/main/java/org/redisson/Redisson.java | 7 +++++++ src/main/java/org/redisson/RedissonClient.java | 17 +++++++++++++++++ .../redisson/connection/ConnectionManager.java | 2 ++ .../MasterSlaveConnectionManager.java | 17 +++++++++++------ 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/redisson/Redisson.java b/src/main/java/org/redisson/Redisson.java index fd9e63201..bf481de52 100755 --- a/src/main/java/org/redisson/Redisson.java +++ b/src/main/java/org/redisson/Redisson.java @@ -75,6 +75,7 @@ import org.redisson.core.RSortedSet; import org.redisson.core.RTopic; import io.netty.util.concurrent.Future; +import java.util.concurrent.TimeUnit; /** * Main infrastructure class allows to get access @@ -524,6 +525,12 @@ public class Redisson implements RedissonClient { public void shutdown() { connectionManager.shutdown(); } + + + @Override + public void shutdown(long quietPeriod, long timeout, TimeUnit unit) { + connectionManager.shutdown(quietPeriod, timeout, unit); + } @Override public Config getConfig() { diff --git a/src/main/java/org/redisson/RedissonClient.java b/src/main/java/org/redisson/RedissonClient.java index fffc0e54b..294752add 100755 --- a/src/main/java/org/redisson/RedissonClient.java +++ b/src/main/java/org/redisson/RedissonClient.java @@ -18,6 +18,7 @@ package org.redisson; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import org.redisson.client.codec.Codec; import org.redisson.core.ClusterNode; @@ -619,8 +620,24 @@ public interface RedissonClient { /** * Shuts down Redisson instance NOT Redis server + * + * This equates to invoke shutdown(2, 15, TimeUnit.SECONDS); */ void shutdown(); + + /** + * Shuts down Redisson instance NOT Redis server + * + * Shutdown ensures that no tasks are submitted for 'the quiet period' + * (usually a couple seconds) before it shuts itself down. If a task is submitted during the quiet period, + * it is guaranteed to be accepted and the quiet period will start over. + * + * @param quietPeriod the quiet period as described in the documentation + * @param timeout the maximum amount of time to wait until the executor is {@linkplain #shutdown()} + * regardless if a task was submitted during the quiet period + * @param unit the unit of {@code quietPeriod} and {@code timeout} + */ + void shutdown(long quietPeriod, long timeout, TimeUnit unit); /** * Allows to get configuration provided diff --git a/src/main/java/org/redisson/connection/ConnectionManager.java b/src/main/java/org/redisson/connection/ConnectionManager.java index dd3ad00f2..d15d630be 100644 --- a/src/main/java/org/redisson/connection/ConnectionManager.java +++ b/src/main/java/org/redisson/connection/ConnectionManager.java @@ -99,6 +99,8 @@ public interface ConnectionManager { void shutdown(); + void shutdown(long quietPeriod, long timeout, TimeUnit unit); + EventLoopGroup getGroup(); Timeout newTimeout(TimerTask task, long delay, TimeUnit unit); diff --git a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java index abdb48f98..f67e8c34b 100644 --- a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java +++ b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java @@ -124,7 +124,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager { private final Map entries = PlatformDependent.newConcurrentHashMap(); private final Promise shutdownPromise; - + private final InfinitySemaphoreLatch shutdownLatch = new InfinitySemaphoreLatch(); private final Set clients = Collections.newSetFromMap(PlatformDependent.newConcurrentHashMap()); @@ -137,7 +137,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager { this(config); init(cfg); } - + public MasterSlaveConnectionManager(Config cfg) { Version.logVersion(); @@ -189,7 +189,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager { protected void init(MasterSlaveServersConfig config) { this.config = config; - int[] timeouts = new int[] {config.getRetryInterval(), config.getTimeout(), config.getReconnectionTimeout()}; + int[] timeouts = new int[]{config.getRetryInterval(), config.getTimeout(), config.getReconnectionTimeout()}; Arrays.sort(timeouts); int minTimeout = timeouts[0]; if (minTimeout % 100 != 0) { @@ -609,15 +609,20 @@ public class MasterSlaveConnectionManager implements ConnectionManager { @Override public void shutdown() { + shutdown(2, 15, TimeUnit.SECONDS);//default netty value + } + + @Override + public void shutdown(long quietPeriod, long timeout, TimeUnit unit) { shutdownLatch.close(); shutdownPromise.trySuccess(true); shutdownLatch.awaitUninterruptibly(); - + for (MasterSlaveEntry entry : entries.values()) { entry.shutdown(); } timer.stop(); - group.shutdownGracefully().syncUninterruptibly(); + group.shutdownGracefully(quietPeriod, timeout, unit).syncUninterruptibly(); } @Override @@ -669,7 +674,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager { public InfinitySemaphoreLatch getShutdownLatch() { return shutdownLatch; } - + @Override public Future getShutdownPromise() { return shutdownPromise; From 8029f91b60231a426d6f92ec87d9ea5e8af69750 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Wed, 27 Apr 2016 23:30:59 +0100 Subject: [PATCH 096/120] Fixed removeAll bug in RedissonSet In removeAll method, wrong list was passed to the command executor. --- src/main/java/org/redisson/RedissonSet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/redisson/RedissonSet.java b/src/main/java/org/redisson/RedissonSet.java index c0db7dea7..fc149db07 100644 --- a/src/main/java/org/redisson/RedissonSet.java +++ b/src/main/java/org/redisson/RedissonSet.java @@ -229,7 +229,7 @@ public class RedissonSet extends RedissonExpirable implements RSet { args.add(getName()); args.addAll(c); - return commandExecutor.writeAsync(getName(), codec, RedisCommands.SREM_SINGLE, c.toArray()); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.SREM_SINGLE, args.toArray()); } @Override From 0b5ffa6370453e5e1f50d1ffb8da3aab2e82c175 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 28 Apr 2016 10:17:23 +0300 Subject: [PATCH 097/120] comment fixed --- src/main/java/org/redisson/RedissonSetCache.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/redisson/RedissonSetCache.java b/src/main/java/org/redisson/RedissonSetCache.java index 2eb59382e..3704be77a 100644 --- a/src/main/java/org/redisson/RedissonSetCache.java +++ b/src/main/java/org/redisson/RedissonSetCache.java @@ -41,8 +41,6 @@ import io.netty.util.concurrent.Future; /** *

Set-based cache with ability to set TTL for each entry via * {@link #put(Object, Object, long, TimeUnit)} method. - * And therefore has an complex lua-scripts inside. - * Uses map(value_hash, value) to tie with sorted set which contains expiration record for every value with TTL. *

* *

Current Redis implementation doesn't have set entry eviction functionality. From 7b7e02110e85fa927a12e0c854b77d596067f52b Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 28 Apr 2016 10:17:53 +0300 Subject: [PATCH 098/120] RSet.removeAll test added --- src/test/java/org/redisson/RedissonSetTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/test/java/org/redisson/RedissonSetTest.java b/src/test/java/org/redisson/RedissonSetTest.java index c2999c3e6..0ee987094 100644 --- a/src/test/java/org/redisson/RedissonSetTest.java +++ b/src/test/java/org/redisson/RedissonSetTest.java @@ -13,6 +13,7 @@ import java.util.concurrent.ExecutionException; import org.junit.Assert; import org.junit.Test; import org.redisson.core.RSet; +import org.redisson.core.RSetCache; import io.netty.util.concurrent.Future; @@ -32,6 +33,18 @@ public class RedissonSetTest extends BaseTest { } + @Test + public void testRemoveAll() { + RSet set = redisson.getSet("set"); + set.add(1); + set.add(2); + set.add(3); + + assertThat(set.removeAll(Arrays.asList(1, 3))).isTrue(); + assertThat(set.removeAll(Arrays.asList(1, 3))).isFalse(); + assertThat(set).containsOnly(2); + } + @Test public void testRemoveRandom() { RSet set = redisson.getSet("simple"); From a249586ef85eed5f6cdb0399acdb980b05044787 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sat, 30 Apr 2016 13:34:48 +0300 Subject: [PATCH 099/120] RSet.diff and RSet.intersection methods were added --- src/main/java/org/redisson/RedissonSet.java | 52 +++++++++++++ .../redisson/RedissonSetMultimapValues.java | 52 +++++++++++++ .../client/protocol/RedisCommands.java | 4 + src/main/java/org/redisson/core/RSet.java | 36 +++++++++ .../java/org/redisson/core/RSetAsync.java | 36 +++++++++ .../java/org/redisson/RedissonSetTest.java | 74 +++++++++++++++++++ 6 files changed, 254 insertions(+) diff --git a/src/main/java/org/redisson/RedissonSet.java b/src/main/java/org/redisson/RedissonSet.java index fc149db07..7303a68cc 100644 --- a/src/main/java/org/redisson/RedissonSet.java +++ b/src/main/java/org/redisson/RedissonSet.java @@ -263,6 +263,58 @@ public class RedissonSet extends RedissonExpirable implements RSet { return commandExecutor.writeAsync(getName(), codec, RedisCommands.SUNION, args.toArray()); } + @Override + public int diff(String... names) { + return get(diffAsync(names)); + } + + @Override + public Future diffAsync(String... names) { + List args = new ArrayList(names.length + 1); + args.add(getName()); + args.addAll(Arrays.asList(names)); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.SDIFFSTORE_INT, args.toArray()); + } + + @Override + public Set readDiff(String... names) { + return get(readDiffAsync(names)); + } + + @Override + public Future> readDiffAsync(String... names) { + List args = new ArrayList(names.length + 1); + args.add(getName()); + args.addAll(Arrays.asList(names)); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.SDIFF, args.toArray()); + } + + @Override + public int intersection(String... names) { + return get(intersectionAsync(names)); + } + + @Override + public Future intersectionAsync(String... names) { + List args = new ArrayList(names.length + 1); + args.add(getName()); + args.addAll(Arrays.asList(names)); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.SINTERSTORE_INT, args.toArray()); + } + + @Override + public Set readIntersection(String... names) { + return get(readIntersectionAsync(names)); + } + + @Override + public Future> readIntersectionAsync(String... names) { + List args = new ArrayList(names.length + 1); + args.add(getName()); + args.addAll(Arrays.asList(names)); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.SINTER, args.toArray()); + } + @Override public void clear() { delete(); diff --git a/src/main/java/org/redisson/RedissonSetMultimapValues.java b/src/main/java/org/redisson/RedissonSetMultimapValues.java index a4cd1513c..629cbd111 100644 --- a/src/main/java/org/redisson/RedissonSetMultimapValues.java +++ b/src/main/java/org/redisson/RedissonSetMultimapValues.java @@ -394,4 +394,56 @@ public class RedissonSetMultimapValues extends RedissonExpirable implements R delete(); } + @Override + public int diff(String... names) { + return get(diffAsync(names)); + } + + @Override + public Future diffAsync(String... names) { + List args = new ArrayList(names.length + 1); + args.add(getName()); + args.addAll(Arrays.asList(names)); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.SDIFFSTORE_INT, args.toArray()); + } + + @Override + public Set readDiff(String... names) { + return get(readDiffAsync(names)); + } + + @Override + public Future> readDiffAsync(String... names) { + List args = new ArrayList(names.length + 1); + args.add(getName()); + args.addAll(Arrays.asList(names)); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.SDIFF, args.toArray()); + } + + @Override + public int intersection(String... names) { + return get(intersectionAsync(names)); + } + + @Override + public Future intersectionAsync(String... names) { + List args = new ArrayList(names.length + 1); + args.add(getName()); + args.addAll(Arrays.asList(names)); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.SINTERSTORE_INT, args.toArray()); + } + + @Override + public Set readIntersection(String... names) { + return get(readIntersectionAsync(names)); + } + + @Override + public Future> readIntersectionAsync(String... names) { + List args = new ArrayList(names.length + 1); + args.add(getName()); + args.addAll(Arrays.asList(names)); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.SINTER, args.toArray()); + } + } diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index 490914c6c..37568cb95 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -129,8 +129,12 @@ public interface RedisCommands { RedisStrictCommand SCARD_INT = new RedisStrictCommand("SCARD", new IntegerReplayConvertor()); RedisStrictCommand SCARD = new RedisStrictCommand("SCARD"); RedisStrictCommand SUNIONSTORE_INT = new RedisStrictCommand("SUNIONSTORE", new IntegerReplayConvertor()); + RedisStrictCommand SDIFFSTORE_INT = new RedisStrictCommand("SDIFFSTORE", new IntegerReplayConvertor()); + RedisStrictCommand SINTERSTORE_INT = new RedisStrictCommand("SINTERSTORE", new IntegerReplayConvertor()); RedisStrictCommand SUNIONSTORE = new RedisStrictCommand("SUNIONSTORE"); RedisCommand> SUNION = new RedisCommand>("SUNION", new ObjectSetReplayDecoder()); + RedisCommand> SDIFF = new RedisCommand>("SDIFF", new ObjectSetReplayDecoder()); + RedisCommand> SINTER = new RedisCommand>("SINTER", new ObjectSetReplayDecoder()); RedisCommand LSET = new RedisCommand("LSET", new VoidReplayConvertor(), 3); RedisCommand LPOP = new RedisCommand("LPOP"); diff --git a/src/main/java/org/redisson/core/RSet.java b/src/main/java/org/redisson/core/RSet.java index d98db75f6..316a1c6ff 100644 --- a/src/main/java/org/redisson/core/RSet.java +++ b/src/main/java/org/redisson/core/RSet.java @@ -68,4 +68,40 @@ public interface RSet extends Set, RExpirable, RSetAsync { */ Set readUnion(String... names); + /** + * Diff sets specified by name and write to current set. + * If current set already exists, it is overwritten. + * + * @param names + * @return + */ + int diff(String... names); + + /** + * Diff sets specified by name with current set. + * Without current set state change. + * + * @param names + * @return + */ + + Set readDiff(String... names); + /** + * Intersection sets specified by name and write to current set. + * If current set already exists, it is overwritten. + * + * @param names + * @return + */ + int intersection(String... names); + + /** + * Intersection sets specified by name with current set. + * Without current set state change. + * + * @param names + * @return + */ + Set readIntersection(String... names); + } diff --git a/src/main/java/org/redisson/core/RSetAsync.java b/src/main/java/org/redisson/core/RSetAsync.java index f74512191..214bebc7d 100644 --- a/src/main/java/org/redisson/core/RSetAsync.java +++ b/src/main/java/org/redisson/core/RSetAsync.java @@ -71,4 +71,40 @@ public interface RSetAsync extends RCollectionAsync { */ Future> readUnionAsync(String... keys); + /** + * Diff sets specified by name and write to current set. + * If current set already exists, it is overwritten. + * + * @param names + * @return + */ + Future diffAsync(String... keys); + + /** + * Diff sets specified by name with current set. + * Without current set state change. + * + * @param names + * @return + */ + Future> readDiffAsync(String... keys); + + /** + * Intersection sets specified by name and write to current set. + * If current set already exists, it is overwritten. + * + * @param names + * @return + */ + Future intersectionAsync(String... keys); + + /** + * Intersection sets specified by name with current set. + * Without current set state change. + * + * @param names + * @return + */ + Future> readIntersectionAsync(String... keys); + } diff --git a/src/test/java/org/redisson/RedissonSetTest.java b/src/test/java/org/redisson/RedissonSetTest.java index 0ee987094..4e0fac31f 100644 --- a/src/test/java/org/redisson/RedissonSetTest.java +++ b/src/test/java/org/redisson/RedissonSetTest.java @@ -334,7 +334,81 @@ public class RedissonSetTest extends BaseTest { assertThat(set).containsOnly(5, 6); } + @Test + public void testDiff() { + RSet set = redisson.getSet("set"); + set.add(5); + set.add(6); + RSet set1 = redisson.getSet("set1"); + set1.add(1); + set1.add(2); + set1.add(3); + RSet set2 = redisson.getSet("set2"); + set2.add(3); + set2.add(4); + set2.add(5); + + assertThat(set.diff("set1", "set2")).isEqualTo(2); + assertThat(set).containsOnly(1, 2); + } + + @Test + public void testReadDiff() { + RSet set = redisson.getSet("set"); + set.add(5); + set.add(7); + set.add(6); + RSet set1 = redisson.getSet("set1"); + set1.add(1); + set1.add(2); + set1.add(5); + RSet set2 = redisson.getSet("set2"); + set2.add(3); + set2.add(4); + set2.add(5); + + assertThat(set.readDiff("set1", "set2")).containsOnly(7, 6); + assertThat(set).containsOnly(6, 5, 7); + } + + @Test + public void testIntersection() { + RSet set = redisson.getSet("set"); + set.add(5); + set.add(6); + RSet set1 = redisson.getSet("set1"); + set1.add(1); + set1.add(2); + set1.add(3); + RSet set2 = redisson.getSet("set2"); + set2.add(3); + set2.add(4); + set2.add(5); + + assertThat(set.intersection("set1", "set2")).isEqualTo(1); + assertThat(set).containsOnly(3); + } + + @Test + public void testReadIntersection() { + RSet set = redisson.getSet("set"); + set.add(5); + set.add(7); + set.add(6); + RSet set1 = redisson.getSet("set1"); + set1.add(1); + set1.add(2); + set1.add(5); + RSet set2 = redisson.getSet("set2"); + set2.add(3); + set2.add(4); + set2.add(5); + + assertThat(set.readIntersection("set1", "set2")).containsOnly(5); + assertThat(set).containsOnly(6, 5, 7); + } + @Test public void testMove() throws Exception { RSet set = redisson.getSet("set"); From 0453ede0dac3aae1b45c66c13cf05f3a0069dfe7 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sat, 30 Apr 2016 13:58:33 +0300 Subject: [PATCH 100/120] RScoredSortedSet.containsAll, removeAll, retainAll methods optimization --- .../org/redisson/RedissonScoredSortedSet.java | 83 +++++++++++-------- .../redisson/RedissonScoredSortedSetTest.java | 2 + 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index d4f714fb1..a6ff1836b 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.net.InetSocketAddress; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -29,8 +30,11 @@ import java.util.Map.Entry; import org.redisson.client.codec.Codec; import org.redisson.client.codec.ScoredCodec; import org.redisson.client.codec.StringCodec; +import org.redisson.client.protocol.RedisCommand; import org.redisson.client.protocol.RedisCommands; import org.redisson.client.protocol.ScoredEntry; +import org.redisson.client.protocol.RedisCommand.ValueType; +import org.redisson.client.protocol.convertor.BooleanReplayConvertor; import org.redisson.client.protocol.decoder.ListScanResult; import org.redisson.command.CommandAsyncExecutor; import org.redisson.core.RScoredSortedSet; @@ -278,27 +282,32 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future containsAllAsync(Collection c) { - return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, - "local s = redis.call('zrange', KEYS[1], 0, -1);" + - "for i = 1, #s, 1 do " + + if (c.isEmpty()) { + return newSucceededFuture(true); + } + + return commandExecutor.evalReadAsync(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 4, ValueType.OBJECTS), "for j = 1, #ARGV, 1 do " - + "if ARGV[j] == s[i] " - + "then table.remove(ARGV, j) end " + + "local expireDateScore = redis.call('zscore', KEYS[1], ARGV[j]) " + + "if expireDateScore == false then " + + "return 0;" + + "end; " + "end; " - + "end;" - + "return #ARGV == 0 and 1 or 0; ", + + "return 1; ", Collections.singletonList(getName()), c.toArray()); } @Override public Future removeAllAsync(Collection c) { - return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, - "local v = 0;" - + "for i=1, #ARGV, 5000 do " - + "v = v + redis.call('zrem', KEYS[1], unpack(ARGV, i, math.min(i+4999, #ARGV))); " - + "end " - + "return v > 0;", - Collections.singletonList(getName()), c.toArray()); + if (c.isEmpty()) { + return newSucceededFuture(false); + } + + List params = new ArrayList(c.size()+1); + params.add(getName()); + params.addAll(c); + + return commandExecutor.writeAsync(getName(), codec, RedisCommands.ZREM, params.toArray()); } @Override @@ -310,30 +319,34 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc public boolean retainAll(Collection c) { return get(retainAllAsync(c)); } + + private byte[] encode(V value) { + try { + return codec.getValueEncoder().encode(value); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } @Override public Future retainAllAsync(Collection c) { - return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, - "local changed = 0 " + - "local s = redis.call('zrange', KEYS[1], 0, -1) " - + "local i = 1 " - + "while i <= #s do " - + "local element = s[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('zrem', KEYS[1], element) " - + "changed = 1 " - + "end " - + "i = i + 1 " - + "end " - + "return changed ", - Collections.singletonList(getName()), c.toArray()); + if (c.isEmpty()) { + return deleteAsync(); + } + + List params = new ArrayList(c.size()*2); + for (Object object : c) { + params.add(0); + params.add(encode((V)object)); + } + + return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN, + "redis.call('zadd', KEYS[2], unpack(ARGV)); " + + "local prevSize = redis.call('zcard', KEYS[1]); " + + "local size = redis.call('zinterstore', KEYS[1], #ARGV/2, KEYS[1], KEYS[2], 'aggregate', 'sum');" + + "redis.call('del', KEYS[2]); " + + "return size ~= prevSize and 1 or 0; ", + Arrays.asList(getName(), "redisson_temp__{" + getName() + "}"), params.toArray()); } @Override diff --git a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index 3b08976af..f4d148148 100644 --- a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -288,6 +288,8 @@ public class RedissonScoredSortedSetTest extends BaseTest { Assert.assertTrue(set.retainAll(Arrays.asList(1, 2))); Assert.assertThat(set, Matchers.containsInAnyOrder(1, 2)); Assert.assertEquals(2, set.size()); + assertThat(set.getScore(1)).isEqualTo(10); + assertThat(set.getScore(2)).isEqualTo(20); } @Test From e6ce9776ebbb26580fb93f3c6f17373bfe50e15d Mon Sep 17 00:00:00 2001 From: Nikita Date: Sat, 30 Apr 2016 14:02:14 +0300 Subject: [PATCH 101/120] [maven-release-plugin] prepare release redisson-2.2.13 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c7cf883d8..404b7602a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.redisson redisson - 2.2.13-SNAPSHOT + 2.2.13 bundle Redisson @@ -15,7 +15,7 @@ scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git - HEAD + redisson-2.2.13 From 701d4cee7b3662acdacf769e4c7caee9a7b53010 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sat, 30 Apr 2016 14:02:21 +0300 Subject: [PATCH 102/120] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 404b7602a..974c60071 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.redisson redisson - 2.2.13 + 2.2.14-SNAPSHOT bundle Redisson @@ -15,7 +15,7 @@ scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git scm:git:git@github.com:mrniko/redisson.git - redisson-2.2.13 + HEAD From e2fda29b828f9206637b90a2513a27c4b4a80243 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Sat, 30 Apr 2016 14:12:07 +0300 Subject: [PATCH 103/120] Update CHANGELOG.md --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 592bd4795..6e4cfb306 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ Redisson Releases History ================================ ####Please Note: trunk is current development branch. +####30-Apr-2016 - version 2.2.13 released + +Feature - `RSet.diff` and `RSet.intersection` methods added +Imporovement - `RScoredSortedSet`.`containsAll`, `removeAll` and `retainAll` methods speed optimization +Imporovement - `RSetCache` memory and speed optimization +Imporovement - `RSet`.`retainAll`, `containsAll`, `removeAll` methods speed optimized up to 100x +Fixed - possible infinity `RLock` expiration renewal process +Fixed - error during `RSetCache.readAll` invocation. +Fixed - expiration override wasn't work in `RSetCache.add` + ####22-Apr-2016 - version 2.2.12 released Imporovement - Replaying phase handling in CommandDecoder From a4dc7153f3f6a73199ea8752fcb9354083faab11 Mon Sep 17 00:00:00 2001 From: Ji Luo Date: Sun, 1 May 2016 17:26:34 +0800 Subject: [PATCH 104/120] Add entryValueReversed && entryValueReversedAsync to RedissonScoredSortedSet --- .../org/redisson/RedissonScoredSortedSet.java | 12 +++++++++++ .../client/protocol/RedisCommands.java | 1 + .../org/redisson/core/RScoredSortedSet.java | 2 ++ .../redisson/core/RScoredSortedSetAsync.java | 2 ++ .../redisson/RedissonScoredSortedSetTest.java | 20 +++++++++++++++++++ 5 files changed, 37 insertions(+) diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index a6ff1836b..de180bf3e 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -448,6 +448,11 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc return get(entryRangeAsync(startScore, startScoreInclusive, endScore, endScoreInclusive, offset, count)); } + @Override + public Collection> entryRangeReversed(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive, int offset, int count) { + return get(entryRangeReversedAsync(startScore, startScoreInclusive, endScore, endScoreInclusive, offset, count)); + } + @Override public Future>> entryRangeAsync(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive, int offset, int count) { String startValue = value(startScore, startScoreInclusive); @@ -455,6 +460,13 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANGEBYSCORE_ENTRY, getName(), startValue, endValue, "WITHSCORES", "LIMIT", offset, count); } + @Override + public Future>> entryRangeReversedAsync(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive, int offset, int count) { + String startValue = value(startScore, startScoreInclusive); + String endValue = value(endScore, endScoreInclusive); + return commandExecutor.readAsync(getName(), codec, RedisCommands.ZREVRANGEBYSCORE_ENTRY, getName(), endValue, startValue, "WITHSCORES", "LIMIT", offset, count); + } + @Override public Future revRankAsync(V o) { return commandExecutor.readAsync(getName(), codec, RedisCommands.ZREVRANK_INT, getName(), o); diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index 37568cb95..253e91507 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -101,6 +101,7 @@ public interface RedisCommands { RedisCommand> ZRANGEBYSCORE = new RedisCommand>("ZRANGEBYSCORE", new ObjectSetReplayDecoder()); RedisCommand> ZRANGEBYSCORE_LIST = new RedisCommand>("ZRANGEBYSCORE", new ObjectListReplayDecoder()); RedisCommand> ZREVRANGEBYSCORE = new RedisCommand>("ZREVRANGEBYSCORE", new ObjectListReplayDecoder()); + RedisCommand>> ZREVRANGEBYSCORE_ENTRY = new RedisCommand>>("ZREVRANGEBYSCORE", new ScoredSortedSetReplayDecoder()); RedisCommand>> ZRANGE_ENTRY = new RedisCommand>>("ZRANGE", new ScoredSortedSetReplayDecoder()); RedisCommand>> ZRANGEBYSCORE_ENTRY = new RedisCommand>>("ZRANGEBYSCORE", new ScoredSortedSetReplayDecoder()); RedisCommand> ZSCAN = new RedisCommand>("ZSCAN", new NestedMultiDecoder(new ScoredSortedSetScanDecoder(), new ScoredSortedSetScanReplayDecoder()), ValueType.OBJECT); diff --git a/src/main/java/org/redisson/core/RScoredSortedSet.java b/src/main/java/org/redisson/core/RScoredSortedSet.java index 27a4c5ea5..4c6b2d758 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSet.java +++ b/src/main/java/org/redisson/core/RScoredSortedSet.java @@ -112,4 +112,6 @@ public interface RScoredSortedSet extends RScoredSortedSetAsync, Iterable< Collection> entryRange(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive, int offset, int count); + Collection> entryRangeReversed(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive, int offset, int count); + } diff --git a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java index 83e41798e..e827c5bac 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java +++ b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java @@ -94,4 +94,6 @@ public interface RScoredSortedSetAsync extends RExpirableAsync { Future>> entryRangeAsync(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive, int offset, int count); + Future>> entryRangeReversedAsync(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive, int offset, int count); + } diff --git a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index f4d148148..d069a7f28 100644 --- a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -653,12 +653,32 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(4, "e"); Collection> r = set.entryRange(1, true, 4, false, 1, 2); + Assert.assertEquals(2, r.size()); ScoredEntry[] a = r.toArray(new ScoredEntry[0]); Assert.assertEquals(2d, a[0].getScore(), 0); Assert.assertEquals(3d, a[1].getScore(), 0); Assert.assertEquals("c", a[0].getValue()); Assert.assertEquals("d", a[1].getValue()); } + + @Test + public void testScoredSortedSetEntryRangeReversed() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + + set.add(0, "a"); + set.add(1, "b"); + set.add(2, "c"); + set.add(3, "d"); + set.add(4, "e"); + + Collection> r = set.entryRangeReversed(1, true, 4, false, 1, 2); + Assert.assertEquals(2, r.size()); + ScoredEntry[] a = r.toArray(new ScoredEntry[0]); + Assert.assertEquals(2d, a[0].getScore(), 0); + Assert.assertEquals(1d, a[1].getScore(), 0); + Assert.assertEquals("c", a[0].getValue()); + Assert.assertEquals("b", a[1].getValue()); + } @Test public void testScoredSortedSetEntryRangeNegativeInf() { From 0cc9c73e1d0efcb2d407ffc3ce0e7df39c6bd88b Mon Sep 17 00:00:00 2001 From: Nikita Date: Sun, 1 May 2016 13:22:19 +0300 Subject: [PATCH 105/120] RScoredSortedSet.readAll methods added --- .../java/org/redisson/RedissonScoredSortedSet.java | 10 ++++++++++ .../java/org/redisson/core/RScoredSortedSet.java | 7 +++++++ .../org/redisson/core/RScoredSortedSetAsync.java | 7 +++++++ .../org/redisson/RedissonScoredSortedSetTest.java | 12 ++++++++++++ 4 files changed, 36 insertions(+) diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index de180bf3e..22500ded8 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -51,6 +51,16 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc super(codec, commandExecutor, name); } + @Override + public Collection readAll() { + return get(readAllAsync()); + } + + @Override + public Future> readAllAsync() { + return valueRangeAsync(0, -1); + } + @Override public V pollFirst() { return get(pollFirstAsync()); diff --git a/src/main/java/org/redisson/core/RScoredSortedSet.java b/src/main/java/org/redisson/core/RScoredSortedSet.java index 4c6b2d758..19ffcb90c 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSet.java +++ b/src/main/java/org/redisson/core/RScoredSortedSet.java @@ -114,4 +114,11 @@ public interface RScoredSortedSet extends RScoredSortedSetAsync, Iterable< Collection> entryRangeReversed(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive, int offset, int count); + /** + * Read all values at once. + * + * @return + */ + Collection readAll(); + } diff --git a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java index e827c5bac..e1fe791aa 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java +++ b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java @@ -96,4 +96,11 @@ public interface RScoredSortedSetAsync extends RExpirableAsync { Future>> entryRangeReversedAsync(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive, int offset, int count); + /** + * Read all values at once. + * + * @return + */ + Future> readAllAsync(); + } diff --git a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index d069a7f28..b51ab43e3 100644 --- a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -27,6 +27,18 @@ import io.netty.util.concurrent.Future; public class RedissonScoredSortedSetTest extends BaseTest { + @Test + public void testReadAll() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(0, "1"); + set.add(1, "4"); + set.add(2, "2"); + set.add(3, "5"); + set.add(4, "3"); + + assertThat(set.readAll()).containsOnly("1", "2", "4", "5", "3"); + } + @Test public void testAddAll() { RScoredSortedSet set = redisson.getScoredSortedSet("simple"); From 62d4e85d2a1997007d5dc638a11f199618911dd0 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sun, 1 May 2016 13:25:34 +0300 Subject: [PATCH 106/120] RLexSortedSet.readAll, pollFirst, pollLast, first, last and revRank methods added --- .../java/org/redisson/core/RLexSortedSet.java | 18 ++++++++ .../org/redisson/core/RLexSortedSetAsync.java | 18 ++++++++ .../redisson/RedissonLexSortedSetTest.java | 43 ++++++++++++++++++- 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/redisson/core/RLexSortedSet.java b/src/main/java/org/redisson/core/RLexSortedSet.java index 2a56ca448..67125e0cf 100644 --- a/src/main/java/org/redisson/core/RLexSortedSet.java +++ b/src/main/java/org/redisson/core/RLexSortedSet.java @@ -20,6 +20,24 @@ import java.util.Set; public interface RLexSortedSet extends RLexSortedSetAsync, Set, RExpirable { + String pollFirst(); + + String pollLast(); + + String first(); + + String last(); + + /** + * Returns rank of value, with the scores ordered from high to low. + * + * @param o + * @return rank or null if value does not exist + */ + Integer revRank(String o); + + Collection readAll(); + int removeRangeTail(String fromElement, boolean fromInclusive); /** diff --git a/src/main/java/org/redisson/core/RLexSortedSetAsync.java b/src/main/java/org/redisson/core/RLexSortedSetAsync.java index 6616e25e5..c82f36b23 100644 --- a/src/main/java/org/redisson/core/RLexSortedSetAsync.java +++ b/src/main/java/org/redisson/core/RLexSortedSetAsync.java @@ -21,6 +21,16 @@ import io.netty.util.concurrent.Future; public interface RLexSortedSetAsync extends RCollectionAsync { + Future pollLastAsync(); + + Future pollFirstAsync(); + + Future firstAsync(); + + Future lastAsync(); + + Future> readAllAsync(); + Future removeRangeAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); /** @@ -126,5 +136,13 @@ public interface RLexSortedSetAsync extends RCollectionAsync { */ @Deprecated Future> valueRangeAsync(int startIndex, int endIndex); + + /** + * Returns rank of value, with the scores ordered from high to low. + * + * @param o + * @return rank or null if value does not exist + */ + Future revRankAsync(String o); } diff --git a/src/test/java/org/redisson/RedissonLexSortedSetTest.java b/src/test/java/org/redisson/RedissonLexSortedSetTest.java index e6ce62d4c..d3d220cb4 100644 --- a/src/test/java/org/redisson/RedissonLexSortedSetTest.java +++ b/src/test/java/org/redisson/RedissonLexSortedSetTest.java @@ -1,12 +1,53 @@ package org.redisson; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; import org.redisson.core.RLexSortedSet; public class RedissonLexSortedSetTest extends BaseTest { + @Test + public void testPollLast() { + RLexSortedSet set = redisson.getLexSortedSet("simple"); + Assert.assertNull(set.pollLast()); + + set.add("a"); + set.add("b"); + set.add("c"); + + Assert.assertEquals("c", set.pollLast()); + MatcherAssert.assertThat(set, Matchers.contains("a", "b")); + } + + @Test + public void testPollFirst() { + RLexSortedSet set = redisson.getLexSortedSet("simple"); + Assert.assertNull(set.pollFirst()); + + set.add("a"); + set.add("b"); + set.add("c"); + + Assert.assertEquals("a", set.pollFirst()); + MatcherAssert.assertThat(set, Matchers.contains("b", "c")); + } + + @Test + public void testFirstLast() { + RLexSortedSet set = redisson.getLexSortedSet("simple"); + set.add("a"); + set.add("b"); + set.add("c"); + set.add("d"); + + Assert.assertEquals("a", set.first()); + Assert.assertEquals("d", set.last()); + } + @Test public void testRemoveLexRangeTail() { RLexSortedSet set = redisson.getLexSortedSet("simple"); From 1d123af02b6f99d5ebd56ecd07d4b7180b38c44f Mon Sep 17 00:00:00 2001 From: Nikita Date: Sun, 1 May 2016 13:32:08 +0300 Subject: [PATCH 107/120] few comments added --- src/main/java/org/redisson/core/RLexSortedSet.java | 5 +++++ src/main/java/org/redisson/core/RLexSortedSetAsync.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/main/java/org/redisson/core/RLexSortedSet.java b/src/main/java/org/redisson/core/RLexSortedSet.java index 67125e0cf..407c01650 100644 --- a/src/main/java/org/redisson/core/RLexSortedSet.java +++ b/src/main/java/org/redisson/core/RLexSortedSet.java @@ -36,6 +36,11 @@ public interface RLexSortedSet extends RLexSortedSetAsync, Set, RExpirab */ Integer revRank(String o); + /** + * Read all values at once. + * + * @return + */ Collection readAll(); int removeRangeTail(String fromElement, boolean fromInclusive); diff --git a/src/main/java/org/redisson/core/RLexSortedSetAsync.java b/src/main/java/org/redisson/core/RLexSortedSetAsync.java index c82f36b23..42541bfa5 100644 --- a/src/main/java/org/redisson/core/RLexSortedSetAsync.java +++ b/src/main/java/org/redisson/core/RLexSortedSetAsync.java @@ -29,6 +29,11 @@ public interface RLexSortedSetAsync extends RCollectionAsync { Future lastAsync(); + /** + * Read all values at once. + * + * @return + */ Future> readAllAsync(); Future removeRangeAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); From 0490d1be9162592dc5220cc16184b8e9e2fb46d0 Mon Sep 17 00:00:00 2001 From: Zizheng Tai Date: Sun, 1 May 2016 05:05:39 -0700 Subject: [PATCH 108/120] Bump up version number in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fdce687bb..9d87ecd05 100644 --- a/README.md +++ b/README.md @@ -82,12 +82,12 @@ Include the following to your dependency list: org.redisson redisson - 2.2.12 + 2.2.13 ### Gradle - compile 'org.redisson:redisson:2.2.12' + compile 'org.redisson:redisson:2.2.13' ### Supported by From 6f6f0ffde484974f9a960924be14ff050046a7ff Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 4 May 2016 20:13:11 +0300 Subject: [PATCH 109/120] Script issue fixed in RScoredSortedSet.retainAll --- src/main/java/org/redisson/RedissonScoredSortedSet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index 22500ded8..43edf86bd 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -353,7 +353,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN, "redis.call('zadd', KEYS[2], unpack(ARGV)); " + "local prevSize = redis.call('zcard', KEYS[1]); " - + "local size = redis.call('zinterstore', KEYS[1], #ARGV/2, KEYS[1], KEYS[2], 'aggregate', 'sum');" + + "local size = redis.call('zinterstore', KEYS[1], 2, KEYS[1], KEYS[2], 'aggregate', 'sum');" + "redis.call('del', KEYS[2]); " + "return size ~= prevSize and 1 or 0; ", Arrays.asList(getName(), "redisson_temp__{" + getName() + "}"), params.toArray()); From 3e95416f341cd3ddff9fe604c20dee382ab2ee43 Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 4 May 2016 20:15:26 +0300 Subject: [PATCH 110/120] comment fixed --- src/main/java/org/redisson/core/RSetCache.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/org/redisson/core/RSetCache.java b/src/main/java/org/redisson/core/RSetCache.java index a2db04c93..902cb3493 100644 --- a/src/main/java/org/redisson/core/RSetCache.java +++ b/src/main/java/org/redisson/core/RSetCache.java @@ -19,10 +19,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; /** - *

Set-based cache with ability to set TTL for each entry via - * {@link #put(Object, Object, long, TimeUnit)} method. - * And therefore has an complex lua-scripts inside. - * Uses map(value_hash, value) to tie with sorted set which contains expiration record for every value with TTL. + *

Set-based cache with ability to set TTL for each object. *

* *

Current Redis implementation doesn't have set entry eviction functionality. From 1190af034998ce0dcb6c48807578c5da4c4b1469 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Fri, 13 May 2016 00:05:55 +0100 Subject: [PATCH 111/120] test case and fix added to fix issue #487 The ArrayOutOfBoundsException is thrown and returned null when polling from a RedissonBlockingQueue with key size is exactly 1. --- .../decoder/KeyValueObjectDecoder.java | 2 +- .../redisson/RedissonBlockingQueueTest.java | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/redisson/client/protocol/decoder/KeyValueObjectDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/KeyValueObjectDecoder.java index 614e9b8df..73f238c23 100644 --- a/src/main/java/org/redisson/client/protocol/decoder/KeyValueObjectDecoder.java +++ b/src/main/java/org/redisson/client/protocol/decoder/KeyValueObjectDecoder.java @@ -27,7 +27,7 @@ public class KeyValueObjectDecoder implements MultiDecoder { @Override public Object decode(ByteBuf buf, State state) { String status = buf.toString(CharsetUtil.UTF_8); - buf.skipBytes(2); + buf.skipBytes(1); return status; } diff --git a/src/test/java/org/redisson/RedissonBlockingQueueTest.java b/src/test/java/org/redisson/RedissonBlockingQueueTest.java index 2542ab5ac..0cc416683 100644 --- a/src/test/java/org/redisson/RedissonBlockingQueueTest.java +++ b/src/test/java/org/redisson/RedissonBlockingQueueTest.java @@ -377,4 +377,35 @@ public class RedissonBlockingQueueTest extends BaseTest { } + + @Test + public void testSingleCharAsKeyName() { + String value = "Long Test Message;Long Test Message;Long Test Message;" + + "Long Test Message;Long Test Message;Long Test Message;Long " + + "Test Message;Long Test Message;Long Test Message;Long Test " + + "Message;Long Test Message;Long Test Message;Long Test Messa" + + "ge;Long Test Message;Long Test Message;Long Test Message;Lo" + + "ng Test Message;Long Test Message;Long Test Message;Long Te" + + "st Message;Long Test Message;Long Test Message;Long Test Me" + + "ssage;Long Test Message;Long Test Message;Long Test Message" + + ";Long Test Message;Long Test Message;Long Test Message;Long" + + " Test Message;Long Test Message;Long Test Message;Long Test" + + " Message;Long Test Message;Long Test Message;Long Test Mess" + + "age;"; + try { + for (int i = 0; i < 10; i++) { + System.out.println("Iteration: " + i); + RBlockingQueue q = redisson.getBlockingQueue(String.valueOf(i)); + q.add(value); + System.out.println("Message added to [" + i + "]"); + q.expire(1, TimeUnit.MINUTES); + System.out.println("Expiry set to [" + i + "]"); + String poll = q.poll(1, TimeUnit.SECONDS); + System.out.println("Message polled from [" + i + "]" + poll); + Assert.assertEquals(value, poll); + } + } catch (Exception e) { + Assert.fail(e.getLocalizedMessage()); + } + } } From 6a30f67d6bccbab63836ccfcfe2851e077cf8e44 Mon Sep 17 00:00:00 2001 From: jackygurui Date: Fri, 13 May 2016 00:17:13 +0100 Subject: [PATCH 112/120] Looks like the Redis spec has changed Since 3.2.0, the GEODIST command seems always rounding up the result to 4 digits decimal places. --- src/test/java/org/redisson/RedissonGeoTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/redisson/RedissonGeoTest.java b/src/test/java/org/redisson/RedissonGeoTest.java index 4b6baf7ee..455ba317f 100644 --- a/src/test/java/org/redisson/RedissonGeoTest.java +++ b/src/test/java/org/redisson/RedissonGeoTest.java @@ -32,7 +32,7 @@ public class RedissonGeoTest extends BaseTest { RGeo geo = redisson.getGeo("test"); geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), new GeoEntry(15.087269, 37.502669, "Catania")); - assertThat(geo.dist("Palermo", "Catania", GeoUnit.METERS)).isEqualTo(166274.15156960033D); + assertThat(geo.dist("Palermo", "Catania", GeoUnit.METERS)).isEqualTo(166274.1516D); } @Test From 8dc10d3e5e025c47dacf5d3dab68b39db15d97d1 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sat, 14 May 2016 12:56:50 +0300 Subject: [PATCH 113/120] CommandEncoder minor optimization --- .../client/handler/CommandEncoder.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/redisson/client/handler/CommandEncoder.java b/src/main/java/org/redisson/client/handler/CommandEncoder.java index ed245e211..54bd57e52 100644 --- a/src/main/java/org/redisson/client/handler/CommandEncoder.java +++ b/src/main/java/org/redisson/client/handler/CommandEncoder.java @@ -15,7 +15,9 @@ */ package org.redisson.client.handler; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.redisson.client.codec.StringCodec; import org.redisson.client.protocol.CommandData; @@ -52,6 +54,8 @@ public class CommandEncoder extends MessageToByteEncoder> { private static final char BYTES_PREFIX = '$'; private static final byte[] CRLF = "\r\n".getBytes(); + private static final Map longCache = new HashMap(); + @Override protected void encode(ChannelHandlerContext ctx, CommandData msg, ByteBuf out) throws Exception { out.writeByte(ARGS_PREFIX); @@ -59,7 +63,7 @@ public class CommandEncoder extends MessageToByteEncoder> { if (msg.getCommand().getSubName() != null) { len++; } - out.writeBytes(toChars(len)); + out.writeBytes(convert(len)); out.writeBytes(CRLF); writeArgument(out, msg.getCommand().getName().getBytes("UTF-8")); @@ -127,7 +131,7 @@ public class CommandEncoder extends MessageToByteEncoder> { private void writeArgument(ByteBuf out, byte[] arg) { out.writeByte(BYTES_PREFIX); - out.writeBytes(toChars(arg.length)); + out.writeBytes(convert(arg.length)); out.writeBytes(CRLF); out.writeBytes(arg); out.writeBytes(CRLF); @@ -192,6 +196,13 @@ public class CommandEncoder extends MessageToByteEncoder> { } } + public static byte[] convert(long i) { + if (i >= 0 && i <= 255) { + return longCache.get(i); + } + return toChars(i); + } + public static byte[] toChars(long i) { int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); byte[] buf = new byte[size]; @@ -199,4 +210,12 @@ public class CommandEncoder extends MessageToByteEncoder> { return buf; } + static { + for (long i = 0; i < 256; i++) { + byte[] value = toChars(i); + longCache.put(i, value); + } + } + + } From 7167b1d056a5aef42a1ae92edb762c948ef124de Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 16 May 2016 19:58:07 +0300 Subject: [PATCH 114/120] RObject.expireAt fixed. #490 --- src/main/java/org/redisson/RedissonExpirable.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/redisson/RedissonExpirable.java b/src/main/java/org/redisson/RedissonExpirable.java index 9bebf502e..87a3d4b37 100644 --- a/src/main/java/org/redisson/RedissonExpirable.java +++ b/src/main/java/org/redisson/RedissonExpirable.java @@ -58,12 +58,12 @@ abstract class RedissonExpirable extends RedissonObject implements RExpirable { @Override public boolean expireAt(Date timestamp) { - return expireAt(timestamp.getTime() / 1000); + return expireAt(timestamp.getTime()); } @Override public Future expireAtAsync(Date timestamp) { - return expireAtAsync(timestamp.getTime() / 1000); + return expireAtAsync(timestamp.getTime()); } @Override From 382236cd17d1bd0918c4af5225548cec8b16c0c7 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 19 May 2016 15:21:06 +0300 Subject: [PATCH 115/120] RScoredSortedSet.count added. #458 --- .../java/org/redisson/RedissonScoredSortedSet.java | 10 ++++++++++ .../org/redisson/client/protocol/RedisCommands.java | 1 + .../java/org/redisson/core/RScoredSortedSet.java | 11 +++++++++++ .../org/redisson/core/RScoredSortedSetAsync.java | 11 +++++++++++ .../org/redisson/RedissonScoredSortedSetTest.java | 12 ++++++++++++ 5 files changed, 45 insertions(+) diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index 43edf86bd..042c44ebf 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -487,4 +487,14 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc return get(revRankAsync(o)); } + public Long count(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive) { + return get(countAsync(startScore, startScoreInclusive, endScore, endScoreInclusive)); + } + + public Future countAsync(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive) { + String startValue = value(startScore, startScoreInclusive); + String endValue = value(endScore, endScoreInclusive); + return commandExecutor.readAsync(getName(), codec, RedisCommands.ZCOUNT, getName(), startValue, endValue); + } + } diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index 253e91507..b8ea2d4d6 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -86,6 +86,7 @@ public interface RedisCommands { RedisCommand ZREM = new RedisCommand("ZREM", new BooleanAmountReplayConvertor(), 2, ValueType.OBJECTS); RedisStrictCommand ZCARD_INT = new RedisStrictCommand("ZCARD", new IntegerReplayConvertor()); RedisStrictCommand ZCARD = new RedisStrictCommand("ZCARD"); + RedisStrictCommand ZCOUNT = new RedisStrictCommand("ZCOUNT"); RedisStrictCommand ZLEXCOUNT = new RedisStrictCommand("ZLEXCOUNT", new IntegerReplayConvertor()); RedisCommand ZSCORE_CONTAINS = new RedisCommand("ZSCORE", new BooleanNotNullReplayConvertor(), 2); RedisStrictCommand ZSCORE = new RedisStrictCommand("ZSCORE", new DoubleReplayConvertor(), 2); diff --git a/src/main/java/org/redisson/core/RScoredSortedSet.java b/src/main/java/org/redisson/core/RScoredSortedSet.java index 19ffcb90c..6898fe2be 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSet.java +++ b/src/main/java/org/redisson/core/RScoredSortedSet.java @@ -114,6 +114,17 @@ public interface RScoredSortedSet extends RScoredSortedSetAsync, Iterable< Collection> entryRangeReversed(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive, int offset, int count); + /** + * Returns the number of elements with a score between startScore and endScore. + * + * @param startScore + * @param startScoreInclusive + * @param endScore + * @param endScoreInclusive + * @return + */ + Long count(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive); + /** * Read all values at once. * diff --git a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java index e1fe791aa..f2b1ffc10 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java +++ b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java @@ -96,6 +96,17 @@ public interface RScoredSortedSetAsync extends RExpirableAsync { Future>> entryRangeReversedAsync(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive, int offset, int count); + /** + * Returns the number of elements with a score between startScore and endScore. + * + * @param startScore + * @param startScoreInclusive + * @param endScore + * @param endScoreInclusive + * @return + */ + Future countAsync(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive); + /** * Read all values at once. * diff --git a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index b51ab43e3..7c241c91b 100644 --- a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -27,6 +27,18 @@ import io.netty.util.concurrent.Future; public class RedissonScoredSortedSetTest extends BaseTest { + @Test + public void testCount() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(0, "1"); + set.add(1, "4"); + set.add(2, "2"); + set.add(3, "5"); + set.add(4, "3"); + + assertThat(set.count(0, true, 3, false)).isEqualTo(3); + } + @Test public void testReadAll() { RScoredSortedSet set = redisson.getScoredSortedSet("simple"); From 541b3e1eafe91a55c8ff8aec9490cdb5bbd8d708 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 19 May 2016 15:40:03 +0300 Subject: [PATCH 116/120] RList.addBefore and RList.addAfter methods added. #429 --- src/main/java/org/redisson/RedissonList.java | 20 ++++++++++++++ .../redisson/RedissonListMultimapValues.java | 20 ++++++++++++++ .../client/protocol/RedisCommands.java | 2 +- src/main/java/org/redisson/core/RList.java | 26 +++++++++++++++++++ .../java/org/redisson/core/RListAsync.java | 26 +++++++++++++++++++ .../java/org/redisson/RedissonListTest.java | 25 ++++++++++++++++++ 6 files changed, 118 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/redisson/RedissonList.java b/src/main/java/org/redisson/RedissonList.java index 8b2d33039..5678e9d98 100644 --- a/src/main/java/org/redisson/RedissonList.java +++ b/src/main/java/org/redisson/RedissonList.java @@ -566,4 +566,24 @@ public class RedissonList extends RedissonExpirable implements RList { return hashCode; } + @Override + public Future addAfterAsync(V elementToFind, V element) { + return commandExecutor.writeAsync(getName(), codec, RedisCommands.LINSERT, getName(), "AFTER", elementToFind, element); + } + + @Override + public Future addBeforeAsync(V elementToFind, V element) { + return commandExecutor.writeAsync(getName(), codec, RedisCommands.LINSERT, getName(), "BEFORE", elementToFind, element); + } + + @Override + public Integer addAfter(V elementToFind, V element) { + return get(addAfterAsync(elementToFind, element)); + } + + @Override + public Integer addBefore(V elementToFind, V element) { + return get(addBeforeAsync(elementToFind, element)); + } + } diff --git a/src/main/java/org/redisson/RedissonListMultimapValues.java b/src/main/java/org/redisson/RedissonListMultimapValues.java index 044985a2e..3cf698a8e 100644 --- a/src/main/java/org/redisson/RedissonListMultimapValues.java +++ b/src/main/java/org/redisson/RedissonListMultimapValues.java @@ -685,4 +685,24 @@ public class RedissonListMultimapValues extends RedissonExpirable implements return hashCode; } + @Override + public Future addAfterAsync(V elementToFind, V element) { + return commandExecutor.writeAsync(getName(), codec, RedisCommands.LINSERT, getName(), "AFTER", elementToFind, element); + } + + @Override + public Future addBeforeAsync(V elementToFind, V element) { + return commandExecutor.writeAsync(getName(), codec, RedisCommands.LINSERT, getName(), "BEFORE", elementToFind, element); + } + + @Override + public Integer addAfter(V elementToFind, V element) { + return get(addAfterAsync(elementToFind, element)); + } + + @Override + public Integer addBefore(V elementToFind, V element) { + return get(addBeforeAsync(elementToFind, element)); + } + } diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index b8ea2d4d6..edd4b8424 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -143,7 +143,7 @@ public interface RedisCommands { RedisCommand LREM_SINGLE = new RedisCommand("LREM", new BooleanReplayConvertor(), 3); RedisStrictCommand LREM = new RedisStrictCommand("LREM", 3); RedisCommand LINDEX = new RedisCommand("LINDEX"); - RedisCommand LINSERT = new RedisCommand("LINSERT", 3, ValueType.OBJECTS); + RedisCommand LINSERT = new RedisCommand("LINSERT", new IntegerReplayConvertor(), 3, ValueType.OBJECTS); RedisStrictCommand LLEN_INT = new RedisStrictCommand("LLEN", new IntegerReplayConvertor()); RedisStrictCommand LLEN = new RedisStrictCommand("LLEN"); RedisStrictCommand LTRIM = new RedisStrictCommand("LTRIM", new VoidReplayConvertor()); diff --git a/src/main/java/org/redisson/core/RList.java b/src/main/java/org/redisson/core/RList.java index efe8da169..02506780b 100644 --- a/src/main/java/org/redisson/core/RList.java +++ b/src/main/java/org/redisson/core/RList.java @@ -27,6 +27,32 @@ import java.util.RandomAccess; */ public interface RList extends List, RExpirable, RListAsync, RandomAccess { + /** + * Add element after elementToFind + * + * @param elementToFind + * @param element + * @return new list size + */ + Integer addAfter(V elementToFind, V element); + + /** + * Add element before elementToFind + * + * @param elementToFind + * @param element + * @return new list size + */ + Integer addBefore(V elementToFind, V element); + + /** + * Set element at index. + * Works faster than {@link #set(int, Object)} but + * doesn't return previous element. + * + * @param index + * @param element + */ void fastSet(int index, V element); RList subList(int fromIndex, int toIndex); diff --git a/src/main/java/org/redisson/core/RListAsync.java b/src/main/java/org/redisson/core/RListAsync.java index fec490fae..d4032587c 100644 --- a/src/main/java/org/redisson/core/RListAsync.java +++ b/src/main/java/org/redisson/core/RListAsync.java @@ -30,12 +30,38 @@ import io.netty.util.concurrent.Future; */ public interface RListAsync extends RCollectionAsync, RandomAccess { + /** + * Add element after elementToFind + * + * @param elementToFind + * @param element + * @return new list size + */ + Future addAfterAsync(V elementToFind, V element); + + /** + * Add element before elementToFind + * + * @param elementToFind + * @param element + * @return new list size + */ + Future addBeforeAsync(V elementToFind, V element); + Future addAllAsync(int index, Collection coll); Future lastIndexOfAsync(Object o); Future indexOfAsync(Object o); + /** + * Set element at index. + * Works faster than {@link #setAsync(int, Object)} but + * doesn't return previous element. + * + * @param index + * @param element + */ Future fastSetAsync(int index, V element); Future setAsync(int index, V element); diff --git a/src/test/java/org/redisson/RedissonListTest.java b/src/test/java/org/redisson/RedissonListTest.java index 173a5dec7..46623be44 100644 --- a/src/test/java/org/redisson/RedissonListTest.java +++ b/src/test/java/org/redisson/RedissonListTest.java @@ -17,6 +17,31 @@ import org.redisson.core.RList; public class RedissonListTest extends BaseTest { + @Test + public void testAddBefore() { + RList list = redisson.getList("list"); + list.add("1"); + list.add("2"); + list.add("3"); + + assertThat(list.addBefore("2", "0")).isEqualTo(4); + + assertThat(list).containsExactly("1", "0", "2", "3"); + } + + @Test + public void testAddAfter() { + RList list = redisson.getList("list"); + list.add("1"); + list.add("2"); + list.add("3"); + + assertThat(list.addAfter("2", "0")).isEqualTo(4); + + assertThat(list).containsExactly("1", "2", "0", "3"); + } + + @Test public void testTrim() { RList list = redisson.getList("list1"); From 61d3293cb3f2ff65b19092cc35a1e3a2203dea12 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 19 May 2016 18:22:20 +0300 Subject: [PATCH 117/120] Matchers and MatcherAssert removed from few tests --- .../redisson/RedissonBlockingQueueTest.java | 24 +++---- .../java/org/redisson/RedissonMapTest.java | 9 +-- .../redisson/RedissonScoredSortedSetTest.java | 67 +++++++++---------- 3 files changed, 43 insertions(+), 57 deletions(-) diff --git a/src/test/java/org/redisson/RedissonBlockingQueueTest.java b/src/test/java/org/redisson/RedissonBlockingQueueTest.java index 0cc416683..265ca9953 100644 --- a/src/test/java/org/redisson/RedissonBlockingQueueTest.java +++ b/src/test/java/org/redisson/RedissonBlockingQueueTest.java @@ -16,8 +16,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; import org.redisson.RedisRunner.RedisProcess; @@ -221,7 +219,7 @@ public class RedissonBlockingQueueTest extends BaseTest { queue2.put(6); queue1.pollLastAndOfferFirstTo(queue2.getName(), 10, TimeUnit.SECONDS); - MatcherAssert.assertThat(queue2, Matchers.contains(3, 4, 5, 6)); + assertThat(queue2).containsExactly(3, 4, 5, 6); } @Test @@ -232,9 +230,9 @@ public class RedissonBlockingQueueTest extends BaseTest { queue.add(3); queue.offer(4); - MatcherAssert.assertThat(queue, Matchers.contains(1, 2, 3, 4)); + assertThat(queue).containsExactly(1, 2, 3, 4); Assert.assertEquals((Integer) 1, queue.poll()); - MatcherAssert.assertThat(queue, Matchers.contains(2, 3, 4)); + assertThat(queue).containsExactly(2, 3, 4); Assert.assertEquals((Integer) 2, queue.element()); } @@ -246,9 +244,9 @@ public class RedissonBlockingQueueTest extends BaseTest { queue.add(3); queue.offer(4); - MatcherAssert.assertThat(queue, Matchers.contains(1, 2, 3, 4)); + assertThat(queue).containsExactly(1, 2, 3, 4); Assert.assertEquals((Integer) 1, queue.poll()); - MatcherAssert.assertThat(queue, Matchers.contains(2, 3, 4)); + assertThat(queue).containsExactly(2, 3, 4); Assert.assertEquals((Integer) 2, queue.element()); } @@ -263,7 +261,7 @@ public class RedissonBlockingQueueTest extends BaseTest { queue.remove(); queue.remove(); - MatcherAssert.assertThat(queue, Matchers.contains(3, 4)); + assertThat(queue).containsExactly(3, 4); queue.remove(); queue.remove(); @@ -281,7 +279,7 @@ public class RedissonBlockingQueueTest extends BaseTest { queue.remove(); queue.remove(); - MatcherAssert.assertThat(queue, Matchers.contains(3, 4)); + assertThat(queue).containsExactly(3, 4); queue.remove(); queue.remove(); @@ -355,7 +353,7 @@ public class RedissonBlockingQueueTest extends BaseTest { ArrayList dst = new ArrayList(); queue1.drainTo(dst); - MatcherAssert.assertThat(dst, Matchers.contains(1, 2L, "e")); + assertThat(dst).containsExactly(1, 2L, "e"); Assert.assertEquals(0, queue1.size()); } @@ -368,14 +366,12 @@ public class RedissonBlockingQueueTest extends BaseTest { ArrayList dst = new ArrayList(); queue1.drainTo(dst, 2); - MatcherAssert.assertThat(dst, Matchers.contains(1, 2L)); + assertThat(dst).containsExactly(1, 2L); Assert.assertEquals(1, queue1.size()); dst.clear(); queue1.drainTo(dst, 2); - MatcherAssert.assertThat(dst, Matchers.contains("e")); - - + assertThat(dst).containsExactly("e"); } @Test diff --git a/src/test/java/org/redisson/RedissonMapTest.java b/src/test/java/org/redisson/RedissonMapTest.java index 8ae9a0196..1c13f09aa 100644 --- a/src/test/java/org/redisson/RedissonMapTest.java +++ b/src/test/java/org/redisson/RedissonMapTest.java @@ -11,20 +11,15 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; -import org.assertj.core.data.MapEntry; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; import org.redisson.client.codec.StringCodec; import org.redisson.codec.JsonJacksonCodec; import org.redisson.core.Predicate; import org.redisson.core.RMap; -import org.redisson.core.RSet; import io.netty.util.concurrent.Future; @@ -667,7 +662,7 @@ public class RedissonMapTest extends BaseTest { map.put(7, 8); Collection keys = map.keySet(); - MatcherAssert.assertThat(keys, Matchers.containsInAnyOrder(1, 3, 4, 7)); + assertThat(keys).containsOnly(1, 3, 4, 7); for (Iterator iterator = map.keyIterator(); iterator.hasNext();) { Integer value = iterator.next(); if (!keys.remove(value)) { @@ -687,7 +682,7 @@ public class RedissonMapTest extends BaseTest { map.put(7, 8); Collection values = map.values(); - MatcherAssert.assertThat(values, Matchers.containsInAnyOrder(0, 5, 6, 8)); + assertThat(values).containsOnly(0, 5, 6, 8); for (Iterator iterator = map.valueIterator(); iterator.hasNext();) { Integer value = iterator.next(); if (!values.remove(value)) { diff --git a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index 7c241c91b..cdc445007 100644 --- a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -13,8 +13,6 @@ import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.ExecutionException; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; import org.redisson.client.codec.StringCodec; @@ -83,7 +81,7 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(0.3, "c"); Assert.assertEquals("c", set.pollLast()); - MatcherAssert.assertThat(set, Matchers.contains("a", "b")); + assertThat(set).containsExactly("a", "b"); } @Test @@ -96,7 +94,7 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(0.3, "c"); Assert.assertEquals("a", set.pollFirst()); - MatcherAssert.assertThat(set, Matchers.contains("b", "c")); + assertThat(set).containsExactly("b", "c"); } @Test @@ -124,7 +122,7 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(0.7, "g"); Assert.assertEquals(2, set.removeRangeByScore(0.1, false, 0.3, true)); - MatcherAssert.assertThat(set, Matchers.contains("a", "d", "e", "f", "g")); + assertThat(set).containsExactly("a", "d", "e", "f", "g"); } @Test @@ -139,7 +137,7 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(0.7, "g"); Assert.assertEquals(3, set.removeRangeByScore(Double.NEGATIVE_INFINITY, false, 0.3, true)); - MatcherAssert.assertThat(set, Matchers.contains("d", "e", "f", "g")); + assertThat(set).containsExactly("d", "e", "f", "g"); } @Test @@ -154,7 +152,7 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(0.7, "g"); Assert.assertEquals(3, set.removeRangeByScore(0.4, false, Double.POSITIVE_INFINITY, true)); - MatcherAssert.assertThat(set, Matchers.contains("a", "b", "c", "d")); + assertThat(set).containsExactly("a", "b", "c", "d"); } @Test @@ -169,7 +167,7 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(0.7, "g"); Assert.assertEquals(2, set.removeRangeByRank(0, 1)); - MatcherAssert.assertThat(set, Matchers.contains("c", "d", "e", "f", "g")); + assertThat(set).containsExactly("c", "d", "e", "f", "g"); } @Test @@ -223,14 +221,14 @@ public class RedissonScoredSortedSetTest extends BaseTest { Assert.assertTrue(set.removeAsync(1).get()); Assert.assertFalse(set.contains(1)); - Assert.assertThat(set, Matchers.contains(3, 7)); + assertThat(set).containsExactly(3, 7); Assert.assertFalse(set.removeAsync(1).get()); - Assert.assertThat(set, Matchers.contains(3, 7)); + assertThat(set).containsExactly(3, 7); set.removeAsync(3).get(); Assert.assertFalse(set.contains(3)); - Assert.assertThat(set, Matchers.contains(7)); + assertThat(set).containsExactly(7); } @Test @@ -261,7 +259,7 @@ public class RedissonScoredSortedSetTest extends BaseTest { } } - Assert.assertThat(set, Matchers.contains("1", "4", "5", "3")); + assertThat(set).containsExactly("1", "4", "5", "3"); int iteration = 0; for (Iterator iterator = set.iterator(); iterator.hasNext();) { @@ -310,7 +308,7 @@ public class RedissonScoredSortedSetTest extends BaseTest { } Assert.assertTrue(set.retainAll(Arrays.asList(1, 2))); - Assert.assertThat(set, Matchers.containsInAnyOrder(1, 2)); + assertThat(set).containsExactly(1, 2); Assert.assertEquals(2, set.size()); assertThat(set.getScore(1)).isEqualTo(10); assertThat(set.getScore(2)).isEqualTo(20); @@ -324,7 +322,7 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(0.3, 3); Assert.assertTrue(set.removeAll(Arrays.asList(1, 2))); - Assert.assertThat(set, Matchers.contains(3)); + assertThat(set).containsOnly(3); Assert.assertEquals(1, set.size()); } @@ -342,15 +340,15 @@ public class RedissonScoredSortedSetTest extends BaseTest { SortedSet hs = set.tailSet(3); hs.add(10); - MatcherAssert.assertThat(hs, Matchers.contains(3, 4, 5, 10)); + assertThat(hs).containsExactly(3, 4, 5, 10); set.remove(4); - MatcherAssert.assertThat(hs, Matchers.contains(3, 5, 10)); + assertThat(hs).containsExactly(3, 5, 10); set.remove(3); - MatcherAssert.assertThat(hs, Matchers.contains(5, 10)); + assertThat(hs).containsExactly(5, 10); hs.add(-1); } @@ -369,15 +367,15 @@ public class RedissonScoredSortedSetTest extends BaseTest { SortedSet hs = set.headSet(3); hs.add(0); - MatcherAssert.assertThat(hs, Matchers.contains(0, 1, 2)); + assertThat(hs).containsExactly(0, 1, 2); set.remove(2); - MatcherAssert.assertThat(hs, Matchers.contains(0, 1)); + assertThat(hs).containsExactly(0, 1); set.remove(3); - MatcherAssert.assertThat(hs, Matchers.contains(0, 1)); + assertThat(hs).containsExactly(0, 1); hs.add(7); } @@ -395,15 +393,15 @@ public class RedissonScoredSortedSetTest extends BaseTest { SortedSet hs = set.tailSet(3); hs.add(10); - MatcherAssert.assertThat(hs, Matchers.contains(3, 4, 5, 10)); + assertThat(hs).containsExactly(3, 4, 5, 10); set.remove(4); - MatcherAssert.assertThat(hs, Matchers.contains(3, 5, 10)); + assertThat(hs).containsExactly(3, 5, 10); set.remove(3); - MatcherAssert.assertThat(hs, Matchers.contains(5, 10)); + assertThat(hs).containsExactly(5, 10); hs.add(-1); } @@ -421,15 +419,15 @@ public class RedissonScoredSortedSetTest extends BaseTest { SortedSet hs = set.headSet(3); hs.add(0); - MatcherAssert.assertThat(hs, Matchers.contains(0, 1, 2)); + assertThat(hs).containsExactly(0, 1, 2); set.remove(2); - MatcherAssert.assertThat(hs, Matchers.contains(0, 1)); + assertThat(hs).containsExactly(0, 1); set.remove(3); - MatcherAssert.assertThat(hs, Matchers.contains(0, 1)); + assertThat(hs).containsExactly(0, 1); hs.add(7); } @@ -445,10 +443,7 @@ public class RedissonScoredSortedSetTest extends BaseTest { Assert.assertTrue(set.add(1, -1)); Assert.assertTrue(set.add(2, 0)); - MatcherAssert.assertThat(set, Matchers.contains(-1, 0, 1, 2, 3, 4, 10)); - -// Assert.assertEquals(-1, (int)set.first()); -// Assert.assertEquals(10, (int)set.last()); + assertThat(set).containsExactly(-1, 0, 1, 2, 3, 4, 10); } @Test @@ -463,7 +458,7 @@ public class RedissonScoredSortedSetTest extends BaseTest { Assert.assertFalse(set.remove(0)); Assert.assertTrue(set.remove(3)); - Assert.assertThat(set, Matchers.contains(1, 2, 4, 5)); + assertThat(set).containsExactly(1, 2, 4, 5); } @Test @@ -486,10 +481,10 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(3, "5"); set.add(4, "3"); - MatcherAssert.assertThat(Arrays.asList(set.toArray()), Matchers.containsInAnyOrder("1", "2", "4", "5", "3")); + assertThat(Arrays.asList(set.toArray())).containsExactly("1", "4", "2", "5", "3"); String[] strs = set.toArray(new String[0]); - MatcherAssert.assertThat(Arrays.asList(strs), Matchers.containsInAnyOrder("1", "4", "2", "5", "3")); + assertThat(Arrays.asList(strs)).containsExactly("1", "4", "2", "5", "3"); } @Test @@ -545,7 +540,7 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(4, 5); Collection vals = set.valueRange(0, -1); - MatcherAssert.assertThat(vals, Matchers.contains(1, 2, 3, 4, 5)); + assertThat(vals).containsExactly(1, 2, 3, 4, 5); } @Test @@ -558,11 +553,11 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(50, 5); Collection> vals = set.entryRange(0, -1); - MatcherAssert.assertThat(vals, Matchers.contains(new ScoredEntry(10D, 1), + assertThat(vals).containsExactly(new ScoredEntry(10D, 1), new ScoredEntry(20D, 2), new ScoredEntry(30D, 3), new ScoredEntry(40D, 4), - new ScoredEntry(50D, 5))); + new ScoredEntry(50D, 5)); } @Test From b49364eee6cf6c9f73d7402861844cfcd5d46183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-David=20Be=CC=81langer?= Date: Thu, 19 May 2016 12:37:16 -0400 Subject: [PATCH 118/120] Allows one to specify the remote service interface prefix (idea from #494) --- src/main/java/org/redisson/Redisson.java | 7 ++++++- src/main/java/org/redisson/RedissonClient.java | 12 ++++++++++-- .../org/redisson/RedissonRemoteService.java | 14 ++++++++++---- .../redisson/RedissonRemoteServiceTest.java | 18 +++++++++++++++++- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/redisson/Redisson.java b/src/main/java/org/redisson/Redisson.java index bf481de52..9105c74df 100755 --- a/src/main/java/org/redisson/Redisson.java +++ b/src/main/java/org/redisson/Redisson.java @@ -390,7 +390,12 @@ public class Redisson implements RedissonClient { public RRemoteService getRemoteSerivce() { return new RedissonRemoteService(this); } - + + @Override + public RRemoteService getRemoteSerivce(String name) { + return new RedissonRemoteService(this, name); + } + @Override public RSortedSet getSortedSet(String name) { return new RedissonSortedSet(commandExecutor, name); diff --git a/src/main/java/org/redisson/RedissonClient.java b/src/main/java/org/redisson/RedissonClient.java index 294752add..fa7ea0bb8 100755 --- a/src/main/java/org/redisson/RedissonClient.java +++ b/src/main/java/org/redisson/RedissonClient.java @@ -594,12 +594,20 @@ public interface RedissonClient { RScript getScript(); /** - * Returns object for remote operations + * Returns object for remote operations prefixed with the default name (redisson_remote_service) * * @return */ RRemoteService getRemoteSerivce(); - + + /** + * Returns object for remote operations prefixed with the specified name + * + * @param name The name used as the Redis key prefix for the services + * @return + */ + RRemoteService getRemoteSerivce(String name); + /** * Return batch object which executes group of * command in pipeline. diff --git a/src/main/java/org/redisson/RedissonRemoteService.java b/src/main/java/org/redisson/RedissonRemoteService.java index 5e2c813df..b2f823136 100644 --- a/src/main/java/org/redisson/RedissonRemoteService.java +++ b/src/main/java/org/redisson/RedissonRemoteService.java @@ -56,9 +56,15 @@ public class RedissonRemoteService implements RRemoteService { private final Map beans = PlatformDependent.newConcurrentHashMap(); private final Redisson redisson; + private final String name; public RedissonRemoteService(Redisson redisson) { + this(redisson, "redisson_remote_service"); + } + + public RedissonRemoteService(Redisson redisson, String name) { this.redisson = redisson; + this.name = name; } @Override @@ -80,7 +86,7 @@ public class RedissonRemoteService implements RRemoteService { } for (int i = 0; i < executorsAmount; i++) { - String requestQueueName = "redisson_remote_service:{" + remoteInterface.getName() + "}"; + String requestQueueName = name + ":{" + remoteInterface.getName() + "}"; RBlockingQueue requestQueue = redisson.getBlockingQueue(requestQueueName); subscribe(remoteInterface, requestQueue); } @@ -107,7 +113,7 @@ public class RedissonRemoteService implements RRemoteService { } final RemoteServiceMethod method = beans.get(new RemoteServiceKey(remoteInterface, request.getMethodName())); - final String responseName = "redisson_remote_service:{" + remoteInterface.getName() + "}:" + request.getRequestId(); + final String responseName = name + ":{" + remoteInterface.getName() + "}:" + request.getRequestId(); Future> ackClientsFuture = send(request.getAckTimeout(), responseName, new RemoteServiceAck()); ackClientsFuture.addListener(new FutureListener>() { @@ -167,13 +173,13 @@ public class RedissonRemoteService implements RRemoteService { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String requestId = generateRequestId(); - String requestQueueName = "redisson_remote_service:{" + remoteInterface.getName() + "}"; + String requestQueueName = name + ":{" + remoteInterface.getName() + "}"; RBlockingQueue requestQueue = redisson.getBlockingQueue(requestQueueName); RemoteServiceRequest request = new RemoteServiceRequest(requestId, method.getName(), args, ackTimeUnit.toMillis(ackTimeout), executionTimeUnit.toMillis(executionTimeout), System.currentTimeMillis()); requestQueue.add(request); - String responseName = "redisson_remote_service:{" + remoteInterface.getName() + "}:" + requestId; + String responseName = name + ":{" + remoteInterface.getName() + "}:" + requestId; RBlockingQueue responseQueue = redisson.getBlockingQueue(responseName); RemoteServiceAck ack = (RemoteServiceAck) responseQueue.poll(ackTimeout, ackTimeUnit); diff --git a/src/test/java/org/redisson/RedissonRemoteServiceTest.java b/src/test/java/org/redisson/RedissonRemoteServiceTest.java index 5739bc7bc..a47a065ef 100644 --- a/src/test/java/org/redisson/RedissonRemoteServiceTest.java +++ b/src/test/java/org/redisson/RedissonRemoteServiceTest.java @@ -104,5 +104,21 @@ public class RedissonRemoteServiceTest extends BaseTest { r1.shutdown(); r2.shutdown(); } - + + @Test + public void testInvocationWithServiceName() { + String name = "MyServiceName"; + + RedissonClient r1 = Redisson.create(); + r1.getRemoteSerivce(name).register(RemoteInterface.class, new RemoteImpl()); + + RedissonClient r2 = Redisson.create(); + RemoteInterface ri = r2.getRemoteSerivce(name).get(RemoteInterface.class); + + ri.voidMethod("someName", 100L); + assertThat(ri.resultMethod(100L)).isEqualTo(200); + + r1.shutdown(); + r2.shutdown(); + } } From 7e1e95d9936285315afd7d269a72f5732178b8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-David=20Be=CC=81langer?= Date: Thu, 19 May 2016 15:19:36 -0400 Subject: [PATCH 119/120] A failing test case to demonstrate that the executorsAmount is not enforced so the server could exceed the allowed concurrency (question is here #493) --- .../redisson/RedissonRemoteServiceTest.java | 86 ++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/redisson/RedissonRemoteServiceTest.java b/src/test/java/org/redisson/RedissonRemoteServiceTest.java index 5739bc7bc..de3a09a62 100644 --- a/src/test/java/org/redisson/RedissonRemoteServiceTest.java +++ b/src/test/java/org/redisson/RedissonRemoteServiceTest.java @@ -3,7 +3,9 @@ package org.redisson; import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.Assert; import org.junit.Test; @@ -59,6 +61,88 @@ public class RedissonRemoteServiceTest extends BaseTest { } + @Test + public void testExecutorsAmountConcurrency() throws InterruptedException { + + // Redisson server and client + final RedissonClient server = Redisson.create(); + final RedissonClient client = Redisson.create(); + + final int serverAmount = 1; + final int clientAmount = 10; + + // to store the current call concurrency count + final AtomicInteger concurrency = new AtomicInteger(0); + + // a flag to indicate the the allowed concurrency was exceeded + final AtomicBoolean concurrencyIsExceeded = new AtomicBoolean(false); + + // the server: register a service with an overrided timeoutMethod method that: + // - incr the concurrency + // - check if concurrency is greater than what was allowed, and if yes set the concurrencyOfOneIsExceeded flag + // - wait 2s + // - decr the concurrency + server.getRemoteSerivce().register(RemoteInterface.class, new RemoteImpl() { + @Override + public void timeoutMethod() throws InterruptedException { + try { + if (concurrency.incrementAndGet() > serverAmount) { + concurrencyIsExceeded.compareAndSet(false, true); + } + super.timeoutMethod(); + } finally { + concurrency.decrementAndGet(); + } + } + }, serverAmount); + + // a latch to force the client threads to execute simultaneously + // (as far as practicable, this is hard to predict) + final CountDownLatch readyLatch = new CountDownLatch(1); + + // the client: starts a couple of threads that will: + // - await for the ready latch + // - then call timeoutMethod + Future[] clientFutures = new Future[clientAmount]; + ExecutorService executor = Executors.newFixedThreadPool(clientAmount); + for (int i = 0; i < clientAmount; i++) { + clientFutures[i] = executor.submit(new Runnable() { + @Override + public void run() { + try { + RemoteInterface ri = client.getRemoteSerivce().get(RemoteInterface.class, clientAmount * 3, TimeUnit.SECONDS, clientAmount * 3, TimeUnit.SECONDS); + readyLatch.await(); + ri.timeoutMethod(); + } catch (InterruptedException e) { + // ignore + } + } + }); + } + + // open the latch to wake the threads + readyLatch.countDown(); + + // await for the client threads to terminate + for (Future clientFuture : clientFutures) { + try { + clientFuture.get(); + } catch (ExecutionException e) { + // ignore + } + } + executor.shutdown(); + executor.awaitTermination(clientAmount * 3, TimeUnit.SECONDS); + + // shutdown the server and the client + server.shutdown(); + client.shutdown(); + + // do the concurrencyIsExceeded flag was set ? + // if yes, that would indicate that the server exceeded its expected concurrency + assertThat(concurrencyIsExceeded.get()).isEqualTo(false); + } + @Test(expected = RemoteServiceTimeoutException.class) public void testTimeout() throws InterruptedException { RedissonClient r1 = Redisson.create(); From 98593473aca3f60023655cd1633c26d4dff5aeeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-David=20Be=CC=81langer?= Date: Fri, 20 May 2016 00:08:24 -0400 Subject: [PATCH 120/120] The red test case (about executorsAmount that is not enforced) is now green --- .../org/redisson/RedissonRemoteService.java | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/redisson/RedissonRemoteService.java b/src/main/java/org/redisson/RedissonRemoteService.java index 5e2c813df..33b2a3e7d 100644 --- a/src/main/java/org/redisson/RedissonRemoteService.java +++ b/src/main/java/org/redisson/RedissonRemoteService.java @@ -95,14 +95,19 @@ public class RedissonRemoteService implements RRemoteService { if (future.cause() instanceof RedissonShutdownException) { return; } + // re-subscribe after a failed takeAsync subscribe(remoteInterface, requestQueue); return; } - subscribe(remoteInterface, requestQueue); + + // do not subscribe now, see https://github.com/mrniko/redisson/issues/493 + // subscribe(remoteInterface, requestQueue); final RemoteServiceRequest request = future.getNow(); if (System.currentTimeMillis() - request.getDate() > request.getAckTimeout()) { log.debug("request: {} has been skipped due to ackTimeout"); + // re-subscribe after a skipped ackTimeout + subscribe(remoteInterface, requestQueue); return; } @@ -115,10 +120,15 @@ public class RedissonRemoteService implements RRemoteService { public void operationComplete(Future> future) throws Exception { if (!future.isSuccess()) { log.error("Can't send ack for request: " + request, future.cause()); + if (future.cause() instanceof RedissonShutdownException) { + return; + } + // re-subscribe after a failed send (ack) + subscribe(remoteInterface, requestQueue); return; } - - invokeMethod(request, method, responseName); + + invokeMethod(remoteInterface, requestQueue, request, method, responseName); } }); } @@ -126,7 +136,7 @@ public class RedissonRemoteService implements RRemoteService { }); } - private void invokeMethod(final RemoteServiceRequest request, RemoteServiceMethod method, String responseName) { + private void invokeMethod(final Class remoteInterface, final RBlockingQueue requestQueue, final RemoteServiceRequest request, RemoteServiceMethod method, String responseName) { final AtomicReference responseHolder = new AtomicReference(); try { Object result = method.getMethod().invoke(method.getBean(), request.getArgs()); @@ -144,8 +154,12 @@ public class RedissonRemoteService implements RRemoteService { public void operationComplete(Future> future) throws Exception { if (!future.isSuccess()) { log.error("Can't send response: " + responseHolder.get() + " for request: " + request, future.cause()); - return; + if (future.cause() instanceof RedissonShutdownException) { + return; + } } + // re-subscribe anyways (fail or success) after the send (response) + subscribe(remoteInterface, requestQueue); } }); }