From 9a756fb96a25ed7a99cc7b12d4e734c5f6befece Mon Sep 17 00:00:00 2001
From: Nikita <abracham.mitchell@gmail.com>
Date: Mon, 22 May 2017 18:03:38 +0300
Subject: [PATCH 01/15] getClientName didn't set properly

---
 .../java/org/redisson/client/handler/BaseConnectionHandler.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redisson/src/main/java/org/redisson/client/handler/BaseConnectionHandler.java b/redisson/src/main/java/org/redisson/client/handler/BaseConnectionHandler.java
index 2fd0953ef..63daa0a71 100644
--- a/redisson/src/main/java/org/redisson/client/handler/BaseConnectionHandler.java
+++ b/redisson/src/main/java/org/redisson/client/handler/BaseConnectionHandler.java
@@ -73,7 +73,7 @@ public abstract class BaseConnectionHandler<C extends RedisConnection> extends C
             futures.add(future);
         }
         if (config.getClientName() != null) {
-            RFuture<Object> future = connection.async(RedisCommands.CLIENT_SETNAME, config.getDatabase());
+            RFuture<Object> future = connection.async(RedisCommands.CLIENT_SETNAME, config.getClientName());
             futures.add(future);
         }
         if (config.isReadOnly()) {

From 946f712931fba6cbd7949747b9e2cc11063cdafc Mon Sep 17 00:00:00 2001
From: Nikita <abracham.mitchell@gmail.com>
Date: Mon, 22 May 2017 18:19:50 +0300
Subject: [PATCH 02/15] RBlockingQueue.takeLastAndOfferFirstTo method added.
 #881

---
 .../org/redisson/RedissonBlockingDeque.java   | 11 +++++++
 .../org/redisson/RedissonBlockingQueue.java   | 11 +++++++
 .../RedissonBoundedBlockingQueue.java         | 14 ++++++++-
 .../java/org/redisson/api/RBlockingQueue.java |  2 ++
 .../org/redisson/api/RBlockingQueueAsync.java |  2 ++
 .../redisson/RedissonBlockingQueueTest.java   | 30 +++++++++++++++++--
 .../RedissonBoundedBlockingQueueTest.java     | 30 ++++++++++++++++++-
 7 files changed, 96 insertions(+), 4 deletions(-)

diff --git a/redisson/src/main/java/org/redisson/RedissonBlockingDeque.java b/redisson/src/main/java/org/redisson/RedissonBlockingDeque.java
index 5699208b0..b82bb0761 100644
--- a/redisson/src/main/java/org/redisson/RedissonBlockingDeque.java
+++ b/redisson/src/main/java/org/redisson/RedissonBlockingDeque.java
@@ -123,6 +123,17 @@ public class RedissonBlockingDeque<V> extends RedissonDeque<V> implements RBlock
     public V pollLastAndOfferFirstTo(String queueName, long timeout, TimeUnit unit) throws InterruptedException {
         return blockingQueue.pollLastAndOfferFirstTo(queueName, timeout, unit);
     }
+    
+    @Override
+    public V takeLastAndOfferFirstTo(String queueName) throws InterruptedException {
+        RFuture<V> res = takeLastAndOfferFirstToAsync(queueName);
+        return res.await().getNow();
+    }
+    
+    @Override
+    public RFuture<V> takeLastAndOfferFirstToAsync(String queueName) {
+        return pollLastAndOfferFirstToAsync(queueName, 0, TimeUnit.SECONDS);
+    }
 
     @Override
     public int remainingCapacity() {
diff --git a/redisson/src/main/java/org/redisson/RedissonBlockingQueue.java b/redisson/src/main/java/org/redisson/RedissonBlockingQueue.java
index 46d8b6e0f..4f518e681 100644
--- a/redisson/src/main/java/org/redisson/RedissonBlockingQueue.java
+++ b/redisson/src/main/java/org/redisson/RedissonBlockingQueue.java
@@ -133,6 +133,17 @@ public class RedissonBlockingQueue<V> extends RedissonQueue<V> implements RBlock
         RFuture<V> res = pollLastAndOfferFirstToAsync(queueName, timeout, unit);
         return res.await().getNow();
     }
+    
+    @Override
+    public V takeLastAndOfferFirstTo(String queueName) throws InterruptedException {
+        RFuture<V> res = takeLastAndOfferFirstToAsync(queueName);
+        return res.await().getNow();
+    }
+    
+    @Override
+    public RFuture<V> takeLastAndOfferFirstToAsync(String queueName) {
+        return pollLastAndOfferFirstToAsync(queueName, 0, TimeUnit.SECONDS);
+    }
 
     @Override
     public int remainingCapacity() {
diff --git a/redisson/src/main/java/org/redisson/RedissonBoundedBlockingQueue.java b/redisson/src/main/java/org/redisson/RedissonBoundedBlockingQueue.java
index f349774aa..ac309c5a9 100644
--- a/redisson/src/main/java/org/redisson/RedissonBoundedBlockingQueue.java
+++ b/redisson/src/main/java/org/redisson/RedissonBoundedBlockingQueue.java
@@ -33,6 +33,7 @@ import org.redisson.command.CommandExecutor;
 import org.redisson.connection.decoder.ListDrainToDecoder;
 import org.redisson.misc.PromiseDelegator;
 import org.redisson.misc.RPromise;
+import org.redisson.misc.RedissonPromise;
 import org.redisson.pubsub.SemaphorePubSub;
 
 import io.netty.util.concurrent.Future;
@@ -136,7 +137,7 @@ public class RedissonBoundedBlockingQueue<V> extends RedissonQueue<V> implements
     }
 
     private RPromise<V> wrapTakeFuture(final RFuture<V> takeFuture) {
-        final RPromise<V> result = new PromiseDelegator<V>(commandExecutor.getConnectionManager().<V>newPromise()) {
+        final RPromise<V> result = new RedissonPromise<V>() {
             @Override
             public boolean cancel(boolean mayInterruptIfRunning) {
                 super.cancel(mayInterruptIfRunning);
@@ -253,6 +254,17 @@ public class RedissonBoundedBlockingQueue<V> extends RedissonQueue<V> implements
         return wrapTakeFuture(takeFuture);
     }
 
+    @Override
+    public V takeLastAndOfferFirstTo(String queueName) throws InterruptedException {
+        RFuture<V> res = takeLastAndOfferFirstToAsync(queueName);
+        return res.await().getNow();
+    }
+    
+    @Override
+    public RFuture<V> takeLastAndOfferFirstToAsync(String queueName) {
+        return pollLastAndOfferFirstToAsync(queueName, 0, TimeUnit.SECONDS);
+    }
+    
     @Override
     public RFuture<V> pollLastAndOfferFirstToAsync(String queueName, long timeout, TimeUnit unit) {
         RFuture<V> takeFuture = commandExecutor.writeAsync(getName(), codec, RedisCommands.BRPOPLPUSH, getName(), queueName, unit.toSeconds(timeout));
diff --git a/redisson/src/main/java/org/redisson/api/RBlockingQueue.java b/redisson/src/main/java/org/redisson/api/RBlockingQueue.java
index b39c79c5d..d259c316a 100644
--- a/redisson/src/main/java/org/redisson/api/RBlockingQueue.java
+++ b/redisson/src/main/java/org/redisson/api/RBlockingQueue.java
@@ -43,5 +43,7 @@ public interface RBlockingQueue<V> extends BlockingQueue<V>, RQueue<V>, RBlockin
     V pollFromAny(long timeout, TimeUnit unit, String ... queueNames) throws InterruptedException;
 
     V pollLastAndOfferFirstTo(String queueName, long timeout, TimeUnit unit) throws InterruptedException;
+    
+    V takeLastAndOfferFirstTo(String queueName) throws InterruptedException;
 
 }
diff --git a/redisson/src/main/java/org/redisson/api/RBlockingQueueAsync.java b/redisson/src/main/java/org/redisson/api/RBlockingQueueAsync.java
index d52047bce..3e09054e6 100644
--- a/redisson/src/main/java/org/redisson/api/RBlockingQueueAsync.java
+++ b/redisson/src/main/java/org/redisson/api/RBlockingQueueAsync.java
@@ -93,6 +93,8 @@ public interface RBlockingQueueAsync<V> extends RQueueAsync<V> {
     RFuture<Integer> drainToAsync(Collection<? super V> c);
 
     RFuture<V> pollLastAndOfferFirstToAsync(String queueName, long timeout, TimeUnit unit);
+    
+    RFuture<V> takeLastAndOfferFirstToAsync(String queueName);
 
     /**
      * Retrieves and removes the head of this queue in async mode, waiting up to the
diff --git a/redisson/src/test/java/org/redisson/RedissonBlockingQueueTest.java b/redisson/src/test/java/org/redisson/RedissonBlockingQueueTest.java
index 54e4192ed..4dab4f004 100644
--- a/redisson/src/test/java/org/redisson/RedissonBlockingQueueTest.java
+++ b/redisson/src/test/java/org/redisson/RedissonBlockingQueueTest.java
@@ -296,16 +296,42 @@ public class RedissonBlockingQueueTest extends BaseTest {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
-        }, 10, TimeUnit.SECONDS);
+        }, 5, TimeUnit.SECONDS);
 
         RBlockingQueue<Integer> queue2 = redisson.getBlockingQueue("{queue}2");
         queue2.put(4);
         queue2.put(5);
         queue2.put(6);
 
-        queue1.pollLastAndOfferFirstTo(queue2.getName(), 10, TimeUnit.SECONDS);
+        Integer value = queue1.pollLastAndOfferFirstTo(queue2.getName(), 5, TimeUnit.SECONDS);
+        assertThat(value).isEqualTo(3);
         assertThat(queue2).containsExactly(3, 4, 5, 6);
     }
+    
+    @Test
+    public void testTakeLastAndOfferFirstTo() throws InterruptedException {
+        final RBlockingQueue<Integer> queue1 = redisson.getBlockingQueue("{queue}1");
+        Executors.newSingleThreadScheduledExecutor().schedule(() -> {
+            try {
+                queue1.put(3);
+            } catch (InterruptedException e) {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+            }
+        }, 3, TimeUnit.SECONDS);
+
+        RBlockingQueue<Integer> queue2 = redisson.getBlockingQueue("{queue}2");
+        queue2.put(4);
+        queue2.put(5);
+        queue2.put(6);
+
+        long startTime = System.currentTimeMillis();
+        Integer value = queue1.takeLastAndOfferFirstTo(queue2.getName());
+        assertThat(System.currentTimeMillis() - startTime).isBetween(2900L, 3200L);
+        assertThat(value).isEqualTo(3);
+        assertThat(queue2).containsExactly(3, 4, 5, 6);
+    }
+
 
     @Test
     public void testAddOfferOrigin() {
diff --git a/redisson/src/test/java/org/redisson/RedissonBoundedBlockingQueueTest.java b/redisson/src/test/java/org/redisson/RedissonBoundedBlockingQueueTest.java
index 2e9cab970..3d533f5d4 100644
--- a/redisson/src/test/java/org/redisson/RedissonBoundedBlockingQueueTest.java
+++ b/redisson/src/test/java/org/redisson/RedissonBoundedBlockingQueueTest.java
@@ -509,10 +509,38 @@ public class RedissonBoundedBlockingQueueTest extends BaseTest {
         queue2.put(5);
         queue2.put(6);
 
-        queue1.pollLastAndOfferFirstTo(queue2.getName(), 10, TimeUnit.SECONDS);
+        Integer value = queue1.pollLastAndOfferFirstTo(queue2.getName(), 10, TimeUnit.SECONDS);
+        assertThat(value).isEqualTo(3);
         assertThat(queue2).containsExactly(3, 4, 5, 6);
     }
 
+    @Test
+    public void testTakeLastAndOfferFirstTo() throws InterruptedException {
+        final RBoundedBlockingQueue<Integer> queue1 = redisson.getBoundedBlockingQueue("{queue}1");
+        queue1.trySetCapacity(10);
+        Executors.newSingleThreadScheduledExecutor().schedule(() -> {
+            try {
+                queue1.put(3);
+            } catch (InterruptedException e) {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+            }
+        }, 3, TimeUnit.SECONDS);
+
+        RBoundedBlockingQueue<Integer> queue2 = redisson.getBoundedBlockingQueue("{queue}2");
+        queue2.trySetCapacity(10);
+        queue2.put(4);
+        queue2.put(5);
+        queue2.put(6);
+
+        long startTime = System.currentTimeMillis();
+        Integer value = queue1.takeLastAndOfferFirstTo(queue2.getName());
+        assertThat(System.currentTimeMillis() - startTime).isBetween(3000L, 3200L);
+        assertThat(value).isEqualTo(3);
+        assertThat(queue2).containsExactly(3, 4, 5, 6);
+    }
+
+    
     @Test
     public void testOffer() {
         RBoundedBlockingQueue<Integer> queue = redisson.getBoundedBlockingQueue("blocking:queue");

From 4f5044995c72fad53e4c9761fe5c4b3fcd8039b5 Mon Sep 17 00:00:00 2001
From: Nikita <abracham.mitchell@gmail.com>
Date: Tue, 23 May 2017 11:44:41 +0300
Subject: [PATCH 03/15] infinite scan fixed. #885

---
 .../java/org/redisson/RedissonBaseIterator.java    | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/redisson/src/main/java/org/redisson/RedissonBaseIterator.java b/redisson/src/main/java/org/redisson/RedissonBaseIterator.java
index fc9151f8d..bccbd0f32 100644
--- a/redisson/src/main/java/org/redisson/RedissonBaseIterator.java
+++ b/redisson/src/main/java/org/redisson/RedissonBaseIterator.java
@@ -101,6 +101,7 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
 
                         currentElementRemoved = false;
                         removeExecuted = false;
+                        
                         client = null;
                         firstValues = null;
                         lastValues = null;
@@ -111,6 +112,19 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
                         }
                         finished = true;
                         return false;
+                    } else if (!firstValues.isEmpty()) {
+                        if (res.getPos() == 0) {
+                            if (tryAgain()) {
+                                client = null;
+                                firstValues = null;
+                                nextIterPos = 0;
+                                prevIterPos = -1;
+                                continue;
+                            }
+                            
+                            finished = true;
+                            return false;
+                        }
                     }
                 }
                 lastIter = res.getValues().iterator();

From 89689b44739cb92ff65917b6391cac6fdffe4cc7 Mon Sep 17 00:00:00 2001
From: Nikita <abracham.mitchell@gmail.com>
Date: Tue, 23 May 2017 11:59:48 +0300
Subject: [PATCH 04/15] Yet another infinite scan fix. #885

---
 .../org/redisson/RedissonBaseIterator.java    | 28 +++++++++++--------
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/redisson/src/main/java/org/redisson/RedissonBaseIterator.java b/redisson/src/main/java/org/redisson/RedissonBaseIterator.java
index bccbd0f32..9faa1d2b2 100644
--- a/redisson/src/main/java/org/redisson/RedissonBaseIterator.java
+++ b/redisson/src/main/java/org/redisson/RedissonBaseIterator.java
@@ -26,6 +26,12 @@ import org.redisson.client.protocol.decoder.ScanObjectEntry;
 
 import io.netty.buffer.ByteBuf;
 
+/**
+ * 
+ * @author Nikita Koksharov
+ *
+ * @param <V> value type
+ */
 abstract class RedissonBaseIterator<V> implements Iterator<V> {
 
     private List<ByteBuf> firstValues;
@@ -112,19 +118,17 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
                         }
                         finished = true;
                         return false;
-                    } else if (!firstValues.isEmpty()) {
-                        if (res.getPos() == 0) {
-                            if (tryAgain()) {
-                                client = null;
-                                firstValues = null;
-                                nextIterPos = 0;
-                                prevIterPos = -1;
-                                continue;
-                            }
-                            
-                            finished = true;
-                            return false;
+                    } else if (lastValues.isEmpty() && res.getPos() == 0) {
+                        if (tryAgain()) {
+                            client = null;
+                            firstValues = null;
+                            nextIterPos = 0;
+                            prevIterPos = -1;
+                            continue;
                         }
+                        
+                        finished = true;
+                        return false;
                     }
                 }
                 lastIter = res.getValues().iterator();

From 51fc7ad6c0b905625655876e513a56a4e8796adb Mon Sep 17 00:00:00 2001
From: Nikita <abracham.mitchell@gmail.com>
Date: Tue, 23 May 2017 12:51:17 +0300
Subject: [PATCH 05/15] underscore support for Uri object

---
 .../org/redisson/config/ConfigSupport.java    | 42 +++++++++++++++++++
 .../connection/SentinelConnectionManager.java | 19 ++++++---
 2 files changed, 55 insertions(+), 6 deletions(-)

diff --git a/redisson/src/main/java/org/redisson/config/ConfigSupport.java b/redisson/src/main/java/org/redisson/config/ConfigSupport.java
index 05a8cddbe..8a7cf8677 100644
--- a/redisson/src/main/java/org/redisson/config/ConfigSupport.java
+++ b/redisson/src/main/java/org/redisson/config/ConfigSupport.java
@@ -19,6 +19,9 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.net.URI;
 import java.net.URL;
 import java.util.List;
@@ -121,62 +124,101 @@ public class ConfigSupport {
     private ObjectMapper jsonMapper = createMapper(null, null);
     private ObjectMapper yamlMapper = createMapper(new YAMLFactory(), null);
 
+    private void patchUriObject() throws IOException {
+        patchUriField("lowMask", "L_DASH");
+        patchUriField("highMask", "H_DASH");
+    }
+    
+    private void patchUriField(String methodName, String fieldName)
+            throws IOException {
+        try {
+            Method lowMask = URI.class.getDeclaredMethod(methodName, String.class);
+            lowMask.setAccessible(true);
+            long lowMaskValue = (long) lowMask.invoke(null, "-_");
+            
+            Field lowDash = URI.class.getDeclaredField(fieldName);
+            
+            Field modifiers = Field.class.getDeclaredField("modifiers");
+            modifiers.setAccessible(true);
+            modifiers.setInt(lowDash, lowDash.getModifiers() & ~Modifier.FINAL);
+            
+            lowDash.setAccessible(true);
+            lowDash.setLong(null, lowMaskValue);
+        } catch (Exception e) {
+            throw new IOException(e);
+        }
+    }
+    
     public <T> T fromJSON(String content, Class<T> configType) throws IOException {
+        patchUriObject();
         return jsonMapper.readValue(content, configType);
     }
 
     public <T> T fromJSON(File file, Class<T> configType) throws IOException {
+        patchUriObject();
         return fromJSON(file, configType, null);
     }
     
     public <T> T fromJSON(File file, Class<T> configType, ClassLoader classLoader) throws IOException {
+        patchUriObject();
         jsonMapper = createMapper(null, classLoader);
         return jsonMapper.readValue(file, configType);
     }
 
     public <T> T fromJSON(URL url, Class<T> configType) throws IOException {
+        patchUriObject();
         return jsonMapper.readValue(url, configType);
     }
 
     public <T> T fromJSON(Reader reader, Class<T> configType) throws IOException {
+        patchUriObject();
         return jsonMapper.readValue(reader, configType);
     }
 
     public <T> T fromJSON(InputStream inputStream, Class<T> configType) throws IOException {
+        patchUriObject();
         return jsonMapper.readValue(inputStream, configType);
     }
 
     public String toJSON(Config config) throws IOException {
+        patchUriObject();
         return jsonMapper.writeValueAsString(config);
     }
 
     public <T> T fromYAML(String content, Class<T> configType) throws IOException {
+        patchUriObject();
         return yamlMapper.readValue(content, configType);
     }
 
     public <T> T fromYAML(File file, Class<T> configType) throws IOException {
+        patchUriObject();
         return yamlMapper.readValue(file, configType);
     }
     
     public <T> T fromYAML(File file, Class<T> configType, ClassLoader classLoader) throws IOException {
+        patchUriObject();
         yamlMapper = createMapper(new YAMLFactory(), classLoader);
         return yamlMapper.readValue(file, configType);
     }
 
 
     public <T> T fromYAML(URL url, Class<T> configType) throws IOException {
+        patchUriObject();
         return yamlMapper.readValue(url, configType);
     }
 
     public <T> T fromYAML(Reader reader, Class<T> configType) throws IOException {
+        patchUriObject();
         return yamlMapper.readValue(reader, configType);
     }
 
     public <T> T fromYAML(InputStream inputStream, Class<T> configType) throws IOException {
+        patchUriObject();
         return yamlMapper.readValue(inputStream, configType);
     }
 
     public String toYAML(Config config) throws IOException {
+        patchUriObject();
         return yamlMapper.writeValueAsString(config);
     }
     
diff --git a/redisson/src/main/java/org/redisson/connection/SentinelConnectionManager.java b/redisson/src/main/java/org/redisson/connection/SentinelConnectionManager.java
index 261041b9c..2cbb7e64e 100755
--- a/redisson/src/main/java/org/redisson/connection/SentinelConnectionManager.java
+++ b/redisson/src/main/java/org/redisson/connection/SentinelConnectionManager.java
@@ -80,7 +80,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
 
                 // TODO async
                 List<String> master = connection.sync(RedisCommands.SENTINEL_GET_MASTER_ADDR_BY_NAME, cfg.getMasterName());
-                String masterHost = "redis://" + master.get(0) + ":" + master.get(1);
+                String masterHost = createAddress(master.get(0), master.get(1));
                 this.config.setMasterAddress(masterHost);
                 currentMaster.set(masterHost);
                 log.info("master: {} added", masterHost);
@@ -96,7 +96,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
                     String port = map.get("port");
                     String flags = map.get("flags");
 
-                    String host = "redis://" + ip + ":" + port;
+                    String host = createAddress(ip, port);
 
                     this.config.addSlaveAddress(host);
                     slaves.put(host, true);
@@ -132,6 +132,13 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
             future.awaitUninterruptibly();
         }
     }
+    
+    private String createAddress(String host, Object port) {
+        if (host.contains(":")) {
+            host = "[" + host + "]";
+        }
+        return "redis://" + host + ":" + port;
+    }
 
     @Override
     protected MasterSlaveEntry createMasterSlaveEntry(MasterSlaveServersConfig config,
@@ -207,7 +214,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
             String ip = parts[2];
             String port = parts[3];
 
-            String addr = "redis://" + ip + ":" + port;
+            String addr = createAddress(ip, port);
             URI uri = URI.create(addr);
             registerSentinel(cfg, uri, c);
         }
@@ -221,7 +228,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
             final String ip = parts[2];
             final String port = parts[3];
 
-            final String slaveAddr = "redis://" + ip + ":" + port;
+            final String slaveAddr = createAddress(ip, port);
 
             if (!isUseSameMaster(parts)) {
                 return;
@@ -309,7 +316,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
         String slaveAddr = ip + ":" + port;
 
         String master = currentMaster.get();
-        String slaveMaster = "redis://" + parts[6] + ":" + parts[7];
+        String slaveMaster = createAddress(parts[6], parts[7]);
         if (!master.equals(slaveMaster)) {
             log.warn("Skipped slave up {} for master {} differs from current {}", slaveAddr, slaveMaster, master);
             return false;
@@ -369,7 +376,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
                 String port = parts[4];
 
                 String current = currentMaster.get();
-                String newMaster = "redis://" + ip + ":" + port;
+                String newMaster = createAddress(ip, port);
                 if (!newMaster.equals(current)
                         && currentMaster.compareAndSet(current, newMaster)) {
                     changeMaster(singleSlotRange.getStartSlot(), URI.create(newMaster));

From 15b18abe74330112133031d2a1d48defb979966c Mon Sep 17 00:00:00 2001
From: Nikita <abracham.mitchell@gmail.com>
Date: Wed, 24 May 2017 16:25:03 +0300
Subject: [PATCH 06/15] RScoredSortedSet.firstScore, lastScore methods added.
 #811

---
 .../org/redisson/RedissonScoredSortedSet.java | 21 ++++++++
 .../org/redisson/api/RScoredSortedSet.java    |  4 ++
 .../redisson/api/RScoredSortedSetAsync.java   |  4 ++
 .../client/protocol/RedisCommands.java        |  3 +-
 .../ObjectFirstScoreReplayDecoder.java        | 49 +++++++++++++++++++
 .../redisson/RedissonScoredSortedSetTest.java | 11 +++++
 6 files changed, 91 insertions(+), 1 deletion(-)
 create mode 100644 redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstScoreReplayDecoder.java

diff --git a/redisson/src/main/java/org/redisson/RedissonScoredSortedSet.java b/redisson/src/main/java/org/redisson/RedissonScoredSortedSet.java
index 7bf4a1521..01a6cc955 100644
--- a/redisson/src/main/java/org/redisson/RedissonScoredSortedSet.java
+++ b/redisson/src/main/java/org/redisson/RedissonScoredSortedSet.java
@@ -143,6 +143,27 @@ public class RedissonScoredSortedSet<V> extends RedissonExpirable implements RSc
     public RFuture<V> lastAsync() {
         return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANGE_SINGLE, getName(), -1, -1);
     }
+    
+    @Override
+    public Double firstScore() {
+        return get(firstScoreAsync());
+    }
+
+    @Override
+    public RFuture<Double> firstScoreAsync() {
+        return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANGE_SINGLE_SCORE, getName(), 0, 0, "WITHSCORES");
+    }
+    
+    @Override
+    public Double lastScore() {
+        return get(lastScoreAsync());
+    }
+
+    @Override
+    public RFuture<Double> lastScoreAsync() {
+        return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANGE_SINGLE_SCORE, getName(), -1, -1, "WITHSCORES");
+    }
+
 
     @Override
     public RFuture<Boolean> addAsync(double score, V object) {
diff --git a/redisson/src/main/java/org/redisson/api/RScoredSortedSet.java b/redisson/src/main/java/org/redisson/api/RScoredSortedSet.java
index 72b548972..869ab8dcb 100644
--- a/redisson/src/main/java/org/redisson/api/RScoredSortedSet.java
+++ b/redisson/src/main/java/org/redisson/api/RScoredSortedSet.java
@@ -52,6 +52,10 @@ public interface RScoredSortedSet<V> extends RScoredSortedSetAsync<V>, Iterable<
     V first();
 
     V last();
+    
+    Double firstScore();
+    
+    Double lastScore();
 
     Long addAll(Map<V, Double> objects);
 
diff --git a/redisson/src/main/java/org/redisson/api/RScoredSortedSetAsync.java b/redisson/src/main/java/org/redisson/api/RScoredSortedSetAsync.java
index 25bcfe5e4..2a98c42d0 100644
--- a/redisson/src/main/java/org/redisson/api/RScoredSortedSetAsync.java
+++ b/redisson/src/main/java/org/redisson/api/RScoredSortedSetAsync.java
@@ -37,6 +37,10 @@ public interface RScoredSortedSetAsync<V> extends RExpirableAsync, RSortableAsyn
     RFuture<V> firstAsync();
 
     RFuture<V> lastAsync();
+    
+    RFuture<Double> firstScoreAsync();
+    
+    RFuture<Double> lastScoreAsync();
 
     RFuture<Long> addAllAsync(Map<V, Double> objects);
 
diff --git a/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java b/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java
index a91c4dda6..b46ded991 100644
--- a/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java
+++ b/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java
@@ -46,11 +46,11 @@ import org.redisson.client.protocol.decoder.ListResultReplayDecoder;
 import org.redisson.client.protocol.decoder.ListScanResult;
 import org.redisson.client.protocol.decoder.ListScanResultReplayDecoder;
 import org.redisson.client.protocol.decoder.Long2MultiDecoder;
-import org.redisson.client.protocol.decoder.LongMultiDecoder;
 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.ObjectFirstResultReplayDecoder;
+import org.redisson.client.protocol.decoder.ObjectFirstScoreReplayDecoder;
 import org.redisson.client.protocol.decoder.ObjectListReplayDecoder;
 import org.redisson.client.protocol.decoder.ObjectMapEntryReplayDecoder;
 import org.redisson.client.protocol.decoder.ObjectMapReplayDecoder;
@@ -115,6 +115,7 @@ public interface RedisCommands {
     RedisCommand<Integer> ZREVRANK_INT = new RedisCommand<Integer>("ZREVRANK", new IntegerReplayConvertor(), 2);
     RedisStrictCommand<Long> ZRANK = new RedisStrictCommand<Long>("ZRANK", 2);
     RedisCommand<Object> ZRANGE_SINGLE = new RedisCommand<Object>("ZRANGE", new ObjectFirstResultReplayDecoder<Object>());
+    RedisStrictCommand<Double> ZRANGE_SINGLE_SCORE = new RedisStrictCommand<Double>("ZRANGE", new ObjectFirstScoreReplayDecoder());
     RedisCommand<List<Object>> ZRANGE = new RedisCommand<List<Object>>("ZRANGE", new ObjectListReplayDecoder<Object>());
     RedisStrictCommand<Integer> ZREMRANGEBYRANK = new RedisStrictCommand<Integer>("ZREMRANGEBYRANK", new IntegerReplayConvertor());
     RedisStrictCommand<Integer> ZREMRANGEBYSCORE = new RedisStrictCommand<Integer>("ZREMRANGEBYSCORE", new IntegerReplayConvertor());
diff --git a/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstScoreReplayDecoder.java b/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstScoreReplayDecoder.java
new file mode 100644
index 000000000..19a0bf0c5
--- /dev/null
+++ b/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstScoreReplayDecoder.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright 2016 Nikita Koksharov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.redisson.client.protocol.decoder;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import org.redisson.client.handler.State;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.util.CharsetUtil;
+
+/**
+ * 
+ * @author Nikita Koksharov
+ *
+ * 
+ */
+public class ObjectFirstScoreReplayDecoder implements MultiDecoder<Double> {
+
+    @Override
+    public Object decode(ByteBuf buf, State state) {
+        return new BigDecimal(buf.toString(CharsetUtil.UTF_8)).doubleValue();
+    }
+
+    @Override
+    public Double decode(List<Object> parts, State state) {
+        return (Double) parts.get(1);
+    }
+
+    @Override
+    public boolean isApplicable(int paramNum, State state) {
+        return paramNum % 2 != 0;
+    }
+
+}
diff --git a/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java
index cf7d0d3c6..e90447e83 100644
--- a/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java
+++ b/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java
@@ -274,7 +274,18 @@ public class RedissonScoredSortedSetTest extends BaseTest {
         Assert.assertEquals("a", set.first());
         Assert.assertEquals("d", set.last());
     }
+    
+    @Test
+    public void testFirstLastScore() {
+        RScoredSortedSet<String> set = redisson.getScoredSortedSet("simple");
+        set.add(0.1, "a");
+        set.add(0.2, "b");
+        set.add(0.3, "c");
+        set.add(0.4, "d");
 
+        assertThat(set.firstScore()).isEqualTo(0.1);
+        assertThat(set.lastScore()).isEqualTo(0.4);
+    }
 
     @Test
     public void testRemoveRangeByScore() {

From ac1d075cd8b7588c888a3234a272b8dbe39a90e7 Mon Sep 17 00:00:00 2001
From: Himanshu Kansal <hikansal@adobe.com>
Date: Wed, 24 May 2017 22:21:57 +0530
Subject: [PATCH 07/15] using scan command in delete by pattern

---
 .../main/java/org/redisson/RedissonKeys.java  | 30 ++++++-------------
 1 file changed, 9 insertions(+), 21 deletions(-)

diff --git a/redisson/src/main/java/org/redisson/RedissonKeys.java b/redisson/src/main/java/org/redisson/RedissonKeys.java
index 863a787d2..c4517c39c 100644
--- a/redisson/src/main/java/org/redisson/RedissonKeys.java
+++ b/redisson/src/main/java/org/redisson/RedissonKeys.java
@@ -230,27 +230,15 @@ public class RedissonKeys implements RKeys {
         };
 
         for (MasterSlaveEntry entry : entries) {
-            RFuture<Collection<String>> findFuture = commandExecutor.readAsync(entry, null, RedisCommands.KEYS, pattern);
-            findFuture.addListener(new FutureListener<Collection<String>>() {
-                @Override
-                public void operationComplete(Future<Collection<String>> future) throws Exception {
-                    if (!future.isSuccess()) {
-                        failed.set(future.cause());
-                        checkExecution(result, failed, count, executed);
-                        return;
-                    }
-
-                    Collection<String> keys = future.getNow();
-                    if (keys.isEmpty()) {
-                        checkExecution(result, failed, count, executed);
-                        return;
-                    }
-
-                    RFuture<Long> deleteFuture = deleteAsync(keys.toArray(new String[keys.size()]));
-                    deleteFuture.addListener(listener);
-                }
-            });
-        }
+            Iterator<String> keysIterator = createKeysIterator(entry, pattern, 10);
+            Collection<String> keys = new HashSet<String>();
+            while (keysIterator.hasNext()) {
+                String key = keysIterator.next();
+                keys.add(key);
+            }
+            RFuture<Long> deleteFuture = deleteAsync(keys.toArray(new String[keys.size()]));
+            deleteFuture.addListener(listener);
+	}
 
         return result;
     }

From f2d1b9d8e5c4af16000fdcc5c77b4e79405fcd66 Mon Sep 17 00:00:00 2001
From: Nikita <abracham.mitchell@gmail.com>
Date: Thu, 25 May 2017 16:43:18 +0300
Subject: [PATCH 08/15] null values checking in RDelayedQueue object fixed

---
 .../src/main/java/org/redisson/RedissonDelayedQueue.java   | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/redisson/src/main/java/org/redisson/RedissonDelayedQueue.java b/redisson/src/main/java/org/redisson/RedissonDelayedQueue.java
index cc1c8f684..e545ba1bc 100644
--- a/redisson/src/main/java/org/redisson/RedissonDelayedQueue.java
+++ b/redisson/src/main/java/org/redisson/RedissonDelayedQueue.java
@@ -25,7 +25,6 @@ import java.util.concurrent.TimeUnit;
 
 import org.redisson.api.RDelayedQueue;
 import org.redisson.api.RFuture;
-import org.redisson.api.RQueue;
 import org.redisson.api.RTopic;
 import org.redisson.client.codec.Codec;
 import org.redisson.client.codec.LongCodec;
@@ -417,7 +416,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
     public RFuture<V> peekAsync() {
         return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_OBJECT,
                 "local v = redis.call('lindex', KEYS[1], 0); "
-              + "if v ~= nil then "
+              + "if v ~= false then "
                   + "local randomId, value = struct.unpack('dLc0', v);"
                   + "return value; "
               + "end "
@@ -429,7 +428,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
     public RFuture<V> pollAsync() {
         return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_OBJECT,
                   "local v = redis.call('lpop', KEYS[1]); "
-                + "if v ~= nil then "
+                + "if v ~= false then "
                     + "redis.call('zrem', KEYS[2], v); "
                     + "local randomId, value = struct.unpack('dLc0', v);"
                     + "return value; "
@@ -447,7 +446,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
     public RFuture<V> pollLastAndOfferFirstToAsync(String queueName) {
         return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_OBJECT,
                 "local v = redis.call('rpop', KEYS[1]); "
-              + "if v ~= nil then "
+              + "if v ~= false then "
                   + "redis.call('zrem', KEYS[2], v); "
                   + "local randomId, value = struct.unpack('dLc0', v);"
                   + "redis.call('lpush', KEYS[3], value); "

From 4ef3d60ac7faef3e0e241eb6a0b932fa0fe3d4b9 Mon Sep 17 00:00:00 2001
From: Nikita <abracham.mitchell@gmail.com>
Date: Thu, 25 May 2017 17:05:51 +0300
Subject: [PATCH 09/15] Compilation fixed

---
 redisson/src/main/java/org/redisson/RedissonKeys.java | 1 +
 1 file changed, 1 insertion(+)

diff --git a/redisson/src/main/java/org/redisson/RedissonKeys.java b/redisson/src/main/java/org/redisson/RedissonKeys.java
index c4517c39c..8cc9d8e1d 100644
--- a/redisson/src/main/java/org/redisson/RedissonKeys.java
+++ b/redisson/src/main/java/org/redisson/RedissonKeys.java
@@ -20,6 +20,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;

From 803fc814e88ec99e4726e0fa100534271927742a Mon Sep 17 00:00:00 2001
From: Nikita <abracham.mitchell@gmail.com>
Date: Fri, 26 May 2017 12:01:08 +0300
Subject: [PATCH 10/15] Iterator infinite scan bug fixed #885

---
 .../org/redisson/RedissonBaseIterator.java    | 30 +++----------
 .../org/redisson/RedissonBaseMapIterator.java | 32 ++++++++------
 .../java/org/redisson/RedissonKeysTest.java   |  1 +
 .../test/java/org/redisson/RedissonTest.java  | 43 ++++++++++++++++++-
 4 files changed, 66 insertions(+), 40 deletions(-)

diff --git a/redisson/src/main/java/org/redisson/RedissonBaseIterator.java b/redisson/src/main/java/org/redisson/RedissonBaseIterator.java
index 9faa1d2b2..4410270cf 100644
--- a/redisson/src/main/java/org/redisson/RedissonBaseIterator.java
+++ b/redisson/src/main/java/org/redisson/RedissonBaseIterator.java
@@ -42,7 +42,6 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
 
     private boolean finished;
     private boolean currentElementRemoved;
-    private boolean removeExecuted;
     private V value;
 
     @Override
@@ -53,7 +52,6 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
                 free(lastValues);
 
                 currentElementRemoved = false;
-                removeExecuted = false;
                 client = null;
                 firstValues = null;
                 lastValues = null;
@@ -64,9 +62,7 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
                 }
                 finished = false;
             }
-            long prevIterPos;
             do {
-                prevIterPos = nextIterPos;
                 ListScanResult<ScanObjectEntry> res = iterator(client, nextIterPos);
                 if (lastValues != null) {
                     free(lastValues);
@@ -82,7 +78,6 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
                         client = null;
                         firstValues = null;
                         nextIterPos = 0;
-                        prevIterPos = -1;
                     }
                 } else {
                     if (firstValues.isEmpty()) {
@@ -93,39 +88,30 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
                                 client = null;
                                 firstValues = null;
                                 nextIterPos = 0;
-                                prevIterPos = -1;
                                 continue;
                             }
                             if (res.getPos() == 0) {
+                                free(firstValues);
+                                free(lastValues);
+                                
                                 finished = true;
                                 return false;
                             }
                         }
-                    } else if (lastValues.removeAll(firstValues)) {
+                    } else if (lastValues.removeAll(firstValues)
+                            || (lastValues.isEmpty() && nextIterPos == 0)) {
                         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;
-                    } else if (lastValues.isEmpty() && res.getPos() == 0) {
-                        if (tryAgain()) {
-                            client = null;
-                            firstValues = null;
-                            nextIterPos = 0;
-                            prevIterPos = -1;
-                            continue;
-                        }
                         
                         finished = true;
                         return false;
@@ -133,10 +119,7 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
                 }
                 lastIter = res.getValues().iterator();
                 nextIterPos = res.getPos();
-            } while (!lastIter.hasNext() && nextIterPos != prevIterPos);
-            if (prevIterPos == nextIterPos && !removeExecuted) {
-                finished = true;
-            }
+            } while (!lastIter.hasNext());
         }
         return lastIter.hasNext();
     }
