From af773c37131629ff405b7c2b3805509e8d0915be Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 10 Sep 2015 18:11:23 +0300 Subject: [PATCH 01/21] new RScoredSortedSet object added. #143 --- src/main/java/org/redisson/Redisson.java | 5 + .../org/redisson/RedissonScoredSortedSet.java | 267 ++++++++++++++++++ .../client/protocol/RedisCommands.java | 15 + .../BooleanNotNullReplayConvertor.java | 26 ++ .../convertor/DoubleReplayConvertor.java | 29 ++ .../client/protocol/decoder/ScoredEntry.java | 68 +++++ .../decoder/ScoredSortedSetReplayDecoder.java | 46 +++ .../ScoredSortedSetScanReplayDecoder.java | 46 +++ .../org/redisson/core/RScoredSortedSet.java | 54 ++++ .../redisson/core/RScoredSortedSetAsync.java | 48 ++++ 10 files changed, 604 insertions(+) create mode 100644 src/main/java/org/redisson/RedissonScoredSortedSet.java create mode 100644 src/main/java/org/redisson/client/protocol/convertor/BooleanNotNullReplayConvertor.java create mode 100644 src/main/java/org/redisson/client/protocol/convertor/DoubleReplayConvertor.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/ScoredEntry.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetReplayDecoder.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetScanReplayDecoder.java create mode 100644 src/main/java/org/redisson/core/RScoredSortedSet.java create mode 100644 src/main/java/org/redisson/core/RScoredSortedSetAsync.java diff --git a/src/main/java/org/redisson/Redisson.java b/src/main/java/org/redisson/Redisson.java index 4bb1c4a7b..7b78ac885 100755 --- a/src/main/java/org/redisson/Redisson.java +++ b/src/main/java/org/redisson/Redisson.java @@ -44,6 +44,7 @@ import org.redisson.core.RLock; import org.redisson.core.RMap; import org.redisson.core.RPatternTopic; import org.redisson.core.RQueue; +import org.redisson.core.RScoredSortedSet; import org.redisson.core.RScript; import org.redisson.core.RSet; import org.redisson.core.RSortedSet; @@ -216,6 +217,10 @@ public class Redisson implements RedissonClient { return new RedissonSortedSet(commandExecutor, name); } + public RScoredSortedSet getScoredSortedSet(String name) { + return new RedissonScoredSortedSet(commandExecutor, name); + } + /** * Returns topic instance by name. * diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java new file mode 100644 index 000000000..bba0dfd73 --- /dev/null +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -0,0 +1,267 @@ +/** + * 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.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +import org.redisson.client.RedisClient; +import org.redisson.client.codec.StringCodec; +import org.redisson.client.protocol.RedisCommand; +import org.redisson.client.protocol.RedisCommands; +import org.redisson.client.protocol.convertor.BooleanReplayConvertor; +import org.redisson.client.protocol.decoder.ListScanResult; +import org.redisson.client.protocol.decoder.ScoredEntry; +import org.redisson.core.RScoredSortedSet; + +import io.netty.util.concurrent.Future; + +public class RedissonScoredSortedSet extends RedissonExpirable implements RScoredSortedSet { + + public RedissonScoredSortedSet(CommandExecutor commandExecutor, String name) { + super(commandExecutor, name); + } + + @Override + public boolean add(double score, V object) { + return get(addAsync(score, object)); + } + + @Override + public Future addAsync(double score, V object) { + return commandExecutor.writeAsync(getName(), RedisCommands.ZADD, getName(), BigDecimal.valueOf(score).toPlainString(), object); + } + + @Override + public boolean remove(Object object) { + return get(removeAsync(object)); + } + + @Override + public void clear() { + delete(); + } + + @Override + public Future removeAsync(Object object) { + return commandExecutor.writeAsync(getName(), RedisCommands.ZREM, getName(), object); + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public int size() { + return get(sizeAsync()); + } + + @Override + public Future sizeAsync() { + return commandExecutor.readAsync(getName(), RedisCommands.ZCARD, getName()); + } + + @Override + public boolean contains(Object object) { + return get(containsAsync(object)); + } + + @Override + public Future containsAsync(Object o) { + return commandExecutor.readAsync(getName(), RedisCommands.ZSCORE_CONTAINS, getName(), o); + } + + @Override + public Double getScore(Object o) { + return get(getScoreAsync(o)); + } + + @Override + public Future getScoreAsync(Object o) { + return commandExecutor.readAsync(getName(), RedisCommands.ZSCORE, getName(), o); + } + + private ListScanResult scanIterator(RedisClient client, long startPos) { + return commandExecutor.read(client, getName(), RedisCommands.ZSCAN, getName(), startPos); + } + + @Override + public Iterator iterator() { + return new Iterator() { + + private Iterator iter; + private RedisClient client; + private Long iterPos; + + private boolean removeExecuted; + private V value; + + @Override + public boolean hasNext() { + if (iter == null) { + ListScanResult res = scanIterator(null, 0); + client = res.getRedisClient(); + iter = res.getValues().iterator(); + iterPos = res.getPos(); + } else if (!iter.hasNext() && iterPos != 0) { + ListScanResult res = scanIterator(client, iterPos); + iter = res.getValues().iterator(); + iterPos = res.getPos(); + } + return iter.hasNext(); + } + + @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(); + RedissonScoredSortedSet.this.remove(value); + removeExecuted = true; + } + + }; + } + + @Override + public Object[] toArray() { + List res = (List) get(valueRangeAsync(0, -1)); + return res.toArray(); + } + + @Override + public T[] toArray(T[] a) { + List res = (List) get(valueRangeAsync(0, -1)); + return res.toArray(a); + } + + @Override + public boolean containsAll(Collection c) { + return get(containsAllAsync(c)); + } + + @Override + public Future containsAllAsync(Collection c) { + return commandExecutor.evalReadAsync(getName(), new RedisCommand("EVAL", new BooleanReplayConvertor(), 4), + "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 " + + "if ARGV[j] == s[i] " + + "then table.remove(ARGV, j) end " + + "end; " + + "end;" + + "return table.getn(ARGV) == 0; ", + Collections.singletonList(getName()), c.toArray()); + } + + @Override + public Future removeAllAsync(Collection c) { + return commandExecutor.evalWriteAsync(getName(), new RedisCommand("EVAL", new BooleanReplayConvertor(), 4), + "local v = false " + + "for i = 0, table.getn(ARGV), 1 do " + + "if redis.call('zrem', KEYS[1], ARGV[i]) == 1 " + + "then v = true end " + +"end " + + "return v ", + Collections.singletonList(getName()), c.toArray()); + } + + @Override + public boolean removeAll(Collection c) { + return get(removeAllAsync(c)); + } + + @Override + public boolean retainAll(Collection c) { + return get(retainAllAsync(c)); + } + + @Override + public Future retainAllAsync(Collection c) { + return commandExecutor.evalWriteAsync(getName(), new RedisCommand("EVAL", new BooleanReplayConvertor(), 4), + "local changed = false " + + "local s = redis.call('zrange', KEYS[1], 0, -1) " + + "local i = 0 " + + "while i <= table.getn(s) do " + + "local element = s[i] " + + "local isInAgrs = false " + + "for j = 0, table.getn(ARGV), 1 do " + + "if ARGV[j] == element then " + + "isInAgrs = true " + + "break " + + "end " + + "end " + + "if isInAgrs == false then " + + "redis.call('zrem', KEYS[1], element) " + + "changed = true " + + "end " + + "i = i + 1 " + + "end " + + "return changed ", + Collections.singletonList(getName()), c.toArray()); + } + + @Override + public Double addScore(V object, Number value) { + return get(addScoreAsync(object, value)); + } + + @Override + public Future addScoreAsync(V object, Number value) { + return commandExecutor.writeAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZINCRBY, + getName(), new BigDecimal(value.toString()).toPlainString(), object); + } + + @Override + public Collection valueRange(int startIndex, int endIndex) { + return get(valueRangeAsync(startIndex, endIndex)); + } + + @Override + public Future> valueRangeAsync(int startIndex, int endIndex) { + return commandExecutor.readAsync(getName(), RedisCommands.ZRANGE, getName(), startIndex, endIndex); + } + + @Override + public Collection> entryRange(int startIndex, int endIndex) { + return get(entryRangeAsync(startIndex, endIndex)); + } + + @Override + public Future>> entryRangeAsync(int startIndex, + int endIndex) { + return commandExecutor.readAsync(getName(), RedisCommands.ZRANGE_ENTRY, getName(), startIndex, endIndex, "WITHSCORES"); + } + +} diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index e204af21c..a3f4e6c17 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -21,7 +21,9 @@ import java.util.Set; import org.redisson.client.protocol.RedisCommand.ValueType; import org.redisson.client.protocol.convertor.BooleanAmountReplayConvertor; +import org.redisson.client.protocol.convertor.BooleanNotNullReplayConvertor; import org.redisson.client.protocol.convertor.BooleanReplayConvertor; +import org.redisson.client.protocol.convertor.DoubleReplayConvertor; import org.redisson.client.protocol.convertor.IntegerReplayConvertor; import org.redisson.client.protocol.convertor.KeyValueConvertor; import org.redisson.client.protocol.convertor.TrueReplayConvertor; @@ -35,6 +37,9 @@ import org.redisson.client.protocol.decoder.NestedMultiDecoder; import org.redisson.client.protocol.decoder.ObjectListReplayDecoder; import org.redisson.client.protocol.decoder.ObjectMapReplayDecoder; import org.redisson.client.protocol.decoder.ObjectSetReplayDecoder; +import org.redisson.client.protocol.decoder.ScoredEntry; +import org.redisson.client.protocol.decoder.ScoredSortedSetReplayDecoder; +import org.redisson.client.protocol.decoder.ScoredSortedSetScanReplayDecoder; import org.redisson.client.protocol.decoder.StringDataDecoder; import org.redisson.client.protocol.decoder.StringListReplayDecoder; import org.redisson.client.protocol.decoder.StringMapDataDecoder; @@ -44,6 +49,16 @@ import org.redisson.client.protocol.pubsub.PubSubStatusDecoder; public interface RedisCommands { + RedisCommand ZADD = new RedisCommand("ZADD", new BooleanAmountReplayConvertor(), 3); + RedisCommand ZREM = new RedisCommand("ZREM", new BooleanAmountReplayConvertor(), 2); + RedisStrictCommand ZCARD = new RedisStrictCommand("ZCARD", new IntegerReplayConvertor()); + RedisCommand ZSCORE_CONTAINS = new RedisCommand("ZSCORE", new BooleanNotNullReplayConvertor(), 2); + RedisStrictCommand ZSCORE = new RedisStrictCommand("ZSCORE", new DoubleReplayConvertor()); + RedisCommand> ZRANGE = new RedisCommand>("ZRANGE", new ObjectListReplayDecoder()); + RedisCommand>> ZRANGE_ENTRY = new RedisCommand>>("ZRANGE", new ScoredSortedSetReplayDecoder()); + RedisCommand> ZSCAN = new RedisCommand>("ZSCAN", new NestedMultiDecoder(new ObjectListReplayDecoder(), new ScoredSortedSetScanReplayDecoder()), ValueType.OBJECT); + RedisStrictCommand ZINCRBY = new RedisStrictCommand("ZINCRBY", new DoubleReplayConvertor()); + RedisCommand> SCAN = new RedisCommand>("SCAN", new NestedMultiDecoder(new ObjectListReplayDecoder(), new ListScanResultReplayDecoder()), ValueType.OBJECT); RedisStrictCommand RANDOM_KEY = new RedisStrictCommand("RANDOMKEY", new StringDataDecoder()); RedisStrictCommand PING = new RedisStrictCommand("PING"); diff --git a/src/main/java/org/redisson/client/protocol/convertor/BooleanNotNullReplayConvertor.java b/src/main/java/org/redisson/client/protocol/convertor/BooleanNotNullReplayConvertor.java new file mode 100644 index 000000000..845ba698d --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/convertor/BooleanNotNullReplayConvertor.java @@ -0,0 +1,26 @@ +/** + * 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.convertor; + +public class BooleanNotNullReplayConvertor extends SingleConvertor { + + @Override + public Boolean convert(Object obj) { + return obj != null; + } + + +} diff --git a/src/main/java/org/redisson/client/protocol/convertor/DoubleReplayConvertor.java b/src/main/java/org/redisson/client/protocol/convertor/DoubleReplayConvertor.java new file mode 100644 index 000000000..4bef92164 --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/convertor/DoubleReplayConvertor.java @@ -0,0 +1,29 @@ +/** + * 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.convertor; + +public class DoubleReplayConvertor extends SingleConvertor { + + @Override + public Double convert(Object obj) { + if (obj == null) { + return null; + } + return Double.valueOf(obj.toString()); + } + + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/ScoredEntry.java b/src/main/java/org/redisson/client/protocol/decoder/ScoredEntry.java new file mode 100644 index 000000000..398fc799b --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/ScoredEntry.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; + +public class ScoredEntry { + + private final Double score; + private final V value; + + public ScoredEntry(Double score, V value) { + super(); + this.score = score; + this.value = value; + } + + public V getValue() { + return value; + } + + public Double getScore() { + return score; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((score == null) ? 0 : score.hashCode()); + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ScoredEntry other = (ScoredEntry) obj; + if (score == null) { + if (other.score != null) + return false; + } else if (!score.equals(other.score)) + return false; + if (value == null) { + if (other.value != null) + return false; + } else if (!value.equals(other.value)) + return false; + return true; + } + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetReplayDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetReplayDecoder.java new file mode 100644 index 000000000..bfa322082 --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetReplayDecoder.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.util.ArrayList; +import java.util.List; + +import org.redisson.client.handler.State; + +import io.netty.buffer.ByteBuf; + +public class ScoredSortedSetReplayDecoder implements MultiDecoder>> { + + @Override + public Object decode(ByteBuf buf, State state) { + throw new UnsupportedOperationException(); + } + + @Override + public List> decode(List parts, State state) { + List> result = new ArrayList>(); + for (int i = 0; i < parts.size(); i += 2) { + result.add(new ScoredEntry(((Number)parts.get(i+1)).doubleValue(), (T)parts.get(i))); + } + return result; + } + + @Override + public boolean isApplicable(int paramNum, State state) { + return false; + } + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetScanReplayDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetScanReplayDecoder.java new file mode 100644 index 000000000..bcbe844c1 --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetScanReplayDecoder.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.util.List; + +import org.redisson.client.handler.State; + +import io.netty.buffer.ByteBuf; +import io.netty.util.CharsetUtil; + +public class ScoredSortedSetScanReplayDecoder implements MultiDecoder> { + + @Override + public Object decode(ByteBuf buf, State state) { + return Long.valueOf(buf.toString(CharsetUtil.UTF_8)); + } + + @Override + public ListScanResult decode(List parts, State state) { + List values = (List)parts.get(1); + for (int i = 1; i < values.size(); i++) { + values.remove(i); + } + return new ListScanResult((Long)parts.get(0), values); + } + + @Override + public boolean isApplicable(int paramNum, State state) { + return paramNum == 0; + } + +} diff --git a/src/main/java/org/redisson/core/RScoredSortedSet.java b/src/main/java/org/redisson/core/RScoredSortedSet.java new file mode 100644 index 000000000..5d55e4902 --- /dev/null +++ b/src/main/java/org/redisson/core/RScoredSortedSet.java @@ -0,0 +1,54 @@ +/** + * 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.Collection; + +import org.redisson.client.protocol.decoder.ScoredEntry; + +public interface RScoredSortedSet extends RScoredSortedSetAsync, Iterable, RExpirable { + + Double getScore(Object o); + + boolean add(double score, V object); + + int size(); + + boolean isEmpty(); + + boolean contains(Object o); + + Object[] toArray(); + + T[] toArray(T[] a); + + boolean remove(Object o); + + boolean containsAll(Collection c); + + boolean removeAll(Collection c); + + boolean retainAll(Collection c); + + void clear(); + + Double addScore(V object, Number value); + + Collection valueRange(int startIndex, int endIndex); + + Collection> entryRange(int startIndex, int endIndex); + +} diff --git a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java new file mode 100644 index 000000000..ef89c3551 --- /dev/null +++ b/src/main/java/org/redisson/core/RScoredSortedSetAsync.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; + +import java.util.Collection; + +import org.redisson.client.protocol.decoder.ScoredEntry; + +import io.netty.util.concurrent.Future; + +public interface RScoredSortedSetAsync extends RExpirableAsync { + + Future getScoreAsync(Object o); + + Future addAsync(double score, V object); + + Future removeAsync(V object); + + Future sizeAsync(); + + Future containsAsync(Object o); + + Future containsAllAsync(Collection c); + + Future removeAllAsync(Collection c); + + Future retainAllAsync(Collection c); + + Future addScoreAsync(V object, Number value); + + Future> valueRangeAsync(int startIndex, int endIndex); + + Future>> entryRangeAsync(int startIndex, int endIndex); + +} From a0f3bc9d064b0be467edc50dcc703b36eacf5f0b Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 10 Sep 2015 18:11:47 +0300 Subject: [PATCH 02/21] iterator.remove wrong state checking added --- src/main/java/org/redisson/RedissonKeys.java | 3 +++ src/main/java/org/redisson/RedissonSet.java | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/redisson/RedissonKeys.java b/src/main/java/org/redisson/RedissonKeys.java index ca91d8356..63bb821d0 100644 --- a/src/main/java/org/redisson/RedissonKeys.java +++ b/src/main/java/org/redisson/RedissonKeys.java @@ -116,6 +116,9 @@ public class RedissonKeys implements RKeys { if (removeExecuted) { throw new IllegalStateException("Element been already deleted"); } + if (iter == null) { + throw new IllegalStateException(); + } iter.remove(); delete(value); diff --git a/src/main/java/org/redisson/RedissonSet.java b/src/main/java/org/redisson/RedissonSet.java index 13343a60f..6e2a94400 100644 --- a/src/main/java/org/redisson/RedissonSet.java +++ b/src/main/java/org/redisson/RedissonSet.java @@ -115,9 +115,10 @@ public class RedissonSet extends RedissonExpirable implements RSet { if (removeExecuted) { throw new IllegalStateException("Element been already deleted"); } + if (iter == null) { + throw new IllegalStateException(); + } - // lazy init iterator -// hasNext(); iter.remove(); RedissonSet.this.remove(value); removeExecuted = true; From 1cc497e17a8a0081824fb765f34fb2903971d569 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 10 Sep 2015 18:12:34 +0300 Subject: [PATCH 03/21] RedissonScoredSortedSetTest added. #143 --- .../redisson/RedissonScoredSortedSetTest.java | 406 ++++++++++++++++++ 1 file changed, 406 insertions(+) create mode 100644 src/test/java/org/redisson/RedissonScoredSortedSetTest.java diff --git a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java new file mode 100644 index 000000000..4af77772d --- /dev/null +++ b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -0,0 +1,406 @@ +package org.redisson; + +import io.netty.util.concurrent.Future; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +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.protocol.decoder.ScoredEntry; +import org.redisson.core.RMap; +import org.redisson.core.RScoredSortedSet; +import org.redisson.core.RSortedSet; + +public class RedissonScoredSortedSetTest extends BaseTest { + + @Test + public void testAddAsync() throws InterruptedException, ExecutionException { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + Future future = set.addAsync(0.323, 2); + Assert.assertTrue(future.get()); + Future future2 = set.addAsync(0.323, 2); + Assert.assertFalse(future2.get()); + + Assert.assertTrue(set.contains(2)); + } + + @Test + public void testRemoveAsync() throws InterruptedException, ExecutionException { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(0.11, 1); + set.add(0.22, 3); + set.add(0.33, 7); + + Assert.assertTrue(set.removeAsync(1).get()); + Assert.assertFalse(set.contains(1)); + Assert.assertThat(set, Matchers.contains(3, 7)); + + Assert.assertFalse(set.removeAsync(1).get()); + Assert.assertThat(set, Matchers.contains(3, 7)); + + set.removeAsync(3).get(); + Assert.assertFalse(set.contains(3)); + Assert.assertThat(set, Matchers.contains(7)); + } + + @Test + public void testIteratorNextNext() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(1, "1"); + set.add(2, "4"); + + Iterator iter = set.iterator(); + Assert.assertEquals("1", iter.next()); + Assert.assertEquals("4", iter.next()); + Assert.assertFalse(iter.hasNext()); + } + + @Test + public void testIteratorRemove() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(1, "1"); + set.add(2, "4"); + set.add(3, "2"); + set.add(4, "5"); + set.add(5, "3"); + + for (Iterator iterator = set.iterator(); iterator.hasNext();) { + String value = iterator.next(); + if (value.equals("2")) { + iterator.remove(); + } + } + + Assert.assertThat(set, Matchers.contains("1", "4", "5", "3")); + + int iteration = 0; + for (Iterator iterator = set.iterator(); iterator.hasNext();) { + iterator.next(); + iterator.remove(); + iteration++; + } + + Assert.assertEquals(4, iteration); + + Assert.assertEquals(0, set.size()); + Assert.assertTrue(set.isEmpty()); + } + + @Test + public void testIteratorSequence() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + for (int i = 0; i < 1000; i++) { + set.add(i, Integer.valueOf(i)); + } + + Set setCopy = new HashSet(); + for (int i = 0; i < 1000; i++) { + setCopy.add(Integer.valueOf(i)); + } + + checkIterator(set, setCopy); + } + + private void checkIterator(RScoredSortedSet set, Set setCopy) { + for (Iterator iterator = set.iterator(); iterator.hasNext();) { + Integer value = iterator.next(); + if (!setCopy.remove(value)) { + Assert.fail(); + } + } + + Assert.assertEquals(0, setCopy.size()); + } + + @Test + public void testRetainAll() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + for (int i = 0; i < 20000; i++) { + set.add(i, i); + } + + Assert.assertTrue(set.retainAll(Arrays.asList(1, 2))); + Assert.assertThat(set, Matchers.containsInAnyOrder(1, 2)); + Assert.assertEquals(2, set.size()); + } + + @Test + public void testRemoveAll() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(0.1, 1); + set.add(0.2, 2); + set.add(0.3, 3); + + Assert.assertTrue(set.removeAll(Arrays.asList(1, 2))); + Assert.assertThat(set, Matchers.contains(3)); + Assert.assertEquals(1, set.size()); + } + + +// @Test(expected = IllegalArgumentException.class) + public void testTailSet() { + RSortedSet set = redisson.getSortedSet("set"); + + set.add(1); + set.add(2); + set.add(3); + set.add(4); + set.add(5); + + SortedSet hs = set.tailSet(3); + hs.add(10); + + MatcherAssert.assertThat(hs, Matchers.contains(3, 4, 5, 10)); + + set.remove(4); + + MatcherAssert.assertThat(hs, Matchers.contains(3, 5, 10)); + + set.remove(3); + + MatcherAssert.assertThat(hs, Matchers.contains(5, 10)); + + hs.add(-1); + } + + +// @Test(expected = IllegalArgumentException.class) + public void testHeadSet() { + RSortedSet set = redisson.getSortedSet("set"); + + set.add(1); + set.add(2); + set.add(3); + set.add(4); + set.add(5); + + SortedSet hs = set.headSet(3); + hs.add(0); + + MatcherAssert.assertThat(hs, Matchers.contains(0, 1, 2)); + + set.remove(2); + + MatcherAssert.assertThat(hs, Matchers.contains(0, 1)); + + set.remove(3); + + MatcherAssert.assertThat(hs, Matchers.contains(0, 1)); + + hs.add(7); + } + + @Test(expected = IllegalArgumentException.class) + public void testTailSetTreeSet() { + TreeSet set = new TreeSet(); + + set.add(1); + set.add(2); + set.add(3); + set.add(4); + set.add(5); + + SortedSet hs = set.tailSet(3); + hs.add(10); + + MatcherAssert.assertThat(hs, Matchers.contains(3, 4, 5, 10)); + + set.remove(4); + + MatcherAssert.assertThat(hs, Matchers.contains(3, 5, 10)); + + set.remove(3); + + MatcherAssert.assertThat(hs, Matchers.contains(5, 10)); + + hs.add(-1); + } + + @Test(expected = IllegalArgumentException.class) + public void testHeadSetTreeSet() { + TreeSet set = new TreeSet(); + + set.add(1); + set.add(2); + set.add(3); + set.add(4); + set.add(5); + + SortedSet hs = set.headSet(3); + hs.add(0); + + MatcherAssert.assertThat(hs, Matchers.contains(0, 1, 2)); + + set.remove(2); + + MatcherAssert.assertThat(hs, Matchers.contains(0, 1)); + + set.remove(3); + + MatcherAssert.assertThat(hs, Matchers.contains(0, 1)); + + hs.add(7); + } + + @Test + public void testSort() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + Assert.assertTrue(set.add(4, 2)); + Assert.assertTrue(set.add(5, 3)); + Assert.assertTrue(set.add(3, 1)); + Assert.assertTrue(set.add(6, 4)); + Assert.assertTrue(set.add(1000, 10)); + 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()); + } + + @Test + public void testRemove() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(4, 5); + set.add(2, 3); + set.add(0, 1); + set.add(1, 2); + set.add(3, 4); + + Assert.assertFalse(set.remove(0)); + Assert.assertTrue(set.remove(3)); + + Assert.assertThat(set, Matchers.contains(1, 2, 4, 5)); + } + + @Test + public void testContainsAll() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + for (int i = 0; i < 200; i++) { + set.add(i, i); + } + + Assert.assertTrue(set.containsAll(Arrays.asList(30, 11))); + Assert.assertFalse(set.containsAll(Arrays.asList(30, 711, 11))); + } + + @Test + public void testToArray() { + 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"); + + MatcherAssert.assertThat(Arrays.asList(set.toArray()), Matchers.containsInAnyOrder("1", "2", "4", "5", "3")); + + String[] strs = set.toArray(new String[0]); + MatcherAssert.assertThat(Arrays.asList(strs), Matchers.containsInAnyOrder("1", "4", "2", "5", "3")); + } + + @Test + public void testContains() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + + set.add(0, new TestObject("1", "2")); + set.add(1, new TestObject("1", "2")); + set.add(2, new TestObject("2", "3")); + set.add(3, new TestObject("3", "4")); + set.add(4, new TestObject("5", "6")); + + Assert.assertTrue(set.contains(new TestObject("2", "3"))); + Assert.assertTrue(set.contains(new TestObject("1", "2"))); + Assert.assertFalse(set.contains(new TestObject("1", "9"))); + } + + @Test + public void testDuplicates() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + + Assert.assertTrue(set.add(0, new TestObject("1", "2"))); + Assert.assertFalse(set.add(0, new TestObject("1", "2"))); + Assert.assertTrue(set.add(2, new TestObject("2", "3"))); + Assert.assertTrue(set.add(3, new TestObject("3", "4"))); + Assert.assertTrue(set.add(4, new TestObject("5", "6"))); + + Assert.assertEquals(4, set.size()); + } + + @Test + public void testSize() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(0, 1); + set.add(1, 2); + set.add(2, 3); + set.add(2, 3); + set.add(3, 4); + set.add(4, 5); + set.add(4, 5); + + Assert.assertEquals(5, set.size()); + } + + @Test + public void testValueRange() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(0, 1); + set.add(1, 2); + set.add(2, 3); + set.add(3, 4); + set.add(4, 5); + set.add(4, 5); + + Collection vals = set.valueRange(0, -1); + MatcherAssert.assertThat(vals, Matchers.contains(1, 2, 3, 4, 5)); + } + + @Test + public void testEntryRange() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(10, 1); + set.add(20, 2); + set.add(30, 3); + set.add(40, 4); + set.add(50, 5); + + Collection> vals = set.entryRange(0, -1); + MatcherAssert.assertThat(vals, Matchers.contains(new ScoredEntry(10D, 1), + new ScoredEntry(20D, 2), + new ScoredEntry(30D, 3), + new ScoredEntry(40D, 4), + new ScoredEntry(50D, 5))); + } + + + @Test + public void testAddAndGet() throws InterruptedException { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(1, 100); + + Double res = set.addScore(100, 11); + Assert.assertEquals(12, (double)res, 0); + Double score = set.getScore(100); + Assert.assertEquals(12, (double)score, 0); + + RScoredSortedSet set2 = redisson.getScoredSortedSet("simple"); + set2.add(100.2, 1); + + Double res2 = set2.addScore(1, new Double(12.1)); + Assert.assertTrue(new Double(112.3).compareTo(res2) == 0); + res2 = set2.getScore(1); + Assert.assertTrue(new Double(112.3).compareTo(res2) == 0); + } + +} From 4e77922ff350257410fd481bd7ab195e8510ebe7 Mon Sep 17 00:00:00 2001 From: Steve Ungerer Date: Thu, 10 Sep 2015 14:59:57 -0400 Subject: [PATCH 04/21] DNS monitoring Resolves the single server endpoint at a configurable interval to determine if the endpoint has been changed to point at a new master node. Designed for use with AWS ElastiCache replication group. Inspired by https://github.com/mrniko/redisson/issues/241 --- .../java/org/redisson/SingleServerConfig.java | 46 +++++++++++++++ .../connection/SingleConnectionManager.java | 58 +++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/src/main/java/org/redisson/SingleServerConfig.java b/src/main/java/org/redisson/SingleServerConfig.java index 3c59b5092..dfa30f453 100644 --- a/src/main/java/org/redisson/SingleServerConfig.java +++ b/src/main/java/org/redisson/SingleServerConfig.java @@ -38,6 +38,21 @@ public class SingleServerConfig extends BaseConfig { */ private int connectionPoolSize = 100; + + /** + * Should the server address be monitored for changes in DNS? Useful for + * AWS ElastiCache where the client is pointed at the endpoint for a replication group + * which is a DNS alias to the current master node.
+ * NB: applications must ensure the JVM DNS cache TTL is low enough to support this. + * e.g., http://docs.aws.amazon.com/AWSSdkDocsJava/latest/DeveloperGuide/java-dg-jvm-ttl.html + */ + private boolean dnsMonitoring = false; + + /** + * Interval in milliseconds to check DNS + */ + private long dnsMonitoringInterval = 5000; + SingleServerConfig() { } @@ -46,6 +61,8 @@ public class SingleServerConfig extends BaseConfig { setAddress(config.getAddress()); setConnectionPoolSize(config.getConnectionPoolSize()); setSubscriptionConnectionPoolSize(config.getSubscriptionConnectionPoolSize()); + setDnsMonitoring(config.isDnsMonitoring()); + setDnsMonitoringInterval(config.getDnsMonitoringInterval()); } /** @@ -93,4 +110,33 @@ public class SingleServerConfig extends BaseConfig { this.address = address; } + /** + * Monitoring of the endpoint address for DNS changes. + * Default is false. + * + * @param dnsMonitoring + * @return + */ + public SingleServerConfig setDnsMonitoring(boolean dnsMonitoring) { + this.dnsMonitoring = dnsMonitoring; + return this; + } + public boolean isDnsMonitoring() { + return dnsMonitoring; + } + + /** + * Interval in milliseconds to check the endpoint DNS if {@link #isDnsMonitoring()} is true. + * Default is 5000. + * + * @param dnsMonitoringInterval + * @return + */ + public SingleServerConfig setDnsMonitoringInterval(long dnsMonitoringInterval) { + this.dnsMonitoringInterval = dnsMonitoringInterval; + return this; + } + public long getDnsMonitoringInterval() { + return dnsMonitoringInterval; + } } diff --git a/src/main/java/org/redisson/connection/SingleConnectionManager.java b/src/main/java/org/redisson/connection/SingleConnectionManager.java index 717725532..f267fc41c 100644 --- a/src/main/java/org/redisson/connection/SingleConnectionManager.java +++ b/src/main/java/org/redisson/connection/SingleConnectionManager.java @@ -15,12 +15,29 @@ */ package org.redisson.connection; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + import org.redisson.Config; import org.redisson.MasterSlaveServersConfig; import org.redisson.SingleServerConfig; +import org.redisson.client.RedisConnectionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.netty.util.concurrent.GlobalEventExecutor; +import io.netty.util.concurrent.ScheduledFuture; public class SingleConnectionManager extends MasterSlaveConnectionManager { + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final AtomicReference currentMaster = new AtomicReference(); + + private ScheduledFuture monitorFuture; + public SingleConnectionManager(SingleServerConfig cfg, Config config) { MasterSlaveServersConfig newconfig = new MasterSlaveServersConfig(); String addr = cfg.getAddress().getHost() + ":" + cfg.getAddress().getPort(); @@ -37,6 +54,16 @@ public class SingleConnectionManager extends MasterSlaveConnectionManager { newconfig.setSlaveSubscriptionConnectionPoolSize(cfg.getSubscriptionConnectionPoolSize()); init(newconfig, config); + + if (cfg.isDnsMonitoring()) { + try { + this.currentMaster.set(InetAddress.getByName(cfg.getAddress().getHost())); + } catch (UnknownHostException e) { + throw new RedisConnectionException("Unknown host", e); + } + log.debug("DNS monitoring enabled; Current master set to {}", currentMaster.get()); + monitorDnsChange(cfg); + } } @Override @@ -44,5 +71,36 @@ public class SingleConnectionManager extends MasterSlaveConnectionManager { SingleEntry entry = new SingleEntry(0, MAX_SLOT, this, config); entries.put(MAX_SLOT, entry); } + + private void monitorDnsChange(final SingleServerConfig cfg) { + monitorFuture = GlobalEventExecutor.INSTANCE.scheduleWithFixedDelay(new Runnable() { + @Override + public void run() { + try { + InetAddress master = currentMaster.get(); + InetAddress now = InetAddress.getByName(cfg.getAddress().getHost()); + if (!now.getHostAddress().equals(master.getHostAddress())) { + log.info("Detected DNS change. {} has changed from {} to {}", cfg.getAddress().getHost(), master.getHostAddress(), now.getHostAddress()); + if (currentMaster.compareAndSet(master, now)) { + changeMaster(MAX_SLOT,cfg.getAddress().getHost(), cfg.getAddress().getPort()); + log.info("Master has been changed"); + } + } + + } catch (Exception e) { + log.error(e.getMessage(), e); + } + + } + + }, cfg.getDnsMonitoringInterval(), cfg.getDnsMonitoringInterval(), TimeUnit.MILLISECONDS); + } + @Override + public void shutdown() { + if (monitorFuture != null) { + monitorFuture.cancel(true); + } + super.shutdown(); + } } From 3c5346f97eee259b0b59d591641625c297c0e04d Mon Sep 17 00:00:00 2001 From: Steve Ungerer Date: Thu, 10 Sep 2015 18:44:46 -0400 Subject: [PATCH 05/21] Elasticache Replication Group server configuration Uses "INFO replication" to determine which node is master and which are slaves. --- src/main/java/org/redisson/Config.java | 37 +++- ...sticacheReplicationGroupServersConfig.java | 86 +++++++++ .../redisson/MasterSlaveServersConfig.java | 5 +- src/main/java/org/redisson/Redisson.java | 3 + .../client/protocol/RedisCommands.java | 1 + ...acheReplicationGroupConnectionManager.java | 164 ++++++++++++++++++ 6 files changed, 294 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/redisson/ElasticacheReplicationGroupServersConfig.java create mode 100644 src/main/java/org/redisson/connection/ElasticacheReplicationGroupConnectionManager.java diff --git a/src/main/java/org/redisson/Config.java b/src/main/java/org/redisson/Config.java index 55c8ccc25..af369a4af 100644 --- a/src/main/java/org/redisson/Config.java +++ b/src/main/java/org/redisson/Config.java @@ -33,6 +33,8 @@ public class Config { private SingleServerConfig singleServerConfig; private ClusterServersConfig clusterServersConfig; + + private ElasticacheReplicationGroupServersConfig elasticacheReplicationGroupServersConfig; /** * Threads amount shared between all redis node clients @@ -71,6 +73,10 @@ public class Config { if (oldConf.getClusterServersConfig() != null ) { setClusterServersConfig(new ClusterServersConfig(oldConf.getClusterServersConfig())); } + if (oldConf.getElasticacheReplicationGroupServersConfig() != null) { + setElasticacheReplicationGroupServersConfig(new ElasticacheReplicationGroupServersConfig(oldConf.getElasticacheReplicationGroupServersConfig())); + } + } /** @@ -91,6 +97,7 @@ public class Config { checkMasterSlaveServersConfig(); checkSentinelServersConfig(); checkSingleServerConfig(); + checkElasticacheReplicationGroupServersConfig(); if (clusterServersConfig == null) { clusterServersConfig = new ClusterServersConfig(); @@ -105,17 +112,37 @@ public class Config { this.clusterServersConfig = clusterServersConfig; } + public ElasticacheReplicationGroupServersConfig useElasticacheReplicationGroupServers() { + checkClusterServersConfig(); + checkMasterSlaveServersConfig(); + checkSentinelServersConfig(); + checkSingleServerConfig(); + + if (elasticacheReplicationGroupServersConfig == null) { + elasticacheReplicationGroupServersConfig = new ElasticacheReplicationGroupServersConfig(); + } + return elasticacheReplicationGroupServersConfig; + } + + ElasticacheReplicationGroupServersConfig getElasticacheReplicationGroupServersConfig() { + return elasticacheReplicationGroupServersConfig; + } + void setElasticacheReplicationGroupServersConfig(ElasticacheReplicationGroupServersConfig elasticacheReplicationGroupServersConfig) { + this.elasticacheReplicationGroupServersConfig = elasticacheReplicationGroupServersConfig; + } + public SingleServerConfig useSingleServer() { checkClusterServersConfig(); checkMasterSlaveServersConfig(); checkSentinelServersConfig(); + checkElasticacheReplicationGroupServersConfig(); if (singleServerConfig == null) { singleServerConfig = new SingleServerConfig(); } return singleServerConfig; } - + SingleServerConfig getSingleServerConfig() { return singleServerConfig; } @@ -127,6 +154,7 @@ public class Config { checkClusterServersConfig(); checkSingleServerConfig(); checkMasterSlaveServersConfig(); + checkElasticacheReplicationGroupServersConfig(); if (sentinelServersConfig == null) { sentinelServersConfig = new SentinelServersConfig(); @@ -145,6 +173,7 @@ public class Config { checkClusterServersConfig(); checkSingleServerConfig(); checkSentinelServersConfig(); + checkElasticacheReplicationGroupServersConfig(); if (masterSlaveServersConfig == null) { masterSlaveServersConfig = new MasterSlaveServersConfig(); @@ -194,6 +223,12 @@ public class Config { throw new IllegalStateException("single server config already used!"); } } + + private void checkElasticacheReplicationGroupServersConfig() { + if (elasticacheReplicationGroupServersConfig != null) { + throw new IllegalStateException("elasticache replication group servers config already used!"); + } + } /** * Activates an unix socket if servers binded to loopback interface. diff --git a/src/main/java/org/redisson/ElasticacheReplicationGroupServersConfig.java b/src/main/java/org/redisson/ElasticacheReplicationGroupServersConfig.java new file mode 100644 index 000000000..c87543826 --- /dev/null +++ b/src/main/java/org/redisson/ElasticacheReplicationGroupServersConfig.java @@ -0,0 +1,86 @@ +/** + * 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.URI; +import java.util.ArrayList; +import java.util.List; + +import org.redisson.misc.URIBuilder; + +/** + * Configuration for an AWS ElastiCache replication group. A replication group is composed + * of a single master endpoint and multiple read slaves. + * + * @author Steve Ungerer + */ +public class ElasticacheReplicationGroupServersConfig extends BaseMasterSlaveServersConfig { + + /** + * Replication group node urls list + */ + private List nodeAddresses = new ArrayList(); + + /** + * Replication group scan interval in milliseconds + */ + private int scanInterval = 1000; + + public ElasticacheReplicationGroupServersConfig() { + } + + ElasticacheReplicationGroupServersConfig(ElasticacheReplicationGroupServersConfig config) { + super(config); + setNodeAddresses(config.getNodeAddresses()); + setScanInterval(config.getScanInterval()); + } + + /** + * Add Redis cluster node address. Use follow format -- host:port + * + * @param addresses in host:port format + * @return + */ + public ElasticacheReplicationGroupServersConfig addNodeAddress(String ... addresses) { + for (String address : addresses) { + nodeAddresses.add(URIBuilder.create(address)); + } + return this; + } + public List getNodeAddresses() { + return nodeAddresses; + } + void setNodeAddresses(List nodeAddresses) { + this.nodeAddresses = nodeAddresses; + } + + public int getScanInterval() { + return scanInterval; + } + /** + * Redis cluster scan interval in milliseconds + * + * @param scanInterval in milliseconds + * @return + */ + public ElasticacheReplicationGroupServersConfig setScanInterval(int scanInterval) { + this.scanInterval = scanInterval; + return this; + } + + + +} diff --git a/src/main/java/org/redisson/MasterSlaveServersConfig.java b/src/main/java/org/redisson/MasterSlaveServersConfig.java index 171aa181c..0c92a439e 100644 --- a/src/main/java/org/redisson/MasterSlaveServersConfig.java +++ b/src/main/java/org/redisson/MasterSlaveServersConfig.java @@ -71,7 +71,10 @@ public class MasterSlaveServersConfig extends BaseMasterSlaveServersConfig getSlaveAddresses() { return slaveAddresses; } diff --git a/src/main/java/org/redisson/Redisson.java b/src/main/java/org/redisson/Redisson.java index 7b78ac885..c6f9e3a75 100755 --- a/src/main/java/org/redisson/Redisson.java +++ b/src/main/java/org/redisson/Redisson.java @@ -25,6 +25,7 @@ import java.util.concurrent.atomic.AtomicLong; import org.redisson.client.protocol.RedisCommands; import org.redisson.connection.ClusterConnectionManager; import org.redisson.connection.ConnectionManager; +import org.redisson.connection.ElasticacheReplicationGroupConnectionManager; import org.redisson.connection.MasterSlaveConnectionManager; import org.redisson.connection.SentinelConnectionManager; import org.redisson.connection.SingleConnectionManager; @@ -78,6 +79,8 @@ public class Redisson implements RedissonClient { connectionManager = new SentinelConnectionManager(configCopy.getSentinelServersConfig(), configCopy); } else if (configCopy.getClusterServersConfig() != null) { connectionManager = new ClusterConnectionManager(configCopy.getClusterServersConfig(), configCopy); + } else if (configCopy.getElasticacheReplicationGroupServersConfig() != null) { + connectionManager = new ElasticacheReplicationGroupConnectionManager(configCopy.getElasticacheReplicationGroupServersConfig(), configCopy); } else { throw new IllegalArgumentException("server(s) address(es) not defined!"); } diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index a3f4e6c17..788c15194 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -173,4 +173,5 @@ public interface RedisCommands { RedisStrictCommand> SENTINEL_GET_MASTER_ADDR_BY_NAME = new RedisStrictCommand>("SENTINEL", "GET-MASTER-ADDR-BY-NAME", new StringListReplayDecoder()); RedisStrictCommand>> SENTINEL_SLAVES = new RedisStrictCommand>>("SENTINEL", "SLAVES", new StringMapReplayDecoder()); + RedisStrictCommand INFO_REPLICATION = new RedisStrictCommand("INFO", "replication", new StringDataDecoder()); } diff --git a/src/main/java/org/redisson/connection/ElasticacheReplicationGroupConnectionManager.java b/src/main/java/org/redisson/connection/ElasticacheReplicationGroupConnectionManager.java new file mode 100644 index 000000000..627da607f --- /dev/null +++ b/src/main/java/org/redisson/connection/ElasticacheReplicationGroupConnectionManager.java @@ -0,0 +1,164 @@ +/** + * 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 java.net.URI; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.redisson.Config; +import org.redisson.ElasticacheReplicationGroupServersConfig; +import org.redisson.MasterSlaveServersConfig; +import org.redisson.client.RedisClient; +import org.redisson.client.RedisConnection; +import org.redisson.client.RedisConnectionException; +import org.redisson.client.protocol.RedisCommands; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.netty.util.concurrent.GlobalEventExecutor; +import io.netty.util.concurrent.ScheduledFuture; + +public class ElasticacheReplicationGroupConnectionManager extends MasterSlaveConnectionManager { + + private static final String ROLE_KEY = "role:"; + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private AtomicReference currentMaster = new AtomicReference(); + + private final Map nodeConnections = new HashMap(); + + private ScheduledFuture monitorFuture; + + private enum Role { + master, + slave + } + + public ElasticacheReplicationGroupConnectionManager(ElasticacheReplicationGroupServersConfig cfg, Config config) { + init(config); + + this.config = create(cfg); + + for (URI addr : cfg.getNodeAddresses()) { + RedisConnection connection = connect(cfg, addr); + if (connection == null) { + continue; + } + + Role role = determineRole(connection.sync(RedisCommands.INFO_REPLICATION)); + if (Role.master.equals(role)) { + currentMaster.set(addr); + log.info("{} is the master", addr); + this.config.setMasterAddress(addr); + } else { + this.config.addSlaveAddress(addr); + } + } + init(this.config); + + monitorRoleChange(cfg); + } + + private RedisConnection connect(ElasticacheReplicationGroupServersConfig cfg, URI addr) { + RedisConnection connection = nodeConnections.get(addr); + if (connection != null) { + return connection; + } + RedisClient client = createClient(addr.getHost(), addr.getPort(), cfg.getTimeout()); + try { + connection = client.connect(); + nodeConnections.put(addr, connection); + } catch (RedisConnectionException e) { + log.warn(e.getMessage(), e); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return connection; + } + + private void monitorRoleChange(final ElasticacheReplicationGroupServersConfig cfg) { + monitorFuture = GlobalEventExecutor.INSTANCE.scheduleWithFixedDelay(new Runnable() { + @Override + public void run() { + try { + URI master = currentMaster.get(); + for (URI addr : cfg.getNodeAddresses()) { + RedisConnection connection = connect(cfg, addr); + String replInfo = connection.sync(RedisCommands.INFO_REPLICATION); + log.trace("{} repl info: {}", addr, replInfo); + + Role role = determineRole(replInfo); + log.debug("Current master: {} / node {} is {}", master, addr, role); + + if (Role.master.equals(role) && master.equals(addr)) { + log.debug("Current master {} unchanged", master); + } else if (Role.master.equals(role) && !master.equals(addr) && currentMaster.compareAndSet(master, addr)) { + log.info("Master has changed from {} to {}", master, addr); + changeMaster(MAX_SLOT, addr.getHost(), addr.getPort()); + slaveDown(MAX_SLOT, master.getHost(), master.getPort()); + break; + } + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + + }, cfg.getScanInterval(), cfg.getScanInterval(), TimeUnit.MILLISECONDS); + } + + private Role determineRole(String data) { + String[] lines = data.split("\\r\\n"); + for (String s : lines) { + if (s.startsWith(ROLE_KEY)) { + return Role.valueOf(s.substring(ROLE_KEY.length())); + } + } + throw new RuntimeException("Cannot determine role from " + data); + } + + private MasterSlaveServersConfig create(ElasticacheReplicationGroupServersConfig cfg) { + MasterSlaveServersConfig c = new MasterSlaveServersConfig(); + c.setRetryInterval(cfg.getRetryInterval()); + c.setRetryAttempts(cfg.getRetryAttempts()); + c.setTimeout(cfg.getTimeout()); + c.setPingTimeout(cfg.getPingTimeout()); + c.setLoadBalancer(cfg.getLoadBalancer()); + c.setPassword(cfg.getPassword()); + c.setDatabase(cfg.getDatabase()); + c.setClientName(cfg.getClientName()); + c.setMasterConnectionPoolSize(cfg.getMasterConnectionPoolSize()); + c.setSlaveConnectionPoolSize(cfg.getSlaveConnectionPoolSize()); + c.setSlaveSubscriptionConnectionPoolSize(cfg.getSlaveSubscriptionConnectionPoolSize()); + c.setSubscriptionsPerConnection(cfg.getSubscriptionsPerConnection()); + return c; + } + + @Override + public void shutdown() { + monitorFuture.cancel(true); + super.shutdown(); + + for (RedisConnection connection : nodeConnections.values()) { + connection.getRedisClient().shutdown(); + } + } +} + From 7e31aad67a1e08486fbd0f9e79d1364fd426480d Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 11 Sep 2015 19:34:49 +0300 Subject: [PATCH 06/21] closeConnectionAfterFailAttempts. #198 --- src/main/java/org/redisson/BaseConfig.java | 36 ++++++++++++++----- .../org/redisson/client/RedisConnection.java | 13 +++++++ .../redisson/connection/BaseLoadBalancer.java | 2 +- .../connection/ClusterConnectionManager.java | 1 + .../MasterSlaveConnectionManager.java | 12 +++++++ .../redisson/connection/MasterSlaveEntry.java | 4 +++ .../connection/SentinelConnectionManager.java | 1 + .../connection/SingleConnectionManager.java | 7 ++-- 8 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/redisson/BaseConfig.java b/src/main/java/org/redisson/BaseConfig.java index 71b403999..6aac92a5d 100644 --- a/src/main/java/org/redisson/BaseConfig.java +++ b/src/main/java/org/redisson/BaseConfig.java @@ -35,6 +35,8 @@ class BaseConfig> { private int retryInterval = 1000; + private int closeConnectionAfterFailAttempts = -1; + /** * Database index used for Redis connection */ @@ -67,6 +69,7 @@ class BaseConfig> { setTimeout(config.getTimeout()); setClientName(config.getClientName()); setPingTimeout(config.getPingTimeout()); + setCloseConnectionAfterFailAttempts(config.getCloseConnectionAfterFailAttempts()); } /** @@ -159,30 +162,45 @@ class BaseConfig> { } /** - * Name for client connection + * Setup connection name during connection init + * via CLIENT SETNAME command + * + * @param name */ - public String getClientName() { - return clientName; - } - public T setClientName(String clientName) { this.clientName = clientName; return (T) this; } + public String getClientName() { + return clientName; + } + /** * Ping timeout used in Node.ping and Node.pingAll operation * - * @return + * @param ping timeout in milliseconds */ - public int getPingTimeout() { - return pingTimeout; - } public T setPingTimeout(int pingTimeout) { this.pingTimeout = pingTimeout; return (T) this; } + public int getPingTimeout() { + return pingTimeout; + } + /** + * Close connection if it has failAttemptsAmount + * fails in a row during command sending. Turned off by default. + * + * @param failAttemptsAmount + */ + public void setCloseConnectionAfterFailAttempts(int failAttemptsAmount) { + this.closeConnectionAfterFailAttempts = failAttemptsAmount; + } + public int getCloseConnectionAfterFailAttempts() { + return closeConnectionAfterFailAttempts; + } } diff --git a/src/main/java/org/redisson/client/RedisConnection.java b/src/main/java/org/redisson/client/RedisConnection.java index 05fa233d1..837745e18 100644 --- a/src/main/java/org/redisson/client/RedisConnection.java +++ b/src/main/java/org/redisson/client/RedisConnection.java @@ -39,6 +39,7 @@ public class RedisConnection implements RedisCommands { private volatile boolean closed; volatile Channel channel; private ReconnectListener reconnectListener; + private int failAttempts; public RedisConnection(RedisClient redisClient, Channel channel) { super(); @@ -55,6 +56,18 @@ public class RedisConnection implements RedisCommands { return reconnectListener; } + public void resetFailAttempt() { + failAttempts = 0; + } + + public void incFailAttempt() { + failAttempts++; + } + + public int getFailAttempts() { + return failAttempts; + } + public void updateChannel(Channel channel) { this.channel = channel; channel.attr(CONNECTION).set(this); diff --git a/src/main/java/org/redisson/connection/BaseLoadBalancer.java b/src/main/java/org/redisson/connection/BaseLoadBalancer.java index 20976ea83..11d3d89f2 100644 --- a/src/main/java/org/redisson/connection/BaseLoadBalancer.java +++ b/src/main/java/org/redisson/connection/BaseLoadBalancer.java @@ -225,7 +225,7 @@ abstract class BaseLoadBalancer implements LoadBalancer { public void returnConnection(RedisConnection connection) { SubscribesConnectionEntry entry = clients.get(connection.getRedisClient()); - if (entry.isFreezed()) { + if (entry.isFreezed() || connection.getFailAttempts() == config.getCloseConnectionAfterFailAttempts()) { connection.closeAsync(); } else { entry.getConnections().add(connection); diff --git a/src/main/java/org/redisson/connection/ClusterConnectionManager.java b/src/main/java/org/redisson/connection/ClusterConnectionManager.java index 9b9929d8c..a1fe7edb3 100644 --- a/src/main/java/org/redisson/connection/ClusterConnectionManager.java +++ b/src/main/java/org/redisson/connection/ClusterConnectionManager.java @@ -244,6 +244,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { c.setPassword(cfg.getPassword()); c.setDatabase(cfg.getDatabase()); c.setClientName(cfg.getClientName()); + c.setCloseConnectionAfterFailAttempts(cfg.getCloseConnectionAfterFailAttempts()); c.setMasterConnectionPoolSize(cfg.getMasterConnectionPoolSize()); c.setSlaveConnectionPoolSize(cfg.getSlaveConnectionPoolSize()); c.setSlaveSubscriptionConnectionPoolSize(cfg.getSlaveSubscriptionConnectionPoolSize()); diff --git a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java index 08f7928ee..b64de1e78 100644 --- a/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java +++ b/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java @@ -166,6 +166,12 @@ public class MasterSlaveConnectionManager implements ConnectionManager { return new FutureListener() { @Override public void operationComplete(io.netty.util.concurrent.Future future) throws Exception { + if (!future.isSuccess()) { + conn.incFailAttempt(); + } else { + conn.resetFailAttempt(); + } + shutdownLatch.release(); timeout.cancel(); releaseWrite(slot, conn); @@ -179,6 +185,12 @@ public class MasterSlaveConnectionManager implements ConnectionManager { return new FutureListener() { @Override public void operationComplete(io.netty.util.concurrent.Future future) throws Exception { + if (!future.isSuccess()) { + conn.incFailAttempt(); + } else { + conn.resetFailAttempt(); + } + shutdownLatch.release(); timeout.cancel(); releaseRead(slot, conn); diff --git a/src/main/java/org/redisson/connection/MasterSlaveEntry.java b/src/main/java/org/redisson/connection/MasterSlaveEntry.java index dde924220..097cbe20f 100644 --- a/src/main/java/org/redisson/connection/MasterSlaveEntry.java +++ b/src/main/java/org/redisson/connection/MasterSlaveEntry.java @@ -175,6 +175,10 @@ public class MasterSlaveEntry { if (!entry.getClient().equals(connection.getRedisClient())) { connection.closeAsync(); return; + } else if (connection.getFailAttempts() == config.getCloseConnectionAfterFailAttempts()) { + connection.closeAsync(); + entry.getConnectionsSemaphore().release(); + return; } entry.getConnections().add(connection); diff --git a/src/main/java/org/redisson/connection/SentinelConnectionManager.java b/src/main/java/org/redisson/connection/SentinelConnectionManager.java index 03ae9122c..eb78e4572 100755 --- a/src/main/java/org/redisson/connection/SentinelConnectionManager.java +++ b/src/main/java/org/redisson/connection/SentinelConnectionManager.java @@ -61,6 +61,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager { c.setPassword(cfg.getPassword()); c.setDatabase(cfg.getDatabase()); c.setClientName(cfg.getClientName()); + c.setCloseConnectionAfterFailAttempts(cfg.getCloseConnectionAfterFailAttempts()); c.setMasterConnectionPoolSize(cfg.getMasterConnectionPoolSize()); c.setSlaveConnectionPoolSize(cfg.getSlaveConnectionPoolSize()); c.setSlaveSubscriptionConnectionPoolSize(cfg.getSlaveSubscriptionConnectionPoolSize()); diff --git a/src/main/java/org/redisson/connection/SingleConnectionManager.java b/src/main/java/org/redisson/connection/SingleConnectionManager.java index f267fc41c..18baafdf5 100644 --- a/src/main/java/org/redisson/connection/SingleConnectionManager.java +++ b/src/main/java/org/redisson/connection/SingleConnectionManager.java @@ -37,7 +37,7 @@ public class SingleConnectionManager extends MasterSlaveConnectionManager { private final AtomicReference currentMaster = new AtomicReference(); private ScheduledFuture monitorFuture; - + public SingleConnectionManager(SingleServerConfig cfg, Config config) { MasterSlaveServersConfig newconfig = new MasterSlaveServersConfig(); String addr = cfg.getAddress().getHost() + ":" + cfg.getAddress().getPort(); @@ -48,13 +48,14 @@ public class SingleConnectionManager extends MasterSlaveConnectionManager { newconfig.setPassword(cfg.getPassword()); newconfig.setDatabase(cfg.getDatabase()); newconfig.setClientName(cfg.getClientName()); + newconfig.setCloseConnectionAfterFailAttempts(cfg.getCloseConnectionAfterFailAttempts()); newconfig.setMasterAddress(addr); newconfig.setMasterConnectionPoolSize(cfg.getConnectionPoolSize()); newconfig.setSubscriptionsPerConnection(cfg.getSubscriptionsPerConnection()); newconfig.setSlaveSubscriptionConnectionPoolSize(cfg.getSubscriptionConnectionPoolSize()); init(newconfig, config); - + if (cfg.isDnsMonitoring()) { try { this.currentMaster.set(InetAddress.getByName(cfg.getAddress().getHost())); @@ -71,7 +72,7 @@ public class SingleConnectionManager extends MasterSlaveConnectionManager { SingleEntry entry = new SingleEntry(0, MAX_SLOT, this, config); entries.put(MAX_SLOT, entry); } - + private void monitorDnsChange(final SingleServerConfig cfg) { monitorFuture = GlobalEventExecutor.INSTANCE.scheduleWithFixedDelay(new Runnable() { @Override From ff6d92d2987945e4b0d8c1c59f1ec2146d9e0508 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 11 Sep 2015 19:40:14 +0300 Subject: [PATCH 07/21] minor fix --- src/main/java/org/redisson/BaseConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/redisson/BaseConfig.java b/src/main/java/org/redisson/BaseConfig.java index 6aac92a5d..4f506b34b 100644 --- a/src/main/java/org/redisson/BaseConfig.java +++ b/src/main/java/org/redisson/BaseConfig.java @@ -195,8 +195,9 @@ class BaseConfig> { * * @param failAttemptsAmount */ - public void setCloseConnectionAfterFailAttempts(int failAttemptsAmount) { + public T setCloseConnectionAfterFailAttempts(int failAttemptsAmount) { this.closeConnectionAfterFailAttempts = failAttemptsAmount; + return (T) this; } public int getCloseConnectionAfterFailAttempts() { return closeConnectionAfterFailAttempts; From 0715a5489c799df85227276a40e3be22247e3356 Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 11 Sep 2015 19:40:41 +0300 Subject: [PATCH 08/21] RBatch.getScoredSortedSet added --- src/main/java/org/redisson/RedissonBatch.java | 6 ++++++ src/main/java/org/redisson/core/RBatch.java | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/main/java/org/redisson/RedissonBatch.java b/src/main/java/org/redisson/RedissonBatch.java index 2703f7600..8e658375a 100644 --- a/src/main/java/org/redisson/RedissonBatch.java +++ b/src/main/java/org/redisson/RedissonBatch.java @@ -28,6 +28,7 @@ import org.redisson.core.RKeysAsync; import org.redisson.core.RListAsync; import org.redisson.core.RMapAsync; import org.redisson.core.RQueueAsync; +import org.redisson.core.RScoredSortedSet; import org.redisson.core.RScriptAsync; import org.redisson.core.RSetAsync; import org.redisson.core.RTopicAsync; @@ -92,6 +93,11 @@ public class RedissonBatch implements RBatch { return new RedissonAtomicLong(executorService, name); } + @Override + public RScoredSortedSet getScoredSortedSet(String name) { + return new RedissonScoredSortedSet(executorService, name); + } + @Override public RScriptAsync getScript() { return new RedissonScript(executorService); diff --git a/src/main/java/org/redisson/core/RBatch.java b/src/main/java/org/redisson/core/RBatch.java index 31c895e98..eccd1088b 100644 --- a/src/main/java/org/redisson/core/RBatch.java +++ b/src/main/java/org/redisson/core/RBatch.java @@ -113,6 +113,8 @@ public interface RBatch { */ RAtomicLongAsync getAtomicLongAsync(String name); + RScoredSortedSet getScoredSortedSet(String name); + /** * Returns script operations object * From 664e8904f7efe0c833d937528e8dd19f507f28dd Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 11 Sep 2015 21:17:11 +0300 Subject: [PATCH 09/21] minor update --- src/main/java/org/redisson/client/RedisConnection.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/redisson/client/RedisConnection.java b/src/main/java/org/redisson/client/RedisConnection.java index 837745e18..3526fdbc3 100644 --- a/src/main/java/org/redisson/client/RedisConnection.java +++ b/src/main/java/org/redisson/client/RedisConnection.java @@ -71,6 +71,7 @@ public class RedisConnection implements RedisCommands { public void updateChannel(Channel channel) { this.channel = channel; channel.attr(CONNECTION).set(this); + resetFailAttempt(); } public RedisClient getRedisClient() { From ad24fed86bf1e24d9952539dd3c9ca289b3ecd4e Mon Sep 17 00:00:00 2001 From: Steve Ungerer Date: Fri, 11 Sep 2015 14:39:52 -0400 Subject: [PATCH 10/21] no need to invoke slaveDown(). also some non-functional cleanup --- ...acheReplicationGroupConnectionManager.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/redisson/connection/ElasticacheReplicationGroupConnectionManager.java b/src/main/java/org/redisson/connection/ElasticacheReplicationGroupConnectionManager.java index 627da607f..aedfec7f4 100644 --- a/src/main/java/org/redisson/connection/ElasticacheReplicationGroupConnectionManager.java +++ b/src/main/java/org/redisson/connection/ElasticacheReplicationGroupConnectionManager.java @@ -27,6 +27,7 @@ import org.redisson.MasterSlaveServersConfig; import org.redisson.client.RedisClient; import org.redisson.client.RedisConnection; import org.redisson.client.RedisConnectionException; +import org.redisson.client.RedisException; import org.redisson.client.protocol.RedisCommands; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,6 +35,13 @@ import org.slf4j.LoggerFactory; import io.netty.util.concurrent.GlobalEventExecutor; import io.netty.util.concurrent.ScheduledFuture; +/** + * {@link ConnectionManager} for AWS ElastiCache Replication Groups. By providing all nodes + * of the replication group to this manager, the role of each node can be polled to determine + * if a failover has occurred resulting in a new master. + * + * @author Steve Ungerer + */ public class ElasticacheReplicationGroupConnectionManager extends MasterSlaveConnectionManager { private static final String ROLE_KEY = "role:"; @@ -64,10 +72,14 @@ public class ElasticacheReplicationGroupConnectionManager extends MasterSlaveCon Role role = determineRole(connection.sync(RedisCommands.INFO_REPLICATION)); if (Role.master.equals(role)) { + if (currentMaster.get() != null) { + throw new RedisException("Multiple masters detected"); + } currentMaster.set(addr); log.info("{} is the master", addr); this.config.setMasterAddress(addr); } else { + log.info("{} is a slave", addr); this.config.addSlaveAddress(addr); } } @@ -99,20 +111,20 @@ public class ElasticacheReplicationGroupConnectionManager extends MasterSlaveCon public void run() { try { URI master = currentMaster.get(); + log.debug("Current master: {}", master); for (URI addr : cfg.getNodeAddresses()) { RedisConnection connection = connect(cfg, addr); String replInfo = connection.sync(RedisCommands.INFO_REPLICATION); log.trace("{} repl info: {}", addr, replInfo); Role role = determineRole(replInfo); - log.debug("Current master: {} / node {} is {}", master, addr, role); + log.debug("node {} is {}", addr, role); if (Role.master.equals(role) && master.equals(addr)) { log.debug("Current master {} unchanged", master); } else if (Role.master.equals(role) && !master.equals(addr) && currentMaster.compareAndSet(master, addr)) { log.info("Master has changed from {} to {}", master, addr); changeMaster(MAX_SLOT, addr.getHost(), addr.getPort()); - slaveDown(MAX_SLOT, master.getHost(), master.getPort()); break; } } @@ -125,13 +137,12 @@ public class ElasticacheReplicationGroupConnectionManager extends MasterSlaveCon } private Role determineRole(String data) { - String[] lines = data.split("\\r\\n"); - for (String s : lines) { + for (String s : data.split("\\r\\n")) { if (s.startsWith(ROLE_KEY)) { return Role.valueOf(s.substring(ROLE_KEY.length())); } } - throw new RuntimeException("Cannot determine role from " + data); + throw new RedisException("Cannot determine node role from provided 'INFO replication' data"); } private MasterSlaveServersConfig create(ElasticacheReplicationGroupServersConfig cfg) { From 803ca4434815e46e5e70b760b1cd787696469fff Mon Sep 17 00:00:00 2001 From: Nikita Date: Sat, 12 Sep 2015 19:12:18 +0300 Subject: [PATCH 11/21] RScoredSortedSet rank and lexCount functions added. #143 --- .../org/redisson/RedissonScoredSortedSet.java | 47 +++++++++++++++++-- .../client/protocol/RedisCommands.java | 3 +- .../protocol/{decoder => }/ScoredEntry.java | 2 +- .../decoder/ScoredSortedSetReplayDecoder.java | 1 + .../org/redisson/core/RScoredSortedSet.java | 8 +++- .../redisson/core/RScoredSortedSetAsync.java | 8 +++- .../redisson/RedissonScoredSortedSetTest.java | 37 +++++++++++++-- 7 files changed, 90 insertions(+), 16 deletions(-) rename src/main/java/org/redisson/client/protocol/{decoder => }/ScoredEntry.java (97%) diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index bba0dfd73..8c51b17b5 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -26,9 +26,9 @@ import org.redisson.client.RedisClient; 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.convertor.BooleanReplayConvertor; import org.redisson.client.protocol.decoder.ListScanResult; -import org.redisson.client.protocol.decoder.ScoredEntry; import org.redisson.core.RScoredSortedSet; import io.netty.util.concurrent.Future; @@ -46,6 +46,10 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future addAsync(double score, V object) { + // String should be encoded as String to let lex functions work properly + if (object instanceof String) { + return commandExecutor.writeAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZADD, getName(), BigDecimal.valueOf(score).toPlainString(), object); + } return commandExecutor.writeAsync(getName(), RedisCommands.ZADD, getName(), BigDecimal.valueOf(score).toPlainString(), object); } @@ -90,15 +94,25 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc } @Override - public Double getScore(Object o) { + public Double getScore(V o) { return get(getScoreAsync(o)); } @Override - public Future getScoreAsync(Object o) { + public Future getScoreAsync(V o) { return commandExecutor.readAsync(getName(), RedisCommands.ZSCORE, getName(), o); } + @Override + public Integer rank(V o) { + return get(rankAsync(o)); + } + + @Override + public Future rankAsync(V o) { + return commandExecutor.readAsync(getName(), RedisCommands.ZRANK, getName(), o); + } + private ListScanResult scanIterator(RedisClient client, long startPos) { return commandExecutor.read(client, getName(), RedisCommands.ZSCAN, getName(), startPos); } @@ -259,9 +273,32 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc } @Override - public Future>> entryRangeAsync(int startIndex, - int endIndex) { + public Future>> entryRangeAsync(int startIndex, int endIndex) { return commandExecutor.readAsync(getName(), RedisCommands.ZRANGE_ENTRY, getName(), startIndex, endIndex, "WITHSCORES"); } + @Override + public Integer lexCount(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { + return get(lexCountAsync(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public Future lexCountAsync(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { + String fromValue = fromElement.toString(); + if (fromInclusive) { + fromValue = "[" + fromValue; + } else { + fromValue = "(" + fromValue; + } + + String toValue = toElement.toString(); + if (toInclusive) { + toValue = "[" + toValue; + } else { + toValue = "(" + toValue; + } + + return commandExecutor.readAsync(getName(), RedisCommands.ZLEXCOUNT, getName(), fromValue, toValue); + } + } diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index a3f4e6c17..4d9000830 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -37,7 +37,6 @@ import org.redisson.client.protocol.decoder.NestedMultiDecoder; import org.redisson.client.protocol.decoder.ObjectListReplayDecoder; import org.redisson.client.protocol.decoder.ObjectMapReplayDecoder; import org.redisson.client.protocol.decoder.ObjectSetReplayDecoder; -import org.redisson.client.protocol.decoder.ScoredEntry; import org.redisson.client.protocol.decoder.ScoredSortedSetReplayDecoder; import org.redisson.client.protocol.decoder.ScoredSortedSetScanReplayDecoder; import org.redisson.client.protocol.decoder.StringDataDecoder; @@ -52,8 +51,10 @@ public interface RedisCommands { RedisCommand ZADD = new RedisCommand("ZADD", new BooleanAmountReplayConvertor(), 3); RedisCommand ZREM = new RedisCommand("ZREM", new BooleanAmountReplayConvertor(), 2); RedisStrictCommand ZCARD = new RedisStrictCommand("ZCARD", new IntegerReplayConvertor()); + RedisStrictCommand ZLEXCOUNT = new RedisStrictCommand("ZLEXCOUNT", new IntegerReplayConvertor()); RedisCommand ZSCORE_CONTAINS = new RedisCommand("ZSCORE", new BooleanNotNullReplayConvertor(), 2); RedisStrictCommand ZSCORE = new RedisStrictCommand("ZSCORE", new DoubleReplayConvertor()); + RedisStrictCommand ZRANK = new RedisStrictCommand("ZRANK", new IntegerReplayConvertor()); RedisCommand> ZRANGE = new RedisCommand>("ZRANGE", new ObjectListReplayDecoder()); RedisCommand>> ZRANGE_ENTRY = new RedisCommand>>("ZRANGE", new ScoredSortedSetReplayDecoder()); RedisCommand> ZSCAN = new RedisCommand>("ZSCAN", new NestedMultiDecoder(new ObjectListReplayDecoder(), new ScoredSortedSetScanReplayDecoder()), ValueType.OBJECT); diff --git a/src/main/java/org/redisson/client/protocol/decoder/ScoredEntry.java b/src/main/java/org/redisson/client/protocol/ScoredEntry.java similarity index 97% rename from src/main/java/org/redisson/client/protocol/decoder/ScoredEntry.java rename to src/main/java/org/redisson/client/protocol/ScoredEntry.java index 398fc799b..9bbd56801 100644 --- a/src/main/java/org/redisson/client/protocol/decoder/ScoredEntry.java +++ b/src/main/java/org/redisson/client/protocol/ScoredEntry.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.redisson.client.protocol.decoder; +package org.redisson.client.protocol; public class ScoredEntry { diff --git a/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetReplayDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetReplayDecoder.java index bfa322082..5a77210ce 100644 --- a/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetReplayDecoder.java +++ b/src/main/java/org/redisson/client/protocol/decoder/ScoredSortedSetReplayDecoder.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; import org.redisson.client.handler.State; +import org.redisson.client.protocol.ScoredEntry; import io.netty.buffer.ByteBuf; diff --git a/src/main/java/org/redisson/core/RScoredSortedSet.java b/src/main/java/org/redisson/core/RScoredSortedSet.java index 5d55e4902..12bb19aef 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSet.java +++ b/src/main/java/org/redisson/core/RScoredSortedSet.java @@ -17,11 +17,15 @@ package org.redisson.core; import java.util.Collection; -import org.redisson.client.protocol.decoder.ScoredEntry; +import org.redisson.client.protocol.ScoredEntry; public interface RScoredSortedSet extends RScoredSortedSetAsync, Iterable, RExpirable { - Double getScore(Object o); + Integer lexCount(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive); + + Integer rank(V o); + + Double getScore(V o); boolean add(double score, V object); diff --git a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java index ef89c3551..8802fa45f 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java +++ b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java @@ -17,13 +17,17 @@ package org.redisson.core; import java.util.Collection; -import org.redisson.client.protocol.decoder.ScoredEntry; +import org.redisson.client.protocol.ScoredEntry; import io.netty.util.concurrent.Future; public interface RScoredSortedSetAsync extends RExpirableAsync { - Future getScoreAsync(Object o); + Future lexCountAsync(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive); + + Future rankAsync(V o); + + Future getScoreAsync(V o); Future addAsync(double score, V object); diff --git a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index 4af77772d..f64096ff5 100644 --- a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -1,10 +1,7 @@ package org.redisson; -import io.netty.util.concurrent.Future; - import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Set; @@ -16,13 +13,43 @@ import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; -import org.redisson.client.protocol.decoder.ScoredEntry; -import org.redisson.core.RMap; +import org.redisson.client.protocol.ScoredEntry; import org.redisson.core.RScoredSortedSet; import org.redisson.core.RSortedSet; +import io.netty.util.concurrent.Future; + public class RedissonScoredSortedSetTest extends BaseTest { + @Test + public void testRank() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(0.1, "a"); + set.add(0.2, "b"); + set.add(0.3, "c"); + set.add(0.4, "d"); + set.add(0.5, "e"); + set.add(0.6, "f"); + set.add(0.7, "g"); + + Assert.assertEquals(3, (int)set.rank("d")); + } + + @Test + public void testLexCount() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(0, "a"); + set.add(0, "b"); + set.add(0, "c"); + set.add(0, "d"); + set.add(0, "e"); + set.add(0, "f"); + set.add(0, "g"); + + Assert.assertEquals(5, (int)set.lexCount("b", true, "f", true)); + Assert.assertEquals(3, (int)set.lexCount("b", false, "f", false)); + } + @Test public void testAddAsync() throws InterruptedException, ExecutionException { RScoredSortedSet set = redisson.getScoredSortedSet("simple"); From 48771521905b4e3f2ac95e628e4bc18832dbd8d8 Mon Sep 17 00:00:00 2001 From: Nikita Date: Sat, 12 Sep 2015 19:28:56 +0300 Subject: [PATCH 12/21] lexRange, lexRangeHead and lexRangeTail methods added. #135 --- .../org/redisson/RedissonScoredSortedSet.java | 53 +++++++++++++++---- .../client/protocol/RedisCommands.java | 1 + .../org/redisson/core/RScoredSortedSet.java | 6 +++ .../redisson/core/RScoredSortedSetAsync.java | 6 +++ .../redisson/RedissonScoredSortedSetTest.java | 46 ++++++++++++++++ 5 files changed, 103 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index 8c51b17b5..92ef84920 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -277,6 +277,42 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc return commandExecutor.readAsync(getName(), RedisCommands.ZRANGE_ENTRY, getName(), startIndex, endIndex, "WITHSCORES"); } + @Override + public Collection lexRange(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { + return get(lexRangeAsync(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public Collection lexRangeHead(V toElement, boolean toInclusive) { + return get(lexRangeHeadAsync(toElement, toInclusive)); + } + + @Override + public Future> lexRangeHeadAsync(V toElement, boolean toInclusive) { + String toValue = value(toElement, toInclusive); + return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZRANGEBYLEX, getName(), "-", toValue); + } + + @Override + public Collection lexRangeTail(V fromElement, boolean fromInclusive) { + return get(lexRangeTailAsync(fromElement, fromInclusive)); + } + + @Override + public Future> lexRangeTailAsync(V fromElement, boolean fromInclusive) { + String fromValue = value(fromElement, fromInclusive); + return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZRANGEBYLEX, getName(), fromValue, "+"); + } + + + @Override + public Future> lexRangeAsync(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { + String fromValue = value(fromElement, fromInclusive); + String toValue = value(toElement, toInclusive); + + return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZRANGEBYLEX, getName(), fromValue, toValue); + } + @Override public Integer lexCount(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { return get(lexCountAsync(fromElement, fromInclusive, toElement, toInclusive)); @@ -284,21 +320,20 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future lexCountAsync(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { + String fromValue = value(fromElement, fromInclusive); + String toValue = value(toElement, toInclusive); + + return commandExecutor.readAsync(getName(), RedisCommands.ZLEXCOUNT, getName(), fromValue, toValue); + } + + private String value(V fromElement, boolean fromInclusive) { String fromValue = fromElement.toString(); if (fromInclusive) { fromValue = "[" + fromValue; } else { fromValue = "(" + fromValue; } - - String toValue = toElement.toString(); - if (toInclusive) { - toValue = "[" + toValue; - } else { - toValue = "(" + toValue; - } - - return commandExecutor.readAsync(getName(), RedisCommands.ZLEXCOUNT, getName(), fromValue, toValue); + return fromValue; } } diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index 4d9000830..0fc389ce5 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -56,6 +56,7 @@ public interface RedisCommands { RedisStrictCommand ZSCORE = new RedisStrictCommand("ZSCORE", new DoubleReplayConvertor()); RedisStrictCommand ZRANK = new RedisStrictCommand("ZRANK", new IntegerReplayConvertor()); RedisCommand> ZRANGE = new RedisCommand>("ZRANGE", new ObjectListReplayDecoder()); + RedisCommand> ZRANGEBYLEX = new RedisCommand>("ZRANGEBYLEX", new ObjectListReplayDecoder()); RedisCommand>> ZRANGE_ENTRY = new RedisCommand>>("ZRANGE", new ScoredSortedSetReplayDecoder()); RedisCommand> ZSCAN = new RedisCommand>("ZSCAN", new NestedMultiDecoder(new ObjectListReplayDecoder(), new ScoredSortedSetScanReplayDecoder()), ValueType.OBJECT); RedisStrictCommand ZINCRBY = new RedisStrictCommand("ZINCRBY", new DoubleReplayConvertor()); diff --git a/src/main/java/org/redisson/core/RScoredSortedSet.java b/src/main/java/org/redisson/core/RScoredSortedSet.java index 12bb19aef..0bd11216f 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSet.java +++ b/src/main/java/org/redisson/core/RScoredSortedSet.java @@ -21,6 +21,12 @@ import org.redisson.client.protocol.ScoredEntry; public interface RScoredSortedSet extends RScoredSortedSetAsync, Iterable, RExpirable { + Collection lexRangeTail(V fromElement, boolean fromInclusive); + + Collection lexRangeHead(V toElement, boolean toInclusive); + + Collection lexRange(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive); + Integer lexCount(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive); Integer rank(V o); diff --git a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java index 8802fa45f..5e9b7f013 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java +++ b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java @@ -23,6 +23,12 @@ import io.netty.util.concurrent.Future; public interface RScoredSortedSetAsync extends RExpirableAsync { + Future> lexRangeTailAsync(V fromElement, boolean fromInclusive); + + Future> lexRangeHeadAsync(V toElement, boolean toInclusive); + + Future> lexRangeAsync(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive); + Future lexCountAsync(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive); Future rankAsync(V o); diff --git a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index f64096ff5..33319a425 100644 --- a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -21,6 +21,52 @@ import io.netty.util.concurrent.Future; public class RedissonScoredSortedSetTest extends BaseTest { + @Test + public void testLexRangeTail() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(0, "a"); + set.add(0, "b"); + set.add(0, "c"); + set.add(0, "d"); + set.add(0, "e"); + set.add(0, "f"); + set.add(0, "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")); + } + + + @Test + public void testLexRangeHead() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(0, "a"); + set.add(0, "b"); + set.add(0, "c"); + set.add(0, "d"); + set.add(0, "e"); + set.add(0, "f"); + set.add(0, "g"); + + MatcherAssert.assertThat(set.lexRangeHead("c", false), Matchers.contains("a", "b")); + MatcherAssert.assertThat(set.lexRangeHead("c", true), Matchers.contains("a", "b", "c")); + } + + + @Test + public void testLexRange() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(0, "a"); + set.add(0, "b"); + set.add(0, "c"); + set.add(0, "d"); + set.add(0, "e"); + set.add(0, "f"); + set.add(0, "g"); + + MatcherAssert.assertThat(set.lexRange("aaa", true, "g", false), Matchers.contains("b", "c", "d", "e", "f")); + } + @Test public void testRank() { RScoredSortedSet set = redisson.getScoredSortedSet("simple"); From 3ab7e1afc8953f39a75e38dd81fce99f735a2dce Mon Sep 17 00:00:00 2001 From: Nikita Date: Sat, 12 Sep 2015 19:49:12 +0300 Subject: [PATCH 13/21] lexCountTail & lexCountHead methods added. #143 --- .../org/redisson/RedissonScoredSortedSet.java | 24 +++++++++++++++++++ .../org/redisson/core/RScoredSortedSet.java | 4 ++++ .../redisson/core/RScoredSortedSetAsync.java | 4 ++++ 3 files changed, 32 insertions(+) diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index 92ef84920..106d4e16c 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -313,6 +313,30 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZRANGEBYLEX, getName(), fromValue, toValue); } + @Override + public Integer lexCountTail(V fromElement, boolean fromInclusive) { + return get(lexCountTailAsync(fromElement, fromInclusive)); + } + + @Override + public Future lexCountTailAsync(V fromElement, boolean fromInclusive) { + String fromValue = value(fromElement, fromInclusive); + + return commandExecutor.readAsync(getName(), RedisCommands.ZLEXCOUNT, getName(), fromValue, "+"); + } + + @Override + public Integer lexCountHead(V toElement, boolean toInclusive) { + return get(lexCountHeadAsync(toElement, toInclusive)); + } + + @Override + public Future lexCountHeadAsync(V toElement, boolean toInclusive) { + String toValue = value(toElement, toInclusive); + + return commandExecutor.readAsync(getName(), RedisCommands.ZLEXCOUNT, getName(), "-", toValue); + } + @Override public Integer lexCount(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { return get(lexCountAsync(fromElement, fromInclusive, toElement, toInclusive)); diff --git a/src/main/java/org/redisson/core/RScoredSortedSet.java b/src/main/java/org/redisson/core/RScoredSortedSet.java index 0bd11216f..6b48b816c 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSet.java +++ b/src/main/java/org/redisson/core/RScoredSortedSet.java @@ -21,6 +21,10 @@ import org.redisson.client.protocol.ScoredEntry; public interface RScoredSortedSet extends RScoredSortedSetAsync, Iterable, RExpirable { + Integer lexCountTail(V fromElement, boolean fromInclusive); + + Integer lexCountHead(V toElement, boolean toInclusive); + Collection lexRangeTail(V fromElement, boolean fromInclusive); Collection lexRangeHead(V toElement, boolean toInclusive); diff --git a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java index 5e9b7f013..9af7b60d5 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java +++ b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java @@ -23,6 +23,10 @@ import io.netty.util.concurrent.Future; public interface RScoredSortedSetAsync extends RExpirableAsync { + Future lexCountTailAsync(V fromElement, boolean fromInclusive); + + Future lexCountHeadAsync(V toElement, boolean toInclusive); + Future> lexRangeTailAsync(V fromElement, boolean fromInclusive); Future> lexRangeHeadAsync(V toElement, boolean toInclusive); From b0b9bb4f86894ec5e73db107a50c4ef759159f5a Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Sun, 13 Sep 2015 10:46:25 +0300 Subject: [PATCH 14/21] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 49d4c4a4c..2efb0a17c 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,8 @@ Monits: [monits.com](http://monits.com/) Brookhaven National Laboratory: [bnl.gov](http://bnl.gov/) Netflix Dyno client: [dyno] (https://github.com/Netflix/dyno) 武林Q传:[nbrpg](http://www.nbrpg.com/) +Ocous: [ocous](http://www.ocous.com/) + Recent Releases ================================ ####Please Note: trunk is current development branch. @@ -248,12 +250,12 @@ Include the following to your dependency list: org.redisson redisson - 2.1.1 + 2.1.2 ### Gradle - compile 'org.redisson:redisson:2.1.1' + compile 'org.redisson:redisson:2.1.2' ### Supported by From 1bfc3a7a138f7e6f8820325b90387a157e9916a4 Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 14 Sep 2015 10:20:49 +0300 Subject: [PATCH 15/21] RLexSortedSet object introduced. #135 --- .../java/org/redisson/CommandExecutor.java | 2 + .../org/redisson/CommandExecutorService.java | 6 + src/main/java/org/redisson/Redisson.java | 5 + .../java/org/redisson/RedissonClient.java | 6 + .../java/org/redisson/RedissonExpirable.java | 5 + .../org/redisson/RedissonLexSortedSet.java | 142 ++++++++++++++++++ src/main/java/org/redisson/RedissonList.java | 3 +- .../java/org/redisson/RedissonObject.java | 11 +- .../org/redisson/RedissonScoredSortedSet.java | 116 +++----------- src/main/java/org/redisson/RedissonSet.java | 3 +- .../client/protocol/RedisCommands.java | 2 +- .../convertor/IntegerReplayConvertor.java | 3 + .../org/redisson/core/RCollectionAsync.java | 2 - .../java/org/redisson/core/RLexSortedSet.java | 39 +++++ .../org/redisson/core/RLexSortedSetAsync.java | 40 +++++ .../org/redisson/core/RScoredSortedSet.java | 12 -- .../redisson/core/RScoredSortedSetAsync.java | 12 -- .../redisson/RedissonScoredSortedSetTest.java | 61 -------- 18 files changed, 277 insertions(+), 193 deletions(-) create mode 100644 src/main/java/org/redisson/RedissonLexSortedSet.java create mode 100644 src/main/java/org/redisson/core/RLexSortedSet.java create mode 100644 src/main/java/org/redisson/core/RLexSortedSetAsync.java diff --git a/src/main/java/org/redisson/CommandExecutor.java b/src/main/java/org/redisson/CommandExecutor.java index 67bf29af0..a0c0da0af 100644 --- a/src/main/java/org/redisson/CommandExecutor.java +++ b/src/main/java/org/redisson/CommandExecutor.java @@ -33,6 +33,8 @@ import io.netty.util.concurrent.Future; //TODO ping support public interface CommandExecutor { + R read(RedisClient client, String key, Codec codec, RedisCommand command, Object ... params); + R read(RedisClient client, String key, RedisCommand command, Object ... params); Future evalWriteAllAsync(RedisCommand command, SlotCallback callback, String script, List keys, Object ... params); diff --git a/src/main/java/org/redisson/CommandExecutorService.java b/src/main/java/org/redisson/CommandExecutorService.java index d0929561f..85a0649e8 100644 --- a/src/main/java/org/redisson/CommandExecutorService.java +++ b/src/main/java/org/redisson/CommandExecutorService.java @@ -201,6 +201,12 @@ public class CommandExecutorService implements CommandExecutor { return get(res); } + public R read(RedisClient client, String key, Codec codec, RedisCommand command, Object ... params) { + Future res = readAsync(client, key, codec, command, params); + return get(res); + } + + public Future readAsync(RedisClient client, String key, Codec codec, RedisCommand command, Object ... params) { Promise mainPromise = connectionManager.newPromise(); int slot = connectionManager.calcSlot(key); diff --git a/src/main/java/org/redisson/Redisson.java b/src/main/java/org/redisson/Redisson.java index 7b78ac885..f97686a69 100755 --- a/src/main/java/org/redisson/Redisson.java +++ b/src/main/java/org/redisson/Redisson.java @@ -39,6 +39,7 @@ import org.redisson.core.RCountDownLatch; import org.redisson.core.RDeque; import org.redisson.core.RHyperLogLog; import org.redisson.core.RKeys; +import org.redisson.core.RLexSortedSet; import org.redisson.core.RList; import org.redisson.core.RLock; import org.redisson.core.RMap; @@ -221,6 +222,10 @@ public class Redisson implements RedissonClient { return new RedissonScoredSortedSet(commandExecutor, name); } + public RLexSortedSet getLexSortedSet(String name) { + return new RedissonLexSortedSet(commandExecutor, name); + } + /** * Returns topic instance by name. * diff --git a/src/main/java/org/redisson/RedissonClient.java b/src/main/java/org/redisson/RedissonClient.java index 3c3290d77..c8149529a 100755 --- a/src/main/java/org/redisson/RedissonClient.java +++ b/src/main/java/org/redisson/RedissonClient.java @@ -29,11 +29,13 @@ import org.redisson.core.RCountDownLatch; import org.redisson.core.RDeque; import org.redisson.core.RHyperLogLog; import org.redisson.core.RKeys; +import org.redisson.core.RLexSortedSet; import org.redisson.core.RList; import org.redisson.core.RLock; import org.redisson.core.RMap; import org.redisson.core.RPatternTopic; import org.redisson.core.RQueue; +import org.redisson.core.RScoredSortedSet; import org.redisson.core.RScript; import org.redisson.core.RSet; import org.redisson.core.RSortedSet; @@ -104,6 +106,10 @@ public interface RedissonClient { */ RSortedSet getSortedSet(String name); + RScoredSortedSet getScoredSortedSet(String name); + + RLexSortedSet getLexSortedSet(String name); + /** * Returns topic instance by name. * diff --git a/src/main/java/org/redisson/RedissonExpirable.java b/src/main/java/org/redisson/RedissonExpirable.java index 90b0ea801..d24a43649 100644 --- a/src/main/java/org/redisson/RedissonExpirable.java +++ b/src/main/java/org/redisson/RedissonExpirable.java @@ -18,6 +18,7 @@ package org.redisson; import java.util.Date; import java.util.concurrent.TimeUnit; +import org.redisson.client.codec.Codec; import org.redisson.client.codec.StringCodec; import org.redisson.client.protocol.RedisCommands; import org.redisson.core.RExpirable; @@ -30,6 +31,10 @@ abstract class RedissonExpirable extends RedissonObject implements RExpirable { super(connectionManager, name); } + RedissonExpirable(Codec codec, CommandExecutor connectionManager, String name) { + super(codec, connectionManager, name); + } + @Override public boolean expire(long timeToLive, TimeUnit timeUnit) { return commandExecutor.get(expireAsync(timeToLive, timeUnit)); diff --git a/src/main/java/org/redisson/RedissonLexSortedSet.java b/src/main/java/org/redisson/RedissonLexSortedSet.java new file mode 100644 index 000000000..bfed02e91 --- /dev/null +++ b/src/main/java/org/redisson/RedissonLexSortedSet.java @@ -0,0 +1,142 @@ +/** + * 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.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.redisson.client.codec.StringCodec; +import org.redisson.client.protocol.RedisCommands; +import org.redisson.core.RLexSortedSet; + +import io.netty.util.concurrent.Future; + +public class RedissonLexSortedSet extends RedissonScoredSortedSet implements RLexSortedSet { + + public RedissonLexSortedSet(CommandExecutor commandExecutor, String name) { + super(StringCodec.INSTANCE, commandExecutor, name); + } + + @Override + public Collection lexRange(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { + return get(lexRangeAsync(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public Collection lexRangeHead(String toElement, boolean toInclusive) { + return get(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 lexRangeTail(String fromElement, boolean fromInclusive) { + return get(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> lexRangeAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { + String fromValue = value(fromElement, fromInclusive); + String toValue = value(toElement, toInclusive); + + return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZRANGEBYLEX, getName(), fromValue, toValue); + } + + @Override + public Integer lexCountTail(String fromElement, boolean fromInclusive) { + return get(lexCountTailAsync(fromElement, fromInclusive)); + } + + @Override + public Future lexCountTailAsync(String fromElement, boolean fromInclusive) { + String fromValue = value(fromElement, fromInclusive); + + return commandExecutor.readAsync(getName(), RedisCommands.ZLEXCOUNT, getName(), fromValue, "+"); + } + + @Override + public Integer lexCountHead(String toElement, boolean toInclusive) { + return get(lexCountHeadAsync(toElement, toInclusive)); + } + + @Override + public Future lexCountHeadAsync(String toElement, boolean toInclusive) { + String toValue = value(toElement, toInclusive); + + return commandExecutor.readAsync(getName(), RedisCommands.ZLEXCOUNT, getName(), "-", toValue); + } + + @Override + public Integer lexCount(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { + return get(lexCountAsync(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public Future lexCountAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { + String fromValue = value(fromElement, fromInclusive); + String toValue = value(toElement, toInclusive); + + return commandExecutor.readAsync(getName(), RedisCommands.ZLEXCOUNT, getName(), fromValue, toValue); + } + + private String value(String fromElement, boolean fromInclusive) { + String fromValue = fromElement.toString(); + if (fromInclusive) { + fromValue = "[" + fromValue; + } else { + fromValue = "(" + fromValue; + } + return fromValue; + } + + @Override + public Future addAsync(String e) { + return addAsync(0, e); + } + + @Override + public Future addAllAsync(Collection c) { + List params = new ArrayList(2*c.size()); + for (Object param : c) { + params.add(0); + params.add(param); + } + return commandExecutor.writeAsync(getName(), codec, RedisCommands.ZADD, getName(), params.toArray()); + } + + @Override + public boolean add(String e) { + return get(addAsync(e)); + } + + @Override + public boolean addAll(Collection c) { + return get(addAllAsync(c)); + } + +} diff --git a/src/main/java/org/redisson/RedissonList.java b/src/main/java/org/redisson/RedissonList.java index ab9385920..f4cd946c1 100644 --- a/src/main/java/org/redisson/RedissonList.java +++ b/src/main/java/org/redisson/RedissonList.java @@ -92,8 +92,7 @@ public class RedissonList extends RedissonExpirable implements RList { return (List) get(readAllAsync()); } - @Override - public Future> readAllAsync() { + private Future> readAllAsync() { return commandExecutor.readAsync(getName(), LRANGE, getName(), 0, -1); } diff --git a/src/main/java/org/redisson/RedissonObject.java b/src/main/java/org/redisson/RedissonObject.java index 23990c00e..bd661c1e1 100644 --- a/src/main/java/org/redisson/RedissonObject.java +++ b/src/main/java/org/redisson/RedissonObject.java @@ -15,6 +15,7 @@ */ package org.redisson; +import org.redisson.client.codec.Codec; import org.redisson.client.protocol.RedisCommands; import org.redisson.core.RObject; @@ -31,10 +32,16 @@ abstract class RedissonObject implements RObject { final CommandExecutor commandExecutor; private final String name; + final Codec codec; - public RedissonObject(CommandExecutor commandExecutor, String name) { - this.commandExecutor = commandExecutor; + public RedissonObject(Codec codec, CommandExecutor commandExecutor, String name) { + this.codec = codec; this.name = name; + this.commandExecutor = commandExecutor; + } + + public RedissonObject(CommandExecutor commandExecutor, String name) { + this(commandExecutor.getConnectionManager().getCodec(), commandExecutor, name); } protected V get(Future future) { diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index 106d4e16c..fbafd7478 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.NoSuchElementException; import org.redisson.client.RedisClient; +import org.redisson.client.codec.Codec; import org.redisson.client.codec.StringCodec; import org.redisson.client.protocol.RedisCommand; import org.redisson.client.protocol.RedisCommands; @@ -39,6 +40,10 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc super(commandExecutor, name); } + public RedissonScoredSortedSet(Codec codec, CommandExecutor commandExecutor, String name) { + super(codec, commandExecutor, name); + } + @Override public boolean add(double score, V object) { return get(addAsync(score, object)); @@ -46,11 +51,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future addAsync(double score, V object) { - // String should be encoded as String to let lex functions work properly - if (object instanceof String) { - return commandExecutor.writeAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZADD, getName(), BigDecimal.valueOf(score).toPlainString(), object); - } - return commandExecutor.writeAsync(getName(), RedisCommands.ZADD, getName(), BigDecimal.valueOf(score).toPlainString(), object); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.ZADD, getName(), BigDecimal.valueOf(score).toPlainString(), object); } @Override @@ -65,7 +66,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future removeAsync(Object object) { - return commandExecutor.writeAsync(getName(), RedisCommands.ZREM, getName(), object); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.ZREM, getName(), object); } @Override @@ -80,7 +81,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future sizeAsync() { - return commandExecutor.readAsync(getName(), RedisCommands.ZCARD, getName()); + return commandExecutor.readAsync(getName(), codec, RedisCommands.ZCARD, getName()); } @Override @@ -90,7 +91,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future containsAsync(Object o) { - return commandExecutor.readAsync(getName(), RedisCommands.ZSCORE_CONTAINS, getName(), o); + return commandExecutor.readAsync(getName(), codec, RedisCommands.ZSCORE_CONTAINS, getName(), o); } @Override @@ -100,7 +101,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future getScoreAsync(V o) { - return commandExecutor.readAsync(getName(), RedisCommands.ZSCORE, getName(), o); + return commandExecutor.readAsync(getName(), codec, RedisCommands.ZSCORE, getName(), o); } @Override @@ -110,11 +111,11 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future rankAsync(V o) { - return commandExecutor.readAsync(getName(), RedisCommands.ZRANK, getName(), o); + return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANK, getName(), o); } private ListScanResult scanIterator(RedisClient client, long startPos) { - return commandExecutor.read(client, getName(), RedisCommands.ZSCAN, getName(), startPos); + return commandExecutor.read(client, getName(), codec, RedisCommands.ZSCAN, getName(), startPos); } @Override @@ -187,7 +188,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future containsAllAsync(Collection c) { - return commandExecutor.evalReadAsync(getName(), new RedisCommand("EVAL", new BooleanReplayConvertor(), 4), + return commandExecutor.evalReadAsync(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 4), "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 " @@ -201,7 +202,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future removeAllAsync(Collection c) { - return commandExecutor.evalWriteAsync(getName(), new RedisCommand("EVAL", new BooleanReplayConvertor(), 4), + return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 4), "local v = false " + "for i = 0, table.getn(ARGV), 1 do " + "if redis.call('zrem', KEYS[1], ARGV[i]) == 1 " @@ -223,7 +224,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future retainAllAsync(Collection c) { - return commandExecutor.evalWriteAsync(getName(), new RedisCommand("EVAL", new BooleanReplayConvertor(), 4), + return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 4), "local changed = false " + "local s = redis.call('zrange', KEYS[1], 0, -1) " + "local i = 0 " @@ -264,7 +265,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future> valueRangeAsync(int startIndex, int endIndex) { - return commandExecutor.readAsync(getName(), RedisCommands.ZRANGE, getName(), startIndex, endIndex); + return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANGE, getName(), startIndex, endIndex); } @Override @@ -274,90 +275,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc @Override public Future>> entryRangeAsync(int startIndex, int endIndex) { - return commandExecutor.readAsync(getName(), RedisCommands.ZRANGE_ENTRY, getName(), startIndex, endIndex, "WITHSCORES"); - } - - @Override - public Collection lexRange(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { - return get(lexRangeAsync(fromElement, fromInclusive, toElement, toInclusive)); - } - - @Override - public Collection lexRangeHead(V toElement, boolean toInclusive) { - return get(lexRangeHeadAsync(toElement, toInclusive)); - } - - @Override - public Future> lexRangeHeadAsync(V toElement, boolean toInclusive) { - String toValue = value(toElement, toInclusive); - return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZRANGEBYLEX, getName(), "-", toValue); - } - - @Override - public Collection lexRangeTail(V fromElement, boolean fromInclusive) { - return get(lexRangeTailAsync(fromElement, fromInclusive)); - } - - @Override - public Future> lexRangeTailAsync(V fromElement, boolean fromInclusive) { - String fromValue = value(fromElement, fromInclusive); - return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZRANGEBYLEX, getName(), fromValue, "+"); - } - - - @Override - public Future> lexRangeAsync(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { - String fromValue = value(fromElement, fromInclusive); - String toValue = value(toElement, toInclusive); - - return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZRANGEBYLEX, getName(), fromValue, toValue); - } - - @Override - public Integer lexCountTail(V fromElement, boolean fromInclusive) { - return get(lexCountTailAsync(fromElement, fromInclusive)); - } - - @Override - public Future lexCountTailAsync(V fromElement, boolean fromInclusive) { - String fromValue = value(fromElement, fromInclusive); - - return commandExecutor.readAsync(getName(), RedisCommands.ZLEXCOUNT, getName(), fromValue, "+"); - } - - @Override - public Integer lexCountHead(V toElement, boolean toInclusive) { - return get(lexCountHeadAsync(toElement, toInclusive)); - } - - @Override - public Future lexCountHeadAsync(V toElement, boolean toInclusive) { - String toValue = value(toElement, toInclusive); - - return commandExecutor.readAsync(getName(), RedisCommands.ZLEXCOUNT, getName(), "-", toValue); - } - - @Override - public Integer lexCount(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { - return get(lexCountAsync(fromElement, fromInclusive, toElement, toInclusive)); - } - - @Override - public Future lexCountAsync(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { - String fromValue = value(fromElement, fromInclusive); - String toValue = value(toElement, toInclusive); - - return commandExecutor.readAsync(getName(), RedisCommands.ZLEXCOUNT, getName(), fromValue, toValue); - } - - private String value(V fromElement, boolean fromInclusive) { - String fromValue = fromElement.toString(); - if (fromInclusive) { - fromValue = "[" + fromValue; - } else { - fromValue = "(" + fromValue; - } - return fromValue; + return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANGE_ENTRY, getName(), startIndex, endIndex, "WITHSCORES"); } } diff --git a/src/main/java/org/redisson/RedissonSet.java b/src/main/java/org/redisson/RedissonSet.java index 6e2a94400..7a38567e0 100644 --- a/src/main/java/org/redisson/RedissonSet.java +++ b/src/main/java/org/redisson/RedissonSet.java @@ -127,8 +127,7 @@ public class RedissonSet extends RedissonExpirable implements RSet { }; } - @Override - public Future> readAllAsync() { + private Future> readAllAsync() { return commandExecutor.readAsync(getName(), RedisCommands.SMEMBERS, getName()); } diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index 0fc389ce5..177933b9d 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -54,7 +54,7 @@ public interface RedisCommands { RedisStrictCommand ZLEXCOUNT = new RedisStrictCommand("ZLEXCOUNT", new IntegerReplayConvertor()); RedisCommand ZSCORE_CONTAINS = new RedisCommand("ZSCORE", new BooleanNotNullReplayConvertor(), 2); RedisStrictCommand ZSCORE = new RedisStrictCommand("ZSCORE", new DoubleReplayConvertor()); - RedisStrictCommand ZRANK = new RedisStrictCommand("ZRANK", new IntegerReplayConvertor()); + RedisCommand ZRANK = new RedisCommand("ZRANK", new IntegerReplayConvertor(), 2); RedisCommand> ZRANGE = new RedisCommand>("ZRANGE", new ObjectListReplayDecoder()); RedisCommand> ZRANGEBYLEX = new RedisCommand>("ZRANGEBYLEX", new ObjectListReplayDecoder()); RedisCommand>> ZRANGE_ENTRY = new RedisCommand>>("ZRANGE", new ScoredSortedSetReplayDecoder()); diff --git a/src/main/java/org/redisson/client/protocol/convertor/IntegerReplayConvertor.java b/src/main/java/org/redisson/client/protocol/convertor/IntegerReplayConvertor.java index 647f3aaf3..11529a0be 100644 --- a/src/main/java/org/redisson/client/protocol/convertor/IntegerReplayConvertor.java +++ b/src/main/java/org/redisson/client/protocol/convertor/IntegerReplayConvertor.java @@ -19,6 +19,9 @@ public class IntegerReplayConvertor extends SingleConvertor { @Override public Integer convert(Object obj) { + if (obj == null) { + return null; + } return ((Long) obj).intValue(); } diff --git a/src/main/java/org/redisson/core/RCollectionAsync.java b/src/main/java/org/redisson/core/RCollectionAsync.java index e1fe641d2..239f8dbae 100644 --- a/src/main/java/org/redisson/core/RCollectionAsync.java +++ b/src/main/java/org/redisson/core/RCollectionAsync.java @@ -31,8 +31,6 @@ public interface RCollectionAsync extends RExpirableAsync { Future removeAsync(Object o); - Future> readAllAsync(); - Future sizeAsync(); Future addAsync(V e); diff --git a/src/main/java/org/redisson/core/RLexSortedSet.java b/src/main/java/org/redisson/core/RLexSortedSet.java new file mode 100644 index 000000000..2a02eec3b --- /dev/null +++ b/src/main/java/org/redisson/core/RLexSortedSet.java @@ -0,0 +1,39 @@ +/** + * 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.Collection; +import java.util.Set; + +public interface RLexSortedSet extends RLexSortedSetAsync, Set, RExpirable { + + Integer lexCountTail(String fromElement, boolean fromInclusive); + + Integer lexCountHead(String toElement, boolean toInclusive); + + Collection lexRangeTail(String fromElement, boolean fromInclusive); + + Collection lexRangeHead(String toElement, boolean toInclusive); + + Collection lexRange(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + + Integer lexCount(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + + Integer rank(String o); + + 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 new file mode 100644 index 000000000..9e705caa1 --- /dev/null +++ b/src/main/java/org/redisson/core/RLexSortedSetAsync.java @@ -0,0 +1,40 @@ +/** + * 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.Collection; + +import io.netty.util.concurrent.Future; + +public interface RLexSortedSetAsync extends RCollectionAsync { + + Future lexCountTailAsync(String fromElement, boolean fromInclusive); + + Future lexCountHeadAsync(String toElement, boolean toInclusive); + + Future> lexRangeTailAsync(String fromElement, boolean fromInclusive); + + Future> lexRangeHeadAsync(String toElement, boolean toInclusive); + + Future> lexRangeAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + + Future lexCountAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + + Future rankAsync(String o); + + Future> valueRangeAsync(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 6b48b816c..c000b362a 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSet.java +++ b/src/main/java/org/redisson/core/RScoredSortedSet.java @@ -21,18 +21,6 @@ import org.redisson.client.protocol.ScoredEntry; public interface RScoredSortedSet extends RScoredSortedSetAsync, Iterable, RExpirable { - Integer lexCountTail(V fromElement, boolean fromInclusive); - - Integer lexCountHead(V toElement, boolean toInclusive); - - Collection lexRangeTail(V fromElement, boolean fromInclusive); - - Collection lexRangeHead(V toElement, boolean toInclusive); - - Collection lexRange(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive); - - Integer lexCount(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive); - Integer rank(V o); Double getScore(V o); diff --git a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java index 9af7b60d5..644eb7c53 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java +++ b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java @@ -23,18 +23,6 @@ import io.netty.util.concurrent.Future; public interface RScoredSortedSetAsync extends RExpirableAsync { - Future lexCountTailAsync(V fromElement, boolean fromInclusive); - - Future lexCountHeadAsync(V toElement, boolean toInclusive); - - Future> lexRangeTailAsync(V fromElement, boolean fromInclusive); - - Future> lexRangeHeadAsync(V toElement, boolean toInclusive); - - Future> lexRangeAsync(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive); - - Future lexCountAsync(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive); - Future rankAsync(V o); Future getScoreAsync(V o); diff --git a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index 33319a425..0a8d14134 100644 --- a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -21,52 +21,6 @@ import io.netty.util.concurrent.Future; public class RedissonScoredSortedSetTest extends BaseTest { - @Test - public void testLexRangeTail() { - RScoredSortedSet set = redisson.getScoredSortedSet("simple"); - set.add(0, "a"); - set.add(0, "b"); - set.add(0, "c"); - set.add(0, "d"); - set.add(0, "e"); - set.add(0, "f"); - set.add(0, "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")); - } - - - @Test - public void testLexRangeHead() { - RScoredSortedSet set = redisson.getScoredSortedSet("simple"); - set.add(0, "a"); - set.add(0, "b"); - set.add(0, "c"); - set.add(0, "d"); - set.add(0, "e"); - set.add(0, "f"); - set.add(0, "g"); - - MatcherAssert.assertThat(set.lexRangeHead("c", false), Matchers.contains("a", "b")); - MatcherAssert.assertThat(set.lexRangeHead("c", true), Matchers.contains("a", "b", "c")); - } - - - @Test - public void testLexRange() { - RScoredSortedSet set = redisson.getScoredSortedSet("simple"); - set.add(0, "a"); - set.add(0, "b"); - set.add(0, "c"); - set.add(0, "d"); - set.add(0, "e"); - set.add(0, "f"); - set.add(0, "g"); - - MatcherAssert.assertThat(set.lexRange("aaa", true, "g", false), Matchers.contains("b", "c", "d", "e", "f")); - } - @Test public void testRank() { RScoredSortedSet set = redisson.getScoredSortedSet("simple"); @@ -81,21 +35,6 @@ public class RedissonScoredSortedSetTest extends BaseTest { Assert.assertEquals(3, (int)set.rank("d")); } - @Test - public void testLexCount() { - RScoredSortedSet set = redisson.getScoredSortedSet("simple"); - set.add(0, "a"); - set.add(0, "b"); - set.add(0, "c"); - set.add(0, "d"); - set.add(0, "e"); - set.add(0, "f"); - set.add(0, "g"); - - Assert.assertEquals(5, (int)set.lexCount("b", true, "f", true)); - Assert.assertEquals(3, (int)set.lexCount("b", false, "f", false)); - } - @Test public void testAddAsync() throws InterruptedException, ExecutionException { RScoredSortedSet set = redisson.getScoredSortedSet("simple"); From 985499bcebbfb4079c8bc3140cc76afec99abc67 Mon Sep 17 00:00:00 2001 From: Steve Ungerer Date: Mon, 14 Sep 2015 07:36:34 -0400 Subject: [PATCH 16/21] https://github.com/mrniko/redisson/pull/244 Non-functional; rename config and connection manager class names --- src/main/java/org/redisson/Config.java | 34 +++++++++---------- ...fig.java => ElasticacheServersConfig.java} | 10 +++--- src/main/java/org/redisson/Redisson.java | 6 ++-- ...java => ElasticacheConnectionManager.java} | 12 +++---- 4 files changed, 31 insertions(+), 31 deletions(-) rename src/main/java/org/redisson/{ElasticacheReplicationGroupServersConfig.java => ElasticacheServersConfig.java} (81%) rename src/main/java/org/redisson/connection/{ElasticacheReplicationGroupConnectionManager.java => ElasticacheConnectionManager.java} (91%) diff --git a/src/main/java/org/redisson/Config.java b/src/main/java/org/redisson/Config.java index af369a4af..db4e88848 100644 --- a/src/main/java/org/redisson/Config.java +++ b/src/main/java/org/redisson/Config.java @@ -34,7 +34,7 @@ public class Config { private ClusterServersConfig clusterServersConfig; - private ElasticacheReplicationGroupServersConfig elasticacheReplicationGroupServersConfig; + private ElasticacheServersConfig elasticacheServersConfig; /** * Threads amount shared between all redis node clients @@ -73,8 +73,8 @@ public class Config { if (oldConf.getClusterServersConfig() != null ) { setClusterServersConfig(new ClusterServersConfig(oldConf.getClusterServersConfig())); } - if (oldConf.getElasticacheReplicationGroupServersConfig() != null) { - setElasticacheReplicationGroupServersConfig(new ElasticacheReplicationGroupServersConfig(oldConf.getElasticacheReplicationGroupServersConfig())); + if (oldConf.getElasticacheServersConfig() != null) { + setElasticacheServersConfig(new ElasticacheServersConfig(oldConf.getElasticacheServersConfig())); } } @@ -97,7 +97,7 @@ public class Config { checkMasterSlaveServersConfig(); checkSentinelServersConfig(); checkSingleServerConfig(); - checkElasticacheReplicationGroupServersConfig(); + checkElasticacheServersConfig(); if (clusterServersConfig == null) { clusterServersConfig = new ClusterServersConfig(); @@ -112,30 +112,30 @@ public class Config { this.clusterServersConfig = clusterServersConfig; } - public ElasticacheReplicationGroupServersConfig useElasticacheReplicationGroupServers() { + public ElasticacheServersConfig useElasticacheServers() { checkClusterServersConfig(); checkMasterSlaveServersConfig(); checkSentinelServersConfig(); checkSingleServerConfig(); - if (elasticacheReplicationGroupServersConfig == null) { - elasticacheReplicationGroupServersConfig = new ElasticacheReplicationGroupServersConfig(); + if (elasticacheServersConfig == null) { + elasticacheServersConfig = new ElasticacheServersConfig(); } - return elasticacheReplicationGroupServersConfig; + return elasticacheServersConfig; } - ElasticacheReplicationGroupServersConfig getElasticacheReplicationGroupServersConfig() { - return elasticacheReplicationGroupServersConfig; + ElasticacheServersConfig getElasticacheServersConfig() { + return elasticacheServersConfig; } - void setElasticacheReplicationGroupServersConfig(ElasticacheReplicationGroupServersConfig elasticacheReplicationGroupServersConfig) { - this.elasticacheReplicationGroupServersConfig = elasticacheReplicationGroupServersConfig; + void setElasticacheServersConfig(ElasticacheServersConfig elasticacheServersConfig) { + this.elasticacheServersConfig = elasticacheServersConfig; } public SingleServerConfig useSingleServer() { checkClusterServersConfig(); checkMasterSlaveServersConfig(); checkSentinelServersConfig(); - checkElasticacheReplicationGroupServersConfig(); + checkElasticacheServersConfig(); if (singleServerConfig == null) { singleServerConfig = new SingleServerConfig(); @@ -154,7 +154,7 @@ public class Config { checkClusterServersConfig(); checkSingleServerConfig(); checkMasterSlaveServersConfig(); - checkElasticacheReplicationGroupServersConfig(); + checkElasticacheServersConfig(); if (sentinelServersConfig == null) { sentinelServersConfig = new SentinelServersConfig(); @@ -173,7 +173,7 @@ public class Config { checkClusterServersConfig(); checkSingleServerConfig(); checkSentinelServersConfig(); - checkElasticacheReplicationGroupServersConfig(); + checkElasticacheServersConfig(); if (masterSlaveServersConfig == null) { masterSlaveServersConfig = new MasterSlaveServersConfig(); @@ -224,8 +224,8 @@ public class Config { } } - private void checkElasticacheReplicationGroupServersConfig() { - if (elasticacheReplicationGroupServersConfig != null) { + private void checkElasticacheServersConfig() { + if (elasticacheServersConfig != null) { throw new IllegalStateException("elasticache replication group servers config already used!"); } } diff --git a/src/main/java/org/redisson/ElasticacheReplicationGroupServersConfig.java b/src/main/java/org/redisson/ElasticacheServersConfig.java similarity index 81% rename from src/main/java/org/redisson/ElasticacheReplicationGroupServersConfig.java rename to src/main/java/org/redisson/ElasticacheServersConfig.java index c87543826..dc0fe18dc 100644 --- a/src/main/java/org/redisson/ElasticacheReplicationGroupServersConfig.java +++ b/src/main/java/org/redisson/ElasticacheServersConfig.java @@ -27,7 +27,7 @@ import org.redisson.misc.URIBuilder; * * @author Steve Ungerer */ -public class ElasticacheReplicationGroupServersConfig extends BaseMasterSlaveServersConfig { +public class ElasticacheServersConfig extends BaseMasterSlaveServersConfig { /** * Replication group node urls list @@ -39,10 +39,10 @@ public class ElasticacheReplicationGroupServersConfig extends BaseMasterSlaveSer */ private int scanInterval = 1000; - public ElasticacheReplicationGroupServersConfig() { + public ElasticacheServersConfig() { } - ElasticacheReplicationGroupServersConfig(ElasticacheReplicationGroupServersConfig config) { + ElasticacheServersConfig(ElasticacheServersConfig config) { super(config); setNodeAddresses(config.getNodeAddresses()); setScanInterval(config.getScanInterval()); @@ -54,7 +54,7 @@ public class ElasticacheReplicationGroupServersConfig extends BaseMasterSlaveSer * @param addresses in host:port format * @return */ - public ElasticacheReplicationGroupServersConfig addNodeAddress(String ... addresses) { + public ElasticacheServersConfig addNodeAddress(String ... addresses) { for (String address : addresses) { nodeAddresses.add(URIBuilder.create(address)); } @@ -76,7 +76,7 @@ public class ElasticacheReplicationGroupServersConfig extends BaseMasterSlaveSer * @param scanInterval in milliseconds * @return */ - public ElasticacheReplicationGroupServersConfig setScanInterval(int scanInterval) { + public ElasticacheServersConfig setScanInterval(int scanInterval) { this.scanInterval = scanInterval; return this; } diff --git a/src/main/java/org/redisson/Redisson.java b/src/main/java/org/redisson/Redisson.java index c6f9e3a75..b586a860a 100755 --- a/src/main/java/org/redisson/Redisson.java +++ b/src/main/java/org/redisson/Redisson.java @@ -25,7 +25,7 @@ import java.util.concurrent.atomic.AtomicLong; import org.redisson.client.protocol.RedisCommands; import org.redisson.connection.ClusterConnectionManager; import org.redisson.connection.ConnectionManager; -import org.redisson.connection.ElasticacheReplicationGroupConnectionManager; +import org.redisson.connection.ElasticacheConnectionManager; import org.redisson.connection.MasterSlaveConnectionManager; import org.redisson.connection.SentinelConnectionManager; import org.redisson.connection.SingleConnectionManager; @@ -79,8 +79,8 @@ public class Redisson implements RedissonClient { connectionManager = new SentinelConnectionManager(configCopy.getSentinelServersConfig(), configCopy); } else if (configCopy.getClusterServersConfig() != null) { connectionManager = new ClusterConnectionManager(configCopy.getClusterServersConfig(), configCopy); - } else if (configCopy.getElasticacheReplicationGroupServersConfig() != null) { - connectionManager = new ElasticacheReplicationGroupConnectionManager(configCopy.getElasticacheReplicationGroupServersConfig(), configCopy); + } else if (configCopy.getElasticacheServersConfig() != null) { + connectionManager = new ElasticacheConnectionManager(configCopy.getElasticacheServersConfig(), configCopy); } else { throw new IllegalArgumentException("server(s) address(es) not defined!"); } diff --git a/src/main/java/org/redisson/connection/ElasticacheReplicationGroupConnectionManager.java b/src/main/java/org/redisson/connection/ElasticacheConnectionManager.java similarity index 91% rename from src/main/java/org/redisson/connection/ElasticacheReplicationGroupConnectionManager.java rename to src/main/java/org/redisson/connection/ElasticacheConnectionManager.java index aedfec7f4..c7a732733 100644 --- a/src/main/java/org/redisson/connection/ElasticacheReplicationGroupConnectionManager.java +++ b/src/main/java/org/redisson/connection/ElasticacheConnectionManager.java @@ -22,7 +22,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.redisson.Config; -import org.redisson.ElasticacheReplicationGroupServersConfig; +import org.redisson.ElasticacheServersConfig; import org.redisson.MasterSlaveServersConfig; import org.redisson.client.RedisClient; import org.redisson.client.RedisConnection; @@ -42,7 +42,7 @@ import io.netty.util.concurrent.ScheduledFuture; * * @author Steve Ungerer */ -public class ElasticacheReplicationGroupConnectionManager extends MasterSlaveConnectionManager { +public class ElasticacheConnectionManager extends MasterSlaveConnectionManager { private static final String ROLE_KEY = "role:"; @@ -59,7 +59,7 @@ public class ElasticacheReplicationGroupConnectionManager extends MasterSlaveCon slave } - public ElasticacheReplicationGroupConnectionManager(ElasticacheReplicationGroupServersConfig cfg, Config config) { + public ElasticacheConnectionManager(ElasticacheServersConfig cfg, Config config) { init(config); this.config = create(cfg); @@ -88,7 +88,7 @@ public class ElasticacheReplicationGroupConnectionManager extends MasterSlaveCon monitorRoleChange(cfg); } - private RedisConnection connect(ElasticacheReplicationGroupServersConfig cfg, URI addr) { + private RedisConnection connect(ElasticacheServersConfig cfg, URI addr) { RedisConnection connection = nodeConnections.get(addr); if (connection != null) { return connection; @@ -105,7 +105,7 @@ public class ElasticacheReplicationGroupConnectionManager extends MasterSlaveCon return connection; } - private void monitorRoleChange(final ElasticacheReplicationGroupServersConfig cfg) { + private void monitorRoleChange(final ElasticacheServersConfig cfg) { monitorFuture = GlobalEventExecutor.INSTANCE.scheduleWithFixedDelay(new Runnable() { @Override public void run() { @@ -145,7 +145,7 @@ public class ElasticacheReplicationGroupConnectionManager extends MasterSlaveCon throw new RedisException("Cannot determine node role from provided 'INFO replication' data"); } - private MasterSlaveServersConfig create(ElasticacheReplicationGroupServersConfig cfg) { + private MasterSlaveServersConfig create(ElasticacheServersConfig cfg) { MasterSlaveServersConfig c = new MasterSlaveServersConfig(); c.setRetryInterval(cfg.getRetryInterval()); c.setRetryAttempts(cfg.getRetryAttempts()); From 55c8534d8f67c99d86d84f446eaf49079bb3436e Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 14 Sep 2015 15:38:56 +0300 Subject: [PATCH 17/21] ZREMRANGEBYRANK support added #143 --- .../org/redisson/RedissonScoredSortedSet.java | 8 ++++++++ .../redisson/client/protocol/RedisCommands.java | 1 + .../java/org/redisson/core/RScoredSortedSet.java | 2 ++ .../org/redisson/core/RScoredSortedSetAsync.java | 2 ++ .../org/redisson/RedissonScoredSortedSetTest.java | 15 +++++++++++++++ 5 files changed, 28 insertions(+) diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index fbafd7478..6aa8da7b6 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -59,6 +59,14 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc return get(removeAsync(object)); } + public int removeRangeByRank(int startIndex, int endIndex) { + return get(removeRangeByRankAsync(startIndex, endIndex)); + } + + public Future removeRangeByRankAsync(int startIndex, int endIndex) { + return commandExecutor.readAsync(getName(), codec, RedisCommands.ZREMRANGEBYRANK, getName(), startIndex, endIndex); + } + @Override public void clear() { delete(); diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index 177933b9d..ed60ea5cc 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -56,6 +56,7 @@ public interface RedisCommands { RedisStrictCommand ZSCORE = new RedisStrictCommand("ZSCORE", new DoubleReplayConvertor()); RedisCommand ZRANK = new RedisCommand("ZRANK", new IntegerReplayConvertor(), 2); RedisCommand> ZRANGE = new RedisCommand>("ZRANGE", new ObjectListReplayDecoder()); + RedisStrictCommand ZREMRANGEBYRANK = new RedisStrictCommand("ZREMRANGEBYRANK", new IntegerReplayConvertor()); RedisCommand> ZRANGEBYLEX = new RedisCommand>("ZRANGEBYLEX", new ObjectListReplayDecoder()); RedisCommand>> ZRANGE_ENTRY = new RedisCommand>>("ZRANGE", new ScoredSortedSetReplayDecoder()); RedisCommand> ZSCAN = new RedisCommand>("ZSCAN", new NestedMultiDecoder(new ObjectListReplayDecoder(), 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 c000b362a..35e96d833 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSet.java +++ b/src/main/java/org/redisson/core/RScoredSortedSet.java @@ -21,6 +21,8 @@ import org.redisson.client.protocol.ScoredEntry; public interface RScoredSortedSet extends RScoredSortedSetAsync, Iterable, RExpirable { + int removeRangeByRank(int startIndex, int endIndex); + Integer rank(V o); Double getScore(V o); diff --git a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java index 644eb7c53..ba9776188 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java +++ b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java @@ -23,6 +23,8 @@ import io.netty.util.concurrent.Future; public interface RScoredSortedSetAsync extends RExpirableAsync { + Future removeRangeByRankAsync(int startIndex, int endIndex); + Future rankAsync(V o); Future getScoreAsync(V o); diff --git a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index 0a8d14134..c389f03e3 100644 --- a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -21,6 +21,21 @@ import io.netty.util.concurrent.Future; public class RedissonScoredSortedSetTest extends BaseTest { + @Test + public void testRemoveRangeByRank() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(0.1, "a"); + set.add(0.2, "b"); + set.add(0.3, "c"); + set.add(0.4, "d"); + set.add(0.5, "e"); + set.add(0.6, "f"); + set.add(0.7, "g"); + + Assert.assertEquals(2, set.removeRangeByRank(0, 1)); + MatcherAssert.assertThat(set, Matchers.contains("c", "d", "e", "f", "g")); + } + @Test public void testRank() { RScoredSortedSet set = redisson.getScoredSortedSet("simple"); From bc0431a4b638deab8f1dadf29be0466352fc1fc9 Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 14 Sep 2015 15:58:21 +0300 Subject: [PATCH 18/21] ZREMRANGEBYSCORE added. #143 --- .../org/redisson/RedissonScoredSortedSet.java | 17 +++++++++++++++++ .../redisson/client/protocol/RedisCommands.java | 1 + .../org/redisson/core/RScoredSortedSet.java | 2 ++ .../redisson/RedissonScoredSortedSetTest.java | 15 +++++++++++++++ 4 files changed, 35 insertions(+) diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index 6aa8da7b6..ee1c68c7d 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -67,6 +67,23 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc return commandExecutor.readAsync(getName(), codec, RedisCommands.ZREMRANGEBYRANK, getName(), startIndex, endIndex); } + public int removeRangeByScore(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive) { + return get(removeRangeByScoreAsync(startScore, startScoreInclusive, endScore, endScoreInclusive)); + } + + public Future removeRangeByScoreAsync(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.readAsync(getName(), codec, RedisCommands.ZREMRANGEBYSCORE, getName(), startValue, endValue); + } + + private String value(String element, boolean inclusive) { + if (!inclusive) { + element = "(" + element; + } + return element; + } + @Override public void clear() { delete(); diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index ed60ea5cc..fa8558031 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -57,6 +57,7 @@ public interface RedisCommands { RedisCommand ZRANK = new RedisCommand("ZRANK", new IntegerReplayConvertor(), 2); RedisCommand> ZRANGE = new RedisCommand>("ZRANGE", new ObjectListReplayDecoder()); RedisStrictCommand ZREMRANGEBYRANK = new RedisStrictCommand("ZREMRANGEBYRANK", new IntegerReplayConvertor()); + RedisStrictCommand ZREMRANGEBYSCORE = new RedisStrictCommand("ZREMRANGEBYSCORE", new IntegerReplayConvertor()); RedisCommand> ZRANGEBYLEX = new RedisCommand>("ZRANGEBYLEX", new ObjectListReplayDecoder()); RedisCommand>> ZRANGE_ENTRY = new RedisCommand>>("ZRANGE", new ScoredSortedSetReplayDecoder()); RedisCommand> ZSCAN = new RedisCommand>("ZSCAN", new NestedMultiDecoder(new ObjectListReplayDecoder(), 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 35e96d833..c06d69a99 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSet.java +++ b/src/main/java/org/redisson/core/RScoredSortedSet.java @@ -21,6 +21,8 @@ import org.redisson.client.protocol.ScoredEntry; public interface RScoredSortedSet extends RScoredSortedSetAsync, Iterable, RExpirable { + int removeRangeByScore(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive); + int removeRangeByRank(int startIndex, int endIndex); Integer rank(V o); diff --git a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index c389f03e3..9f0f90f6d 100644 --- a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -21,6 +21,21 @@ import io.netty.util.concurrent.Future; public class RedissonScoredSortedSetTest extends BaseTest { + @Test + public void testRemoveRangeByScore() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(0.1, "a"); + set.add(0.2, "b"); + set.add(0.3, "c"); + set.add(0.4, "d"); + set.add(0.5, "e"); + set.add(0.6, "f"); + 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")); + } + @Test public void testRemoveRangeByRank() { RScoredSortedSet set = redisson.getScoredSortedSet("simple"); From ca7a504c3e8703e2506fbbaf4c69a0badefdf4e7 Mon Sep 17 00:00:00 2001 From: Nikita Date: Mon, 14 Sep 2015 16:23:15 +0300 Subject: [PATCH 19/21] ZREMRANGEBYLEX support added. #143 --- .../org/redisson/RedissonLexSortedSet.java | 41 +++++++++++++++++-- .../org/redisson/RedissonScoredSortedSet.java | 2 +- .../client/protocol/RedisCommands.java | 1 + .../java/org/redisson/core/RLexSortedSet.java | 14 +++++-- .../org/redisson/core/RLexSortedSetAsync.java | 6 +++ .../org/redisson/core/RScoredSortedSet.java | 2 +- 6 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/redisson/RedissonLexSortedSet.java b/src/main/java/org/redisson/RedissonLexSortedSet.java index bfed02e91..100f1a6b8 100644 --- a/src/main/java/org/redisson/RedissonLexSortedSet.java +++ b/src/main/java/org/redisson/RedissonLexSortedSet.java @@ -31,6 +31,41 @@ public class RedissonLexSortedSet extends RedissonScoredSortedSet implem super(StringCodec.INSTANCE, commandExecutor, name); } + @Override + public int removeRangeByLex(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { + return get(removeRangeByLexAsync(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public int removeRangeHeadByLex(String toElement, boolean toInclusive) { + return get(removeRangeHeadByLexAsync(toElement, toInclusive)); + } + + @Override + public Future removeRangeHeadByLexAsync(String toElement, boolean toInclusive) { + String toValue = value(toElement, toInclusive); + return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZREMRANGEBYLEX, getName(), "-", toValue); + } + + @Override + public int removeRangeTailByLex(String fromElement, boolean fromInclusive) { + return get(removeRangeTailByLexAsync(fromElement, fromInclusive)); + } + + @Override + public Future removeRangeTailByLexAsync(String fromElement, boolean fromInclusive) { + String fromValue = value(fromElement, fromInclusive); + return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZREMRANGEBYLEX, getName(), fromValue, "+"); + } + + @Override + public Future removeRangeByLexAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { + String fromValue = value(fromElement, fromInclusive); + String toValue = value(toElement, toInclusive); + + return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.ZREMRANGEBYLEX, getName(), fromValue, toValue); + } + @Override public Collection lexRange(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { return get(lexRangeAsync(fromElement, fromInclusive, toElement, toInclusive)); @@ -68,7 +103,7 @@ public class RedissonLexSortedSet extends RedissonScoredSortedSet implem } @Override - public Integer lexCountTail(String fromElement, boolean fromInclusive) { + public int lexCountTail(String fromElement, boolean fromInclusive) { return get(lexCountTailAsync(fromElement, fromInclusive)); } @@ -80,7 +115,7 @@ public class RedissonLexSortedSet extends RedissonScoredSortedSet implem } @Override - public Integer lexCountHead(String toElement, boolean toInclusive) { + public int lexCountHead(String toElement, boolean toInclusive) { return get(lexCountHeadAsync(toElement, toInclusive)); } @@ -92,7 +127,7 @@ public class RedissonLexSortedSet extends RedissonScoredSortedSet implem } @Override - public Integer lexCount(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { + public int lexCount(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive) { return get(lexCountAsync(fromElement, fromInclusive, toElement, toInclusive)); } diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index ee1c68c7d..904b5b48c 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -130,7 +130,7 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc } @Override - public Integer rank(V o) { + public int rank(V o) { return get(rankAsync(o)); } diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index fa8558031..b937264ca 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -58,6 +58,7 @@ public interface RedisCommands { RedisCommand> ZRANGE = new RedisCommand>("ZRANGE", new ObjectListReplayDecoder()); RedisStrictCommand ZREMRANGEBYRANK = new RedisStrictCommand("ZREMRANGEBYRANK", new IntegerReplayConvertor()); RedisStrictCommand ZREMRANGEBYSCORE = new RedisStrictCommand("ZREMRANGEBYSCORE", new IntegerReplayConvertor()); + RedisStrictCommand ZREMRANGEBYLEX = new RedisStrictCommand("ZREMRANGEBYLEX", new IntegerReplayConvertor()); RedisCommand> ZRANGEBYLEX = new RedisCommand>("ZRANGEBYLEX", new ObjectListReplayDecoder()); RedisCommand>> ZRANGE_ENTRY = new RedisCommand>>("ZRANGE", new ScoredSortedSetReplayDecoder()); RedisCommand> ZSCAN = new RedisCommand>("ZSCAN", new NestedMultiDecoder(new ObjectListReplayDecoder(), new ScoredSortedSetScanReplayDecoder()), ValueType.OBJECT); diff --git a/src/main/java/org/redisson/core/RLexSortedSet.java b/src/main/java/org/redisson/core/RLexSortedSet.java index 2a02eec3b..b79424042 100644 --- a/src/main/java/org/redisson/core/RLexSortedSet.java +++ b/src/main/java/org/redisson/core/RLexSortedSet.java @@ -20,9 +20,15 @@ import java.util.Set; public interface RLexSortedSet extends RLexSortedSetAsync, Set, RExpirable { - Integer lexCountTail(String fromElement, boolean fromInclusive); + int removeRangeTailByLex(String fromElement, boolean fromInclusive); - Integer lexCountHead(String toElement, boolean toInclusive); + int removeRangeHeadByLex(String toElement, boolean toInclusive); + + int removeRangeByLex(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + + int lexCountTail(String fromElement, boolean fromInclusive); + + int lexCountHead(String toElement, boolean toInclusive); Collection lexRangeTail(String fromElement, boolean fromInclusive); @@ -30,9 +36,9 @@ public interface RLexSortedSet extends RLexSortedSetAsync, Set, RExpirab Collection lexRange(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); - Integer lexCount(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + int lexCount(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); - Integer rank(String o); + int rank(String o); 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 9e705caa1..e83d058cb 100644 --- a/src/main/java/org/redisson/core/RLexSortedSetAsync.java +++ b/src/main/java/org/redisson/core/RLexSortedSetAsync.java @@ -21,6 +21,12 @@ import io.netty.util.concurrent.Future; public interface RLexSortedSetAsync extends RCollectionAsync { + Future removeRangeByLexAsync(String fromElement, boolean fromInclusive, String toElement, boolean toInclusive); + + Future removeRangeTailByLexAsync(String fromElement, boolean fromInclusive); + + Future removeRangeHeadByLexAsync(String toElement, boolean toInclusive); + Future lexCountTailAsync(String fromElement, boolean fromInclusive); Future lexCountHeadAsync(String toElement, boolean toInclusive); diff --git a/src/main/java/org/redisson/core/RScoredSortedSet.java b/src/main/java/org/redisson/core/RScoredSortedSet.java index c06d69a99..a3796b7c7 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSet.java +++ b/src/main/java/org/redisson/core/RScoredSortedSet.java @@ -25,7 +25,7 @@ public interface RScoredSortedSet extends RScoredSortedSetAsync, Iterable< int removeRangeByRank(int startIndex, int endIndex); - Integer rank(V o); + int rank(V o); Double getScore(V o); From 53d4ff826b6350d59a3def9a327345238d734fa5 Mon Sep 17 00:00:00 2001 From: Steve Ungerer Date: Mon, 14 Sep 2015 09:52:55 -0400 Subject: [PATCH 20/21] non-functional; correct javadoc --- src/main/java/org/redisson/ElasticacheServersConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/redisson/ElasticacheServersConfig.java b/src/main/java/org/redisson/ElasticacheServersConfig.java index dc0fe18dc..a96e055ab 100644 --- a/src/main/java/org/redisson/ElasticacheServersConfig.java +++ b/src/main/java/org/redisson/ElasticacheServersConfig.java @@ -71,7 +71,7 @@ public class ElasticacheServersConfig extends BaseMasterSlaveServersConfig Date: Mon, 14 Sep 2015 21:14:31 +0300 Subject: [PATCH 21/21] first and last methods added. #143 --- .../org/redisson/RedissonScoredSortedSet.java | 16 ++++++++ .../client/protocol/RedisCommands.java | 2 + .../ObjectFirstResultReplayDecoder.java | 41 +++++++++++++++++++ .../org/redisson/core/RScoredSortedSet.java | 4 ++ .../redisson/core/RScoredSortedSetAsync.java | 4 ++ .../redisson/RedissonScoredSortedSetTest.java | 13 ++++++ 6 files changed, 80 insertions(+) create mode 100644 src/main/java/org/redisson/client/protocol/decoder/ObjectFirstResultReplayDecoder.java diff --git a/src/main/java/org/redisson/RedissonScoredSortedSet.java b/src/main/java/org/redisson/RedissonScoredSortedSet.java index 904b5b48c..37dce78d9 100644 --- a/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -49,6 +49,22 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc return get(addAsync(score, object)); } + public V first() { + return get(firstAsync()); + } + + public Future firstAsync() { + return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANGE_SINGLE, getName(), 0, 0); + } + + public V last() { + return get(lastAsync()); + } + + public Future lastAsync() { + return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANGE_SINGLE, getName(), -1, -1); + } + @Override public Future addAsync(double score, V object) { return commandExecutor.writeAsync(getName(), codec, RedisCommands.ZADD, getName(), BigDecimal.valueOf(score).toPlainString(), object); diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index b937264ca..ce3100456 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -34,6 +34,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.ObjectFirstResultReplayDecoder; import org.redisson.client.protocol.decoder.ObjectListReplayDecoder; import org.redisson.client.protocol.decoder.ObjectMapReplayDecoder; import org.redisson.client.protocol.decoder.ObjectSetReplayDecoder; @@ -55,6 +56,7 @@ public interface RedisCommands { RedisCommand ZSCORE_CONTAINS = new RedisCommand("ZSCORE", new BooleanNotNullReplayConvertor(), 2); RedisStrictCommand ZSCORE = new RedisStrictCommand("ZSCORE", new DoubleReplayConvertor()); RedisCommand ZRANK = new RedisCommand("ZRANK", new IntegerReplayConvertor(), 2); + RedisCommand ZRANGE_SINGLE = new RedisCommand("ZRANGE", new ObjectFirstResultReplayDecoder()); RedisCommand> ZRANGE = new RedisCommand>("ZRANGE", new ObjectListReplayDecoder()); RedisStrictCommand ZREMRANGEBYRANK = new RedisStrictCommand("ZREMRANGEBYRANK", new IntegerReplayConvertor()); RedisStrictCommand ZREMRANGEBYSCORE = new RedisStrictCommand("ZREMRANGEBYSCORE", new IntegerReplayConvertor()); diff --git a/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstResultReplayDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstResultReplayDecoder.java new file mode 100644 index 000000000..a022ff52c --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstResultReplayDecoder.java @@ -0,0 +1,41 @@ +/** + * 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 org.redisson.client.handler.State; + +import io.netty.buffer.ByteBuf; + +public class ObjectFirstResultReplayDecoder implements MultiDecoder { + + @Override + public Object decode(ByteBuf buf, State state) { + throw new UnsupportedOperationException(); + } + + @Override + public T decode(List parts, State state) { + return (T) parts.get(0); + } + + @Override + public boolean isApplicable(int paramNum, State state) { + return false; + } + +} diff --git a/src/main/java/org/redisson/core/RScoredSortedSet.java b/src/main/java/org/redisson/core/RScoredSortedSet.java index a3796b7c7..3f51debde 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSet.java +++ b/src/main/java/org/redisson/core/RScoredSortedSet.java @@ -21,6 +21,10 @@ import org.redisson.client.protocol.ScoredEntry; public interface RScoredSortedSet extends RScoredSortedSetAsync, Iterable, RExpirable { + V first(); + + V last(); + int removeRangeByScore(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive); int removeRangeByRank(int startIndex, int endIndex); diff --git a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java index ba9776188..067dc1dd3 100644 --- a/src/main/java/org/redisson/core/RScoredSortedSetAsync.java +++ b/src/main/java/org/redisson/core/RScoredSortedSetAsync.java @@ -23,6 +23,10 @@ import io.netty.util.concurrent.Future; public interface RScoredSortedSetAsync extends RExpirableAsync { + Future firstAsync(); + + Future lastAsync(); + Future removeRangeByRankAsync(int startIndex, int endIndex); Future rankAsync(V o); diff --git a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index 9f0f90f6d..9ee92e651 100644 --- a/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -21,6 +21,19 @@ import io.netty.util.concurrent.Future; public class RedissonScoredSortedSetTest extends BaseTest { + @Test + public void testFirstLast() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + set.add(0.1, "a"); + set.add(0.2, "b"); + set.add(0.3, "c"); + set.add(0.4, "d"); + + Assert.assertEquals("a", set.first()); + Assert.assertEquals("d", set.last()); + } + + @Test public void testRemoveRangeByScore() { RScoredSortedSet set = redisson.getScoredSortedSet("simple");