Merge branch 'master' into 3.0.0

# Conflicts:
#	pom.xml
#	redisson-all/pom.xml
#	redisson-tomcat/pom.xml
#	redisson-tomcat/redisson-tomcat-6/pom.xml
#	redisson-tomcat/redisson-tomcat-7/pom.xml
#	redisson-tomcat/redisson-tomcat-8/pom.xml
#	redisson/pom.xml
pull/872/head
Nikita 8 years ago
commit 304de3b008

@ -4,6 +4,25 @@ Redisson Releases History
Try __ULTRA-FAST__ [Redisson PRO](https://redisson.pro) edition.
## Please take part in [Redisson survey](https://www.surveymonkey.com/r/QXQZH5D)
### 21-Mar-2017 - versions 2.8.2 and 3.3.2 released
Feature - Redisson's Spring custom namespace support (thanks to Rui Gu)
Feature - ability to set custom connection manager (thanks to Saikiran Daripelli)
Feature - autoconfigured Spring Boot CacheStatisticsProvider implementation (thanks to Craig Andrews)
Feature - `RKeys.touch` and `RObject.touch` methods added
Feature - `RedissonCompletionService` implementation added
Feature - `RMap.getReadWriteLock` method added
Fixed - NPE during `RLocalCachedMap.fastRemove` invocation
Fixed - `redisson-tomcat-8` module is not compatible with Tomcat 8.5
Fixed - URLBuilder methods should be synchronized
Fixed - use PSETEX in `RBucket.set` method
Fixed - `DelayedQueue.remove()` and `DelayedQueue.removeAll()`
Fixed - unable to define Type and AvroSchema for AvroJacksonCodec
Fixed - ReadWriteLock leaseTimeout calculation
Fixed - `Config.fromJson(file)` method, throws StackOverflowError
### 04-Mar-2017 - versions 2.8.1 and 3.3.1 released
Feature - Cache with SoftReference support added for `RLocalCachedMap`

@ -1,14 +1,14 @@
Redisson: Redis based In-Memory Data Grid for Java.
====
[Quick start](https://github.com/redisson/redisson#quick-start) | [Documentation](https://github.com/redisson/redisson/wiki) | [Javadocs](http://www.javadoc.io/doc/org.redisson/redisson/3.3.1) | [Changelog](https://github.com/redisson/redisson/blob/master/CHANGELOG.md) | [Code examples](https://github.com/redisson/redisson-examples) | [Support chat](https://gitter.im/mrniko/redisson)
[Quick start](https://github.com/redisson/redisson#quick-start) | [Documentation](https://github.com/redisson/redisson/wiki) | [Javadocs](http://www.javadoc.io/doc/org.redisson/redisson/3.3.2) | [Changelog](https://github.com/redisson/redisson/blob/master/CHANGELOG.md) | [Code examples](https://github.com/redisson/redisson-examples) | [Support chat](https://gitter.im/mrniko/redisson) | [Ultra-fast version](https://redisson.pro)
Based on high-performance async and lock-free Java Redis client and [Netty](http://netty.io) framework.
Redis 2.8+ compatible.
## Please take part in [Redisson survey](https://www.surveymonkey.com/r/QXQZH5D)
| Stable Release Version | JDK Version compatibility | Release Date |
| ------------- | ------------- | ------------|
| 3.3.1 | 1.8+ | 03.03.2017 |
| 2.8.1 | 1.6, 1.7, 1.8 and Android | 03.03.2017 |
| 3.3.2 | 1.8+ | 21.03.2017 |
| 2.8.2 | 1.6, 1.7, 1.8 and Android | 21.03.2017 |
__NOTE__: Both version lines have same features except `CompletionStage` interface added in 3.x.x
@ -55,7 +55,7 @@ Features
Who uses Redisson
================================
[Electronic Arts](http://ea.com), [Baidu](http://baidu.com), [New Relic Synthetics](https://newrelic.com/synthetics), [Brookhaven National Laboratory](http://bnl.gov/), [Singtel](http://singtel.com), [Infor](http://www.infor.com/), [Setronica](http://setronica.com/), [Monits](http://monits.com/), [Netflix Dyno client](https://github.com/Netflix/dyno), [武林Q传](http://www.nbrpg.com/), [Ocous](http://www.ocous.com/), [Invaluable](http://www.invaluable.com/), [Clover](https://www.clover.com/) , [Apache Karaf Decanter](https://karaf.apache.org/projects.html#decanter), [Atmosphere Framework](http://async-io.org/), [BrandsEye](http://brandseye.com), [Datorama](http://datorama.com/), [BrightCloud](http://brightcloud.com/), [Azar](http://azarlive.com/), [Snapfish](http://snapfish.com), [Crimson Hexagon](http://www.crimsonhexagon.com), [Quby](http://quby.com/), [Base CRM](http://getbase.com)
[Electronic Arts](http://ea.com), [Baidu](http://baidu.com), [New Relic Synthetics](https://newrelic.com/synthetics), [Brookhaven National Laboratory](http://bnl.gov/), [Singtel](http://singtel.com), [Infor](http://www.infor.com/), [Netflix Dyno client](https://github.com/Netflix/dyno), [Ocous](http://www.ocous.com/), [Invaluable](http://www.invaluable.com/), [Clover](https://www.clover.com/) , [Apache Karaf Decanter](https://karaf.apache.org/projects.html#decanter), [Atmosphere Framework](http://async-io.org/), [BrandsEye](http://brandseye.com), [Datorama](http://datorama.com/), [BrightCloud](http://brightcloud.com/), [Azar](http://azarlive.com/), [Snapfish](http://snapfish.com), [Crimson Hexagon](http://www.crimsonhexagon.com), [Quby](http://quby.com/), [Base CRM](http://getbase.com)
Articles
================================
@ -82,23 +82,23 @@ Quick start
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.3.1</version>
<version>3.3.2</version>
</dependency>
<!-- JDK 1.6+ compatible -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>2.8.1</version>
<version>2.8.2</version>
</dependency>
#### Gradle
// JDK 1.8+ compatible
compile 'org.redisson:redisson:3.3.1'
compile 'org.redisson:redisson:3.3.2'
// JDK 1.6+ compatible
compile 'org.redisson:redisson:2.8.1'
compile 'org.redisson:redisson:2.8.2'
#### Java
@ -123,11 +123,11 @@ RExecutorService executor = redisson.getExecutorService("myExecutorService");
Downloads
===============================
[Redisson 3.3.1](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=3.3.1&e=jar),
[Redisson node 3.3.1](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.3.1&e=jar)
[Redisson 3.3.2](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=3.3.2&e=jar),
[Redisson node 3.3.2](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.3.2&e=jar)
[Redisson 2.8.1](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=2.8.1&e=jar),
[Redisson node 2.8.1](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=2.8.1&e=jar)
[Redisson 2.8.2](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=2.8.2&e=jar),
[Redisson node 2.8.2](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=2.8.2&e=jar)
### Supported by

@ -3,7 +3,7 @@
<groupId>org.redisson</groupId>
<artifactId>redisson-parent</artifactId>
<version>3.3.3-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Redisson</name>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-parent</artifactId>
<version>3.3.3-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-parent</artifactId>
<version>3.3.3-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-tomcat</artifactId>
<version>3.3.3-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-tomcat</artifactId>
<version>3.3.3-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-tomcat</artifactId>
<version>3.3.3-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-parent</artifactId>
<version>3.3.3-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
@ -186,36 +186,36 @@
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.8.7</version>
<version>2.6.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.8.7</version>
<version>2.6.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.7</version>
<version>2.6.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-cbor</artifactId>
<version>2.8.7</version>
<version>2.6.7</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-smile</artifactId>
<version>2.8.7</version>
<version>2.6.7</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-avro</artifactId>
<version>2.8.7</version>
<version>2.6.7</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
@ -263,6 +263,7 @@
<artifactId>spring-boot-actuator</artifactId>
<version>[1.4,)</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>

@ -75,12 +75,15 @@ public class PubSubPatternStatusListener<V> implements RedisPubSubListener<V> {
@Override
public boolean onStatus(PubSubType type, String channel) {
if (type == PubSubType.PSUBSCRIBE) {
listener.onPSubscribe(channel);
} else if (type == PubSubType.PUNSUBSCRIBE) {
listener.onPUnsubscribe(channel);
if (channel.equals(name)) {
if (type == PubSubType.PSUBSCRIBE) {
listener.onPSubscribe(channel);
} else if (type == PubSubType.PUNSUBSCRIBE) {
listener.onPUnsubscribe(channel);
}
return true;
}
return true;
return false;
}
}

@ -75,12 +75,15 @@ public class PubSubStatusListener<V> implements RedisPubSubListener<V> {
@Override
public boolean onStatus(PubSubType type, String channel) {
if (type == PubSubType.SUBSCRIBE) {
listener.onSubscribe(channel);
} else if (type == PubSubType.UNSUBSCRIBE) {
listener.onUnsubscribe(channel);
if (channel.equals(name)) {
if (type == PubSubType.SUBSCRIBE) {
listener.onSubscribe(channel);
} else if (type == PubSubType.UNSUBSCRIBE) {
listener.onUnsubscribe(channel);
}
return true;
}
return true;
return false;
}
}

@ -193,12 +193,12 @@ public class Redisson implements RedissonClient {
@Override
public <V> RGeo<V> getGeo(String name) {
return new RedissonGeo<V>(connectionManager.getCommandExecutor(), name);
return new RedissonGeo<V>(connectionManager.getCommandExecutor(), name, this);
}
@Override
public <V> RGeo<V> getGeo(String name, Codec codec) {
return new RedissonGeo<V>(codec, connectionManager.getCommandExecutor(), name);
return new RedissonGeo<V>(codec, connectionManager.getCommandExecutor(), name, this);
}
@Override
@ -233,12 +233,12 @@ public class Redisson implements RedissonClient {
@Override
public <V> RList<V> getList(String name) {
return new RedissonList<V>(connectionManager.getCommandExecutor(), name);
return new RedissonList<V>(connectionManager.getCommandExecutor(), name, this);
}
@Override
public <V> RList<V> getList(String name, Codec codec) {
return new RedissonList<V>(codec, connectionManager.getCommandExecutor(), name);
return new RedissonList<V>(codec, connectionManager.getCommandExecutor(), name, this);
}
@Override
@ -253,17 +253,17 @@ public class Redisson implements RedissonClient {
@Override
public <K, V> RLocalCachedMap<K, V> getLocalCachedMap(String name, LocalCachedMapOptions options) {
return new RedissonLocalCachedMap<K, V>(id, connectionManager.getCommandExecutor(), name, options);
return new RedissonLocalCachedMap<K, V>(id, connectionManager.getCommandExecutor(), name, options, evictionScheduler, this);
}
@Override
public <K, V> RLocalCachedMap<K, V> getLocalCachedMap(String name, Codec codec, LocalCachedMapOptions options) {
return new RedissonLocalCachedMap<K, V>(id, codec, connectionManager.getCommandExecutor(), name, options);
return new RedissonLocalCachedMap<K, V>(id, codec, connectionManager.getCommandExecutor(), name, options, evictionScheduler, this);
}
@Override
public <K, V> RMap<K, V> getMap(String name) {
return new RedissonMap<K, V>(id, connectionManager.getCommandExecutor(), name);
return new RedissonMap<K, V>(id, connectionManager.getCommandExecutor(), name, this);
}
@Override
@ -298,27 +298,27 @@ public class Redisson implements RedissonClient {
@Override
public <V> RSetCache<V> getSetCache(String name) {
return new RedissonSetCache<V>(evictionScheduler, connectionManager.getCommandExecutor(), name);
return new RedissonSetCache<V>(evictionScheduler, connectionManager.getCommandExecutor(), name, this);
}
@Override
public <V> RSetCache<V> getSetCache(String name, Codec codec) {
return new RedissonSetCache<V>(codec, evictionScheduler, connectionManager.getCommandExecutor(), name);
return new RedissonSetCache<V>(codec, evictionScheduler, connectionManager.getCommandExecutor(), name, this);
}
@Override
public <K, V> RMapCache<K, V> getMapCache(String name) {
return new RedissonMapCache<K, V>(id, evictionScheduler, connectionManager.getCommandExecutor(), name);
return new RedissonMapCache<K, V>(id, evictionScheduler, connectionManager.getCommandExecutor(), name, this);
}
@Override
public <K, V> RMapCache<K, V> getMapCache(String name, Codec codec) {
return new RedissonMapCache<K, V>(id, codec, evictionScheduler, connectionManager.getCommandExecutor(), name);
return new RedissonMapCache<K, V>(id, codec, evictionScheduler, connectionManager.getCommandExecutor(), name, this);
}
@Override
public <K, V> RMap<K, V> getMap(String name, Codec codec) {
return new RedissonMap<K, V>(id, codec, connectionManager.getCommandExecutor(), name);
return new RedissonMap<K, V>(id, codec, connectionManager.getCommandExecutor(), name, this);
}
@Override
@ -338,12 +338,12 @@ public class Redisson implements RedissonClient {
@Override
public <V> RSet<V> getSet(String name) {
return new RedissonSet<V>(connectionManager.getCommandExecutor(), name);
return new RedissonSet<V>(connectionManager.getCommandExecutor(), name, this);
}
@Override
public <V> RSet<V> getSet(String name, Codec codec) {
return new RedissonSet<V>(codec, connectionManager.getCommandExecutor(), name);
return new RedissonSet<V>(codec, connectionManager.getCommandExecutor(), name, this);
}
@Override
@ -399,17 +399,17 @@ public class Redisson implements RedissonClient {
@Override
public <V> RScoredSortedSet<V> getScoredSortedSet(String name) {
return new RedissonScoredSortedSet<V>(connectionManager.getCommandExecutor(), name);
return new RedissonScoredSortedSet<V>(connectionManager.getCommandExecutor(), name, this);
}
@Override
public <V> RScoredSortedSet<V> getScoredSortedSet(String name, Codec codec) {
return new RedissonScoredSortedSet<V>(codec, connectionManager.getCommandExecutor(), name);
return new RedissonScoredSortedSet<V>(codec, connectionManager.getCommandExecutor(), name, this);
}
@Override
public RLexSortedSet getLexSortedSet(String name) {
return new RedissonLexSortedSet(connectionManager.getCommandExecutor(), name);
return new RedissonLexSortedSet(connectionManager.getCommandExecutor(), name, this);
}
@Override
@ -434,12 +434,12 @@ public class Redisson implements RedissonClient {
@Override
public <V> RBlockingFairQueue<V> getBlockingFairQueue(String name) {
return new RedissonBlockingFairQueue<V>(connectionManager.getCommandExecutor(), name, semaphorePubSub, id);
return new RedissonBlockingFairQueue<V>(connectionManager.getCommandExecutor(), name, semaphorePubSub, id, this);
}
@Override
public <V> RBlockingFairQueue<V> getBlockingFairQueue(String name, Codec codec) {
return new RedissonBlockingFairQueue<V>(codec, connectionManager.getCommandExecutor(), name, semaphorePubSub, id);
return new RedissonBlockingFairQueue<V>(codec, connectionManager.getCommandExecutor(), name, semaphorePubSub, id, this);
}
@Override
@ -452,52 +452,52 @@ public class Redisson implements RedissonClient {
@Override
public <V> RQueue<V> getQueue(String name) {
return new RedissonQueue<V>(connectionManager.getCommandExecutor(), name);
return new RedissonQueue<V>(connectionManager.getCommandExecutor(), name, this);
}
@Override
public <V> RQueue<V> getQueue(String name, Codec codec) {
return new RedissonQueue<V>(codec, connectionManager.getCommandExecutor(), name);
return new RedissonQueue<V>(codec, connectionManager.getCommandExecutor(), name, this);
}
@Override
public <V> RBlockingQueue<V> getBlockingQueue(String name) {
return new RedissonBlockingQueue<V>(connectionManager.getCommandExecutor(), name);
return new RedissonBlockingQueue<V>(connectionManager.getCommandExecutor(), name, this);
}
@Override
public <V> RBlockingQueue<V> getBlockingQueue(String name, Codec codec) {
return new RedissonBlockingQueue<V>(codec, connectionManager.getCommandExecutor(), name);
return new RedissonBlockingQueue<V>(codec, connectionManager.getCommandExecutor(), name, this);
}
@Override
public <V> RBoundedBlockingQueue<V> getBoundedBlockingQueue(String name) {
return new RedissonBoundedBlockingQueue<V>(semaphorePubSub, connectionManager.getCommandExecutor(), name);
return new RedissonBoundedBlockingQueue<V>(semaphorePubSub, connectionManager.getCommandExecutor(), name, this);
}
@Override
public <V> RBoundedBlockingQueue<V> getBoundedBlockingQueue(String name, Codec codec) {
return new RedissonBoundedBlockingQueue<V>(semaphorePubSub, codec, connectionManager.getCommandExecutor(), name);
return new RedissonBoundedBlockingQueue<V>(semaphorePubSub, codec, connectionManager.getCommandExecutor(), name, this);
}
@Override
public <V> RDeque<V> getDeque(String name) {
return new RedissonDeque<V>(connectionManager.getCommandExecutor(), name);
return new RedissonDeque<V>(connectionManager.getCommandExecutor(), name, this);
}
@Override
public <V> RDeque<V> getDeque(String name, Codec codec) {
return new RedissonDeque<V>(codec, connectionManager.getCommandExecutor(), name);
return new RedissonDeque<V>(codec, connectionManager.getCommandExecutor(), name, this);
}
@Override
public <V> RBlockingDeque<V> getBlockingDeque(String name) {
return new RedissonBlockingDeque<V>(connectionManager.getCommandExecutor(), name);
return new RedissonBlockingDeque<V>(connectionManager.getCommandExecutor(), name, this);
}
@Override
public <V> RBlockingDeque<V> getBlockingDeque(String name, Codec codec) {
return new RedissonBlockingDeque<V>(codec, connectionManager.getCommandExecutor(), name);
return new RedissonBlockingDeque<V>(codec, connectionManager.getCommandExecutor(), name, this);
};
@Override

@ -17,6 +17,7 @@ package org.redisson;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.redisson.api.RAtomicDoubleAsync;
import org.redisson.api.RAtomicLongAsync;
@ -42,7 +43,6 @@ import org.redisson.api.RScriptAsync;
import org.redisson.api.RSetAsync;
import org.redisson.api.RSetCacheAsync;
import org.redisson.api.RTopicAsync;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.redisson.command.CommandBatchService;
import org.redisson.connection.ConnectionManager;
@ -60,6 +60,10 @@ public class RedissonBatch implements RBatch {
private final CommandBatchService executorService;
private final UUID id;
private long timeout;
private int retryAttempts;
private long retryInterval;
protected RedissonBatch(UUID id, EvictionScheduler evictionScheduler, ConnectionManager connectionManager) {
this.executorService = new CommandBatchService(connectionManager);
this.evictionScheduler = evictionScheduler;
@ -88,32 +92,32 @@ public class RedissonBatch implements RBatch {
@Override
public <V> RListAsync<V> getList(String name) {
return new RedissonList<V>(executorService, name);
return new RedissonList<V>(executorService, name, null);
}
@Override
public <V> RListAsync<V> getList(String name, Codec codec) {
return new RedissonList<V>(codec, executorService, name);
return new RedissonList<V>(codec, executorService, name, null);
}
@Override
public <K, V> RMapAsync<K, V> getMap(String name) {
return new RedissonMap<K, V>(id, executorService, name);
return new RedissonMap<K, V>(id, executorService, name, null);
}
@Override
public <K, V> RMapAsync<K, V> getMap(String name, Codec codec) {
return new RedissonMap<K, V>(id, codec, executorService, name);
return new RedissonMap<K, V>(id, codec, executorService, name, null);
}
@Override
public <V> RSetAsync<V> getSet(String name) {
return new RedissonSet<V>(executorService, name);
return new RedissonSet<V>(executorService, name, null);
}
@Override
public <V> RSetAsync<V> getSet(String name, Codec codec) {
return new RedissonSet<V>(codec, executorService, name);
return new RedissonSet<V>(codec, executorService, name, null);
}
@Override
@ -128,42 +132,42 @@ public class RedissonBatch implements RBatch {
@Override
public <V> RQueueAsync<V> getQueue(String name) {
return new RedissonQueue<V>(executorService, name);
return new RedissonQueue<V>(executorService, name, null);
}
@Override
public <V> RQueueAsync<V> getQueue(String name, Codec codec) {
return new RedissonQueue<V>(codec, executorService, name);
return new RedissonQueue<V>(codec, executorService, name, null);
}
@Override
public <V> RBlockingQueueAsync<V> getBlockingQueue(String name) {
return new RedissonBlockingQueue<V>(executorService, name);
return new RedissonBlockingQueue<V>(executorService, name, null);
}
@Override
public <V> RBlockingQueueAsync<V> getBlockingQueue(String name, Codec codec) {
return new RedissonBlockingQueue<V>(codec, executorService, name);
return new RedissonBlockingQueue<V>(codec, executorService, name, null);
}
@Override
public <V> RBlockingDequeAsync<V> getBlockingDeque(String name) {
return new RedissonBlockingDeque<V>(executorService, name);
return new RedissonBlockingDeque<V>(executorService, name, null);
}
@Override
public <V> RBlockingDequeAsync<V> getBlockingDeque(String name, Codec codec) {
return new RedissonBlockingDeque<V>(codec, executorService, name);
return new RedissonBlockingDeque<V>(codec, executorService, name, null);
}
@Override
public <V> RDequeAsync<V> getDeque(String name) {
return new RedissonDeque<V>(executorService, name);
return new RedissonDeque<V>(executorService, name, null);
}
@Override
public <V> RDequeAsync<V> getDeque(String name, Codec codec) {
return new RedissonDeque<V>(codec, executorService, name);
return new RedissonDeque<V>(codec, executorService, name, null);
}
@Override
@ -178,17 +182,17 @@ public class RedissonBatch implements RBatch {
@Override
public <V> RScoredSortedSetAsync<V> getScoredSortedSet(String name) {
return new RedissonScoredSortedSet<V>(executorService, name);
return new RedissonScoredSortedSet<V>(executorService, name, null);
}
@Override
public <V> RScoredSortedSetAsync<V> getScoredSortedSet(String name, Codec codec) {
return new RedissonScoredSortedSet<V>(codec, executorService, name);
return new RedissonScoredSortedSet<V>(codec, executorService, name, null);
}
@Override
public RLexSortedSetAsync getLexSortedSet(String name) {
return new RedissonLexSortedSet(executorService, name);
return new RedissonLexSortedSet(executorService, name, null);
}
@Override
@ -198,12 +202,12 @@ public class RedissonBatch implements RBatch {
@Override
public <K, V> RMapCacheAsync<K, V> getMapCache(String name, Codec codec) {
return new RedissonMapCache<K, V>(id, codec, evictionScheduler, executorService, name);
return new RedissonMapCache<K, V>(id, codec, evictionScheduler, executorService, name, null);
}
@Override
public <K, V> RMapCacheAsync<K, V> getMapCache(String name) {
return new RedissonMapCache<K, V>(id, evictionScheduler, executorService, name);
return new RedissonMapCache<K, V>(id, evictionScheduler, executorService, name, null);
}
@Override
@ -218,32 +222,50 @@ public class RedissonBatch implements RBatch {
@Override
public <V> RSetCacheAsync<V> getSetCache(String name) {
return new RedissonSetCache<V>(evictionScheduler, executorService, name);
return new RedissonSetCache<V>(evictionScheduler, executorService, name, null);
}
@Override
public <V> RSetCacheAsync<V> getSetCache(String name, Codec codec) {
return new RedissonSetCache<V>(codec, evictionScheduler, executorService, name);
return new RedissonSetCache<V>(codec, evictionScheduler, executorService, name, null);
}
@Override
public RBatch retryAttempts(int retryAttempts) {
this.retryAttempts = retryAttempts;
return this;
}
@Override
public RBatch retryInterval(long retryInterval, TimeUnit unit) {
this.retryInterval = unit.toMillis(retryInterval);
return this;
}
@Override
public RBatch timeout(long timeout, TimeUnit unit) {
this.timeout = unit.toMillis(timeout);
return this;
}
@Override
public List<?> execute() {
return executorService.execute();
return executorService.execute(timeout, retryAttempts, retryInterval);
}
@Override
public void executeSkipResult() {
executorService.executeSkipResult();
executorService.executeSkipResult(timeout, retryAttempts, retryInterval);
}
@Override
public RFuture<Void> executeSkipResultAsync() {
return executorService.executeSkipResultAsync();
return executorService.executeSkipResultAsync(timeout, retryAttempts, retryInterval);
}
@Override
public RFuture<List<?>> executeAsync() {
return executorService.executeAsync();
return executorService.executeAsync(timeout, retryAttempts, retryInterval);
}
@Override
@ -268,12 +290,12 @@ public class RedissonBatch implements RBatch {
@Override
public <V> RGeoAsync<V> getGeo(String name) {
return new RedissonGeo<V>(executorService, name);
return new RedissonGeo<V>(executorService, name, null);
}
@Override
public <V> RGeoAsync<V> getGeo(String name, Codec codec) {
return new RedissonGeo<V>(codec, executorService, name);
return new RedissonGeo<V>(codec, executorService, name, null);
}
@Override

@ -22,6 +22,7 @@ import java.util.concurrent.TimeUnit;
import org.redisson.api.RBlockingDeque;
import org.redisson.api.RFuture;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.command.CommandAsyncExecutor;
@ -38,14 +39,14 @@ public class RedissonBlockingDeque<V> extends RedissonDeque<V> implements RBlock
private final RedissonBlockingQueue<V> blockingQueue;
protected RedissonBlockingDeque(CommandAsyncExecutor commandExecutor, String name) {
super(commandExecutor, name);
blockingQueue = new RedissonBlockingQueue<V>(commandExecutor, name);
protected RedissonBlockingDeque(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(commandExecutor, name, redisson);
blockingQueue = new RedissonBlockingQueue<V>(commandExecutor, name, redisson);
}
protected RedissonBlockingDeque(Codec codec, CommandAsyncExecutor commandExecutor, String name) {
super(codec, commandExecutor, name);
blockingQueue = new RedissonBlockingQueue<V>(codec, commandExecutor, name);
protected RedissonBlockingDeque(Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(codec, commandExecutor, name, redisson);
blockingQueue = new RedissonBlockingQueue<V>(codec, commandExecutor, name, redisson);
}
@Override

@ -25,6 +25,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.redisson.api.RBlockingFairQueue;
import org.redisson.api.RFuture;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.codec.StringCodec;
@ -51,15 +52,15 @@ public class RedissonBlockingFairQueue<V> extends RedissonBlockingQueue<V> imple
private final AtomicInteger instances = new AtomicInteger();
private final SemaphorePubSub semaphorePubSub;
protected RedissonBlockingFairQueue(CommandExecutor commandExecutor, String name, SemaphorePubSub semaphorePubSub, UUID id) {
super(commandExecutor, name);
protected RedissonBlockingFairQueue(CommandExecutor commandExecutor, String name, SemaphorePubSub semaphorePubSub, UUID id, RedissonClient redisson) {
super(commandExecutor, name, redisson);
this.semaphorePubSub = semaphorePubSub;
this.id = id;
instances.incrementAndGet();
}
protected RedissonBlockingFairQueue(Codec codec, CommandExecutor commandExecutor, String name, SemaphorePubSub semaphorePubSub, UUID id) {
super(codec, commandExecutor, name);
protected RedissonBlockingFairQueue(Codec codec, CommandExecutor commandExecutor, String name, SemaphorePubSub semaphorePubSub, UUID id, RedissonClient redisson) {
super(codec, commandExecutor, name, redisson);
this.semaphorePubSub = semaphorePubSub;
this.id = id;
instances.incrementAndGet();

@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RFuture;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.redisson.client.protocol.RedisCommand;
import org.redisson.client.protocol.RedisCommands;
@ -40,12 +41,12 @@ import org.redisson.connection.decoder.ListDrainToDecoder;
*/
public class RedissonBlockingQueue<V> extends RedissonQueue<V> implements RBlockingQueue<V> {
protected RedissonBlockingQueue(CommandAsyncExecutor commandExecutor, String name) {
super(commandExecutor, name);
protected RedissonBlockingQueue(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(commandExecutor, name, redisson);
}
protected RedissonBlockingQueue(Codec codec, CommandAsyncExecutor commandExecutor, String name) {
super(codec, commandExecutor, name);
protected RedissonBlockingQueue(Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(codec, commandExecutor, name, redisson);
}
@Override

@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
import org.redisson.api.RBoundedBlockingQueue;
import org.redisson.api.RFuture;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.protocol.RedisCommand;
@ -47,14 +48,14 @@ public class RedissonBoundedBlockingQueue<V> extends RedissonQueue<V> implements
private final CommandExecutor commandExecutor;
private final SemaphorePubSub semaphorePubSub;
protected RedissonBoundedBlockingQueue(SemaphorePubSub semaphorePubSub, CommandExecutor commandExecutor, String name) {
super(commandExecutor, name);
protected RedissonBoundedBlockingQueue(SemaphorePubSub semaphorePubSub, CommandExecutor commandExecutor, String name, RedissonClient redisson) {
super(commandExecutor, name, redisson);
this.semaphorePubSub = semaphorePubSub;
this.commandExecutor = commandExecutor;
}
protected RedissonBoundedBlockingQueue(SemaphorePubSub semaphorePubSub, Codec codec, CommandExecutor commandExecutor, String name) {
super(codec, commandExecutor, name);
protected RedissonBoundedBlockingQueue(SemaphorePubSub semaphorePubSub, Codec codec, CommandExecutor commandExecutor, String name, RedissonClient redisson) {
super(codec, commandExecutor, name, redisson);
this.semaphorePubSub = semaphorePubSub;
this.commandExecutor = commandExecutor;
}

@ -492,11 +492,6 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
return get(pollLastAndOfferFirstToAsync(dequeName));
}
@Override
public V pollLastAndOfferFirstTo(RQueue<V> deque) {
return get(pollLastAndOfferFirstToAsync(deque.getName()));
}
@Override
public void destroy() {
queueTransferService.remove(getQueueName());

@ -20,6 +20,7 @@ import java.util.NoSuchElementException;
import org.redisson.api.RDeque;
import org.redisson.api.RFuture;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.redisson.client.protocol.RedisCommand;
import org.redisson.client.protocol.RedisCommand.ValueType;
@ -41,12 +42,12 @@ public class RedissonDeque<V> extends RedissonQueue<V> implements RDeque<V> {
private static final RedisCommand<Object> LRANGE_SINGLE = new RedisCommand<Object>("LRANGE", new ListFirstObjectDecoder());
protected RedissonDeque(CommandAsyncExecutor commandExecutor, String name) {
super(commandExecutor, name);
protected RedissonDeque(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(commandExecutor, name, redisson);
}
public RedissonDeque(Codec codec, CommandAsyncExecutor commandExecutor, String name) {
super(codec, commandExecutor, name);
public RedissonDeque(Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(codec, commandExecutor, name, redisson);
}
@Override

@ -18,7 +18,6 @@ package org.redisson;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
@ -39,13 +38,14 @@ import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import org.redisson.api.CronSchedule;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RFuture;
import org.redisson.api.RRemoteService;
import org.redisson.api.RScheduledExecutorService;
import org.redisson.api.RScheduledFuture;
import org.redisson.api.RSemaphore;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.api.RemoteInvocationOptions;
import org.redisson.api.annotation.RInject;
import org.redisson.api.listener.MessageListener;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.LongCodec;
@ -59,12 +59,15 @@ import org.redisson.executor.RemoteExecutorServiceAsync;
import org.redisson.executor.RemoteExecutorServiceImpl;
import org.redisson.executor.RemotePromise;
import org.redisson.executor.ScheduledExecutorRemoteService;
import org.redisson.misc.Injector;
import org.redisson.misc.RPromise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.ThreadLocalRandom;
/**
*
@ -87,9 +90,16 @@ public class RedissonExecutorService implements RScheduledExecutorService {
private final String schedulerQueueName;
private final String schedulerChannelName;
private final String workersChannelName;
private final String workersSemaphoreName;
private final String workersCounterName;
private final String tasksCounterName;
private final String statusName;
private final RTopic<Integer> terminationTopic;
private final RRemoteService remoteService;
private final RTopic<String> workersTopic;
private int workersGroupListenerId;
private final RemoteExecutorServiceAsync asyncScheduledService;
private final RemoteExecutorServiceAsync asyncScheduledServiceAtFixed;
@ -120,6 +130,13 @@ public class RedissonExecutorService implements RScheduledExecutorService {
schedulerQueueName = objectName + ":scheduler";
schedulerTasksName = objectName + ":scheduler-tasks";
workersChannelName = objectName + ":workers-channel";
workersSemaphoreName = objectName + ":workers-semaphore";
workersCounterName = objectName + ":workers-counter";
remoteService = redisson.getRemoteService(name, codec);
workersTopic = redisson.getTopic(workersChannelName);
ExecutorRemoteService remoteService = new ExecutorRemoteService(codec, redisson, name, commandExecutor);
remoteService.setTerminationTopicName(terminationTopic.getChannelNames().get(0));
remoteService.setTasksCounterName(tasksCounterName);
@ -138,13 +155,36 @@ public class RedissonExecutorService implements RScheduledExecutorService {
asyncScheduledServiceAtFixed = scheduledRemoteService.get(RemoteExecutorServiceAsync.class, RemoteInvocationOptions.defaults().noAck().noResult());
}
protected String generateRequestId() {
byte[] id = new byte[16];
// TODO JDK UPGRADE replace to native ThreadLocalRandom
ThreadLocalRandom.current().nextBytes(id);
return ByteBufUtil.hexDump(id);
}
@Override
public int countActiveWorkers() {
String id = generateRequestId();
int subscribers = (int) workersTopic.publish(id);
RSemaphore semaphore = redisson.getSemaphore(workersSemaphoreName + ":" + id);
try {
semaphore.tryAcquire(subscribers, 10, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
RAtomicLong atomicLong = redisson.getAtomicLong(workersCounterName + ":" + id);
long result = atomicLong.get();
redisson.getKeys().delete(semaphore, atomicLong);
return (int) result;
}
@Override
public void registerWorkers(int workers) {
registerWorkers(workers, commandExecutor.getConnectionManager().getExecutor());
}
@Override
public void registerWorkers(int workers, ExecutorService executor) {
public void registerWorkers(final int workers, ExecutorService executor) {
QueueTransferTask scheduler = new QueueTransferTask(connectionManager) {
@Override
protected RTopic<Long> getTopic() {
@ -181,7 +221,14 @@ public class RedissonExecutorService implements RScheduledExecutorService {
service.setSchedulerChannelName(schedulerChannelName);
service.setSchedulerQueueName(schedulerQueueName);
redisson.getRemoteService(name, codec).register(RemoteExecutorService.class, service, workers, executor);
remoteService.register(RemoteExecutorService.class, service, workers, executor);
workersGroupListenerId = workersTopic.addListener(new MessageListener<String>() {
@Override
public void onMessage(String channel, String id) {
redisson.getAtomicLong(workersCounterName + ":" + id).getAndAdd(workers);
redisson.getSemaphore(workersSemaphoreName + ":" + id).release();
}
});
}
@Override
@ -195,18 +242,7 @@ public class RedissonExecutorService implements RScheduledExecutorService {
private byte[] encode(Object task) {
// erase RedissonClient field to avoid its serialization
Field[] fields = task.getClass().getDeclaredFields();
for (Field field : fields) {
if (RedissonClient.class.isAssignableFrom(field.getType())
&& field.isAnnotationPresent(RInject.class)) {
field.setAccessible(true);
try {
field.set(task, null);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
Injector.inject(task, null);
try {
return codec.getValueEncoder().encode(task);
@ -238,6 +274,9 @@ public class RedissonExecutorService implements RScheduledExecutorService {
@Override
public void shutdown() {
remoteService.deregister(RemoteExecutorService.class);
workersTopic.removeListener(workersGroupListenerId);
commandExecutor.evalWrite(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_VOID,
"if redis.call('exists', KEYS[2]) == 0 then "
+ "if redis.call('get', KEYS[1]) == '0' or redis.call('exists', KEYS[1]) == 0 then "

@ -27,6 +27,7 @@ import org.redisson.api.GeoPosition;
import org.redisson.api.GeoUnit;
import org.redisson.api.RFuture;
import org.redisson.api.RGeo;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.GeoEntryCodec;
import org.redisson.client.codec.ScoredCodec;
@ -55,14 +56,14 @@ public class RedissonGeo<V> extends RedissonScoredSortedSet<V> implements RGeo<V
MultiDecoder<Map<Object, Object>> postitionDecoder;
MultiDecoder<Map<Object, Object>> distanceDecoder;
public RedissonGeo(CommandAsyncExecutor connectionManager, String name) {
super(connectionManager, name);
public RedissonGeo(CommandAsyncExecutor connectionManager, String name, RedissonClient redisson) {
super(connectionManager, name, redisson);
postitionDecoder = new NestedMultiDecoder(new GeoPositionDecoder(), new GeoDistanceDecoder(codec), new GeoMapReplayDecoder(), true);
distanceDecoder = new FlatNestedMultiDecoder(new GeoDistanceDecoder(codec), new GeoMapReplayDecoder(), true);
}
public RedissonGeo(Codec codec, CommandAsyncExecutor connectionManager, String name) {
super(codec, connectionManager, name);
public RedissonGeo(Codec codec, CommandAsyncExecutor connectionManager, String name, RedissonClient redisson) {
super(codec, connectionManager, name, redisson);
postitionDecoder = new NestedMultiDecoder(new GeoPositionDecoder(), new GeoDistanceDecoder(codec), new GeoMapReplayDecoder(), true);
distanceDecoder = new FlatNestedMultiDecoder(new GeoDistanceDecoder(codec), new GeoMapReplayDecoder(), true);
}

@ -25,11 +25,13 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import org.redisson.api.RFuture;
import org.redisson.api.RKeys;
import org.redisson.api.RObject;
import org.redisson.api.RType;
import org.redisson.client.RedisException;
import org.redisson.client.codec.ScanCodec;
@ -257,7 +259,22 @@ public class RedissonKeys implements RKeys {
public long delete(String ... keys) {
return commandExecutor.get(deleteAsync(keys));
}
@Override
public long delete(RObject ... objects) {
return commandExecutor.get(deleteAsync(objects));
}
@Override
public RFuture<Long> deleteAsync(RObject ... objects) {
List<String> keys = new ArrayList<String>();
for (RObject obj : objects) {
keys.add(obj.getName());
}
return deleteAsync(keys.toArray(new String[keys.size()]));
}
@Override
public RFuture<Long> deleteAsync(String ... keys) {
if (!commandExecutor.getConnectionManager().isClusterMode()) {
@ -368,4 +385,85 @@ public class RedissonKeys implements RKeys {
}
}
@Override
public long remainTimeToLive(String name) {
return commandExecutor.get(remainTimeToLiveAsync(name));
}
@Override
public RFuture<Long> remainTimeToLiveAsync(String name) {
return commandExecutor.readAsync(name, StringCodec.INSTANCE, RedisCommands.PTTL, name);
}
@Override
public void rename(String currentName, String newName) {
commandExecutor.get(renameAsync(currentName, newName));
}
@Override
public RFuture<Void> renameAsync(String currentName, String newName) {
return commandExecutor.writeAsync(currentName, RedisCommands.RENAME, currentName, newName);
}
@Override
public boolean renamenx(String oldName, String newName) {
return commandExecutor.get(renamenxAsync(oldName, newName));
}
@Override
public RFuture<Boolean> renamenxAsync(String oldName, String newName) {
return commandExecutor.writeAsync(oldName, RedisCommands.RENAMENX, oldName, newName);
}
@Override
public boolean clearExpire(String name) {
return commandExecutor.get(clearExpireAsync(name));
}
@Override
public RFuture<Boolean> clearExpireAsync(String name) {
return commandExecutor.writeAsync(name, StringCodec.INSTANCE, RedisCommands.PERSIST, name);
}
@Override
public boolean expireAt(String name, long timestamp) {
return commandExecutor.get(expireAtAsync(name, timestamp));
}
@Override
public RFuture<Boolean> expireAtAsync(String name, long timestamp) {
return commandExecutor.writeAsync(name, StringCodec.INSTANCE, RedisCommands.PEXPIREAT, name, timestamp);
}
@Override
public boolean expire(String name, long timeToLive, TimeUnit timeUnit) {
return commandExecutor.get(expireAsync(name, timeToLive, timeUnit));
}
@Override
public RFuture<Boolean> expireAsync(String name, long timeToLive, TimeUnit timeUnit) {
return commandExecutor.writeAsync(name, StringCodec.INSTANCE, RedisCommands.PEXPIRE, name, timeUnit.toMillis(timeToLive));
}
@Override
public void migrate(String name, String host, int port, int database) {
commandExecutor.get(migrateAsync(name, host, port, database));
}
@Override
public RFuture<Void> migrateAsync(String name, String host, int port, int database) {
return commandExecutor.writeAsync(name, RedisCommands.MIGRATE, host, port, name, database);
}
@Override
public boolean move(String name, int database) {
return commandExecutor.get(moveAsync(name, database));
}
@Override
public RFuture<Boolean> moveAsync(String name, int database) {
return commandExecutor.writeAsync(name, RedisCommands.MOVE, name, database);
}
}

@ -21,14 +21,21 @@ import java.util.List;
import org.redisson.api.RFuture;
import org.redisson.api.RLexSortedSet;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.StringCodec;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.command.CommandAsyncExecutor;
/**
* Sorted set contained values of String type
*
* @author Nikita Koksharov
*
*/
public class RedissonLexSortedSet extends RedissonScoredSortedSet<String> implements RLexSortedSet {
public RedissonLexSortedSet(CommandAsyncExecutor commandExecutor, String name) {
super(StringCodec.INSTANCE, commandExecutor, name);
public RedissonLexSortedSet(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(StringCodec.INSTANCE, commandExecutor, name, redisson);
}
@Override

@ -34,7 +34,9 @@ import java.util.NoSuchElementException;
import org.redisson.api.RFuture;
import org.redisson.api.RList;
import org.redisson.api.RedissonClient;
import org.redisson.api.SortOrder;
import org.redisson.api.mapreduce.RCollectionMapReduce;
import org.redisson.client.codec.Codec;
import org.redisson.client.protocol.RedisCommand;
import org.redisson.client.protocol.RedisCommand.ValueType;
@ -44,6 +46,7 @@ import org.redisson.client.protocol.convertor.BooleanReplayConvertor;
import org.redisson.client.protocol.convertor.Convertor;
import org.redisson.client.protocol.convertor.IntegerReplayConvertor;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.mapreduce.RedissonCollectionMapReduce;
/**
* Distributed and concurrent implementation of {@link java.util.List}
@ -56,14 +59,23 @@ public class RedissonList<V> extends RedissonExpirable implements RList<V> {
public static final RedisCommand<Boolean> EVAL_BOOLEAN_ARGS2 = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 5, ValueType.OBJECTS);
public RedissonList(CommandAsyncExecutor commandExecutor, String name) {
private RedissonClient redisson;
public RedissonList(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(commandExecutor, name);
this.redisson = redisson;
}
public RedissonList(Codec codec, CommandAsyncExecutor commandExecutor, String name) {
public RedissonList(Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(codec, commandExecutor, name);
this.redisson = redisson;
}
@Override
public <KOut, VOut> RCollectionMapReduce<V, KOut, VOut> mapReduce() {
return new RedissonCollectionMapReduce<V, KOut, VOut>(this, redisson, commandExecutor.getConnectionManager());
}
@Override
public int size() {
return get(sizeAsync());

@ -185,7 +185,7 @@ public class RedissonListMultimap<K, V> extends RedissonMultimap<K, V> implement
final String keyHash = hash(keyState);
final String setName = getValuesName(keyHash);
return new RedissonList<V>(codec, commandExecutor, setName) {
return new RedissonList<V>(codec, commandExecutor, setName, null) {
@Override
public RFuture<Boolean> deleteAsync() {

@ -28,7 +28,7 @@ public class RedissonListMultimapIterator<K, V, M> extends RedissonMultiMapItera
@Override
protected Iterator<V> getIterator(String name) {
RedissonList<V> set = new RedissonList<V>(codec, commandExecutor, map.getValuesName(name));
RedissonList<V> set = new RedissonList<V>(codec, commandExecutor, map.getValuesName(name), null);
return set.iterator();
}

@ -30,6 +30,7 @@ import java.util.concurrent.TimeUnit;
import org.redisson.api.RFuture;
import org.redisson.api.RList;
import org.redisson.api.SortOrder;
import org.redisson.api.mapreduce.RCollectionMapReduce;
import org.redisson.client.codec.Codec;
import org.redisson.client.protocol.RedisCommand;
import org.redisson.client.protocol.RedisCommand.ValueType;
@ -68,7 +69,12 @@ public class RedissonListMultimapValues<V> extends RedissonExpirable implements
super(codec, commandExecutor, name);
this.timeoutSetName = timeoutSetName;
this.key = key;
this.list = new RedissonList<V>(codec, commandExecutor, name);
this.list = new RedissonList<V>(codec, commandExecutor, name, null);
}
@Override
public <KOut, VOut> RCollectionMapReduce<V, KOut, VOut> mapReduce() {
return null;
}
@Override

@ -31,18 +31,24 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.redisson.api.LocalCachedMapOptions;
import org.redisson.api.LocalCachedMapOptions.EvictionPolicy;
import org.redisson.api.LocalCachedMapOptions.InvalidationPolicy;
import org.redisson.api.RFuture;
import org.redisson.api.RLocalCachedMap;
import org.redisson.api.RScoredSortedSet;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.api.listener.BaseStatusListener;
import org.redisson.api.listener.MessageListener;
import org.redisson.cache.Cache;
import org.redisson.cache.LFUCacheMap;
import org.redisson.cache.LRUCacheMap;
import org.redisson.cache.NoneCacheMap;
import org.redisson.cache.SoftCacheMap;
import org.redisson.cache.ReferenceCacheMap;
import org.redisson.client.codec.ByteArrayCodec;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.codec.StringCodec;
@ -51,10 +57,14 @@ import org.redisson.client.protocol.RedisCommand.ValueType;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.client.protocol.convertor.NumberConvertor;
import org.redisson.client.protocol.decoder.ObjectMapEntryReplayDecoder;
import org.redisson.client.protocol.decoder.ObjectMapReplayDecoder;
import org.redisson.client.protocol.decoder.ObjectSetReplayDecoder;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.eviction.EvictionScheduler;
import org.redisson.misc.Hash;
import org.redisson.misc.RPromise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
@ -65,8 +75,11 @@ import io.netty.util.internal.ThreadLocalRandom;
* @author Nikita Koksharov
*
*/
@SuppressWarnings("serial")
public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements RLocalCachedMap<K, V> {
private static final Logger log = LoggerFactory.getLogger(RedissonLocalCachedMap.class);
public static class LocalCachedMapClear implements Serializable {
}
@ -180,66 +193,147 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
}
private static final RedisCommand<Set<Object>> ALL_KEYS = new RedisCommand<Set<Object>>("EVAL", new ObjectSetReplayDecoder(), ValueType.MAP_KEY);
private static final RedisCommand<Set<Object>> ALL_KEYS = new RedisCommand<Set<Object>>("EVAL", new ObjectSetReplayDecoder<Object>(), ValueType.MAP_KEY);
private static final RedisCommand<Set<Entry<Object, Object>>> ALL_ENTRIES = new RedisCommand<Set<Entry<Object, Object>>>("EVAL", new ObjectMapEntryReplayDecoder(), ValueType.MAP);
private static final RedisCommand<Map<Object, Object>> ALL_MAP = new RedisCommand<Map<Object, Object>>("EVAL", new ObjectMapReplayDecoder(), ValueType.MAP);
private static final RedisCommand<Object> EVAL_PUT = new RedisCommand<Object>("EVAL", -1, ValueType.OBJECT, ValueType.MAP_VALUE);
private static final RedisCommand<Object> EVAL_REMOVE = new RedisCommand<Object>("EVAL", -1, ValueType.OBJECT, ValueType.MAP_VALUE);
private long cacheUpdateLogTime = TimeUnit.MINUTES.toMillis(10);
private byte[] instanceId;
private RTopic<Object> invalidationTopic;
private Cache<CacheKey, CacheValue> cache;
private int invalidateEntryOnChange;
private int invalidationListenerId;
private int invalidationStatusListenerId;
private volatile long lastInvalidate;
protected RedissonLocalCachedMap(UUID id, CommandAsyncExecutor commandExecutor, String name, LocalCachedMapOptions options) {
super(id, commandExecutor, name);
init(id, name, options);
protected RedissonLocalCachedMap(UUID id, CommandAsyncExecutor commandExecutor, String name, LocalCachedMapOptions options, EvictionScheduler evictionScheduler, RedissonClient redisson) {
super(id, commandExecutor, name, redisson);
init(id, name, options, redisson, evictionScheduler);
}
protected RedissonLocalCachedMap(UUID id, Codec codec, CommandAsyncExecutor connectionManager, String name, LocalCachedMapOptions options) {
super(id, codec, connectionManager, name);
init(id, name, options);
protected RedissonLocalCachedMap(UUID id, Codec codec, CommandAsyncExecutor connectionManager, String name, LocalCachedMapOptions options, EvictionScheduler evictionScheduler, RedissonClient redisson) {
super(id, codec, connectionManager, name, redisson);
init(id, name, options, redisson, evictionScheduler);
}
private void init(UUID id, String name, LocalCachedMapOptions options) {
private void init(UUID id, String name, LocalCachedMapOptions options, RedissonClient redisson, EvictionScheduler evictionScheduler) {
instanceId = generateId();
if (options.isInvalidateEntryOnChange()) {
if (options.getInvalidationPolicy() == InvalidationPolicy.ON_CHANGE
|| options.getInvalidationPolicy() == InvalidationPolicy.ON_CHANGE_WITH_CLEAR_ON_RECONNECT) {
invalidateEntryOnChange = 1;
}
if (options.getEvictionPolicy() == EvictionPolicy.NONE) {
cache = new NoneCacheMap<CacheKey, CacheValue>(options.getTimeToLiveInMillis(), options.getMaxIdleInMillis());
}
if (options.getEvictionPolicy() == EvictionPolicy.LRU) {
cache = new LRUCacheMap<CacheKey, CacheValue>(options.getCacheSize(), options.getTimeToLiveInMillis(), options.getMaxIdleInMillis());
}
if (options.getEvictionPolicy() == EvictionPolicy.LFU) {
cache = new LFUCacheMap<CacheKey, CacheValue>(options.getCacheSize(), options.getTimeToLiveInMillis(), options.getMaxIdleInMillis());
}
if (options.getEvictionPolicy() == EvictionPolicy.SOFT) {
cache = new SoftCacheMap<CacheKey, CacheValue>(options.getTimeToLiveInMillis(), options.getMaxIdleInMillis());
if (options.getInvalidationPolicy() == InvalidationPolicy.ON_CHANGE_WITH_LOAD_ON_RECONNECT) {
invalidateEntryOnChange = 2;
evictionScheduler.schedule(getUpdatesLogName(), cacheUpdateLogTime + TimeUnit.MINUTES.toMillis(1));
}
cache = createCache(options);
addListeners(name, options, redisson);
}
private void addListeners(String name, final LocalCachedMapOptions options, final RedissonClient redisson) {
invalidationTopic = new RedissonTopic<Object>(commandExecutor, suffixName(name, "topic"));
if (options.isInvalidateEntryOnChange()) {
invalidationListenerId = invalidationTopic.addListener(new MessageListener<Object>() {
if (options.getInvalidationPolicy() == InvalidationPolicy.NONE) {
return;
}
if (options.getInvalidationPolicy() != InvalidationPolicy.ON_CHANGE) {
invalidationStatusListenerId = invalidationTopic.addListener(new BaseStatusListener() {
@Override
public void onMessage(String channel, Object msg) {
if (msg instanceof LocalCachedMapClear) {
public void onSubscribe(String channel) {
if (options.getInvalidationPolicy() == InvalidationPolicy.ON_CHANGE_WITH_CLEAR_ON_RECONNECT) {
cache.clear();
}
if (msg instanceof LocalCachedMapInvalidate) {
LocalCachedMapInvalidate invalidateMsg = (LocalCachedMapInvalidate)msg;
if (!Arrays.equals(invalidateMsg.getExcludedId(), instanceId)) {
for (byte[] keyHash : invalidateMsg.getKeyHashes()) {
CacheKey key = new CacheKey(keyHash);
cache.remove(key);
}
if (options.getInvalidationPolicy() == InvalidationPolicy.ON_CHANGE_WITH_LOAD_ON_RECONNECT
// check if instance has already been used
&& lastInvalidate > 0) {
if (System.currentTimeMillis() - lastInvalidate > cacheUpdateLogTime) {
cache.clear();
return;
}
isExistsAsync().addListener(new FutureListener<Boolean>() {
@Override
public void operationComplete(Future<Boolean> future) throws Exception {
if (!future.isSuccess()) {
return;
}
if (!future.getNow()) {
cache.clear();
return;
}
RScoredSortedSet<byte[]> logs = redisson.getScoredSortedSet(getUpdatesLogName(), ByteArrayCodec.INSTANCE);
logs.valueRangeAsync(lastInvalidate, true, Double.POSITIVE_INFINITY, true)
.addListener(new FutureListener<Collection<byte[]>>() {
@Override
public void operationComplete(Future<Collection<byte[]>> future) throws Exception {
if (!future.isSuccess()) {
log.error("Can't load update log", future.cause());
return;
}
for (byte[] entry : future.getNow()) {
byte[] keyHash = Arrays.copyOf(entry, 16);
CacheKey key = new CacheKey(keyHash);
cache.remove(key);
}
}
});
}
});
}
}
});
}
invalidationListenerId = invalidationTopic.addListener(new MessageListener<Object>() {
@Override
public void onMessage(String channel, Object msg) {
if (msg instanceof LocalCachedMapClear) {
cache.clear();
}
if (msg instanceof LocalCachedMapInvalidate) {
LocalCachedMapInvalidate invalidateMsg = (LocalCachedMapInvalidate)msg;
if (!Arrays.equals(invalidateMsg.getExcludedId(), instanceId)) {
for (byte[] keyHash : invalidateMsg.getKeyHashes()) {
CacheKey key = new CacheKey(keyHash);
cache.remove(key);
}
}
if (options.getInvalidationPolicy() == InvalidationPolicy.ON_CHANGE_WITH_LOAD_ON_RECONNECT) {
lastInvalidate = System.currentTimeMillis();
}
}
}
});
}
protected Cache<CacheKey, CacheValue> createCache(LocalCachedMapOptions options) {
if (options.getEvictionPolicy() == EvictionPolicy.NONE) {
return new NoneCacheMap<CacheKey, CacheValue>(options.getTimeToLiveInMillis(), options.getMaxIdleInMillis());
}
if (options.getEvictionPolicy() == EvictionPolicy.LRU) {
return new LRUCacheMap<CacheKey, CacheValue>(options.getCacheSize(), options.getTimeToLiveInMillis(), options.getMaxIdleInMillis());
}
if (options.getEvictionPolicy() == EvictionPolicy.LFU) {
return new LFUCacheMap<CacheKey, CacheValue>(options.getCacheSize(), options.getTimeToLiveInMillis(), options.getMaxIdleInMillis());
}
if (options.getEvictionPolicy() == EvictionPolicy.SOFT) {
return ReferenceCacheMap.soft(options.getTimeToLiveInMillis(), options.getMaxIdleInMillis());
}
if (options.getEvictionPolicy() == EvictionPolicy.WEAK) {
return ReferenceCacheMap.weak(options.getTimeToLiveInMillis(), options.getMaxIdleInMillis());
}
throw new IllegalArgumentException("Invalid eviction policy: " + options.getEvictionPolicy());
}
private CacheKey toCacheKey(Object key) {
@ -298,13 +392,30 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
return future;
}
protected byte[] generateId() {
String getUpdatesLogName() {
return prefixName("redisson__cache_updates_log", getName());
}
protected static byte[] generateId() {
byte[] id = new byte[16];
// TODO JDK UPGRADE replace to native ThreadLocalRandom
ThreadLocalRandom.current().nextBytes(id);
return id;
}
protected static byte[] generateLogEntryId(byte[] keyHash) {
byte[] result = new byte[keyHash.length + 1 + 8];
result[16] = ':';
byte[] id = new byte[8];
// TODO JDK UPGRADE replace to native ThreadLocalRandom
ThreadLocalRandom.current().nextBytes(id);
System.arraycopy(keyHash, 0, result, 0, keyHash.length);
System.arraycopy(id, 0, result, 17, id.length);
return result;
}
@Override
public RFuture<V> putAsync(K key, V value) {
if (key == null) {
@ -316,17 +427,24 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
byte[] mapKey = encodeMapKey(key);
CacheKey cacheKey = toCacheKey(mapKey);
byte[] entryId = generateLogEntryId(cacheKey.getKeyHash());
byte[] msg = encode(new LocalCachedMapInvalidate(instanceId, cacheKey.getKeyHash()));
CacheValue cacheValue = new CacheValue(key, value);
cache.put(cacheKey, cacheValue);
return commandExecutor.evalWriteAsync(getName(), codec, EVAL_PUT,
"local v = redis.call('hget', KEYS[1], ARGV[1]); "
+ "if redis.call('hset', KEYS[1], ARGV[1], ARGV[2]) == 0 and ARGV[4] == '1' then "
+ "redis.call('publish', KEYS[2], ARGV[3]); "
+ "if redis.call('hset', KEYS[1], ARGV[1], ARGV[2]) == 0 then "
+ "if ARGV[4] == '1' then "
+ "redis.call('publish', KEYS[2], ARGV[3]); "
+ "end;"
+ "if ARGV[4] == '2' then "
+ "redis.call('zadd', KEYS[3], ARGV[5], ARGV[6]);"
+ "redis.call('publish', KEYS[2], ARGV[3]); "
+ "end;"
+ "end; "
+ "return v; ",
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0)),
mapKey, encodeMapValue(value), msg, invalidateEntryOnChange);
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0), getUpdatesLogName()),
mapKey, encodeMapValue(value), msg, invalidateEntryOnChange, System.currentTimeMillis(), entryId);
}
@Override
@ -339,8 +457,9 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
}
byte[] encodedKey = encodeMapKey(key);
byte[] encodedValue = encodeMapKey(value);
byte[] encodedValue = encodeMapValue(value);
CacheKey cacheKey = toCacheKey(encodedKey);
byte[] entryId = generateLogEntryId(cacheKey.getKeyHash());
byte[] msg = encode(new LocalCachedMapInvalidate(instanceId, cacheKey.getKeyHash()));
CacheValue cacheValue = new CacheValue(key, value);
cache.put(cacheKey, cacheValue);
@ -349,11 +468,15 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
+ "if ARGV[4] == '1' then "
+ "redis.call('publish', KEYS[2], ARGV[3]); "
+ "end;"
+ "if ARGV[4] == '2' then "
+ "redis.call('zadd', KEYS[3], ARGV[5], ARGV[6]);"
+ "redis.call('publish', KEYS[2], ARGV[3]); "
+ "end;"
+ "return 0; "
+ "end; "
+ "return 1; ",
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0)),
encodedKey, encodedValue, msg, invalidateEntryOnChange);
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0), getUpdatesLogName()),
encodedKey, encodedValue, msg, invalidateEntryOnChange, System.currentTimeMillis(), entryId);
}
@Override
@ -361,6 +484,9 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
if (invalidationListenerId != 0) {
invalidationTopic.removeListener(invalidationListenerId);
}
if (invalidationStatusListenerId != 0) {
invalidationTopic.removeListener(invalidationStatusListenerId);
}
}
@Override
@ -371,16 +497,23 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
byte[] keyEncoded = encodeMapKey(key);
CacheKey cacheKey = toCacheKey(keyEncoded);
byte[] entryId = generateLogEntryId(cacheKey.getKeyHash());
byte[] msgEncoded = encode(new LocalCachedMapInvalidate(instanceId, cacheKey.getKeyHash()));
cache.remove(cacheKey);
return commandExecutor.evalWriteAsync(getName(), codec, EVAL_REMOVE,
"local v = redis.call('hget', KEYS[1], ARGV[1]); "
+ "if redis.call('hdel', KEYS[1], ARGV[1]) == 1 and ARGV[3] == '1' then "
+ "redis.call('publish', KEYS[2], ARGV[2]); "
+ "if redis.call('hdel', KEYS[1], ARGV[1]) == 1 then "
+ "if ARGV[3] == '1' then "
+ "redis.call('publish', KEYS[2], ARGV[2]); "
+ "end; "
+ "if ARGV[3] == '2' then "
+ "redis.call('zadd', KEYS[3], ARGV[4], ARGV[5]);"
+ "redis.call('publish', KEYS[2], ARGV[2]); "
+ "end;"
+ "end; "
+ "return v",
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0)),
keyEncoded, msgEncoded, invalidateEntryOnChange);
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0), getUpdatesLogName()),
keyEncoded, msgEncoded, invalidateEntryOnChange, System.currentTimeMillis(), entryId);
}
@Override
@ -389,30 +522,60 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
throw new NullPointerException();
}
if (invalidateEntryOnChange == 1) {
List<Object> params = new ArrayList<Object>(keys.length*2);
for (K k : keys) {
byte[] keyEncoded = encodeMapKey(k);
params.add(keyEncoded);
CacheKey cacheKey = toCacheKey(keyEncoded);
cache.remove(cacheKey);
byte[] msgEncoded = encode(new LocalCachedMapInvalidate(instanceId, cacheKey.getKeyHash()));
params.add(msgEncoded);
if (invalidateEntryOnChange == 1) {
List<Object> params = new ArrayList<Object>(keys.length*2);
for (K k : keys) {
byte[] keyEncoded = encodeMapKey(k);
params.add(keyEncoded);
CacheKey cacheKey = toCacheKey(keyEncoded);
cache.remove(cacheKey);
byte[] msgEncoded = encode(new LocalCachedMapInvalidate(instanceId, cacheKey.getKeyHash()));
params.add(msgEncoded);
}
return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_LONG,
"local counter = 0; " +
"for j = 1, #ARGV, 2 do "
+ "if redis.call('hdel', KEYS[1], ARGV[j]) == 1 then "
+ "redis.call('publish', KEYS[2], ARGV[j+1]); "
+ "counter = counter + 1;"
+ "end;"
+ "end;"
+ "return counter;",
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0)),
params.toArray());
}
return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_LONG,
"local counter = 0; " +
"for j = 1, #ARGV, 2 do "
+ "if redis.call('hdel', KEYS[1], ARGV[j]) == 1 then "
+ "redis.call('publish', KEYS[2], ARGV[j+1]); "
+ "counter = counter + 1;"
+ "end;"
+ "end;"
+ "return counter;",
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0)),
params.toArray());
}
if (invalidateEntryOnChange == 2) {
List<Object> params = new ArrayList<Object>(keys.length*3);
params.add(System.currentTimeMillis());
for (K k : keys) {
byte[] keyEncoded = encodeMapKey(k);
params.add(keyEncoded);
CacheKey cacheKey = toCacheKey(keyEncoded);
cache.remove(cacheKey);
byte[] msgEncoded = encode(new LocalCachedMapInvalidate(instanceId, cacheKey.getKeyHash()));
params.add(msgEncoded);
byte[] entryId = generateLogEntryId(cacheKey.getKeyHash());
params.add(entryId);
}
return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_LONG,
"local counter = 0; " +
"for j = 2, #ARGV, 3 do "
+ "if redis.call('hdel', KEYS[1], ARGV[j]) == 1 then "
+ "redis.call('zadd', KEYS[3], ARGV[1], ARGV[j+2]);"
+ "redis.call('publish', KEYS[2], ARGV[j+1]); "
+ "counter = counter + 1;"
+ "end;"
+ "end;"
+ "return counter;",
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0), getUpdatesLogName()),
params.toArray());
}
List<Object> params = new ArrayList<Object>(keys.length + 1);
params.add(getName());
@ -433,12 +596,12 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
cache.clear();
byte[] msgEncoded = encode(new LocalCachedMapClear());
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"if redis.call('del', KEYS[1]) == 1 and ARGV[2] == '1' then "
"if redis.call('del', KEYS[1], KEYS[3]) > 0 and ARGV[2] ~= '0' then "
+ "redis.call('publish', KEYS[2], ARGV[1]); "
+ "return 1;"
+ "end; "
+ "return 0;",
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0)),
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0), getUpdatesLogName()),
msgEncoded, invalidateEntryOnChange);
}
@ -749,18 +912,30 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
i++;
}
if (invalidateEntryOnChange == 2) {
long time = System.currentTimeMillis();
for (byte[] hash : hashes) {
byte[] entryId = generateLogEntryId(hash);
params.add(time);
params.add(entryId);
}
}
byte[] msgEncoded = encode(new LocalCachedMapInvalidate(instanceId, hashes));
params.add(msgEncoded);
final RPromise<Void> result = newPromise();
RFuture<Void> future = commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_VOID,
"redis.call('hmset', KEYS[1], unpack(ARGV, 3, tonumber(ARGV[2]) + 2));"
+ "if ARGV[1] == '1' then "
// + "for i = tonumber(ARGV[2]) + 3, #ARGV, 1 do "
+ "redis.call('publish', KEYS[2], ARGV[#ARGV]); "
// + "end; "
+ "end;",
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0)), params.toArray());
+ "if ARGV[1] == '1' then "
+ "redis.call('publish', KEYS[2], ARGV[#ARGV]); "
+ "end;"
+ "if ARGV[1] == '2' then "
+ "redis.call('zadd', KEYS[3], unpack(ARGV, tonumber(ARGV[2]) + 2 + 1, #ARGV - 1));"
+ "redis.call('publish', KEYS[2], ARGV[#ARGV]); "
+ "end;",
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0), getUpdatesLogName()),
params.toArray());
future.addListener(new FutureListener<Void>() {
@Override
@ -781,15 +956,19 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
final byte[] keyState = encodeMapKey(key);
CacheKey cacheKey = toCacheKey(keyState);
byte[] msg = encode(new LocalCachedMapInvalidate(instanceId, cacheKey.getKeyHash()));
byte[] entryId = generateLogEntryId(cacheKey.getKeyHash());
RFuture<V> future = commandExecutor.evalWriteAsync(getName(), StringCodec.INSTANCE, new RedisCommand<Object>("EVAL", new NumberConvertor(value.getClass())),
"local result = redis.call('HINCRBYFLOAT', KEYS[1], ARGV[1], ARGV[2]); "
+ "if ARGV[3] == '1' then "
+ "redis.call('publish', KEYS[2], ARGV[4]); "
+ "end; "
+ "redis.call('publish', KEYS[2], ARGV[4]); "
+ "end;"
+ "if ARGV[3] == '2' then "
+ "redis.call('zadd', KEYS[3], ARGV[5], ARGV[6]);"
+ "redis.call('publish', KEYS[2], ARGV[4]); "
+ "end;"
+ "return result; ",
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0)),
keyState, new BigDecimal(value.toString()).toPlainString(), invalidateEntryOnChange, msg);
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0), getUpdatesLogName()),
keyState, new BigDecimal(value.toString()).toPlainString(), invalidateEntryOnChange, msg, System.currentTimeMillis(), entryId);
future.addListener(new FutureListener<V>() {
@Override
@ -874,6 +1053,37 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
return promise;
}
@Override
public RFuture<Map<K, V>> readAllMapAsync() {
final Map<K, V> result = new HashMap<K, V>();
List<Object> mapKeys = new ArrayList<Object>();
for (CacheValue value : cache.values()) {
mapKeys.add(encodeMapKey(value.getKey()));
result.put((K)value.getKey(), (V)value.getValue());
}
final RPromise<Map<K, V>> promise = newPromise();
RFuture<Map<K, V>> future = readAll(ALL_MAP, mapKeys, result);
future.addListener(new FutureListener<Map<K, V>>() {
@Override
public void operationComplete(Future<Map<K, V>> future) throws Exception {
if (!future.isSuccess()) {
return;
}
for (java.util.Map.Entry<K, V> entry : future.getNow().entrySet()) {
CacheKey cacheKey = toCacheKey(entry.getKey());
cache.put(cacheKey, new CacheValue(entry.getKey(), entry.getValue()));
}
result.putAll(future.getNow());
promise.trySuccess(result);
}
});
return promise;
}
@Override
public RFuture<Set<Entry<K, V>>> readAllEntrySetAsync() {
final Set<Entry<K, V>> result = new HashSet<Entry<K, V>>();
@ -882,9 +1092,31 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
mapKeys.add(encodeMapKey(value.getKey()));
result.add(new AbstractMap.SimpleEntry<K, V>((K)value.getKey(), (V)value.getValue()));
}
final RPromise<Set<Entry<K, V>>> promise = newPromise();
RFuture<Set<Entry<K, V>>> future = commandExecutor.evalReadAsync(getName(), codec, ALL_ENTRIES,
RFuture<Set<Entry<K, V>>> future = readAll(ALL_ENTRIES, mapKeys, result);
future.addListener(new FutureListener<Set<Entry<K, V>>>() {
@Override
public void operationComplete(Future<Set<Entry<K, V>>> future) throws Exception {
if (!future.isSuccess()) {
return;
}
for (java.util.Map.Entry<K, V> entry : future.getNow()) {
CacheKey cacheKey = toCacheKey(entry.getKey());
cache.put(cacheKey, new CacheValue(entry.getKey(), entry.getValue()));
}
result.addAll(future.getNow());
promise.trySuccess(result);
}
});
return promise;
}
private <R> RFuture<R> readAll(RedisCommand<?> evalCommandType, List<Object> mapKeys, R result) {
return commandExecutor.evalReadAsync(getName(), codec, evalCommandType,
"local entries = redis.call('hgetall', KEYS[1]); "
+ "local result = {};"
+ "for j, v in ipairs(entries) do "
@ -904,24 +1136,6 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
+ "return result; ",
Arrays.<Object>asList(getName()),
mapKeys.toArray());
future.addListener(new FutureListener<Set<Entry<K, V>>>() {
@Override
public void operationComplete(Future<Set<Entry<K, V>>> future) throws Exception {
if (!future.isSuccess()) {
return;
}
for (java.util.Map.Entry<K, V> entry : future.getNow()) {
CacheKey cacheKey = toCacheKey(entry.getKey());
cache.put(cacheKey, new CacheValue(entry.getKey(), entry.getValue()));
}
result.addAll(future.getNow());
promise.trySuccess(result);
}
});
return promise;
}
@Override
@ -929,21 +1143,28 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
final byte[] keyState = encodeMapKey(key);
byte[] valueState = encodeMapValue(value);
final CacheKey cacheKey = toCacheKey(keyState);
byte[] entryId = generateLogEntryId(cacheKey.getKeyHash());
byte[] msg = encode(new LocalCachedMapInvalidate(instanceId, cacheKey.getKeyHash()));
RFuture<V> future = commandExecutor.evalWriteAsync(getName(key), codec, RedisCommands.EVAL_MAP_VALUE,
"if redis.call('hexists', KEYS[1], ARGV[1]) == 1 then "
+ "local v = redis.call('hget', KEYS[1], ARGV[1]); "
+ "redis.call('hset', KEYS[1], ARGV[1], ARGV[2]); "
+ "if ARGV[3] == '1' then "
+ "redis.call('publish', KEYS[2], ARGV[4]); "
+ "end; "
+ "end;"
+ "if ARGV[3] == '2' then "
+ "redis.call('zadd', KEYS[3], ARGV[5], ARGV[6]);"
+ "redis.call('publish', KEYS[2], ARGV[4]); "
+ "end;"
+ "return v; "
+ "else "
+ "return nil; "
+ "end",
Arrays.<Object>asList(getName(key), invalidationTopic.getChannelNames().get(0)),
keyState, valueState, invalidateEntryOnChange, msg);
Arrays.<Object>asList(getName(key), invalidationTopic.getChannelNames().get(0), getUpdatesLogName()),
keyState, valueState, invalidateEntryOnChange, msg, System.currentTimeMillis(), entryId);
future.addListener(new FutureListener<V>() {
@Override
@ -968,6 +1189,7 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
byte[] oldValueState = encodeMapValue(oldValue);
byte[] newValueState = encodeMapValue(newValue);
final CacheKey cacheKey = toCacheKey(keyState);
byte[] entryId = generateLogEntryId(cacheKey.getKeyHash());
byte[] msg = encode(new LocalCachedMapInvalidate(instanceId, cacheKey.getKeyHash()));
RFuture<Boolean> future = commandExecutor.evalWriteAsync(getName(key), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
@ -975,13 +1197,17 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
+ "redis.call('hset', KEYS[1], ARGV[1], ARGV[3]); "
+ "if ARGV[4] == '1' then "
+ "redis.call('publish', KEYS[2], ARGV[5]); "
+ "end; "
+ "end;"
+ "if ARGV[4] == '2' then "
+ "redis.call('zadd', KEYS[3], ARGV[6], ARGV[7]);"
+ "redis.call('publish', KEYS[2], ARGV[5]); "
+ "end;"
+ "return 1; "
+ "else "
+ "return 0; "
+ "end",
Arrays.<Object>asList(getName(key), invalidationTopic.getChannelNames().get(0)),
keyState, oldValueState, newValueState, invalidateEntryOnChange, msg);
Arrays.<Object>asList(getName(key), invalidationTopic.getChannelNames().get(0), getUpdatesLogName()),
keyState, oldValueState, newValueState, invalidateEntryOnChange, msg, System.currentTimeMillis(), entryId);
future.addListener(new FutureListener<Boolean>() {
@Override
@ -1004,19 +1230,24 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
final byte[] keyState = encodeMapKey(key);
byte[] valueState = encodeMapValue(value);
final CacheKey cacheKey = toCacheKey(keyState);
byte[] entryId = generateLogEntryId(cacheKey.getKeyHash());
byte[] msg = encode(new LocalCachedMapInvalidate(instanceId, cacheKey.getKeyHash()));
RFuture<Boolean> future = commandExecutor.evalWriteAsync(getName(key), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"if redis.call('hget', KEYS[1], ARGV[1]) == ARGV[2] then "
+ "if ARGV[3] == '1' then "
+ "redis.call('publish', KEYS[2], ARGV[4]); "
+ "end; "
+ "end;"
+ "if ARGV[3] == '2' then "
+ "redis.call('zadd', KEYS[3], ARGV[5], ARGV[6]);"
+ "redis.call('publish', KEYS[2], ARGV[4]); "
+ "end;"
+ "return redis.call('hdel', KEYS[1], ARGV[1]) "
+ "else "
+ "return 0 "
+ "end",
Arrays.<Object>asList(getName(key), invalidationTopic.getChannelNames().get(0)),
keyState, valueState, invalidateEntryOnChange, msg);
Arrays.<Object>asList(getName(key), invalidationTopic.getChannelNames().get(0), getUpdatesLogName()),
keyState, valueState, invalidateEntryOnChange, msg, System.currentTimeMillis(), entryId);
future.addListener(new FutureListener<Boolean>() {
@Override

@ -34,6 +34,8 @@ import org.redisson.api.RFuture;
import org.redisson.api.RLock;
import org.redisson.api.RMap;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.redisson.api.mapreduce.RMapReduce;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.MapScanCodec;
import org.redisson.client.codec.StringCodec;
@ -47,6 +49,7 @@ import org.redisson.client.protocol.decoder.ScanObjectEntry;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.command.CommandExecutor;
import org.redisson.connection.decoder.MapGetAllDecoder;
import org.redisson.mapreduce.RedissonMapReduce;
import org.redisson.misc.Hash;
/**
@ -67,15 +70,23 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
static final RedisCommand<Object> EVAL_PUT = EVAL_REPLACE;
private final UUID id;
private final RedissonClient redisson;
protected RedissonMap(UUID id, CommandAsyncExecutor commandExecutor, String name) {
protected RedissonMap(UUID id, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(commandExecutor, name);
this.id = id;
this.redisson = redisson;
}
public RedissonMap(UUID id, Codec codec, CommandAsyncExecutor commandExecutor, String name) {
public RedissonMap(UUID id, Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(codec, commandExecutor, name);
this.id = id;
this.redisson = redisson;
}
@Override
public <KOut, VOut> RMapReduce<K, V, KOut, VOut> mapReduce() {
return new RedissonMapReduce<K, V, KOut, VOut>(this, redisson, commandExecutor.getConnectionManager());
}
@Override
@ -267,6 +278,17 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
return commandExecutor.readAsync(getName(), codec, RedisCommands.HGETALL_ENTRY, getName());
}
@Override
public Map<K, V> readAllMap() {
return get(readAllMapAsync());
}
@Override
public RFuture<Map<K, V>> readAllMapAsync() {
return commandExecutor.readAsync(getName(), codec, RedisCommands.HGETALL, getName());
}
@Override
public V putIfAbsent(K key, V value) {
return get(putIfAbsentAsync(key, value));
@ -392,10 +414,6 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
return commandExecutor.readAsync(getName(key), codec, RedisCommands.HGET, getName(key), key);
}
protected String getName(Object key) {
return getName();
}
@Override
public RFuture<V> putAsync(K key, V value) {
if (key == null) {

@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit;
import org.redisson.api.RFuture;
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.codec.MapScanCodec;
@ -50,6 +51,10 @@ import org.redisson.eviction.EvictionScheduler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import java.io.IOException;
import java.math.BigDecimal;
import org.redisson.client.codec.StringCodec;
import org.redisson.client.protocol.convertor.NumberConvertor;
/**
* <p>Map-based cache with ability to set TTL for each entry via
@ -80,27 +85,29 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
private static final RedisCommand<Object> EVAL_REMOVE = new RedisCommand<Object>("EVAL", 4, ValueType.MAP_KEY, ValueType.MAP_VALUE);
private static final RedisCommand<Boolean> EVAL_REMOVE_VALUE = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 5, ValueType.MAP);
private static final RedisCommand<Object> EVAL_PUT_TTL = new RedisCommand<Object>("EVAL", 9, ValueType.MAP, ValueType.MAP_VALUE);
private static final RedisCommand<Object> EVAL_PUT_TTL_IF_ABSENT = new RedisCommand<Object>("EVAL", 10, ValueType.MAP, ValueType.MAP_VALUE);
private static final RedisCommand<Boolean> EVAL_FAST_PUT_TTL = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 9, ValueType.MAP, ValueType.MAP_VALUE);
private static final RedisCommand<Boolean> EVAL_FAST_PUT_TTL_IF_ABSENT = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 10, ValueType.MAP, ValueType.MAP_VALUE);
private static final RedisCommand<Object> EVAL_GET_TTL = new RedisCommand<Object>("EVAL", 7, ValueType.MAP_KEY, ValueType.MAP_VALUE);
private static final RedisCommand<Boolean> EVAL_CONTAINS_KEY = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 7, ValueType.MAP_KEY);
static final RedisCommand<Boolean> EVAL_CONTAINS_VALUE = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 7, ValueType.MAP_VALUE);
static final RedisCommand<Long> EVAL_FAST_REMOVE = new RedisCommand<Long>("EVAL", 5, ValueType.MAP_KEY);
RedissonMapCache(UUID id, CommandAsyncExecutor commandExecutor, String name) {
super(id, commandExecutor, name);
RedissonMapCache(UUID id, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(id, commandExecutor, name, redisson);
}
RedissonMapCache(UUID id, Codec codec, CommandAsyncExecutor commandExecutor, String name) {
super(id, codec, commandExecutor, name);
RedissonMapCache(UUID id, Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(id, codec, commandExecutor, name, redisson);
}
public RedissonMapCache(UUID id, EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name) {
super(id, commandExecutor, name);
public RedissonMapCache(UUID id, EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(id, commandExecutor, name, redisson);
evictionScheduler.schedule(getName(), getTimeoutSetName(), getIdleSetName());
}
public RedissonMapCache(UUID id, Codec codec, EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name) {
super(id, codec, commandExecutor, name);
public RedissonMapCache(UUID id, Codec codec, EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(id, codec, commandExecutor, name, redisson);
evictionScheduler.schedule(getName(), getTimeoutSetName(), getIdleSetName());
}
@ -267,26 +274,57 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
maxIdleTimeout = System.currentTimeMillis() + maxIdleDelta;
}
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_PUT_TTL,
"if redis.call('hexists', KEYS[1], ARGV[4]) == 0 then "
+ "if tonumber(ARGV[1]) > 0 then "
+ "redis.call('zadd', KEYS[2], ARGV[1], ARGV[4]); "
+ "end; "
+ "if tonumber(ARGV[2]) > 0 then "
+ "redis.call('zadd', KEYS[3], ARGV[2], ARGV[4]); "
+ "end; "
+ "local value = struct.pack('dLc0', ARGV[3], string.len(ARGV[5]), ARGV[5]); "
+ "redis.call('hset', KEYS[1], ARGV[4], value); "
+ "return nil; "
+ "else "
+ "local value = redis.call('hget', KEYS[1], ARGV[4]); "
+ "if value == false then "
+ "return nil; "
+ "end;"
+ "local t, val = struct.unpack('dLc0', value); "
+ "return val; "
+ "end",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), ttlTimeout, maxIdleTimeout, maxIdleDelta, key, value);
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_PUT_TTL_IF_ABSENT,
"local insertable = false; "
+ "local value = redis.call('hget', KEYS[1], ARGV[5]); "
+ "if value == false then "
+ "insertable = true; "
+ "else "
+ "if insertable == false then "
+ "local t, val = struct.unpack('dLc0', value); "
+ "local expireDate = 92233720368547758; "
+ "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[5]); "
+ "if expireDateScore ~= false then "
+ "expireDate = tonumber(expireDateScore) "
+ "end; "
+ "if t ~= 0 then "
+ "local expireIdle = redis.call('zscore', KEYS[3], ARGV[5]); "
+ "if expireIdle ~= false then "
+ "expireDate = math.min(expireDate, tonumber(expireIdle)) "
+ "end; "
+ "end; "
+ "if expireDate <= tonumber(ARGV[1]) then "
+ "insertable = true; "
+ "end; "
+ "end; "
+ "end; "
+ "if insertable == true then "
// ttl
+ "if tonumber(ARGV[2]) > 0 then "
+ "redis.call('zadd', KEYS[2], ARGV[2], ARGV[5]); "
+ "else "
+ "redis.call('zrem', KEYS[2], ARGV[5]); "
+ "end; "
// idle
+ "if tonumber(ARGV[3]) > 0 then "
+ "redis.call('zadd', KEYS[3], ARGV[3], ARGV[5]); "
+ "else "
+ "redis.call('zrem', KEYS[3], ARGV[5]); "
+ "end; "
// value
+ "local val = struct.pack('dLc0', ARGV[4], string.len(ARGV[6]), ARGV[6]); "
+ "redis.call('hset', KEYS[1], ARGV[5], val); "
+ "return nil;"
+ "else "
+ "local t, val = struct.unpack('dLc0', value); "
+ "redis.call('zadd', KEYS[3], t + ARGV[1], ARGV[5]); "
+ "return val;"
+ "end; ",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), System.currentTimeMillis(), ttlTimeout, maxIdleTimeout, maxIdleDelta, key, value);
}
@Override
@ -362,7 +400,7 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_PUT,
"local value = struct.pack('dLc0', 0, string.len(ARGV[2]), ARGV[2]); "
+ "if redis.call('hsetnx', KEYS[1], ARGV[1], value) == 1 then "
+ "return nil "
+ "return nil;"
+ "else "
+ "local v = redis.call('hget', KEYS[1], ARGV[1]); "
+ "if v == false then "
@ -374,6 +412,56 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
Collections.<Object>singletonList(getName(key)), key, value);
}
@Override
public V addAndGet(K key, Number value) {
return get(addAndGetAsync(key, value));
}
@Override
public RFuture<V> addAndGetAsync(K key, Number value) {
byte[] keyState = encodeMapKey(key);
byte[] valueState;
try {
valueState = StringCodec.INSTANCE
.getMapValueEncoder()
.encode(new BigDecimal(value.toString()).toPlainString());
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
return commandExecutor.evalWriteAsync(getName(key), StringCodec.INSTANCE,
new RedisCommand<Object>("EVAL", new NumberConvertor(value.getClass())),
"local value = redis.call('hget', KEYS[1], ARGV[2]); "
+ "local expireDate = 92233720368547758; "
+ "local t = 0; "
+ "local val = 0; "
+ "if value ~= false then "
+ "t, val = struct.unpack('dLc0', value); "
+ "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[2]); "
+ "if expireDateScore ~= false then "
+ "expireDate = tonumber(expireDateScore) "
+ "end; "
+ "if t ~= 0 then "
+ "local expireIdle = redis.call('zscore', KEYS[3], ARGV[2]); "
+ "if expireIdle ~= false then "
+ "if tonumber(expireIdle) > tonumber(ARGV[1]) then "
+ "local value = struct.pack('dLc0', t, string.len(val), val); "
+ "redis.call('hset', KEYS[1], ARGV[2], value); "
+ "redis.call('zadd', KEYS[3], t + tonumber(ARGV[1]), ARGV[2]); "
+ "end; "
+ "expireDate = math.min(expireDate, tonumber(expireIdle)) "
+ "end; "
+ "end; "
+ "end; "
+ "local newValue = tonumber(ARGV[3]); "
+ "if expireDate >= tonumber(ARGV[1]) then "
+ "newValue = tonumber(val) + newValue; "
+ "end; "
+ "local newValuePack = struct.pack('dLc0', t + tonumber(ARGV[1]), string.len(newValue), newValue); "
+ "redis.call('hset', KEYS[1], ARGV[2], newValuePack); "
+ "return tostring(newValue); ",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), System.currentTimeMillis(), keyState, valueState);
}
@Override
public boolean fastPut(K key, V value, long ttl, TimeUnit ttlUnit) {
return get(fastPutAsync(key, value, ttl, ttlUnit));
@ -685,6 +773,99 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
+ "return 1; ",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), System.currentTimeMillis(), key, value);
}
@Override
public boolean fastPutIfAbsent(K key, V value, long ttl, TimeUnit ttlUnit) {
return fastPutIfAbsent(key, value, ttl, ttlUnit, 0, null);
}
@Override
public boolean fastPutIfAbsent(K key, V value, long ttl, TimeUnit ttlUnit, long maxIdleTime, TimeUnit maxIdleUnit) {
return get(fastPutIfAbsentAsync(key, value, ttl, ttlUnit, maxIdleTime, maxIdleUnit));
}
@Override
public RFuture<Boolean> fastPutIfAbsentAsync(K key, V value, long ttl, TimeUnit ttlUnit, long maxIdleTime, TimeUnit maxIdleUnit) {
if (ttl < 0) {
throw new IllegalArgumentException("ttl can't be negative");
}
if (maxIdleTime < 0) {
throw new IllegalArgumentException("maxIdleTime can't be negative");
}
if (ttl == 0 && maxIdleTime == 0) {
return fastPutIfAbsentAsync(key, value);
}
if (ttl > 0 && ttlUnit == null) {
throw new NullPointerException("ttlUnit param can't be null");
}
if (maxIdleTime > 0 && maxIdleUnit == null) {
throw new NullPointerException("maxIdleUnit param can't be null");
}
long ttlTimeout = 0;
if (ttl > 0) {
ttlTimeout = System.currentTimeMillis() + ttlUnit.toMillis(ttl);
}
long maxIdleTimeout = 0;
long maxIdleDelta = 0;
if (maxIdleTime > 0) {
maxIdleDelta = maxIdleUnit.toMillis(maxIdleTime);
maxIdleTimeout = System.currentTimeMillis() + maxIdleDelta;
}
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_FAST_PUT_TTL_IF_ABSENT,
"local insertable = false; "
+ "local value = redis.call('hget', KEYS[1], ARGV[5]); "
+ "if value == false then "
+ "insertable = true; "
+ "else "
+ "if insertable == false then "
+ "local t, val = struct.unpack('dLc0', value); "
+ "local expireDate = 92233720368547758; "
+ "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[5]); "
+ "if expireDateScore ~= false then "
+ "expireDate = tonumber(expireDateScore) "
+ "end; "
+ "if t ~= 0 then "
+ "local expireIdle = redis.call('zscore', KEYS[3], ARGV[5]); "
+ "if expireIdle ~= false then "
+ "expireDate = math.min(expireDate, tonumber(expireIdle)) "
+ "end; "
+ "end; "
+ "if expireDate <= tonumber(ARGV[1]) then "
+ "insertable = true; "
+ "end; "
+ "end; "
+ "end; "
+ "if insertable == true then "
// ttl
+ "if tonumber(ARGV[2]) > 0 then "
+ "redis.call('zadd', KEYS[2], ARGV[2], ARGV[5]); "
+ "else "
+ "redis.call('zrem', KEYS[2], ARGV[5]); "
+ "end; "
// idle
+ "if tonumber(ARGV[3]) > 0 then "
+ "redis.call('zadd', KEYS[3], ARGV[3], ARGV[5]); "
+ "else "
+ "redis.call('zrem', KEYS[3], ARGV[5]); "
+ "end; "
// value
+ "local val = struct.pack('dLc0', ARGV[4], string.len(ARGV[6]), ARGV[6]); "
+ "redis.call('hset', KEYS[1], ARGV[5], val); "
+ "return 1; "
+ "else "
+ "return 0; "
+ "end; ",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), System.currentTimeMillis(), ttlTimeout, maxIdleTimeout, maxIdleDelta, key, value);
}
@Override
public RFuture<Boolean> replaceAsync(K key, V oldValue, V newValue) {
@ -835,7 +1016,11 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
@Override
public RFuture<Set<java.util.Map.Entry<K, V>>> readAllEntrySetAsync() {
return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_MAP_ENTRY,
return readAll(RedisCommands.EVAL_MAP_ENTRY);
}
private <R> RFuture<R> readAll(RedisCommand<?> evalCommandType) {
return commandExecutor.evalWriteAsync(getName(), codec, evalCommandType,
"local s = redis.call('hgetall', KEYS[1]); "
+ "local result = {}; "
+ "for i, v in ipairs(s) do "
@ -867,6 +1052,12 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
"return result;",
Arrays.<Object>asList(getName(), getTimeoutSetName(), getIdleSetName()), System.currentTimeMillis());
}
@Override
public RFuture<Map<K, V>> readAllMapAsync() {
return readAll(RedisCommands.EVAL_MAP);
}
@Override
public RFuture<Collection<V>> readAllValuesAsync() {
@ -901,5 +1092,4 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
"return result;",
Arrays.<Object>asList(getName(), getTimeoutSetName(), getIdleSetName()), System.currentTimeMillis());
}
}

@ -33,6 +33,7 @@ import java.util.concurrent.TimeUnit;
import org.redisson.api.RFuture;
import org.redisson.api.RLock;
import org.redisson.api.RMultimap;
import org.redisson.api.RReadWriteLock;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.codec.MapScanCodec;
@ -71,6 +72,12 @@ public abstract class RedissonMultimap<K, V> extends RedissonExpirable implement
return new RedissonLock((CommandExecutor)commandExecutor, lockName, id);
}
@Override
public RReadWriteLock getReadWriteLock(K key) {
String lockName = getLockName(key);
return new RedissonReadWriteLock((CommandExecutor)commandExecutor, lockName, id);
}
private String getLockName(Object key) {
try {
byte[] keyState = codec.getMapKeyEncoder().encode(key);
@ -168,6 +175,16 @@ public abstract class RedissonMultimap<K, V> extends RedissonExpirable implement
return new EntrySet();
}
@Override
public Set<K> readAllKeySet() {
return get(readAllKeySetAsync());
}
@Override
public RFuture<Set<K>> readAllKeySetAsync() {
return commandExecutor.readAsync(getName(), codec, RedisCommands.HKEYS, getName());
}
@Override
public long fastRemove(K ... keys) {
return get(fastRemoveAsync(keys));

@ -20,10 +20,10 @@ import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Map.Entry;
import org.redisson.api.RExecutorService;
import org.redisson.api.RFuture;
import org.redisson.api.RedissonClient;
import org.redisson.client.RedisConnection;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.config.RedissonNodeConfig;
import org.redisson.connection.ConnectionManager;
import org.redisson.connection.MasterSlaveEntry;
@ -134,6 +134,12 @@ public class RedissonNode {
config.getRedissonNodeInitializer().onStartup(this);
}
int mapReduceWorkers = config.getMapReduceWorkers();
if (mapReduceWorkers == 0) {
mapReduceWorkers = Runtime.getRuntime().availableProcessors();
}
redisson.getExecutorService(RExecutorService.MAPREDUCE_NAME).registerWorkers(mapReduceWorkers);
for (Entry<String, Integer> entry : config.getExecutorServiceWorkers().entrySet()) {
String name = entry.getKey();
int workers = entry.getValue();

@ -93,7 +93,7 @@ public class RedissonPriorityQueue<V> extends RedissonList<V> implements RPriori
private RBucket<String> comparatorHolder;
protected RedissonPriorityQueue(CommandExecutor commandExecutor, String name, Redisson redisson) {
super(commandExecutor, name);
super(commandExecutor, name, redisson);
this.commandExecutor = commandExecutor;
comparatorHolder = redisson.getBucket(getComparatorKeyName(), StringCodec.INSTANCE);
@ -103,7 +103,7 @@ public class RedissonPriorityQueue<V> extends RedissonList<V> implements RPriori
}
public RedissonPriorityQueue(Codec codec, CommandExecutor commandExecutor, String name, Redisson redisson) {
super(codec, commandExecutor, name);
super(codec, commandExecutor, name, redisson);
this.commandExecutor = commandExecutor;
comparatorHolder = redisson.getBucket(getComparatorKeyName(), StringCodec.INSTANCE);

@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit;
import org.redisson.api.RFuture;
import org.redisson.api.RQueue;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.command.CommandAsyncExecutor;
@ -33,12 +34,12 @@ import org.redisson.command.CommandAsyncExecutor;
*/
public class RedissonQueue<V> extends RedissonList<V> implements RQueue<V> {
protected RedissonQueue(CommandAsyncExecutor commandExecutor, String name) {
super(commandExecutor, name);
protected RedissonQueue(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(commandExecutor, name, redisson);
}
protected RedissonQueue(Codec codec, CommandAsyncExecutor commandExecutor, String name) {
super(codec, commandExecutor, name);
protected RedissonQueue(Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(codec, commandExecutor, name, redisson);
}
@Override
@ -115,9 +116,4 @@ public class RedissonQueue<V> extends RedissonList<V> implements RQueue<V> {
return commandExecutor.writeAsync(getName(), codec, RedisCommands.RPOPLPUSH, getName(), queueName);
}
@Override
public V pollLastAndOfferFirstTo(RQueue<V> queue) {
return pollLastAndOfferFirstTo(queue.getName());
}
}

@ -17,8 +17,10 @@ package org.redisson;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@ -59,6 +61,7 @@ public class RedissonRemoteService extends BaseRemoteService implements RRemoteS
private static final Logger log = LoggerFactory.getLogger(RedissonRemoteService.class);
private final Map<RemoteServiceKey, RemoteServiceMethod> beans = PlatformDependent.newConcurrentHashMap();
private final Map<Class<?>, Set<RFuture<RemoteServiceRequest>>> futures = PlatformDependent.newConcurrentHashMap();
public RedissonRemoteService(RedissonClient redisson, CommandExecutor commandExecutor) {
super(redisson, commandExecutor);
@ -81,6 +84,23 @@ public class RedissonRemoteService extends BaseRemoteService implements RRemoteS
register(remoteInterface, object, 1);
}
@Override
public <T> void deregister(Class<T> remoteInterface) {
for (Method method : remoteInterface.getMethods()) {
RemoteServiceKey key = new RemoteServiceKey(remoteInterface, method.getName(), getMethodSignatures(method));
beans.remove(key);
}
Set<RFuture<RemoteServiceRequest>> removedFutures = futures.remove(remoteInterface);
if (removedFutures == null) {
return;
}
for (RFuture<RemoteServiceRequest> future : removedFutures) {
future.cancel(false);
}
}
@Override
public <T> void register(Class<T> remoteInterface, T object, int workers) {
register(remoteInterface, object, workers, commandExecutor.getConnectionManager().getExecutor());
@ -99,19 +119,33 @@ public class RedissonRemoteService extends BaseRemoteService implements RRemoteS
}
}
Set<RFuture<RemoteServiceRequest>> values = Collections.newSetFromMap(PlatformDependent.<RFuture<RemoteServiceRequest>, Boolean>newConcurrentHashMap());
futures.put(remoteInterface, values);
String requestQueueName = getRequestQueueName(remoteInterface);
RBlockingQueue<RemoteServiceRequest> requestQueue = redisson.getBlockingQueue(requestQueueName, getCodec());
for (int i = 0; i < workers; i++) {
String requestQueueName = getRequestQueueName(remoteInterface);
RBlockingQueue<RemoteServiceRequest> requestQueue = redisson.getBlockingQueue(requestQueueName, getCodec());
subscribe(remoteInterface, requestQueue, executor);
}
}
private <T> void subscribe(final Class<T> remoteInterface, final RBlockingQueue<RemoteServiceRequest> requestQueue,
final ExecutorService executor) {
RFuture<RemoteServiceRequest> take = requestQueue.takeAsync();
Set<RFuture<RemoteServiceRequest>> futuresSet = futures.get(remoteInterface);
if (futuresSet == null) {
return;
}
final RFuture<RemoteServiceRequest> take = requestQueue.takeAsync();
futuresSet.add(take);
take.addListener(new FutureListener<RemoteServiceRequest>() {
@Override
public void operationComplete(Future<RemoteServiceRequest> future) throws Exception {
Set<RFuture<RemoteServiceRequest>> futuresSet = futures.get(remoteInterface);
if (futuresSet == null) {
return;
}
futuresSet.remove(take);
if (!future.isSuccess()) {
if (future.cause() instanceof RedissonShutdownException) {
return;
@ -157,10 +191,10 @@ public class RedissonRemoteService extends BaseRemoteService implements RRemoteS
@Override
public void operationComplete(Future<Boolean> future) throws Exception {
if (!future.isSuccess()) {
log.error("Can't send ack for request: " + request, future.cause());
if (future.cause() instanceof RedissonShutdownException) {
return;
}
log.error("Can't send ack for request: " + request, future.cause());
// re-subscribe after a failed send (ack)
subscribe(remoteInterface, requestQueue, executor);
return;
@ -260,12 +294,17 @@ public class RedissonRemoteService extends BaseRemoteService implements RRemoteS
clientsFuture.addListener(new FutureListener<List<?>>() {
@Override
public void operationComplete(Future<List<?>> future) throws Exception {
// interface has been deregistered
if (futures.get(remoteInterface) == null) {
return;
}
if (!future.isSuccess()) {
log.error("Can't send response: " + responseHolder.get() + " for request: " + request,
future.cause());
if (future.cause() instanceof RedissonShutdownException) {
return;
}
log.error("Can't send response: " + responseHolder.get() + " for request: " + request,
future.cause());
}
// re-subscribe anyways (fail or success) after the send

@ -30,7 +30,9 @@ import java.util.Map.Entry;
import org.redisson.api.RFuture;
import org.redisson.api.RScoredSortedSet;
import org.redisson.api.RedissonClient;
import org.redisson.api.SortOrder;
import org.redisson.api.mapreduce.RCollectionMapReduce;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.DoubleCodec;
import org.redisson.client.codec.LongCodec;
@ -44,6 +46,7 @@ import org.redisson.client.protocol.convertor.BooleanReplayConvertor;
import org.redisson.client.protocol.decoder.ListScanResult;
import org.redisson.client.protocol.decoder.ScanObjectEntry;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.mapreduce.RedissonCollectionMapReduce;
/**
*
@ -53,12 +56,21 @@ import org.redisson.command.CommandAsyncExecutor;
*/
public class RedissonScoredSortedSet<V> extends RedissonExpirable implements RScoredSortedSet<V> {
public RedissonScoredSortedSet(CommandAsyncExecutor commandExecutor, String name) {
private RedissonClient redisson;
public RedissonScoredSortedSet(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(commandExecutor, name);
this.redisson = redisson;
}
public RedissonScoredSortedSet(Codec codec, CommandAsyncExecutor commandExecutor, String name) {
public RedissonScoredSortedSet(Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(codec, commandExecutor, name);
this.redisson = redisson;
}
@Override
public <KOut, VOut> RCollectionMapReduce<V, KOut, VOut> mapReduce() {
return new RedissonCollectionMapReduce<V, KOut, VOut>(this, redisson, commandExecutor.getConnectionManager());
}
@Override

@ -26,7 +26,9 @@ import java.util.Set;
import org.redisson.api.RFuture;
import org.redisson.api.RSet;
import org.redisson.api.RedissonClient;
import org.redisson.api.SortOrder;
import org.redisson.api.mapreduce.RCollectionMapReduce;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.ScanCodec;
import org.redisson.client.protocol.RedisCommand;
@ -36,6 +38,7 @@ import org.redisson.client.protocol.convertor.BooleanReplayConvertor;
import org.redisson.client.protocol.decoder.ListScanResult;
import org.redisson.client.protocol.decoder.ScanObjectEntry;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.mapreduce.RedissonCollectionMapReduce;
/**
* Distributed and concurrent implementation of {@link java.util.Set}
@ -46,12 +49,21 @@ import org.redisson.command.CommandAsyncExecutor;
*/
public class RedissonSet<V> extends RedissonExpirable implements RSet<V>, ScanIterator {
protected RedissonSet(CommandAsyncExecutor commandExecutor, String name) {
RedissonClient redisson;
protected RedissonSet(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(commandExecutor, name);
this.redisson = redisson;
}
public RedissonSet(Codec codec, CommandAsyncExecutor commandExecutor, String name) {
public RedissonSet(Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(codec, commandExecutor, name);
this.redisson = redisson;
}
@Override
public <KOut, VOut> RCollectionMapReduce<V, KOut, VOut> mapReduce() {
return new RedissonCollectionMapReduce<V, KOut, VOut>(this, redisson, commandExecutor.getConnectionManager());
}
@Override
@ -79,10 +91,6 @@ public class RedissonSet<V> extends RedissonExpirable implements RSet<V>, ScanIt
return commandExecutor.readAsync(getName(o), codec, RedisCommands.SISMEMBER, getName(o), o);
}
protected String getName(Object o) {
return getName();
}
@Override
public ListScanResult<ScanObjectEntry> scanIterator(String name, InetSocketAddress client, long startPos) {
RFuture<ListScanResult<ScanObjectEntry>> f = commandExecutor.readAsync(client, name, new ScanCodec(codec), RedisCommands.SSCAN, name, startPos);

@ -27,6 +27,8 @@ import java.util.concurrent.TimeUnit;
import org.redisson.api.RFuture;
import org.redisson.api.RSetCache;
import org.redisson.api.RedissonClient;
import org.redisson.api.mapreduce.RCollectionMapReduce;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.ScanCodec;
import org.redisson.client.protocol.RedisCommand;
@ -38,6 +40,7 @@ import org.redisson.client.protocol.decoder.ListScanResult;
import org.redisson.client.protocol.decoder.ScanObjectEntry;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.eviction.EvictionScheduler;
import org.redisson.mapreduce.RedissonCollectionMapReduce;
/**
* <p>Set-based cache with ability to set TTL for each entry via
@ -59,22 +62,27 @@ import org.redisson.eviction.EvictionScheduler;
*/
public class RedissonSetCache<V> extends RedissonExpirable implements RSetCache<V>, ScanIterator {
RedissonSetCache(CommandAsyncExecutor commandExecutor, String name) {
RedissonClient redisson;
public RedissonSetCache(EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(commandExecutor, name);
if (evictionScheduler != null) {
evictionScheduler.schedule(getName(), 0);
}
this.redisson = redisson;
}
RedissonSetCache(Codec codec, CommandAsyncExecutor commandExecutor, String name) {
public RedissonSetCache(Codec codec, EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(codec, commandExecutor, name);
if (evictionScheduler != null) {
evictionScheduler.schedule(getName(), 0);
}
this.redisson = redisson;
}
public RedissonSetCache(EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name) {
super(commandExecutor, name);
evictionScheduler.schedule(getName());
}
public RedissonSetCache(Codec codec, EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name) {
super(codec, commandExecutor, name);
evictionScheduler.schedule(getName());
@Override
public <KOut, VOut> RCollectionMapReduce<V, KOut, VOut> mapReduce() {
return new RedissonCollectionMapReduce<V, KOut, VOut>(this, redisson, commandExecutor.getConnectionManager());
}
@Override

@ -162,7 +162,7 @@ public class RedissonSetMultimap<K, V> extends RedissonMultimap<K, V> implements
final String keyHash = hash(keyState);
final String setName = getValuesName(keyHash);
return new RedissonSet<V>(codec, commandExecutor, setName) {
return new RedissonSet<V>(codec, commandExecutor, setName, null) {
@Override
public RFuture<Boolean> deleteAsync() {

@ -28,7 +28,7 @@ public class RedissonSetMultimapIterator<K, V, M> extends RedissonMultiMapIterat
@Override
protected Iterator<V> getIterator(String name) {
RedissonSet<V> set = new RedissonSet<V>(codec, commandExecutor, map.getValuesName(name));
RedissonSet<V> set = new RedissonSet<V>(codec, commandExecutor, map.getValuesName(name), null);
return set.iterator();
}

@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit;
import org.redisson.api.RFuture;
import org.redisson.api.RSet;
import org.redisson.api.SortOrder;
import org.redisson.api.mapreduce.RCollectionMapReduce;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.ScanCodec;
import org.redisson.client.protocol.RedisCommand;
@ -66,7 +67,7 @@ public class RedissonSetMultimapValues<V> extends RedissonExpirable implements R
super(codec, commandExecutor, name);
this.timeoutSetName = timeoutSetName;
this.key = key;
this.set = new RedissonSet<V>(codec, commandExecutor, name);
this.set = new RedissonSet<V>(codec, commandExecutor, name, null);
}
@Override
@ -74,6 +75,11 @@ public class RedissonSetMultimapValues<V> extends RedissonExpirable implements R
return get(sizeAsync());
}
@Override
public <KOut, VOut> RCollectionMapReduce<V, KOut, VOut> mapReduce() {
return null;
}
@Override
public RFuture<Boolean> clearExpireAsync() {
throw new UnsupportedOperationException("This operation is not supported for SetMultimap values Set");

@ -15,7 +15,9 @@
*/
package org.redisson;
public class RedissonShutdownException extends RuntimeException {
import org.redisson.client.RedisException;
public class RedissonShutdownException extends RedisException {
private static final long serialVersionUID = -2694051226420789395L;

@ -32,10 +32,13 @@ import org.redisson.api.RBucket;
import org.redisson.api.RFuture;
import org.redisson.api.RLock;
import org.redisson.api.RSortedSet;
import org.redisson.api.RedissonClient;
import org.redisson.api.mapreduce.RCollectionMapReduce;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.StringCodec;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.command.CommandExecutor;
import org.redisson.mapreduce.RedissonCollectionMapReduce;
import org.redisson.misc.RPromise;
/**
@ -94,10 +97,12 @@ public class RedissonSortedSet<V> extends RedissonObject implements RSortedSet<V
private RLock lock;
private RedissonList<V> list;
private RBucket<String> comparatorHolder;
private RedissonClient redisson;
protected RedissonSortedSet(CommandExecutor commandExecutor, String name, Redisson redisson) {
protected RedissonSortedSet(CommandExecutor commandExecutor, String name, RedissonClient redisson) {
super(commandExecutor, name);
this.commandExecutor = commandExecutor;
this.redisson = redisson;
comparatorHolder = redisson.getBucket(getComparatorKeyName(), StringCodec.INSTANCE);
lock = redisson.getLock("redisson_sortedset_lock:{" + getName() + "}");
@ -116,6 +121,11 @@ public class RedissonSortedSet<V> extends RedissonObject implements RSortedSet<V
loadComparator();
}
@Override
public <KOut, VOut> RCollectionMapReduce<V, KOut, VOut> mapReduce() {
return new RedissonCollectionMapReduce<V, KOut, VOut>(this, redisson, commandExecutor.getConnectionManager());
}
private void loadComparator() {
try {

@ -58,7 +58,7 @@ public class RedissonSubList<V> extends RedissonList<V> implements RList<V> {
int size = -1;
protected RedissonSubList(Codec codec, CommandAsyncExecutor commandExecutor, String name, int fromIndex, int toIndex) {
super(codec, commandExecutor, name);
super(codec, commandExecutor, name, null);
this.fromIndex = fromIndex;
this.toIndex.set(toIndex);
}

@ -25,9 +25,69 @@ import java.util.concurrent.TimeUnit;
*/
public class LocalCachedMapOptions {
public enum EvictionPolicy {NONE, LRU, LFU, SOFT};
public enum InvalidationPolicy {
/**
* No invalidation on map changes
*/
NONE,
/**
* Invalidate cache entry across all LocalCachedMap instances on map entry change.
*/
ON_CHANGE,
/**
* Invalidate cache entry across all LocalCachedMap instances on map entry change.
* <p>
* Clear cache if LocalCachedMap instance has been disconnected for a while.
* It's applied to avoid stale objects in cache.
*/
ON_CHANGE_WITH_CLEAR_ON_RECONNECT,
/**
* Invalidate cache entry across all LocalCachedMap instances on map entry change.
* <p>
* Store invalidated entry hash in invalidation log for 10 minutes.
* Cache keys for stored invalidated entry hashes will be removed
* if LocalCachedMap instance has been disconnected less than 10 minutes
* or whole cache will be cleaned otherwise.
* It's applied to avoid stale objects in cache.
*/
ON_CHANGE_WITH_LOAD_ON_RECONNECT
}
public enum EvictionPolicy {
/**
* Cache without eviction.
*/
NONE,
/**
* Least Recently Used cache.
*/
LRU,
/**
* Least Frequently Used cache.
*/
LFU,
/**
* Cache with Soft Reference used for values.
* All references will be collected by GC
*/
SOFT,
/**
* Cache with Weak Reference used for values.
* All references will be collected by GC
*/
WEAK
};
private boolean invalidateEntryOnChange;
private InvalidationPolicy invalidationPolicy;
private EvictionPolicy evictionPolicy;
private int cacheSize;
private long timeToLiveInMillis;
@ -37,7 +97,7 @@ public class LocalCachedMapOptions {
}
protected LocalCachedMapOptions(LocalCachedMapOptions copy) {
this.invalidateEntryOnChange = copy.invalidateEntryOnChange;
this.invalidationPolicy = copy.invalidationPolicy;
this.evictionPolicy = copy.evictionPolicy;
this.cacheSize = copy.cacheSize;
this.timeToLiveInMillis = copy.timeToLiveInMillis;
@ -62,13 +122,9 @@ public class LocalCachedMapOptions {
return new LocalCachedMapOptions()
.cacheSize(0).timeToLive(0).maxIdle(0)
.evictionPolicy(EvictionPolicy.NONE)
.invalidateEntryOnChange(true);
.invalidationPolicy(InvalidationPolicy.ON_CHANGE);
}
public boolean isInvalidateEntryOnChange() {
return invalidateEntryOnChange;
}
public EvictionPolicy getEvictionPolicy() {
return evictionPolicy;
}
@ -95,6 +151,27 @@ public class LocalCachedMapOptions {
this.cacheSize = cacheSize;
return this;
}
public InvalidationPolicy getInvalidationPolicy() {
return invalidationPolicy;
}
/**
* Sets entry invalidation policy.
*
* @param invalidationPolicy
* <p><code>NONE</code> - no invalidation applied.
* <p><code>ON_CHANGE</code> - invalidation message which removes corresponding entry from cache
* will be sent to all other RLocalCachedMap instances on each entry update/remove operation.
* <p><code>ON_CHANGE_WITH_CLEAR_ON_RECONNECT</code> - includes <code>ON_CHANGE</code> policy
* and clears local cache of current instance in case of reconnection to Redis.
*
* @return LocalCachedMapOptions instance
*/
public LocalCachedMapOptions invalidationPolicy(InvalidationPolicy invalidationPolicy) {
this.invalidationPolicy = invalidationPolicy;
return this;
}
/**
* Sets entry invalidation behavior.
@ -104,9 +181,12 @@ public class LocalCachedMapOptions {
* if <code>false</code> then invalidation message won't be sent
* @return LocalCachedMapOptions instance
*/
@Deprecated
public LocalCachedMapOptions invalidateEntryOnChange(boolean value) {
this.invalidateEntryOnChange = value;
return this;
if (value) {
return invalidationPolicy(InvalidationPolicy.ON_CHANGE);
}
return invalidationPolicy(InvalidationPolicy.NONE);
}
/**

@ -16,6 +16,7 @@
package org.redisson.api;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.redisson.client.RedisException;
import org.redisson.client.codec.Codec;
@ -27,7 +28,7 @@ import org.redisson.client.codec.Codec;
* from this interface are batched to separate queue and could be executed later
* with <code>execute()</code> or <code>executeAsync()</code> methods.
* <p>
* Please be ware, atomicity <b>is not</b> guaranteed.
* Please be aware, atomicity <b>is not</b> guaranteed.
*
*
* @author Nikita Koksharov
@ -426,4 +427,46 @@ public interface RBatch {
*
*/
RFuture<Void> executeSkipResultAsync();
/**
* Defines timeout for Redis response.
* Starts to countdown when Redis command has been successfully sent.
* <p>
* <code>0</code> value means use <code>Config.setTimeout</code> value instead.
* <p>
* Default is <code>0</code>
*
* @param timeout value
* @param unit value
* @return self instance
*/
RBatch timeout(long timeout, TimeUnit unit);
/**
* Defines time interval for another one attempt send Redis commands batch
* if it hasn't been sent already.
* <p>
* <code>0</code> value means use <code>Config.setRetryInterval</code> value instead.
* <p>
* Default is <code>0</code>
*
* @param retryInterval value
* @param unit value
* @return self instance
*/
RBatch retryInterval(long retryInterval, TimeUnit unit);
/**
* Defines attempts amount to re-send Redis commands batch
* if it hasn't been sent already.
* <p>
* <code>0</code> value means use <code>Config.setRetryAttempts</code> value instead.
* <p>
* Default is <code>0</code>
*
* @param retryAttempts value
* @return self instance
*/
RBatch retryAttempts(int retryAttempts);
}

@ -19,13 +19,18 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
/**
* Distributed implementation of {@link java.util.concurrent.ExecutorService}
* Redis based implementation of {@link java.util.concurrent.ExecutorService}
*
* @author Nikita Koksharov
*
*/
public interface RExecutorService extends ExecutorService, RExecutorServiceAsync {
/**
* MapReduce's executor name
*/
String MAPREDUCE_NAME = "redisson_mapreduce";
/**
* Submits a value-returning task for execution and returns a
* Future representing the pending results of the task. The
@ -85,11 +90,18 @@ public interface RExecutorService extends ExecutorService, RExecutorServiceAsync
void registerWorkers(int workers);
/**
* Register workers with custom executor which executes each task
* Register workers with custom executor
*
* @param workers - workers amount
* @param executor - executor instance
*/
void registerWorkers(int workers, ExecutorService executor);
/**
* Returns active worker groups
*
* @return active worker groups count
*/
int countActiveWorkers();
}

@ -18,7 +18,7 @@ package org.redisson.api;
import java.util.concurrent.Callable;
/**
* Distributed implementation of {@link java.util.concurrent.ExecutorService}
* Redis based implementation of {@link java.util.concurrent.ExecutorService}
*
* @author Nikita Koksharov
*
@ -33,7 +33,7 @@ public interface RExecutorServiceAsync {
RFuture<Boolean> deleteAsync();
/**
* Submit task for execution in async mode with listeners support
* Use {@link RExecutorService#submit(Callable)}
*
* @param <T> type of return value
* @param task - task to execute
@ -42,7 +42,7 @@ public interface RExecutorServiceAsync {
<T> RFuture<T> submitAsync(Callable<T> task);
/**
* Submit task for execution in async mode with listeners support
* Use {@link RExecutorService#submit(Runnable)}
*
* @param task - task to execute
* @return Future object

@ -16,6 +16,7 @@
package org.redisson.api;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
/**
*
@ -24,9 +25,87 @@ import java.util.Collection;
*/
public interface RKeys extends RKeysAsync {
/**
* Move object to another database
*
* @param name of object
* @param database - Redis database number
* @return <code>true</code> if key was moved else <code>false</code>
*/
boolean move(String name, int database);
/**
* Transfer an object from source Redis instance to destination Redis instance
*
* @param name of object
* @param host - destination host
* @param port - destination port
* @param database - destination database
*/
void migrate(String name, String host, int port, int database);
/**
* Set a timeout for object. After the timeout has expired,
* the key will automatically be deleted.
*
* @param name of object
* @param timeToLive - timeout before object will be deleted
* @param timeUnit - timeout time unit
* @return <code>true</code> if the timeout was set and <code>false</code> if not
*/
boolean expire(String name, long timeToLive, TimeUnit timeUnit);
/**
* Set an expire date for object. When expire date comes
* the key will automatically be deleted.
*
* @param name of object
* @param timestamp - expire date in milliseconds (Unix timestamp)
* @return <code>true</code> if the timeout was set and <code>false</code> if not
*/
boolean expireAt(String name, long timestamp);
/**
* Clear an expire timeout or expire date for object.
*
* @param name of object
* @return <code>true</code> if timeout was removed
* <code>false</code> if object does not exist or does not have an associated timeout
*/
boolean clearExpire(String name);
/**
* Rename object with <code>oldName</code> to <code>newName</code>
* only if new key is not exists
*
* @param oldName - old name of object
* @param newName - new name of object
* @return <code>true</code> if object has been renamed successfully and <code>false</code> otherwise
*/
boolean renamenx(String oldName, String newName);
/**
* Rename current object key to <code>newName</code>
*
* @param currentName - current name of object
* @param newName - new name of object
*/
void rename(String currentName, String newName);
/**
* Remaining time to live of Redisson object that has a timeout
*
* @param name of key
* @return time in milliseconds
* -2 if the key does not exist.
* -1 if the key exists but has no associated expire.
*/
long remainTimeToLive(String name);
/**
* Update the last access time of an object.
*
* @param names of keys
* @return count of objects were touched
*/
long touch(String... names);
@ -135,6 +214,14 @@ public interface RKeys extends RKeysAsync {
*/
long deleteByPattern(String pattern);
/**
* Delete multiple objects
*
* @param objects of Redisson
* @return number of removed keys
*/
long delete(RObject ... objects);
/**
* Delete multiple objects by name
*

@ -16,6 +16,7 @@
package org.redisson.api;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
/**
*
@ -24,9 +25,87 @@ import java.util.Collection;
*/
public interface RKeysAsync {
/**
* Move object to another database
*
* @param name of object
* @param database - Redis database number
* @return <code>true</code> if key was moved else <code>false</code>
*/
RFuture<Boolean> moveAsync(String name, int database);
/**
* Transfer an object from source Redis instance to destination Redis instance
*
* @param name of object
* @param host - destination host
* @param port - destination port
* @param database - destination database
*/
RFuture<Void> migrateAsync(String name, String host, int port, int database);
/**
* Set a timeout for object. After the timeout has expired,
* the key will automatically be deleted.
*
* @param name of object
* @param timeToLive - timeout before object will be deleted
* @param timeUnit - timeout time unit
* @return <code>true</code> if the timeout was set and <code>false</code> if not
*/
RFuture<Boolean> expireAsync(String name, long timeToLive, TimeUnit timeUnit);
/**
* Set an expire date for object. When expire date comes
* the key will automatically be deleted.
*
* @param name of object
* @param timestamp - expire date in milliseconds (Unix timestamp)
* @return <code>true</code> if the timeout was set and <code>false</code> if not
*/
RFuture<Boolean> expireAtAsync(String name, long timestamp);
/**
* Clear an expire timeout or expire date for object.
*
* @param name of object
* @return <code>true</code> if timeout was removed
* <code>false</code> if object does not exist or does not have an associated timeout
*/
RFuture<Boolean> clearExpireAsync(String name);
/**
* Rename object with <code>oldName</code> to <code>newName</code>
* only if new key is not exists
*
* @param oldName - old name of object
* @param newName - new name of object
* @return <code>true</code> if object has been renamed successfully and <code>false</code> otherwise
*/
RFuture<Boolean> renamenxAsync(String oldName, String newName);
/**
* Rename current object key to <code>newName</code>
*
* @param currentName - current name of object
* @param newName - new name of object
*/
RFuture<Void> renameAsync(String currentName, String newName);
/**
* Remaining time to live of Redisson object that has a timeout
*
* @param name of key
* @return time in milliseconds
* -2 if the key does not exist.
* -1 if the key exists but has no associated expire.
*/
RFuture<Long> remainTimeToLiveAsync(String name);
/**
* Update the last access time of an object.
*
* @param names of keys
* @return count of objects were touched
*/
RFuture<Long> touchAsync(String... names);
@ -91,6 +170,14 @@ public interface RKeysAsync {
*/
RFuture<Long> deleteByPatternAsync(String pattern);
/**
* Delete multiple objects
*
* @param objects of Redisson
* @return number of removed keys
*/
RFuture<Long> deleteAsync(RObject ... objects);
/**
* Delete multiple objects by name
*

@ -18,8 +18,25 @@ package org.redisson.api;
import java.util.Collection;
import java.util.Set;
import org.redisson.api.mapreduce.RCollectionMapReduce;
/**
* Sorted set contained values of String type
*
* @author Nikita Koksharov
*
*/
public interface RLexSortedSet extends RLexSortedSetAsync, Set<String>, RExpirable {
/**
* Returns <code>RMapReduce</code> object associated with this object
*
* @param <KOut> output key
* @param <VOut> output value
* @return MapReduce instance
*/
<KOut, VOut> RCollectionMapReduce<String, KOut, VOut> mapReduce();
String pollFirst();
String pollLast();

@ -17,6 +17,12 @@ package org.redisson.api;
import java.util.Collection;
/**
* Sorted set contained values of String type
*
* @author Nikita Koksharov
*
*/
public interface RLexSortedSetAsync extends RCollectionAsync<String> {
RFuture<String> pollLastAsync();

@ -18,6 +18,8 @@ package org.redisson.api;
import java.util.List;
import java.util.RandomAccess;
import org.redisson.api.mapreduce.RCollectionMapReduce;
/**
* Distributed and concurrent implementation of {@link java.util.List}
*
@ -27,6 +29,15 @@ import java.util.RandomAccess;
*/
public interface RList<V> extends List<V>, RExpirable, RListAsync<V>, RSortable<List<V>>, RandomAccess {
/**
* Returns <code>RMapReduce</code> object associated with this map
*
* @param <KOut> output key
* @param <VOut> output value
* @return MapReduce instance
*/
<KOut, VOut> RCollectionMapReduce<V, KOut, VOut> mapReduce();
/**
* Add <code>element</code> after <code>elementToFind</code>
*

@ -20,6 +20,8 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import org.redisson.api.mapreduce.RMapReduce;
/**
* Distributed and concurrent implementation of {@link java.util.concurrent.ConcurrentMap}
* and {@link java.util.Map}
@ -33,6 +35,15 @@ import java.util.concurrent.ConcurrentMap;
*/
public interface RMap<K, V> extends ConcurrentMap<K, V>, RExpirable, RMapAsync<K, V> {
/**
* Returns <code>RMapReduce</code> object associated with this map
*
* @param <KOut> output key
* @param <VOut> output value
* @return MapReduce instance
*/
<KOut, VOut> RMapReduce<K, V, KOut, VOut> mapReduce();
/**
* Returns <code>RReadWriteLock</code> instance associated with key
*
@ -128,6 +139,13 @@ public interface RMap<K, V> extends ConcurrentMap<K, V>, RExpirable, RMapAsync<K
*/
Set<Entry<K, V>> readAllEntrySet();
/**
* Read all map as local instance at once
*
* @return map
*/
Map<K, V> readAllMap();
/**
* Returns key set.
* This method <b>DOESN'T</b> fetch all of them as {@link #readAllKeySet()} does.

@ -98,6 +98,13 @@ public interface RMapAsync<K, V> extends RExpirableAsync {
*/
RFuture<Set<Entry<K, V>>> readAllEntrySetAsync();
/**
* Read all map as local instance at once
*
* @return map
*/
RFuture<Map<K, V>> readAllMapAsync();
RFuture<V> getAsync(K key);
RFuture<V> putAsync(K key, V value);

@ -161,6 +161,53 @@ public interface RMapCache<K, V> extends RMap<K, V>, RMapCacheAsync<K, V> {
* <code>false</code> if key already exists in the hash and the value was updated.
*/
boolean fastPut(K key, V value, long ttl, TimeUnit ttlUnit, long maxIdleTime, TimeUnit maxIdleUnit);
/**
* If the specified key is not already associated
* with a value, associate it with the given value.
* <p>
* Stores value mapped by key with specified time to live.
* Entry expires after specified time to live.
* <p>
* Works faster than usual {@link #putIfAbsent(Object, Object, long, TimeUnit)}
* as it not returns previous value.
*
* @param key - map key
* @param value - map value
* @param ttl - time to live for key\value entry.
* If <code>0</code> then stores infinitely.
* @param ttlUnit - time unit
* @return <code>true</code> if key is a new key in the hash and value was set.
* <code>false</code> if key already exists in the hash
*/
boolean fastPutIfAbsent(K key, V value, long ttl, TimeUnit ttlUnit);
/**
* If the specified key is not already associated
* with a value, associate it with the given value.
* <p>
* Stores value mapped by key with specified time to live and max idle time.
* Entry expires when specified time to live or max idle time has expired.
* <p>
* Works faster than usual {@link #putIfAbsent(Object, Object, long, TimeUnit, long, TimeUnit)}
* as it not returns previous value.
*
* @param key - map key
* @param value - map value
* @param ttl - time to live for key\value entry.
* If <code>0</code> then time to live doesn't affect entry expiration.
* @param ttlUnit - time unit
* @param maxIdleTime - max idle time for key\value entry.
* If <code>0</code> then max idle time doesn't affect entry expiration.
* @param maxIdleUnit - time unit
* <p>
* if <code>maxIdleTime</code> and <code>ttl</code> params are equal to <code>0</code>
* then entry stores infinitely.
*
* @return <code>true</code> if key is a new key in the hash and value was set.
* <code>false</code> if key already exists in the hash.
*/
boolean fastPutIfAbsent(K key, V value, long ttl, TimeUnit ttlUnit, long maxIdleTime, TimeUnit maxIdleUnit);
/**
* Returns the number of entries in cache.

@ -163,6 +163,32 @@ public interface RMapCacheAsync<K, V> extends RMapAsync<K, V> {
* @return <code>true</code> if value has been set successfully
*/
RFuture<Boolean> fastPutAsync(K key, V value, long ttl, TimeUnit ttlUnit, long maxIdleTime, TimeUnit maxIdleUnit);
/**
* If the specified key is not already associated
* with a value, associate it with the given value.
* <p>
* Stores value mapped by key with specified time to live and max idle time.
* Entry expires when specified time to live or max idle time has expired.
* <p>
* Works faster than usual {@link #putIfAbsentAsync(Object, Object, long, TimeUnit, long, TimeUnit)}
* as it not returns previous value.
*
* @param key - map key
* @param value - map value
* @param ttl - time to live for key\value entry.
* If <code>0</code> then time to live doesn't affect entry expiration.
* @param ttlUnit - time unit
* @param maxIdleTime - max idle time for key\value entry.
* If <code>0</code> then max idle time doesn't affect entry expiration.
* @param maxIdleUnit - time unit
* <p>
* if <code>maxIdleTime</code> and <code>ttl</code> params are equal to <code>0</code>
* then entry stores infinitely.
*
* @return previous associated value
*/
RFuture<Boolean> fastPutIfAbsentAsync(K key, V value, long ttl, TimeUnit ttlUnit, long maxIdleTime, TimeUnit maxIdleUnit);
/**
* Returns the number of entries in cache.

@ -29,6 +29,14 @@ import java.util.Set;
*/
public interface RMultimap<K, V> extends RExpirable, RMultimapAsync<K, V> {
/**
* Returns <code>RReadWriteLock</code> instance associated with key
*
* @param key - map key
* @return readWriteLock
*/
RReadWriteLock getReadWriteLock(K key);
/**
* Returns <code>RLock</code> instance associated with key
*
@ -236,5 +244,11 @@ public interface RMultimap<K, V> extends RExpirable, RMultimapAsync<K, V> {
*/
long fastRemove(K ... keys);
/**
* Read all keys at once
*
* @return keys
*/
Set<K> readAllKeySet();
}

@ -16,6 +16,7 @@
package org.redisson.api;
import java.util.Collection;
import java.util.Set;
/**
* Base asynchronous MultiMap interface. A collection that maps multiple values per one key.
*
@ -158,5 +159,11 @@ public interface RMultimapAsync<K, V> extends RExpirableAsync {
*/
RFuture<Long> fastRemoveAsync(K ... keys);
/**
* Read all keys at once
*
* @return keys
*/
RFuture<Set<K>> readAllKeySetAsync();
}

@ -29,9 +29,6 @@ public interface RQueue<V> extends Queue<V>, RExpirable, RQueueAsync<V> {
V pollLastAndOfferFirstTo(String dequeName);
@Deprecated
V pollLastAndOfferFirstTo(RQueue<V> deque);
List<V> readAll();
}

@ -88,6 +88,14 @@ public interface RRemoteService {
*/
<T> void register(Class<T> remoteInterface, T object, int workers, ExecutorService executor);
/**
* Deregister all workers for remote service
*
* @param <T> type of remote service
* @param remoteInterface - remote service interface
*/
<T> void deregister(Class<T> remoteInterface);
/**
* Get remote service object for remote invocations.
* <p>

@ -21,7 +21,7 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* Distributed implementation of {@link java.util.concurrent.ScheduledExecutorService}
* Redis based implementation of {@link java.util.concurrent.ScheduledExecutorService}
*
* @author Nikita Koksharov
*

@ -19,7 +19,7 @@ import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
/**
* Distributed implementation of {@link java.util.concurrent.ScheduledExecutorService}
* Redis based implementation of {@link java.util.concurrent.ScheduledExecutorService}
*
* @author Nikita Koksharov
*

@ -19,6 +19,7 @@ import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.redisson.api.mapreduce.RCollectionMapReduce;
import org.redisson.client.protocol.ScoredEntry;
/**
@ -35,6 +36,15 @@ public interface RScoredSortedSet<V> extends RScoredSortedSetAsync<V>, Iterable<
}
/**
* Returns <code>RMapReduce</code> object associated with this object
*
* @param <KOut> output key
* @param <VOut> output value
* @return MapReduce instance
*/
<KOut, VOut> RCollectionMapReduce<V, KOut, VOut> mapReduce();
V pollFirst();
V pollLast();
@ -117,8 +127,36 @@ public interface RScoredSortedSet<V> extends RScoredSortedSetAsync<V>, Iterable<
Collection<ScoredEntry<V>> entryRangeReversed(int startIndex, int endIndex);
/**
* Returns all values between <code>startScore</code> and <code>endScore</code>.
*
* @param startScore - start score.
* Use <code>Double.POSITIVE_INFINITY</code> or <code>Double.NEGATIVE_INFINITY</code>
* to define infinity numbers
* @param startScoreInclusive - start score inclusive
* @param endScore - end score
* Use <code>Double.POSITIVE_INFINITY</code> or <code>Double.NEGATIVE_INFINITY</code>
* to define infinity numbers
*
* @param endScoreInclusive - end score inclusive
* @return values
*/
Collection<V> valueRange(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive);
/**
* Returns all values between <code>startScore</code> and <code>endScore</code> in reversed order.
*
* @param startScore - start score.
* Use <code>Double.POSITIVE_INFINITY</code> or <code>Double.NEGATIVE_INFINITY</code>
* to define infinity numbers
* @param startScoreInclusive - start score inclusive
* @param endScore - end score
* Use <code>Double.POSITIVE_INFINITY</code> or <code>Double.NEGATIVE_INFINITY</code>
* to define infinity numbers
*
* @param endScoreInclusive - end score inclusive
* @return values
*/
Collection<V> valueRangeReversed(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive);
Collection<ScoredEntry<V>> entryRange(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive);

@ -92,8 +92,36 @@ public interface RScoredSortedSetAsync<V> extends RExpirableAsync, RSortableAsyn
RFuture<Collection<ScoredEntry<V>>> entryRangeReversedAsync(int startIndex, int endIndex);
/**
* Returns all values between <code>startScore</code> and <code>endScore</code>.
*
* @param startScore - start score.
* Use <code>Double.POSITIVE_INFINITY</code> or <code>Double.NEGATIVE_INFINITY</code>
* to define infinity numbers
* @param startScoreInclusive - start score inclusive
* @param endScore - end score
* Use <code>Double.POSITIVE_INFINITY</code> or <code>Double.NEGATIVE_INFINITY</code>
* to define infinity numbers
*
* @param endScoreInclusive - end score inclusive
* @return values
*/
RFuture<Collection<V>> valueRangeAsync(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive);
/**
* Returns all values between <code>startScore</code> and <code>endScore</code> in reversed order.
*
* @param startScore - start score.
* Use <code>Double.POSITIVE_INFINITY</code> or <code>Double.NEGATIVE_INFINITY</code>
* to define infinity numbers
* @param startScoreInclusive - start score inclusive
* @param endScore - end score
* Use <code>Double.POSITIVE_INFINITY</code> or <code>Double.NEGATIVE_INFINITY</code>
* to define infinity numbers
*
* @param endScoreInclusive - end score inclusive
* @return values
*/
RFuture<Collection<V>> valueRangeReversedAsync(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive);
RFuture<Collection<ScoredEntry<V>>> entryRangeAsync(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive);

@ -17,6 +17,8 @@ package org.redisson.api;
import java.util.Set;
import org.redisson.api.mapreduce.RCollectionMapReduce;
/**
* Distributed and concurrent implementation of {@link java.util.Set}
*
@ -26,6 +28,15 @@ import java.util.Set;
*/
public interface RSet<V> extends Set<V>, RExpirable, RSetAsync<V>, RSortable<Set<V>> {
/**
* Returns <code>RMapReduce</code> object associated with this object
*
* @param <KOut> output key
* @param <VOut> output value
* @return MapReduce instance
*/
<KOut, VOut> RCollectionMapReduce<V, KOut, VOut> mapReduce();
/**
* Removes and returns random elements from set
*

@ -18,6 +18,8 @@ package org.redisson.api;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.redisson.api.mapreduce.RCollectionMapReduce;
/**
* <p>Set-based cache with ability to set TTL for each object.
* </p>
@ -37,6 +39,15 @@ import java.util.concurrent.TimeUnit;
*/
public interface RSetCache<V> extends Set<V>, RExpirable, RSetCacheAsync<V> {
/**
* Returns <code>RMapReduce</code> object associated with this map
*
* @param <KOut> output key
* @param <VOut> output value
* @return MapReduce instance
*/
<KOut, VOut> RCollectionMapReduce<V, KOut, VOut> mapReduce();
/**
* Stores value with specified time to live.
* Value expires after specified time to live.

@ -19,6 +19,8 @@ import java.util.Comparator;
import java.util.Set;
import java.util.SortedSet;
import org.redisson.api.mapreduce.RCollectionMapReduce;
/**
*
* @author Nikita Koksharov
@ -27,6 +29,15 @@ import java.util.SortedSet;
*/
public interface RSortedSet<V> extends SortedSet<V>, RObject {
/**
* Returns <code>RMapReduce</code> object associated with this object
*
* @param <KOut> output key
* @param <VOut> output value
* @return MapReduce instance
*/
<KOut, VOut> RCollectionMapReduce<V, KOut, VOut> mapReduce();
Set<V> readAll();
RFuture<Set<V>> readAllAsync();

@ -28,7 +28,7 @@ public interface StatusListener extends EventListener {
/**
* Executes then Redisson successfully subscribed to channel.
* Invoked during re-connection
* Invoked during re-connection or failover process
*
* @param channel to subscribe
*/

@ -0,0 +1,41 @@
/**
* Copyright 2016 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.api.mapreduce;
import java.io.Serializable;
import java.util.Map;
/**
* Collates result from {@link RReducer} tasks and produces a single result object.
* Executes only once.
*
* @author Nikita Koksharov
*
* @param <K> key type
* @param <V> value type
* @param <R> result type
*/
public interface RCollator<K, V, R> extends Serializable {
/**
* Collates result map from reduce phase of MapReduce process.
*
* @param resultMap contains reduced entires
* @return single result object
*/
R collate(Map<K, V> resultMap);
}

@ -0,0 +1,118 @@
/**
* Copyright 2016 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.api.mapreduce;
import java.util.concurrent.TimeUnit;
import org.redisson.api.RList;
import org.redisson.api.RScoredSortedSet;
import org.redisson.api.RSet;
import org.redisson.api.RSetCache;
import org.redisson.api.RSortedSet;
/**
*
* MapReduce allows to process large amount of data stored in
* {@link RSet}, {@link RList}, {@link RSetCache}, {@link RScoredSortedSet}, {@link RSortedSet} and others
* using Mapper, Reducer and/or Collator tasks launched across Redisson Nodes.
* <p>
* Usage example:
*
* <pre>
* public class WordMapper implements RCollectionMapper&lt;String, String, Integer&gt; {
*
* public void map(String value, RCollector&lt;String, Integer&gt; collector) {
* String[] words = value.split(&quot;[^a-zA-Z]&quot;);
* for (String word : words) {
* collector.emit(word, 1);
* }
* }
*
* }
*
* public class WordReducer implements RReducer&lt;String, Integer&gt; {
*
* public Integer reduce(String reducedKey, Iterator&lt;Integer&gt; iter) {
* int sum = 0;
* while (iter.hasNext()) {
* Integer i = (Integer) iter.next();
* sum += i;
* }
* return sum;
* }
*
* }
*
* public class WordCollator implements RCollator&lt;String, Integer, Integer&gt; {
*
* public Integer collate(Map&lt;String, Integer&gt; resultMap) {
* int result = 0;
* for (Integer count : resultMap.values()) {
* result += count;
* }
* return result;
* }
*
* }
*
* RList&lt;String&gt; list = redisson.getList(&quot;myWords&quot;);
*
* Map&lt;String, Integer&gt; wordsCount = list.&lt;String, Integer&gt;mapReduce()
* .mapper(new WordMapper())
* .reducer(new WordReducer())
* .execute();
*
* Integer totalCount = list.&lt;String, Integer&gt;mapReduce()
* .mapper(new WordMapper())
* .reducer(new WordReducer())
* .execute(new WordCollator());
*
* </pre>
*
* @author Nikita Koksharov
*
* @param <VIn> input value
* @param <KOut> output key
* @param <VOut> output value
*/
public interface RCollectionMapReduce<VIn, KOut, VOut> extends RMapReduceExecutor<VIn, KOut, VOut> {
/**
* Defines timeout for MapReduce process
*
* @param timeout
* @param unit
* @return self instance
*/
RCollectionMapReduce<VIn, KOut, VOut> timeout(long timeout, TimeUnit unit);
/**
* Setup Mapper object
*
* @param mapper used during MapReduce
* @return self instance
*/
RCollectionMapReduce<VIn, KOut, VOut> mapper(RCollectionMapper<VIn, KOut, VOut> mapper);
/**
* Setup Reducer object
*
* @param reducer used during MapReduce
* @return self instance
*/
RCollectionMapReduce<VIn, KOut, VOut> reducer(RReducer<KOut, VOut> reducer);
}

@ -0,0 +1,42 @@
/**
* Copyright 2016 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.api.mapreduce;
import java.io.Serializable;
/**
* Mapper task invoked during map phase of MapReduce process and launched across Redisson Nodes.
* Every task stores transformed result of input key and value into {@link RCollector} instance.
* Collected results are handled by {@link RReducer} instance once
* all Mapper tasks have finished.
*
* @author Nikita Koksharov
*
* @param <VIn> input value
* @param <KOut> output key
* @param <VOut> output value
*/
public interface RCollectionMapper<VIn, KOut, VOut> extends Serializable {
/**
* Invoked for each Collection source entry
*
* @param value - input value
* @param collector - instance shared across all Mapper tasks
*/
void map(VIn value, RCollector<KOut, VOut> collector);
}

@ -0,0 +1,38 @@
/**
* Copyright 2016 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.api.mapreduce;
/**
* Stores each key/value mapping during map phase of MapReduce process.
* Later used in reduce phase.
*
*
* @author Nikita Koksharov
*
* @param <K> key type
* @param <V> value type
*/
public interface RCollector<K, V> {
/**
* Store key/value
*
* @param key available to reduce
* @param value available to reduce
*/
void emit(K key, V value);
}

@ -0,0 +1,113 @@
/**
* Copyright 2016 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.api.mapreduce;
import java.util.concurrent.TimeUnit;
/**
*
* MapReduce allows to process large amount of data stored in Redis map
* using Mapper, Reducer and/or Collator tasks launched across Redisson Nodes.
* <p>
* Usage example:
*
* <pre>
* public class WordMapper implements RMapper&lt;String, String, String, Integer&gt; {
*
* public void map(String key, String value, RCollector&lt;String, Integer&gt; collector) {
* String[] words = value.split(&quot;[^a-zA-Z]&quot;);
* for (String word : words) {
* collector.emit(word, 1);
* }
* }
*
* }
*
* public class WordReducer implements RReducer&lt;String, Integer&gt; {
*
* public Integer reduce(String reducedKey, Iterator&lt;Integer&gt; iter) {
* int sum = 0;
* while (iter.hasNext()) {
* Integer i = (Integer) iter.next();
* sum += i;
* }
* return sum;
* }
*
* }
*
* public class WordCollator implements RCollator&lt;String, Integer, Integer&gt; {
*
* public Integer collate(Map&lt;String, Integer&gt; resultMap) {
* int result = 0;
* for (Integer count : resultMap.values()) {
* result += count;
* }
* return result;
* }
*
* }
*
* RMap&lt;String, String&gt; map = redisson.getMap(&quot;myWords&quot;);
*
* Map&lt;String, Integer&gt; wordsCount = map.&lt;String, Integer&gt;mapReduce()
* .mapper(new WordMapper())
* .reducer(new WordReducer())
* .execute();
*
* Integer totalCount = map.&lt;String, Integer&gt;mapReduce()
* .mapper(new WordMapper())
* .reducer(new WordReducer())
* .execute(new WordCollator());
*
* </pre>
*
* @author Nikita Koksharov
*
* @param <KIn> input key
* @param <VIn> input value
* @param <KOut> output key
* @param <VOut> output value
*/
public interface RMapReduce<KIn, VIn, KOut, VOut> extends RMapReduceExecutor<VIn, KOut, VOut> {
/**
* Defines timeout for MapReduce process.
* <code>0</code> means infinity timeout.
*
* @param timeout
* @param unit
* @return self instance
*/
RMapReduce<KIn, VIn, KOut, VOut> timeout(long timeout, TimeUnit unit);
/**
* Setup Mapper object
*
* @param mapper used during MapReduce
* @return self instance
*/
RMapReduce<KIn, VIn, KOut, VOut> mapper(RMapper<KIn, VIn, KOut, VOut> mapper);
/**
* Setup Reducer object
*
* @param reducer used during MapReduce
* @return self instance
*/
RMapReduce<KIn, VIn, KOut, VOut> reducer(RReducer<KOut, VOut> reducer);
}

@ -0,0 +1,85 @@
/**
* Copyright 2016 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.api.mapreduce;
import java.util.Map;
import org.redisson.api.RFuture;
/**
* Contains methods for MapReduce process execution.
*
* @author Nikita Koksharov
*
* @param <VIn> input value
* @param <KOut> output key
* @param <VOut> output value
*/
public interface RMapReduceExecutor<VIn, KOut, VOut> {
/**
* Executes MapReduce process across Redisson Nodes
*
* @return map containing reduced keys and values
*/
Map<KOut, VOut> execute();
/**
* Executes MapReduce process across Redisson Nodes
* in asynchronous mode
*
* @return map containing reduced keys and values
*/
RFuture<Map<KOut, VOut>> executeAsync();
/**
* Executes MapReduce process across Redisson Nodes
* and stores result in map with <code>resultMapName</code>
*
* @param resultMapName - destination map name
*/
void execute(String resultMapName);
/**
* Executes MapReduce process across Redisson Nodes
* in asynchronous mode and stores result in map with <code>resultMapName</code>
*
* @param resultMapName - destination map name
* @return void
*/
RFuture<Void> executeAsync(String resultMapName);
/**
* Executes MapReduce process across Redisson Nodes
* and collides result using defined <code>collator</code>
*
* @param <R> result type
* @param collator applied to result
* @return collated result
*/
<R> R execute(RCollator<KOut, VOut, R> collator);
/**
* Executes MapReduce process across Redisson Nodes
* in asynchronous mode and collides result using defined <code>collator</code>
*
* @param <R> result type
* @param collator applied to result
* @return collated result
*/
<R> RFuture<R> executeAsync(RCollator<KOut, VOut, R> collator);
}

@ -0,0 +1,44 @@
/**
* Copyright 2016 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.api.mapreduce;
import java.io.Serializable;
/**
* Mapper task invoked during map phase of MapReduce process and launched across Redisson Nodes.
* Every task stores transformed result of input key/value into {@link RCollector} instance.
* Collected results are handled by {@link RReducer} instance once
* all Mapper tasks have finished.
*
* @author Nikita Koksharov
*
* @param <KIn> input key
* @param <VIn> input value
* @param <KOut> output key
* @param <VOut> output value
*/
public interface RMapper<KIn, VIn, KOut, VOut> extends Serializable {
/**
* Invoked for each Map source entry
*
* @param key - input key
* @param value - input value
* @param collector - instance shared across all Mapper tasks
*/
void map(KIn key, VIn value, RCollector<KOut, VOut> collector);
}

@ -0,0 +1,40 @@
/**
* Copyright 2016 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.api.mapreduce;
import java.io.Serializable;
import java.util.Iterator;
/**
* Reduces values mapped by key into single value.
*
* @author Nikita Koksharov
*
* @param <K> key type
* @param <V> value type
*/
public interface RReducer<K, V> extends Serializable {
/**
* Invoked for each key
*
* @param reducedKey - key
* @param iter - collection of values
* @return value
*/
V reduce(K reducedKey, Iterator<V> iter);
}

@ -18,11 +18,17 @@ package org.redisson.cache;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
public class CachedValueReference<V> extends SoftReference<V> {
/**
*
* @author Nikita Koksharov
*
* @param <V> value type
*/
public class CachedValueSoftReference<V> extends SoftReference<V> {
private final CachedValue<?, ?> owner;
public CachedValueReference(CachedValue<?, ?> owner, V referent, ReferenceQueue<? super V> q) {
public CachedValueSoftReference(CachedValue<?, ?> owner, V referent, ReferenceQueue<? super V> q) {
super(referent, q);
this.owner = owner;
}

@ -0,0 +1,40 @@
/**
* Copyright 2016 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.cache;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
/**
*
* @author Nikita Koksharov
*
* @param <V> value type
*/
public class CachedValueWeakReference<V> extends WeakReference<V> {
private final CachedValue<?, ?> owner;
public CachedValueWeakReference(CachedValue<?, ?> owner, V referent, ReferenceQueue<? super V> q) {
super(referent, q);
this.owner = owner;
}
public CachedValue<?, ?> getOwner() {
return owner;
}
}

@ -15,8 +15,14 @@
*/
package org.redisson.cache;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
/**
* LRU (least recently used) cache.
@ -28,24 +34,38 @@ import java.util.concurrent.ConcurrentLinkedQueue;
*/
public class LRUCacheMap<K, V> extends AbstractCacheMap<K, V> {
private final Queue<CachedValue> queue = new ConcurrentLinkedQueue<CachedValue>();
private final AtomicLong index = new AtomicLong();
private final List<Collection<CachedValue<K, V>>> queues =
new ArrayList<Collection<CachedValue<K, V>>>(Runtime.getRuntime().availableProcessors()*2);
public LRUCacheMap(int size, long timeToLiveInMillis, long maxIdleInMillis) {
super(size, timeToLiveInMillis, maxIdleInMillis);
for (int i = 0; i < Runtime.getRuntime().availableProcessors()*2; i++) {
Set<CachedValue<K, V>> instance = Collections.synchronizedSet(new LinkedHashSet<CachedValue<K, V>>());
queues.add(instance);
}
}
@Override
protected void onValueCreate(CachedValue value) {
protected void onValueCreate(CachedValue<K, V> value) {
Collection<CachedValue<K, V>> queue = getQueue(value);
queue.add(value);
}
private Collection<CachedValue<K, V>> getQueue(CachedValue<K, V> value) {
return queues.get(value.hashCode() % queues.size());
}
@Override
protected void onValueRemove(CachedValue value) {
protected void onValueRemove(CachedValue<K, V> value) {
Collection<CachedValue<K, V>> queue = getQueue(value);
queue.remove(value);
}
@Override
protected void onValueRead(CachedValue value) {
protected void onValueRead(CachedValue<K, V> value) {
Collection<CachedValue<K, V>> queue = getQueue(value);
// move value to tail of queue
if (queue.remove(value)) {
queue.add(value);
@ -54,15 +74,33 @@ public class LRUCacheMap<K, V> extends AbstractCacheMap<K, V> {
@Override
protected void onMapFull() {
CachedValue value = queue.poll();
if (value != null) {
map.remove(value.getKey(), value);
int startIndex = -1;
while (true) {
int queueIndex = (int)Math.abs(index.incrementAndGet() % queues.size());
if (queueIndex == startIndex) {
return;
}
if (startIndex == -1) {
startIndex = queueIndex;
}
Collection<CachedValue<K, V>> queue = queues.get(queueIndex);
synchronized (queue) {
Iterator<CachedValue<K, V>> iter = queue.iterator();
if (iter.hasNext()) {
CachedValue<K, V> value = iter.next();
iter.remove();
map.remove(value.getKey(), value);
return;
}
}
}
}
@Override
public void clear() {
queue.clear();
for (Collection<CachedValue<K, V>> collection : queues) {
collection.clear();
}
super.clear();
}

@ -17,6 +17,8 @@ package org.redisson.cache;
import java.lang.ref.ReferenceQueue;
import org.redisson.cache.ReferenceCachedValue.Type;
/**
*
* @author Nikita Koksharov
@ -24,22 +26,33 @@ import java.lang.ref.ReferenceQueue;
* @param <K> key
* @param <V> value
*/
public class SoftCacheMap<K, V> extends AbstractCacheMap<K, V> {
public class ReferenceCacheMap<K, V> extends AbstractCacheMap<K, V> {
private final ReferenceQueue<V> queue = new ReferenceQueue<V>();
public SoftCacheMap(long timeToLiveInMillis, long maxIdleInMillis) {
private ReferenceCachedValue.Type type;
public static <K, V> ReferenceCacheMap<K, V> weak(long timeToLiveInMillis, long maxIdleInMillis) {
return new ReferenceCacheMap<K, V>(timeToLiveInMillis, maxIdleInMillis, Type.WEAK);
}
public static <K, V> ReferenceCacheMap<K, V> soft(long timeToLiveInMillis, long maxIdleInMillis) {
return new ReferenceCacheMap<K, V>(timeToLiveInMillis, maxIdleInMillis, Type.SOFT);
}
ReferenceCacheMap(long timeToLiveInMillis, long maxIdleInMillis, ReferenceCachedValue.Type type) {
super(0, timeToLiveInMillis, maxIdleInMillis);
this.type = type;
}
protected CachedValue<K, V> create(K key, V value, long ttl, long maxIdleTime) {
return new SoftCachedValue<K, V>(key, value, ttl, maxIdleTime, queue);
return new ReferenceCachedValue<K, V>(key, value, ttl, maxIdleTime, queue, type);
}
@Override
protected boolean removeExpiredEntries() {
while (true) {
CachedValueReference<V> value = (CachedValueReference<V>) queue.poll();
CachedValueSoftReference<V> value = (CachedValueSoftReference<V>) queue.poll();
if (value == null) {
break;
}

@ -15,19 +15,26 @@
*/
package org.redisson.cache;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
/**
* Created by jribble on 2/20/17.
*/
public class SoftCachedValue<K, V> extends StdCachedValue<K, V> implements CachedValue<K, V> {
public class ReferenceCachedValue<K, V> extends StdCachedValue<K, V> implements CachedValue<K, V> {
private final CachedValueReference<V> ref;
public enum Type {SOFT, WEAK}
private final Reference<V> ref;
public SoftCachedValue(K key, V value, long ttl, long maxIdleTime, ReferenceQueue<V> queue) {
public ReferenceCachedValue(K key, V value, long ttl, long maxIdleTime, ReferenceQueue<V> queue, Type type) {
super(key, null, ttl, maxIdleTime);
this.ref = new CachedValueReference<V>(this, value, queue);
if (type == Type.SOFT) {
this.ref = new CachedValueSoftReference<V>(this, value, queue);
} else {
this.ref = new CachedValueWeakReference<V>(this, value, queue);
}
}
@Override

@ -19,7 +19,10 @@ import org.redisson.client.protocol.Decoder;
import org.redisson.client.protocol.Encoder;
/**
* Redis codec interface
* Redis codec interface.
* <p>
* It's required for implementation to have two constructors
* default and with ClassLoader object as parameter.
*
* @author Nikita Koksharov
*

@ -226,6 +226,7 @@ public interface RedisCommands {
RedisCommand<Object> EVAL_OBJECT = new RedisCommand<Object>("EVAL");
RedisCommand<Object> EVAL_MAP_VALUE = new RedisCommand<Object>("EVAL", ValueType.MAP_VALUE);
RedisCommand<Set<Entry<Object, Object>>> EVAL_MAP_ENTRY = new RedisCommand<Set<Entry<Object, Object>>>("EVAL", new ObjectMapEntryReplayDecoder(), ValueType.MAP);
RedisCommand<Map<Object, Object>> EVAL_MAP = new RedisCommand<Map<Object, Object>>("EVAL", new ObjectMapReplayDecoder(), ValueType.MAP);
RedisCommand<List<Object>> EVAL_MAP_VALUE_LIST = new RedisCommand<List<Object>>("EVAL", new ObjectListReplayDecoder<Object>(), ValueType.MAP_VALUE);
RedisCommand<Set<Object>> EVAL_MAP_KEY_SET = new RedisCommand<Set<Object>>("EVAL", new ObjectSetReplayDecoder<Object>(), ValueType.MAP_KEY);

@ -15,6 +15,11 @@
*/
package org.redisson.client.protocol.convertor;
/**
*
* @author Nikita Koksharov
*
*/
public class BooleanReplayConvertor extends SingleConvertor<Boolean> {
@Override

@ -15,6 +15,12 @@
*/
package org.redisson.client.protocol.convertor;
/**
*
* @author Nikita Koksharov
*
* @param <R> type
*/
public interface Convertor<R> {
Object convertMulti(Object obj);

@ -15,6 +15,12 @@
*/
package org.redisson.client.protocol.convertor;
/**
*
* @author Nikita Koksharov
*
* @param <R> type
*/
public abstract class SingleConvertor<R> implements Convertor<R> {
@Override

@ -20,6 +20,12 @@ import java.util.List;
import org.redisson.client.handler.State;
import org.redisson.client.protocol.Decoder;
/**
*
* @author Nikita Koksharov
*
* @param <T> type
*/
public interface MultiDecoder<T> extends Decoder<Object> {
boolean isApplicable(int paramNum, State state);

@ -21,6 +21,12 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
* @param <T> type
*/
public class ObjectListReplayDecoder<T> implements MultiDecoder<List<T>> {
@Override

@ -25,6 +25,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
*/
public class ObjectMapEntryReplayDecoder implements MultiDecoder<Set<Entry<Object, Object>>> {
@Override

@ -23,6 +23,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
*/
public class ObjectMapReplayDecoder implements MultiDecoder<Map<Object, Object>> {
@Override

@ -0,0 +1,80 @@
/**
* Copyright 2016 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.codec;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
import com.fasterxml.jackson.databind.deser.ValueInstantiators.Base;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.avro.PackageVersion;
/**
* Fix for https://github.com/FasterXML/jackson-databind/issues/1599
*
* @author Nikita Koksharov
*
* TODO remove after update to latest version of Jackson
*
*/
public class DefenceModule extends SimpleModule {
private static final long serialVersionUID = -429891510707420220L;
public static class DefenceValueInstantiator extends Base {
protected final static Set<String> DEFAULT_NO_DESER_CLASS_NAMES;
static {
Set<String> s = new HashSet<String>();
// Courtesy of [https://github.com/kantega/notsoserial]:
// (and wrt [databind#1599]
s.add("org.apache.commons.collections.functors.InvokerTransformer");
s.add("org.apache.commons.collections.functors.InstantiateTransformer");
s.add("org.apache.commons.collections4.functors.InvokerTransformer");
s.add("org.apache.commons.collections4.functors.InstantiateTransformer");
s.add("org.codehaus.groovy.runtime.ConvertedClosure");
s.add("org.codehaus.groovy.runtime.MethodClosure");
s.add("org.springframework.beans.factory.ObjectFactory");
s.add("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
DEFAULT_NO_DESER_CLASS_NAMES = Collections.unmodifiableSet(s);
}
@Override
public ValueInstantiator findValueInstantiator(DeserializationConfig config, BeanDescription beanDesc,
ValueInstantiator defaultInstantiator) {
if (DEFAULT_NO_DESER_CLASS_NAMES.contains(beanDesc.getClassInfo().getRawType().getName())) {
throw new IllegalArgumentException("Illegal type " + beanDesc.getClassInfo().getRawType().getName() + " to deserialize: prevented for security reasons");
}
return super.findValueInstantiator(config, beanDesc, defaultInstantiator);
}
}
public DefenceModule() {
super(PackageVersion.VERSION);
}
@Override
public void setupModule(SetupContext context) {
context.addValueInstantiators(new DefenceValueInstantiator());
}
}

@ -17,6 +17,9 @@ package org.redisson.codec;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.redisson.client.codec.Codec;
import org.redisson.client.handler.State;
@ -29,15 +32,19 @@ import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTypeResolverBuilder;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import com.fasterxml.jackson.databind.deser.ValueInstantiators;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.dataformat.avro.PackageVersion;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
@ -136,6 +143,8 @@ public class JsonJacksonCodec implements Codec {
}
protected void init(ObjectMapper objectMapper) {
objectMapper.registerModule(new DefenceModule());
objectMapper.setSerializationInclusion(Include.NON_NULL);
objectMapper.setVisibilityChecker(objectMapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY).withGetterVisibility(JsonAutoDetect.Visibility.NONE)

@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.redisson.RedissonReference;
import org.redisson.RedissonShutdownException;
import org.redisson.api.RFuture;
import org.redisson.client.RedisAskException;
import org.redisson.client.RedisConnection;
@ -126,14 +127,18 @@ public class CommandBatchService extends CommandReactiveService {
}
public List<?> execute() {
return get(executeAsync());
return get(executeAsync(0, 0, 0));
}
public List<?> execute(long responseTimeout, int retryAttempts, long retryInterval) {
return get(executeAsync(responseTimeout, retryAttempts, retryInterval));
}
public RFuture<Void> executeAsyncVoid() {
return executeAsyncVoid(false);
return executeAsyncVoid(false, 0, 0, 0);
}
private RFuture<Void> executeAsyncVoid(boolean noResult) {
private RFuture<Void> executeAsyncVoid(boolean noResult, long responseTimeout, int retryAttempts, long retryInterval) {
if (executed) {
throw new IllegalStateException("Batch already executed!");
}
@ -145,11 +150,11 @@ public class CommandBatchService extends CommandReactiveService {
if (noResult) {
for (Entry entry : commands.values()) {
RPromise<Object> s = connectionManager.newPromise();
BatchCommandData commandData = new BatchCommandData(s, null, RedisCommands.CLIENT_REPLY, new Object[] { "OFF" }, index.incrementAndGet());
entry.getCommands().addFirst(commandData);
BatchCommandData<?, ?> offCommand = new BatchCommandData(s, null, RedisCommands.CLIENT_REPLY, new Object[] { "OFF" }, index.incrementAndGet());
entry.getCommands().addFirst(offCommand);
RPromise<Object> s1 = connectionManager.newPromise();
BatchCommandData commandData1 = new BatchCommandData(s1, null, RedisCommands.CLIENT_REPLY, new Object[] { "ON" }, index.incrementAndGet());
entry.getCommands().add(commandData1);
BatchCommandData<?, ?> onCommand = new BatchCommandData(s1, null, RedisCommands.CLIENT_REPLY, new Object[] { "ON" }, index.incrementAndGet());
entry.getCommands().add(onCommand);
}
}
@ -165,20 +170,24 @@ public class CommandBatchService extends CommandReactiveService {
AtomicInteger slots = new AtomicInteger(commands.size());
for (java.util.Map.Entry<MasterSlaveEntry, Entry> e : commands.entrySet()) {
execute(e.getValue(), new NodeSource(e.getKey()), voidPromise, slots, 0, true);
execute(e.getValue(), new NodeSource(e.getKey()), voidPromise, slots, 0, true, responseTimeout, retryAttempts, retryInterval);
}
return voidPromise;
}
public void executeSkipResult() {
get(executeSkipResultAsync());
public void executeSkipResult(long timeout, int retryAttempts, long retryInterval) {
get(executeSkipResultAsync(timeout, retryAttempts, retryInterval));
}
public RFuture<Void> executeSkipResultAsync() {
return executeAsyncVoid(true);
public RFuture<Void> executeSkipResultAsync(long timeout, int retryAttempts, long retryInterval) {
return executeAsyncVoid(true, timeout, retryAttempts, retryInterval);
}
public RFuture<List<?>> executeAsync() {
return executeAsync(0, 0, 0);
}
public RFuture<List<?>> executeAsync(long responseTimeout, int retryAttempts, long retryInterval) {
if (executed) {
throw new IllegalStateException("Batch already executed!");
}
@ -222,18 +231,19 @@ public class CommandBatchService extends CommandReactiveService {
AtomicInteger slots = new AtomicInteger(commands.size());
for (java.util.Map.Entry<MasterSlaveEntry, Entry> e : commands.entrySet()) {
execute(e.getValue(), new NodeSource(e.getKey()), voidPromise, slots, 0, false);
execute(e.getValue(), new NodeSource(e.getKey()), voidPromise, slots, 0, false, responseTimeout, retryAttempts, retryInterval);
}
return promise;
}
private void execute(final Entry entry, final NodeSource source, final RPromise<Void> mainPromise, final AtomicInteger slots, final int attempt, final boolean noResult) {
private void execute(final Entry entry, final NodeSource source, final RPromise<Void> mainPromise, final AtomicInteger slots,
final int attempt, final boolean noResult, final long responseTimeout, final int retryAttempts, final long retryInterval) {
if (mainPromise.isCancelled()) {
return;
}
if (!connectionManager.getShutdownLatch().acquire()) {
mainPromise.tryFailure(new IllegalStateException("Redisson is shutdown"));
mainPromise.tryFailure(new RedissonShutdownException("Redisson is shutdown"));
return;
}
@ -271,7 +281,11 @@ public class CommandBatchService extends CommandReactiveService {
return;
}
if (attempt == connectionManager.getConfig().getRetryAttempts()) {
int attempts = connectionManager.getConfig().getRetryAttempts();
if (retryAttempts > 0) {
attempts = retryAttempts;
}
if (attempt == attempts) {
if (details.getException() == null) {
details.setException(new RedisTimeoutException("Batch command execution timeout"));
}
@ -283,17 +297,22 @@ public class CommandBatchService extends CommandReactiveService {
}
int count = attempt + 1;
execute(entry, source, mainPromise, slots, count, noResult);
execute(entry, source, mainPromise, slots, count, noResult, responseTimeout, retryAttempts, retryInterval);
}
};
Timeout timeout = connectionManager.newTimeout(retryTimerTask, connectionManager.getConfig().getRetryInterval(), TimeUnit.MILLISECONDS);
long interval = connectionManager.getConfig().getRetryInterval();
if (retryInterval > 0) {
interval = retryInterval;
}
Timeout timeout = connectionManager.newTimeout(retryTimerTask, interval, TimeUnit.MILLISECONDS);
details.setTimeout(timeout);
connectionFuture.addListener(new FutureListener<RedisConnection>() {
@Override
public void operationComplete(Future<RedisConnection> connFuture) throws Exception {
checkConnectionFuture(entry, source, mainPromise, attemptPromise, details, connectionFuture, noResult);
checkConnectionFuture(entry, source, mainPromise, attemptPromise, details, connectionFuture, noResult, responseTimeout);
}
});
@ -308,18 +327,20 @@ public class CommandBatchService extends CommandReactiveService {
if (future.cause() instanceof RedisMovedException) {
RedisMovedException ex = (RedisMovedException)future.cause();
entry.clearErrors();
execute(entry, new NodeSource(ex.getSlot(), ex.getAddr(), Redirect.MOVED), mainPromise, slots, attempt, noResult);
NodeSource nodeSource = new NodeSource(ex.getSlot(), ex.getAddr(), Redirect.MOVED);
execute(entry, nodeSource, mainPromise, slots, attempt, noResult, responseTimeout, retryAttempts, retryInterval);
return;
}
if (future.cause() instanceof RedisAskException) {
RedisAskException ex = (RedisAskException)future.cause();
entry.clearErrors();
execute(entry, new NodeSource(ex.getSlot(), ex.getAddr(), Redirect.ASK), mainPromise, slots, attempt, noResult);
NodeSource nodeSource = new NodeSource(ex.getSlot(), ex.getAddr(), Redirect.ASK);
execute(entry, nodeSource, mainPromise, slots, attempt, noResult, responseTimeout, retryAttempts, retryInterval);
return;
}
if (future.cause() instanceof RedisLoadingException) {
entry.clearErrors();
execute(entry, source, mainPromise, slots, attempt, noResult);
execute(entry, source, mainPromise, slots, attempt, noResult, responseTimeout, retryAttempts, retryInterval);
return;
}
if (future.cause() instanceof RedisTryAgainException) {
@ -327,7 +348,7 @@ public class CommandBatchService extends CommandReactiveService {
connectionManager.newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
execute(entry, source, mainPromise, slots, attempt, noResult);
execute(entry, source, mainPromise, slots, attempt, noResult, responseTimeout, retryAttempts, retryInterval);
}
}, 1, TimeUnit.SECONDS);
return;
@ -346,7 +367,7 @@ public class CommandBatchService extends CommandReactiveService {
}
private void checkWriteFuture(final RPromise<Void> attemptPromise, AsyncDetails details,
final RedisConnection connection, ChannelFuture future, boolean noResult) {
final RedisConnection connection, ChannelFuture future, boolean noResult, long responseTimeout) {
if (attemptPromise.isDone() || future.isCancelled()) {
return;
}
@ -355,21 +376,26 @@ public class CommandBatchService extends CommandReactiveService {
details.setException(new WriteRedisConnectionException("Can't write command batch to channel: " + future.channel(), future.cause()));
} else {
details.getTimeout().cancel();
TimerTask timeoutTask = new TimerTask() {
TimerTask timerTask = new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
attemptPromise.tryFailure(
new RedisTimeoutException("Redis server response timeout during command batch execution. Channel: " + connection.getChannel()));
}
};
Timeout timeout = connectionManager.newTimeout(timeoutTask, connectionManager.getConfig().getTimeout(), TimeUnit.MILLISECONDS);
details.setTimeout(timeout);
long timeout = connectionManager.getConfig().getTimeout();
if (responseTimeout > 0) {
timeout = responseTimeout;
}
Timeout timeoutTask = connectionManager.newTimeout(timerTask, timeout, TimeUnit.MILLISECONDS);
details.setTimeout(timeoutTask);
}
}
private void checkConnectionFuture(final Entry entry, final NodeSource source,
final RPromise<Void> mainPromise, final RPromise<Void> attemptPromise, final AsyncDetails details,
RFuture<RedisConnection> connFuture, final boolean noResult) {
RFuture<RedisConnection> connFuture, final boolean noResult, final long responseTimeout) {
if (attemptPromise.isDone() || mainPromise.isCancelled() || connFuture.isCancelled()) {
return;
}
@ -401,7 +427,7 @@ public class CommandBatchService extends CommandReactiveService {
details.getWriteFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
checkWriteFuture(attemptPromise, details, connection, future, noResult);
checkWriteFuture(attemptPromise, details, connection, future, noResult, responseTimeout);
}
});

@ -141,8 +141,8 @@ class BaseConfig<T extends BaseConfig<T>> {
}
/**
* Error will be thrown if Redis command can't be sended to Redis server after <code>retryAttempts</code>.
* But if it sent succesfully then <code>timeout</code> will be started.
* Error will be thrown if Redis command can't be sent to Redis server after <code>retryAttempts</code>.
* But if it sent successfully then <code>timeout</code> will be started.
* <p>
* Default is <code>3</code> attempts
*
@ -160,7 +160,9 @@ class BaseConfig<T extends BaseConfig<T>> {
}
/**
* Time interval after which another one attempt to send Redis command will be executed.
* Defines time interval for another one attempt send Redis command
* if it hasn't been sent already.
*
* <p>
* Default is <code>1500</code> milliseconds
*
@ -178,7 +180,7 @@ class BaseConfig<T extends BaseConfig<T>> {
}
/**
* Redis server response timeout.
* Redis server response timeout. Starts to countdown when Redis command has been successfully sent.
* <p>
* Default is <code>3000</code> milliseconds
*

@ -319,7 +319,7 @@ public class Config {
*
* @see ReplicatedConnectionManager on how to implement a connection
* manager.
* @param connectionManager
* @param connectionManager for supply
*/
public void useCustomServers(ConnectionManager connectionManager) {
this.connectionManager = connectionManager;

@ -23,12 +23,14 @@ import java.util.Map;
import org.redisson.api.RedissonNodeInitializer;
/**
* Redisson Node configuration
*
* @author Nikita Koksharov
*
*/
public class RedissonNodeConfig extends Config {
private int mapReduceWorkers = 0;
private RedissonNodeInitializer redissonNodeInitializer;
private Map<String, Integer> executorServiceWorkers = new HashMap<String, Integer>();
@ -46,6 +48,23 @@ public class RedissonNodeConfig extends Config {
this.redissonNodeInitializer = oldConf.redissonNodeInitializer;
}
/**
* MapReduce workers amount.
* 0 = current_processors_amount
* <p>
* Default is <code>0</code>
*
* @param mapReduceWorkers workers for MapReduce
* @return config
*/
public RedissonNodeConfig setMapReduceWorkers(int mapReduceWorkers) {
this.mapReduceWorkers = mapReduceWorkers;
return this;
}
public int getMapReduceWorkers() {
return mapReduceWorkers;
}
/**
* Executor service workers amount per service name
*
@ -60,10 +79,6 @@ public class RedissonNodeConfig extends Config {
return executorServiceWorkers;
}
public RedissonNodeInitializer getRedissonNodeInitializer() {
return redissonNodeInitializer;
}
/**
* Redisson node initializer
*
@ -74,6 +89,10 @@ public class RedissonNodeConfig extends Config {
this.redissonNodeInitializer = redissonNodeInitializer;
return this;
}
public RedissonNodeInitializer getRedissonNodeInitializer() {
return redissonNodeInitializer;
}
/**
* Read config object stored in JSON format from <code>File</code>

@ -64,9 +64,9 @@ public interface ConnectionManager {
boolean isShuttingDown();
RFuture<PubSubConnectionEntry> subscribe(Codec codec, String channelName, RedisPubSubListener<?> listener);
RFuture<PubSubConnectionEntry> subscribe(Codec codec, String channelName, RedisPubSubListener<?>... listeners);
RFuture<PubSubConnectionEntry> subscribe(Codec codec, String channelName, RedisPubSubListener<?> listener, AsyncSemaphore semaphore);
RFuture<PubSubConnectionEntry> subscribe(Codec codec, String channelName, AsyncSemaphore semaphore, RedisPubSubListener<?>... listeners);
ConnectionInitializer getConnectListener();
@ -106,9 +106,9 @@ public interface ConnectionManager {
PubSubConnectionEntry getPubSubEntry(String channelName);
RFuture<PubSubConnectionEntry> psubscribe(String pattern, Codec codec, RedisPubSubListener<?> listener);
RFuture<PubSubConnectionEntry> psubscribe(String pattern, Codec codec, RedisPubSubListener<?>... listeners);
RFuture<PubSubConnectionEntry> psubscribe(String pattern, Codec codec, RedisPubSubListener<?> listener, AsyncSemaphore semaphore);
RFuture<PubSubConnectionEntry> psubscribe(String pattern, Codec codec, AsyncSemaphore semaphore, RedisPubSubListener<?>... listeners);
Codec unsubscribe(String channelName, AsyncSemaphore lock);

@ -55,8 +55,8 @@ import org.redisson.connection.ClientConnectionsEntry.FreezeReason;
import org.redisson.misc.InfinitySemaphoreLatch;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedissonPromise;
import org.redisson.misc.TransferListener;
import org.redisson.pubsub.AsyncSemaphore;
import org.redisson.pubsub.TransferListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -364,41 +364,41 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
}
@Override
public RFuture<PubSubConnectionEntry> psubscribe(final String channelName, final Codec codec, final RedisPubSubListener<?> listener) {
public RFuture<PubSubConnectionEntry> psubscribe(final String channelName, final Codec codec, final RedisPubSubListener<?>... listeners) {
final AsyncSemaphore lock = getSemaphore(channelName);
final RPromise<PubSubConnectionEntry> result = newPromise();
lock.acquire(new Runnable() {
@Override
public void run() {
RFuture<PubSubConnectionEntry> future = psubscribe(channelName, codec, listener, lock);
RFuture<PubSubConnectionEntry> future = psubscribe(channelName, codec, lock, listeners);
future.addListener(new TransferListener<PubSubConnectionEntry>(result));
}
});
return result;
}
public RFuture<PubSubConnectionEntry> psubscribe(String channelName, Codec codec, RedisPubSubListener<?> listener, AsyncSemaphore semaphore) {
public RFuture<PubSubConnectionEntry> psubscribe(String channelName, Codec codec, AsyncSemaphore semaphore, RedisPubSubListener<?>... listeners) {
RPromise<PubSubConnectionEntry> promise = newPromise();
subscribe(codec, channelName, listener, promise, PubSubType.PSUBSCRIBE, semaphore);
subscribe(codec, channelName, promise, PubSubType.PSUBSCRIBE, semaphore, listeners);
return promise;
}
public RFuture<PubSubConnectionEntry> subscribe(final Codec codec, final String channelName, final RedisPubSubListener<?> listener) {
public RFuture<PubSubConnectionEntry> subscribe(final Codec codec, final String channelName, final RedisPubSubListener<?>... listeners) {
final AsyncSemaphore lock = getSemaphore(channelName);
final RPromise<PubSubConnectionEntry> result = newPromise();
lock.acquire(new Runnable() {
@Override
public void run() {
RFuture<PubSubConnectionEntry> future = subscribe(codec, channelName, listener, lock);
RFuture<PubSubConnectionEntry> future = subscribe(codec, channelName, lock, listeners);
future.addListener(new TransferListener<PubSubConnectionEntry>(result));
}
});
return result;
}
public RFuture<PubSubConnectionEntry> subscribe(Codec codec, String channelName, RedisPubSubListener<?> listener, AsyncSemaphore semaphore) {
public RFuture<PubSubConnectionEntry> subscribe(Codec codec, String channelName, AsyncSemaphore semaphore, RedisPubSubListener<?>... listeners) {
RPromise<PubSubConnectionEntry> promise = newPromise();
subscribe(codec, channelName, listener, promise, PubSubType.SUBSCRIBE, semaphore);
subscribe(codec, channelName, promise, PubSubType.SUBSCRIBE, semaphore, listeners);
return promise;
}
@ -406,18 +406,11 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
return locks[Math.abs(channelName.hashCode() % locks.length)];
}
private void subscribe(final Codec codec, final String channelName, final RedisPubSubListener<?> listener,
final RPromise<PubSubConnectionEntry> promise, final PubSubType type, final AsyncSemaphore lock) {
private void subscribe(final Codec codec, final String channelName,
final RPromise<PubSubConnectionEntry> promise, final PubSubType type, final AsyncSemaphore lock, final RedisPubSubListener<?>... listeners) {
final PubSubConnectionEntry connEntry = name2PubSubConnection.get(channelName);
if (connEntry != null) {
connEntry.addListener(channelName, listener);
connEntry.getSubscribeFuture(channelName, type).addListener(new FutureListener<Void>() {
@Override
public void operationComplete(Future<Void> future) throws Exception {
lock.release();
promise.trySuccess(connEntry);
}
});
subscribe(channelName, promise, type, lock, connEntry, listeners);
return;
}
@ -431,7 +424,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
final PubSubConnectionEntry freeEntry = freePubSubConnections.peek();
if (freeEntry == null) {
connect(codec, channelName, listener, promise, type, lock);
connect(codec, channelName, promise, type, lock, listeners);
return;
}
@ -445,14 +438,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
freeEntry.release();
freePubSubLock.release();
oldEntry.addListener(channelName, listener);
oldEntry.getSubscribeFuture(channelName, type).addListener(new FutureListener<Void>() {
@Override
public void operationComplete(Future<Void> future) throws Exception {
lock.release();
promise.trySuccess(oldEntry);
}
});
subscribe(channelName, promise, type, lock, oldEntry, listeners);
return;
}
@ -461,14 +447,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
}
freePubSubLock.release();
freeEntry.addListener(channelName, listener);
freeEntry.getSubscribeFuture(channelName, type).addListener(new FutureListener<Void>() {
@Override
public void operationComplete(Future<Void> future) throws Exception {
lock.release();
promise.trySuccess(freeEntry);
}
});
subscribe(channelName, promise, type, lock, freeEntry, listeners);
if (PubSubType.PSUBSCRIBE == type) {
freeEntry.psubscribe(codec, channelName);
@ -480,8 +459,23 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
});
}
private void connect(final Codec codec, final String channelName, final RedisPubSubListener<?> listener,
final RPromise<PubSubConnectionEntry> promise, final PubSubType type, final AsyncSemaphore lock) {
private void subscribe(final String channelName, final RPromise<PubSubConnectionEntry> promise,
final PubSubType type, final AsyncSemaphore lock, final PubSubConnectionEntry connEntry,
final RedisPubSubListener<?>... listeners) {
for (RedisPubSubListener<?> listener : listeners) {
connEntry.addListener(channelName, listener);
}
connEntry.getSubscribeFuture(channelName, type).addListener(new FutureListener<Void>() {
@Override
public void operationComplete(Future<Void> future) throws Exception {
lock.release();
promise.trySuccess(connEntry);
}
});
}
private void connect(final Codec codec, final String channelName,
final RPromise<PubSubConnectionEntry> promise, final PubSubType type, final AsyncSemaphore lock, final RedisPubSubListener<?>... listeners) {
final int slot = calcSlot(channelName);
RFuture<RedisPubSubConnection> connFuture = nextPubSubConnection(slot);
connFuture.addListener(new FutureListener<RedisPubSubConnection>() {
@ -505,29 +499,15 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
releaseSubscribeConnection(slot, entry);
freePubSubLock.release();
oldEntry.addListener(channelName, listener);
oldEntry.getSubscribeFuture(channelName, type).addListener(new FutureListener<Void>() {
@Override
public void operationComplete(Future<Void> future) throws Exception {
lock.release();
promise.trySuccess(oldEntry);
}
});
subscribe(channelName, promise, type, lock, oldEntry, listeners);
return;
}
freePubSubConnections.add(entry);
freePubSubLock.release();
entry.addListener(channelName, listener);
entry.getSubscribeFuture(channelName, type).addListener(new FutureListener<Void>() {
@Override
public void operationComplete(Future<Void> future) throws Exception {
lock.release();
promise.trySuccess(entry);
}
});
subscribe(channelName, promise, type, lock, entry, listeners);
if (PubSubType.PSUBSCRIBE == type) {
entry.psubscribe(codec, channelName);

@ -211,21 +211,15 @@ public class MasterSlaveEntry {
private void subscribe(final String channelName, final Collection<RedisPubSubListener<?>> listeners,
final Codec subscribeCodec) {
RFuture<PubSubConnectionEntry> subscribeFuture = connectionManager.subscribe(subscribeCodec, channelName, null);
RFuture<PubSubConnectionEntry> subscribeFuture = connectionManager.subscribe(subscribeCodec, channelName, listeners.toArray(new RedisPubSubListener[listeners.size()]));
subscribeFuture.addListener(new FutureListener<PubSubConnectionEntry>() {
@Override
public void operationComplete(Future<PubSubConnectionEntry> future)
throws Exception {
if (!future.isSuccess()) {
subscribe(channelName, listeners, subscribeCodec);
return;
}
PubSubConnectionEntry newEntry = future.getNow();
for (RedisPubSubListener<?> redisPubSubListener : listeners) {
newEntry.addListener(channelName, redisPubSubListener);
if (future.isSuccess()) {
log.debug("resubscribed listeners of '{}' channel to {}", channelName, future.getNow().getConnection().getRedisClient());
}
log.debug("resubscribed listeners of '{}' channel to {}", channelName, newEntry.getConnection().getRedisClient());
}
});
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save