@@ -188,7 +171,6 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
         lastIter.remove();
         remove(value);
         currentElementRemoved = true;
-        removeExecuted = true;
     }
 
     abstract void remove(V value);
diff --git a/redisson/src/main/java/org/redisson/RedissonBaseMapIterator.java b/redisson/src/main/java/org/redisson/RedissonBaseMapIterator.java
index f2924e205..de2363299 100644
--- a/redisson/src/main/java/org/redisson/RedissonBaseMapIterator.java
+++ b/redisson/src/main/java/org/redisson/RedissonBaseMapIterator.java
@@ -28,6 +28,14 @@ import org.redisson.client.protocol.decoder.ScanObjectEntry;
 
 import io.netty.buffer.ByteBuf;
 
+/**
+ * 
+ * @author Nikita Koksharov
+ *
+ * @param <K> key type
+ * @param <V> value type
+ * @param <M> loaded value type
+ */
 public abstract class RedissonBaseMapIterator<K, V, M> implements Iterator<M> {
 
     private Map<ByteBuf, ByteBuf> firstValues;
@@ -38,7 +46,6 @@ public abstract class RedissonBaseMapIterator<K, V, M> implements Iterator<M> {
 
     private boolean finished;
     private boolean currentElementRemoved;
-    private boolean removeExecuted;
     protected Map.Entry<ScanObjectEntry, ScanObjectEntry> entry;
 
     @Override
@@ -49,7 +56,6 @@ public abstract class RedissonBaseMapIterator<K, V, M> implements Iterator<M> {
                 free(lastValues);
 
                 currentElementRemoved = false;
-                removeExecuted = false;
                 client = null;
                 firstValues = null;
                 lastValues = null;
@@ -60,15 +66,15 @@ public abstract class RedissonBaseMapIterator<K, V, M> implements Iterator<M> {
                 }
                 finished = false;
             }
-            long prevIterPos;
             do {
-                prevIterPos = nextIterPos;
                 MapScanResult<ScanObjectEntry, ScanObjectEntry> res = iterator();
                 if (lastValues != null) {
                     free(lastValues);
                 }
+                
                 lastValues = convert(res.getMap());
                 client = res.getRedisClient();
+                
                 if (nextIterPos == 0 && firstValues == null) {
                     firstValues = lastValues;
                     lastValues = null;
@@ -76,7 +82,6 @@ public abstract class RedissonBaseMapIterator<K, V, M> implements Iterator<M> {
                         client = null;
                         firstValues = null;
                         nextIterPos = 0;
-                        prevIterPos = -1;
                     }
                 } else {
                     if (firstValues.isEmpty()) {
@@ -87,38 +92,38 @@ public abstract class RedissonBaseMapIterator<K, V, M> implements Iterator<M> {
                                 client = null;
                                 firstValues = null;
                                 nextIterPos = 0;
-                                prevIterPos = -1;
                                 continue;
                             }
                             if (res.getPos() == 0) {
+                                free(firstValues);
+                                free(lastValues);
+                                
                                 finished = true;
                                 return false;
                             }
                         }
-                    } else if (lastValues.keySet().removeAll(firstValues.keySet())) {
+                    } else if (lastValues.keySet().removeAll(firstValues.keySet())
+                            || (lastValues.isEmpty() && nextIterPos == 0)) {
                         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;
                     }
                 }
                 lastIter = res.getMap().entrySet().iterator();
                 nextIterPos = res.getPos();
-            } while (!lastIter.hasNext() && nextIterPos != prevIterPos);
-            if (prevIterPos == nextIterPos && !removeExecuted) {
-                finished = true;
-            }
+            } while (!lastIter.hasNext());
         }
         return lastIter.hasNext();
         
@@ -184,7 +189,6 @@ public abstract class RedissonBaseMapIterator<K, V, M> implements Iterator<M> {
         lastIter.remove();
         removeKey();
         currentElementRemoved = true;
-        removeExecuted = true;
         entry = null;
     }
 
diff --git a/redisson/src/test/java/org/redisson/RedissonKeysTest.java b/redisson/src/test/java/org/redisson/RedissonKeysTest.java
index c19d167c5..6c13be4d2 100644
--- a/redisson/src/test/java/org/redisson/RedissonKeysTest.java
+++ b/redisson/src/test/java/org/redisson/RedissonKeysTest.java
@@ -66,6 +66,7 @@ public class RedissonKeysTest extends BaseTest {
         for (int i = 0; i < 115; i++) {
             String key = "key" + Math.random();
             RBucket<String> bucket = redisson.getBucket(key);
+            keys.add(key);
             bucket.set("someValue");
         }
 
diff --git a/redisson/src/test/java/org/redisson/RedissonTest.java b/redisson/src/test/java/org/redisson/RedissonTest.java
index c6be7a759..38843b04a 100644
--- a/redisson/src/test/java/org/redisson/RedissonTest.java
+++ b/redisson/src/test/java/org/redisson/RedissonTest.java
@@ -6,6 +6,7 @@ import static org.redisson.BaseTest.createInstance;
 
 import java.io.IOException;
 import java.net.InetSocketAddress;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.Map;
@@ -33,10 +34,13 @@ import org.redisson.client.RedisConnectionException;
 import org.redisson.client.RedisException;
 import org.redisson.client.RedisOutOfMemoryException;
 import org.redisson.client.protocol.decoder.ListScanResult;
+import org.redisson.client.protocol.decoder.ScanObjectEntry;
 import org.redisson.codec.SerializationCodec;
 import org.redisson.config.Config;
 import org.redisson.connection.ConnectionListener;
 
+import io.netty.buffer.Unpooled;
+
 public class RedissonTest {
 
     protected RedissonClient redisson;
@@ -76,7 +80,7 @@ public class RedissonTest {
     }
     
     @Test
-    public void testIterator() {
+    public void testIteratorNotLooped() {
         RedissonBaseIterator iter = new RedissonBaseIterator() {
             int i;
             @Override
@@ -101,6 +105,41 @@ public class RedissonTest {
         Assert.assertFalse(iter.hasNext());
     }
     
+    @Test
+    public void testIteratorNotLooped2() {
+        RedissonBaseIterator<Integer> iter = new RedissonBaseIterator<Integer>() {
+            int i;
+            @Override
+            ListScanResult<ScanObjectEntry> iterator(InetSocketAddress client, long nextIterPos) {
+                i++;
+                if (i == 1) {
+                    return new ListScanResult<ScanObjectEntry>(14L, Arrays.asList(new ScanObjectEntry(Unpooled.wrappedBuffer(new byte[] {1}), 1)));
+                }
+                if (i == 2) {
+                    return new ListScanResult(7L, Collections.emptyList());
+                }
+                if (i == 3) {
+                    return new ListScanResult(0L, Collections.emptyList());
+                }
+                if (i == 4) {
+                    return new ListScanResult(14L, Collections.emptyList());
+                }
+                Assert.fail();
+                return null;
+            }
+
+            @Override
+            void remove(Integer value) {
+            }
+            
+        };
+        
+        Assert.assertTrue(iter.hasNext());
+        assertThat(iter.next()).isEqualTo(1);
+        Assert.assertFalse(iter.hasNext());
+    }
+
+    
     @BeforeClass
     public static void beforeClass() throws IOException, InterruptedException {
         if (!RedissonRuntimeEnvironment.isTravis) {
@@ -250,7 +289,7 @@ public class RedissonTest {
 
         Assert.assertEquals(0, pp.stop());
 
-        await().atMost(1, TimeUnit.SECONDS).until(() -> assertThat(connectCounter.get()).isEqualTo(1));
+        await().atMost(2, TimeUnit.SECONDS).until(() -> assertThat(connectCounter.get()).isEqualTo(1));
         await().until(() -> assertThat(disconnectCounter.get()).isEqualTo(1));
     }
 

From aa79c51f548f88b7f0e4e732f532870023c3e76f Mon Sep 17 00:00:00 2001
From: Nikita <abracham.mitchell@gmail.com>
Date: Fri, 26 May 2017 12:01:24 +0300
Subject: [PATCH 11/15] compilation fixed

---
 redisson/src/main/java/org/redisson/config/ConfigSupport.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/redisson/src/main/java/org/redisson/config/ConfigSupport.java b/redisson/src/main/java/org/redisson/config/ConfigSupport.java
index 8a7cf8677..b61437af0 100644
--- a/redisson/src/main/java/org/redisson/config/ConfigSupport.java
+++ b/redisson/src/main/java/org/redisson/config/ConfigSupport.java
@@ -134,7 +134,7 @@ public class ConfigSupport {
         try {
             Method lowMask = URI.class.getDeclaredMethod(methodName, String.class);
             lowMask.setAccessible(true);
-            long lowMaskValue = (long) lowMask.invoke(null, "-_");
+            Long lowMaskValue = (Long) lowMask.invoke(null, "-_");
             
             Field lowDash = URI.class.getDeclaredField(fieldName);
             

From f6b288d3dd83aa5d5e819c40d0c0f57578cc2577 Mon Sep 17 00:00:00 2001
From: Nikita <abracham.mitchell@gmail.com>
Date: Fri, 26 May 2017 12:03:13 +0300
Subject: [PATCH 12/15] Spring Cache dynamic resolving.  #886

---
 .../cache/RedissonSpringCacheManager.java     | 24 +++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/redisson/src/main/java/org/redisson/spring/cache/RedissonSpringCacheManager.java b/redisson/src/main/java/org/redisson/spring/cache/RedissonSpringCacheManager.java
index fea4663fa..78d1fd449 100644
--- a/redisson/src/main/java/org/redisson/spring/cache/RedissonSpringCacheManager.java
+++ b/redisson/src/main/java/org/redisson/spring/cache/RedissonSpringCacheManager.java
@@ -45,6 +45,8 @@ public class RedissonSpringCacheManager implements CacheManager, ResourceLoaderA
 
     private ResourceLoader resourceLoader;
 
+    private boolean dynamic = true;
+    
     private Codec codec;
 
     private RedissonClient redisson;
@@ -123,6 +125,25 @@ public class RedissonSpringCacheManager implements CacheManager, ResourceLoaderA
         this.codec = codec;
     }
 
+    /**
+     * Defines 'fixed' cache names. 
+     * A new cache instance will not be created in dynamic for non-defined names.
+     * <p>
+     * `null` parameter setups dynamic mode 
+     * 
+     * @param names of caches
+     */
+    public void setCacheNames(Collection<String> names) {
+        if (names != null) {
+            for (String name : names) {
+                getCache(name);
+            }
+            dynamic = false;
+        } else {
+            dynamic = true;
+        }
+    }
+    
     /**
      * Set cache config location
      *
@@ -165,6 +186,9 @@ public class RedissonSpringCacheManager implements CacheManager, ResourceLoaderA
         if (cache != null) {
             return cache;
         }
+        if (!dynamic) {
+            return cache;
+        }
         
         CacheConfig config = configMap.get(name);
         if (config == null) {

From aac6de5a619c96ed22ad303bdfd2eed882b42dbb Mon Sep 17 00:00:00 2001
From: Nikita <abracham.mitchell@gmail.com>
Date: Fri, 26 May 2017 12:28:05 +0300
Subject: [PATCH 13/15] RedissonCacheManager.setAllowNullValues method added.
 #882

---
 .../redisson/spring/cache/RedissonCache.java  | 34 +++++++++++++++----
 .../cache/RedissonSpringCacheManager.java     | 17 ++++++++--
 2 files changed, 43 insertions(+), 8 deletions(-)

diff --git a/redisson/src/main/java/org/redisson/spring/cache/RedissonCache.java b/redisson/src/main/java/org/redisson/spring/cache/RedissonCache.java
index bc648735d..5ae73e626 100644
--- a/redisson/src/main/java/org/redisson/spring/cache/RedissonCache.java
+++ b/redisson/src/main/java/org/redisson/spring/cache/RedissonCache.java
@@ -39,18 +39,22 @@ public class RedissonCache implements Cache {
 
     private CacheConfig config;
     
+    private final boolean allowNullValues;
+    
     private final AtomicLong hits = new AtomicLong();
 
     private final AtomicLong misses = new AtomicLong();
 
-    public RedissonCache(RMapCache<Object, Object> mapCache, CacheConfig config) {
+    public RedissonCache(RMapCache<Object, Object> mapCache, CacheConfig config, boolean allowNullValues) {
         this.mapCache = mapCache;
         this.map = mapCache;
         this.config = config;
+        this.allowNullValues = allowNullValues;
     }
 
-    public RedissonCache(RMap<Object, Object> map) {
+    public RedissonCache(RMap<Object, Object> map, boolean allowNullValues) {
         this.map = map;
+        this.allowNullValues = allowNullValues;
     }
 
     @Override
@@ -92,6 +96,15 @@ public class RedissonCache implements Cache {
 
     @Override
     public void put(Object key, Object value) {
+        if (!allowNullValues && value == null) {
+            if (mapCache != null) {
+                mapCache.remove(key);
+            } else {
+                map.remove(key);
+            }
+            return;
+        }
+        
         value = toStoreValue(value);
         if (mapCache != null) {
             mapCache.fastPut(key, value, config.getTTL(), TimeUnit.MILLISECONDS, config.getMaxIdleTime(), TimeUnit.MILLISECONDS);
@@ -101,13 +114,22 @@ public class RedissonCache implements Cache {
     }
 
     public ValueWrapper putIfAbsent(Object key, Object value) {
-        value = toStoreValue(value);
         Object prevValue;
-        if (mapCache != null) {
-            prevValue = mapCache.putIfAbsent(key, value, config.getTTL(), TimeUnit.MILLISECONDS, config.getMaxIdleTime(), TimeUnit.MILLISECONDS);
+        if (!allowNullValues && value == null) {
+            if (mapCache != null) {
+                prevValue = mapCache.get(key);
+            } else {
+                prevValue = map.get(key);
+            }
         } else {
-            prevValue = map.putIfAbsent(key, value);
+            value = toStoreValue(value);
+            if (mapCache != null) {
+                prevValue = mapCache.putIfAbsent(key, value, config.getTTL(), TimeUnit.MILLISECONDS, config.getMaxIdleTime(), TimeUnit.MILLISECONDS);
+            } else {
+                prevValue = map.putIfAbsent(key, value);
+            }
         }
+        
         return toValueWrapper(prevValue);
     }
 
diff --git a/redisson/src/main/java/org/redisson/spring/cache/RedissonSpringCacheManager.java b/redisson/src/main/java/org/redisson/spring/cache/RedissonSpringCacheManager.java
index 78d1fd449..c8a31de97 100644
--- a/redisson/src/main/java/org/redisson/spring/cache/RedissonSpringCacheManager.java
+++ b/redisson/src/main/java/org/redisson/spring/cache/RedissonSpringCacheManager.java
@@ -47,6 +47,8 @@ public class RedissonSpringCacheManager implements CacheManager, ResourceLoaderA
 
     private boolean dynamic = true;
     
+    private boolean allowNullValues = true;
+    
     private Codec codec;
 
     private RedissonClient redisson;
@@ -124,6 +126,17 @@ public class RedissonSpringCacheManager implements CacheManager, ResourceLoaderA
         this.configLocation = configLocation;
         this.codec = codec;
     }
+    
+    /**
+     * Defines possibility of storing {@code null} values.
+     * <p>
+     * Default is <code>true</code>
+     * 
+     * @param allowNullValues - stores if <code>true</code>
+     */
+    public void setAllowNullValues(boolean allowNullValues) {
+        this.allowNullValues = allowNullValues;
+    }
 
     /**
      * Defines 'fixed' cache names. 
@@ -213,7 +226,7 @@ public class RedissonSpringCacheManager implements CacheManager, ResourceLoaderA
             map = redisson.getMap(name);
         }
         
-        Cache cache = new RedissonCache(map);
+        Cache cache = new RedissonCache(map, allowNullValues);
         Cache oldCache = instanceMap.putIfAbsent(name, cache);
         if (oldCache != null) {
             cache = oldCache;
@@ -229,7 +242,7 @@ public class RedissonSpringCacheManager implements CacheManager, ResourceLoaderA
             map = redisson.getMapCache(name);
         }
         
-        Cache cache = new RedissonCache(map, config);
+        Cache cache = new RedissonCache(map, config, allowNullValues);
         Cache oldCache = instanceMap.putIfAbsent(name, cache);
         if (oldCache != null) {
             cache = oldCache;

From 4ac9b3e30a1eeef8a5b5b6584d60fec778fec283 Mon Sep 17 00:00:00 2001
From: Nikita Koksharov <nkoksharov@redisson.org>
Date: Fri, 26 May 2017 12:35:21 +0300
Subject: [PATCH 14/15] Update README.md

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 56dde5c7b..9a1b5be20 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 Redisson: Redis based In-Memory Data Grid for Java.
 ====
-[Quick start](https://github.com/redisson/redisson#quick-start) | [Documentation](https://github.com/redisson/redisson/wiki) | [Javadocs](http://www.javadoc.io/doc/org.redisson/redisson/3.4.1) | [Changelog](https://github.com/redisson/redisson/blob/master/CHANGELOG.md) | [Code examples](https://github.com/redisson/redisson-examples) | [Support chat](https://gitter.im/mrniko/redisson) | [Ultra-fast version](https://redisson.pro)
+[Quick start](https://github.com/redisson/redisson#quick-start) | [Documentation](https://github.com/redisson/redisson/wiki) | [Javadocs](http://www.javadoc.io/doc/org.redisson/redisson/3.4.1) | [Changelog](https://github.com/redisson/redisson/blob/master/CHANGELOG.md) | [Code examples](https://github.com/redisson/redisson-examples) | [Support chat](https://gitter.im/mrniko/redisson) | **[Ultra-fast version](https://redisson.pro)**
 
 Based on high-performance async and lock-free Java Redis client and [Netty](http://netty.io) framework.  
 ## Please take part in [Redisson survey](https://www.surveymonkey.com/r/QXQZH5D)

From 0693ef74a52ef9a8b518f44050a59f5a2e10f8c1 Mon Sep 17 00:00:00 2001
From: Nikita Koksharov <mrniko@users.noreply.github.com>
Date: Fri, 26 May 2017 12:51:17 +0300
Subject: [PATCH 15/15] Update CHANGELOG.md

---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0cf579841..77b46d7f4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,7 @@ Redisson Releases History
 
 Try __ULTRA-FAST__ [Redisson PRO](https://redisson.pro) edition.  
 
-### 10-Apr-2017 - versions 2.9.2 and 3.4.2 released
+### 10-May-2017 - versions 2.9.2 and 3.4.2 released
 
 Feature - __Dropwizard metrics integration__ More details [here](https://github.com/redisson/redisson/wiki/14.-Integration-with-frameworks#147-dropwizard-metrics)  
 Feature - `RLocalCachedMap.preloadCache` method added (thanks to Steve Draper)