Merge branch 'redisson/master'

pull/894/head
Rui Gu 8 years ago
commit 7b0266a19a

@ -1,10 +1,29 @@
Redisson Releases History
================================
####Please Note: trunk is current development branch.
### Please Note: trunk is current development branch.
Try __ULTRA-FAST__ [Redisson PRO](https://redisson.pro) edition.
####04-Mar-2017 - versions 2.8.1 and 3.3.1 released
## 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`
Feature - `Config.subscriptionMode` setting added
@ -13,7 +32,7 @@ Fixed - StackOverflowException in URLBuilder
Fixed - TomcatSessionManager can't be used in Tomcat if Redisson has been deployed in web application
Fixed - skip cluster nodes with the "handshake" flag (thanks to @dcheckoway)
####19-Feb-2017 - versions 2.8.0 and 3.3.0 released
### 19-Feb-2017 - versions 2.8.0 and 3.3.0 released
Feature - __`RClusteredLocalCachedMap` object added__ More details [here](https://github.com/redisson/redisson/wiki/7.-distributed-collections#713-map-data-partitioning)
Feature - __`RClusteredMapCache` object added__ More details [here](https://github.com/redisson/redisson/wiki/7.-distributed-collections#713-map-data-partitioning)
@ -28,7 +47,7 @@ Fixed - cascade slaves are not supported in cluster mode
Fixed - shutdown checking during master change state check added
Fixed - master isn't checked during new slave discovery in Sentinel mode
####02-Feb-2017 - versions 2.7.4 and 3.2.4 released
### 02-Feb-2017 - versions 2.7.4 and 3.2.4 released
Feature - Allow to specify Redisson instance/config during JCache cache creation
Fixed - `ByteBuf.release` method invocation is missed in `LZ4Codec` and `SnappyCodec`
@ -40,7 +59,7 @@ Fixed - `JCache` expiration listener doesn't work
Fixed - `RLocalCachedMap` doesn't work with `SerializationCodec`
Fixed - `Can't find entry` error during operation execution on slave nodes
####19-Jan-2017 - versions 2.7.3 and 3.2.3 released
### 19-Jan-2017 - versions 2.7.3 and 3.2.3 released
Redisson Team is pleased to announce __ULTRA-FAST__ Redisson PRO edition.
Performance measure results available in [Benchmark whitepaper](https://redisson.pro/Redisson%20PRO%20benchmark%20whitepaper.pdf)
@ -55,7 +74,7 @@ Fixed - `RSortedSet.removeAsync` and `RSortedSet.addAsync`
Fixed - `RBloomFilter.tryInit` were not validated properly
Fixed - CommandDecoder should print all replay body on error
####19-Dec-2016 - versions 2.7.2 and 3.2.2 released
### 19-Dec-2016 - versions 2.7.2 and 3.2.2 released
Feature - `RList`, `RSet` and `RScoredSortedSet` implements `RSortable` interface with SORT command support
Feature - `NodeAsync` interface
@ -63,7 +82,7 @@ Feature - `Node.info`, `Node.getNode` methods added
Fixed - elements distribution of `RBlockingFairQueue` across consumers
Fixed - `factory already defined` error during Redisson initialization under Apache Tomcat
####14-Dec-2016 - versions 2.7.1 and 3.2.1 released
### 14-Dec-2016 - versions 2.7.1 and 3.2.1 released
Url format used in config files __has changed__. For example:
@ -77,7 +96,7 @@ Fixed - command timeout didn't respect during topic subscription
Fixed - possible PublishSubscribe race-condition
Fixed - blocking queue/deque poll method blocks infinitely if delay less than 1 second
####26-Nov-2016 - versions 2.7.0 and 3.2.0 released
### 26-Nov-2016 - versions 2.7.0 and 3.2.0 released
Feature - __Spring Session implementation__. More details [here](https://github.com/redisson/redisson/wiki/14.-Integration%20with%20frameworks/#145-spring-session)
Feature - __Tomcat Session Manager implementation__. More details [here](https://github.com/redisson/redisson/wiki/14.-Integration%20with%20frameworks/#144-tomcat-redis-session-manager)
@ -89,31 +108,31 @@ Fixed - Wrong nodes parsing in result of cluster info command
Fixed - NullPointerException in CommandDecoder.handleResult
Fixed - Redisson shutdown status should be checked during async command invocation
####07-Nov-2016 - versions 2.6.0 and 3.1.0 released
### 07-Nov-2016 - versions 2.6.0 and 3.1.0 released
Feature - __new object added__ `RBinaryStream`. More info about it [here](https://github.com/redisson/redisson/wiki/6.-distributed-objects/#62-binary-stream-holder)
Improvement - limit Payload String on RedisTimeoutException
Improvement - Elasticache master node change detection process optimization
####27-Oct-2016 - versions 2.5.1 and 3.0.1 released
### 27-Oct-2016 - versions 2.5.1 and 3.0.1 released
Include all code changes from __2.2.27__ version
Fixed - RMapCache.fastPutIfAbsentAsync doesn't take in account expiration
Fixed - timer field of RedisClient hasn't been initialized properly in some cases
####27-Oct-2016 - version 2.2.27 released
### 27-Oct-2016 - version 2.2.27 released
This version fixes old and annonying problem with `ConnectionPool exhusted` error. From this moment connection pool waits for free connection instead of throwing pool exhausted error. This leads to more effective Redis connection utilization.
Improvement - remove `Connection pool exhausted` exception
####17-Oct-2016 - version 3.0.0 released
### 17-Oct-2016 - version 3.0.0 released
Fully compatible with JDK 8. Includes all code changes from __2.5.0__ version
Feature - `RFeature` extends `CompletionStage`
####17-Oct-2016 - version 2.5.0 released
### 17-Oct-2016 - version 2.5.0 released
This version brings greatly improved version of `RLiveObjectService` and adds cascade handling, cyclic dependency resolving, simplified object creation. Read more in this [article](https://dzone.com/articles/java-distributed-in-memory-data-model-powered-by-r)
Includes all code changes from __2.2.26__ version
@ -139,11 +158,11 @@ Fixed - `RLiveObjectService` can't detach content of List object
Fixed - `RLiveObjectService` doesn't create objects mapped to Redisson objects in runtime during getter accesss
Fixed - `RLiveObjectService` can't recognize id field of object without setter
####17-Oct-2016 - version 2.2.26 released
### 17-Oct-2016 - version 2.2.26 released
Fixed - NPE in CommandDecoder
Fixed - PubSub connection re-subscription doesn't work in case when there is only one slave available
####27-Sep-2016 - version 2.4.0 released
### 27-Sep-2016 - version 2.4.0 released
Includes all code changes from __2.2.25__ version
Feature - __new object added__ `RPermitExpirableSemaphore`. More info about it [here](https://github.com/redisson/redisson/wiki/8.-distributed-locks-and-synchronizers#87-permitexpirablesemaphore)
@ -162,7 +181,7 @@ Improvement - `RMultimap.get` should return `RSet` or `RList` interface instead
Fixed - `RExecutorService` should reject non-static inner task class
Fixed - wrong object encoding in `RScoredSortedSet.addScore` method
####27-Sep-2016 - version 2.2.25 released
### 27-Sep-2016 - version 2.2.25 released
Improvement - log unexpected errors in netty handlers
Improvement - `Not all slots are covered` error should be more informative
Improvement - implement random wait time in `lock` method of `RedissonRedLock` and `RedissonMultiLock` objects
@ -176,7 +195,7 @@ Fixed - TRYAGAIN error handling in cluster mode
Fixed - sync commands in connectionListener leads to connection timeout exception
Fixed - can't find slave error in cluster mode if failed slave hasn't been added before
####24-Aug-2016 - version 2.3.0 released
### 24-Aug-2016 - version 2.3.0 released
Starting from this version Redisson could be run as standalone node to execute distributed tasks. More features will be added to it in future. Read docs about it [here](https://github.com/mrniko/redisson/wiki/12.-Standalone-node)
Feature - __new service added__ `RExecutorService`. More info about it [here](https://github.com/mrniko/redisson/wiki/9.-distributed-services/#93-distributed-executor-service)
@ -194,11 +213,11 @@ __Breaking api change__ - all classes moved from `org.redisson.core` to `org.red
__Breaking api change__ - switched from `io.netty.util.concurrent.Future` to `org.redisson.api.RFuture` interface
Fixed - division by zero in WeightedRoundRobinBalancer (thanks to Shailender R Bathula)
####08-Aug-2016 - version 2.2.24 released
### 08-Aug-2016 - version 2.2.24 released
Fixed - PubSub connection in cluster mode should be connected to node according slot derived from channel name
Fixed - `RLock.tryLock` could block forever under some conditions
####04-Aug-2016 - version 2.2.23 released
### 04-Aug-2016 - version 2.2.23 released
Improvement - `Future.cancel` method handling for RemoteService async call
Fixed - unable to redefine RedisClient command execution timeout
Fixed - exception occured in CommandEncoder leads to reponse timeout exception
@ -206,23 +225,23 @@ Fixed - exception occured in CommandDecoder leads to reponse timeout exception
Fixed - BLPOP timeout calculation fixed
Fixed - object used in RemoteService to prevent race-condition during ack receiving should be created per request
####26-Jul-2016 - version 2.2.22 released
### 26-Jul-2016 - version 2.2.22 released
Fixed - java.lang.UnsupportedOperationException during command batch usage with netty 4.0.38 and higher
####15-Jul-2016 - version 2.2.21 released
### 15-Jul-2016 - version 2.2.21 released
Fixed - `RLock`, `RReadWriteLock`, `RSemaphore` and `RCountDownLatch` can blocks forever under some conditions
####14-Jul-2016 - version 2.2.20 released
### 14-Jul-2016 - version 2.2.20 released
Fixed - NPE during pubsub re-subscription (regression since 2.2.18)
Fixed - `RSortedSet` doesn't work in cluster mode (regression since 2.2.16)
Fixed - PubSub connection pool initialization in cluster mode
Fixed - NPE during pubsub usage in cluster mode (regression since 2.2.18)
####13-Jul-2016 - version 2.2.19 released
### 13-Jul-2016 - version 2.2.19 released
Feature - `RSetReactive.readIntersection`, `RSetReactive.diff` and `RSetReactive.intersection` added
Fixed - cluster commands handling regression (regression since 2.2.18)
####13-Jul-2016 - version 2.2.18 released
### 13-Jul-2016 - version 2.2.18 released
Feature - `RSet.randomAsync` and `RSet.random` commands added (thanks to dcheckoway)
Feature - commandTimeout param added to RedisClient
Feature - `JsonJacksonMapValueCodec` basic typed map value codec added (thanks to andrejserafim)
@ -235,7 +254,7 @@ Fixed - response parsing of cluster nodes command
Fixed - Connections weren't closing during `RedisClient` shutdown
Fixed - `RedissonRedLock.unlock`
####30-Jun-2016 - version 2.2.17 released
### 30-Jun-2016 - version 2.2.17 released
Feature - `RMultimap.keySize` method added
Feature - `RKeys.getType` method added
Feature - `RKeys.getKeysByPattern` method with count param added
@ -251,7 +270,7 @@ Fixed - RLock.lock can hang in some cases
Fixed - PubSub subscription may stuck in some cases
Fixed - return value of `RedissonMultimap.keySet.size` method
####12-Jun-2016 - version 2.2.16 released
### 12-Jun-2016 - version 2.2.16 released
Feature - `RGeo`, `RMultimapCache` added to `RBatch`
Feature - `fastRemove` and `fastRemoveAsync` methods were added to `RList`
Improvement - added Spring 4.3.0 support to RedissonSpringCacheManager
@ -261,14 +280,14 @@ Improvement - ability to define `Codec` for `RRemoteService`
Fixed - cluster state managing with redis masters only
Fixed - dead lock during `RLock`, `RSemaphore`, `RReadWriteLock`, `RCountDownLatch` usage under heavy load
####08-Jun-2016 - version 2.2.15 released
### 08-Jun-2016 - version 2.2.15 released
Improvement - Performance boost up to 30% for `RSortedSet.add` method
Fixed - auth during reconnection (thanks to fransiskusx)
Fixed - Infinity loop with iterator
Fixed - NPE in `RSortedSet`
Fixed - `RSortedSet.remove` and `iterator.remove` methods can break elements ordering
####27-May-2016 - version 2.2.14 released
### 27-May-2016 - version 2.2.14 released
Redisson Team is pleased to announce [Redisson PRO](http://redisson.pro) edition. This version is based on open-source edition and has 24x7 support and some features.
Feature - __data sharding for `RMap`, `RSet` structures in cluster mode__ available only in [Redisson PRO](http://redisson.pro) edition
@ -293,7 +312,7 @@ Fixed - FSTObjectOutput shouldn't be closed after write
Fixed - possible race-condition during ack waiting in `RRemoteService` object
Fixed - timeWait checking fixed in `RLock.tryLockAsync`
####30-Apr-2016 - version 2.2.13 released
### 30-Apr-2016 - version 2.2.13 released
Feature - `RSet.diff` and `RSet.intersection` methods added
Imporovement - `RScoredSortedSet`.`containsAll`, `removeAll` and `retainAll` methods speed optimization
@ -303,7 +322,7 @@ Fixed - possible infinity `RLock` expiration renewal process
Fixed - error during `RSetCache.readAll` invocation.
Fixed - expiration override wasn't work in `RSetCache.add`
####22-Apr-2016 - version 2.2.12 released
### 22-Apr-2016 - version 2.2.12 released
Imporovement - Replaying phase handling in CommandDecoder
Fixed - cluster state update manager can't try next node if current node has failed to response
@ -320,7 +339,7 @@ Fixed - array start index in LUA scripts
Fixed - RMap iterator
Fixed - probably thread blocking issues
####04-Apr-2016 - version 2.2.11 released
### 04-Apr-2016 - version 2.2.11 released
Since this version Redisson has __perfomance boost up to 43%__
@ -335,7 +354,7 @@ Fixed - RTopic listeners hangs during synchronous commands execution inside it
Fixed - Redisson hangs during shutdown if `RBlockingQueue\Deque.take` or `RBlockingQueue\Deque.poll` methods were invoked
####23-Mar-2016 - version 2.2.10 released
### 23-Mar-2016 - version 2.2.10 released
Feature - __new object added__ `RRemoteService`
Feature - __new object added__ `RSetMultimapCache`
@ -350,7 +369,7 @@ Fixed - `RLock.tryLockAsync` NPE
Fixed - possible NPE during Redisson version logging
Fixed - Netty threads shutdown after connection error
####04-Mar-2016 - version 2.2.9 released
### 04-Mar-2016 - version 2.2.9 released
Feature - __new object added__ `RSetMultimap`
Feature - __new object added__ `RListMultimap`
@ -361,7 +380,7 @@ Improvement - Add DynamicImport-Package to OSGi headers
Fixed - `RedissonSpringCacheManager` Sentinel compatibility
Fixed - `RAtomicLong.compareAndSet` doesn't work when expected value is 0 and it wasn't initialized
####12-Feb-2016 - version 2.2.8 released
### 12-Feb-2016 - version 2.2.8 released
Feature - `union`, `unionAsync`, `readUnion` and `readUnionAsync` methods were added to `RSet` object
Feature - `readAll` and `readAllAsync` methods were added to `RSetCache` object
@ -370,7 +389,7 @@ Fixed - Script error during `RSetCache.toArray` and `RSetCache.readAll` methods
Fixed - Sentinel doesn't support AUTH command
Fixed - RMap iterator
####03-Feb-2016 - version 2.2.7 released
### 03-Feb-2016 - version 2.2.7 released
Feature - `readAllKeySet`, `readAllValues`, `readAllEntry`, `readAllKeySetAsync`, `readAllValuesAsync`, `readAllEntryAsync` methods were added to `RMap` object
Improvement - `RKeys.delete` optimization in Cluster mode
@ -385,7 +404,7 @@ Fixed - offline slaves handling during Redisson start in Sentinel mode
Fixed - `SELECT` command can't be executed in Sentinel mode
Fixed - `database` setting removed from cluster config
####28-Jan-2016 - version 2.2.6 released
### 28-Jan-2016 - version 2.2.6 released
Feature - __new object added__ `RedissonMultiLock`
Feature - `move` method added to `RSet`, `RSetReactive` objects (thanks to thrau)
@ -405,7 +424,7 @@ Fixed - can't connect with password to Sentinel and Elasticache servers
Fixed - Cluster slave discovery (regression since 2.1.5)
Fixed - Sentinel slave discovery (regression since 2.1.5)
####09-Jan-2015 - version 2.2.5 released
### 09-Jan-2015 - version 2.2.5 released
Feature - __new object added__ `RBloomFilter`
Feature - __new object added__ `RAtomicDouble`
@ -424,7 +443,7 @@ Fixed - `RMap.addAndGetAsync` key encoding
Fixed - `RBatch` errors handling
Fixed - `RBlockingQueue.pollLastAndOfferFirstToAsync` does not block properly
####25-Dec-2015 - version 2.2.4 released
### 25-Dec-2015 - version 2.2.4 released
Please update to this version ASAP due to connection leak discovered in previous versions since Redisson 2.1.4.
Feature - __new object added__ `RBlockingDeque`
@ -441,18 +460,18 @@ Fixed - `RReadWriteLock.forceUnlock` works only for current thread
Fixed - MapKeyDecoder and MapValueDecoder are called in wrong order
Fixed - `RReadWriteLock` doesn't work in cluster mode
####15-Dec-2015 - version 2.2.3 released
### 15-Dec-2015 - version 2.2.3 released
Feature - ability to set connection listener via `Config.connectionListener` setting
Fixed - `RLock` expiration bug fixed (regression bug since 2.2.2)
Fixed - NPE in `RedissonSortedSet` constructor
####14-Dec-2015 - version 2.2.2 released
### 14-Dec-2015 - version 2.2.2 released
Feature - `isShuttingDown` and `isShutdown` methods were added to RedissonClient and RedissonReactiveClient
Feature - __new object added__ `RSetCacheReactive`
Fixed - RLock expiration renewal task scheduling fixed (regression bug since 2.2.1)
Fixed - RExpirable.expireAsync timeUnit precision fixed (regression bug since 2.2.1)
####11-Dec-2015 - version 2.2.1 released
### 11-Dec-2015 - version 2.2.1 released
Feature - __new object added__ `RReadWriteLock` with reentrant read/write locking
Feature - __new object added__ `RMapCache` map-based cache with TTL support for each entry
Feature - __new object added__ `RSetCache` set-based cache with TTL support for each value
@ -461,7 +480,7 @@ Feature - `RMap.values()`, `RMap.keySet()`, `RMap.entrySet()` reimplemented with
Feature - `RObjectReactive.isExists`, `RObject.isExists` and `RObject.isExistsAsync` added
Fixed - `RLock.unlock` not thrown IllegalMonitorStateException
####04-Dec-2015 - version 2.2.0 released
### 04-Dec-2015 - version 2.2.0 released
Since 2.2.0 version Redisson supports [Reactive Streams](http://www.reactive-streams.org). Use `Redisson.createReactive` method to access Reactive objects.
Feature - [Reactive Streams](http://www.reactive-streams.org) support
@ -476,12 +495,12 @@ Fixed - `RLock.delete` didn't check lock existence
Deprecated methods are dropped
####30-Nov-2015 - version 2.1.6 released
### 30-Nov-2015 - version 2.1.6 released
Fixed - connection pool regression bug
Fixed - connection init during `Node.ping` and `ClusterNode.info` invocation
####24-Nov-2015 - version 2.1.5 released
### 24-Nov-2015 - version 2.1.5 released
Feature - new methods with `limit` option support were added to `RLexSortedSet`: `lexRange`, `lexRangeHead`, `lexRangeHeadAsync`, `lexRangeTail`, `lexRangeTailAsync`, `lexRangeAsync` (thanks to jackygurui)
Feature - new methods with `limit` option support were added to `RScoredSortedSet`: `valueRange`, `valueRangeAsync`, `entryRange`, `entryRangeAsync`, `valueRange`, `valueRangeAsync` (thanks to jackygurui)
Feature - `LOADING` Redis server response handling
@ -497,7 +516,7 @@ Fixed - `RSet.iterator`
Fixed - `RBatch.execute` and `RBatch.executeAsync` errors handling
####11-Nov-2015 - version 2.1.4 released
### 11-Nov-2015 - version 2.1.4 released
Cluster support improvements. New codecs. Stability improvements.
Feature - [LZ4](https://github.com/jpountz/lz4-java) compression codec support
@ -522,7 +541,7 @@ Fixed - NPE during Publish/Subscribe event handling
Fixed - Redisson shutdown handling
Fixed - EOFException during RLock usage with SerializationCodec (thanks to Oleg Ternovoi)
####17-Sep-2015 - version 2.1.3 released
### 17-Sep-2015 - version 2.1.3 released
Feature - Ability to define `Codec` for each object
Feature - `refreshConnectionAfterFails` setting added
Feature - [AWS Elasticache](https://aws.amazon.com/elasticache/) support via `Config.useElasticacheServers` method (thanks to Steve Ungerer)
@ -530,7 +549,7 @@ Feature - `RScoredSortedSet` and `RLexSortedSet` added. Both uses native Redis S
Fixed - missed AUTH during channel reconnection
Fixed - resubscribe to subscribed topics during channel reconnection
####05-Sep-2015 - version 2.1.2 released
### 05-Sep-2015 - version 2.1.2 released
Fixed - possible NPE during channel reconnection
Fixed - executeAsync freezes in cluster mode
Fixed - use same node for SCAN/SSCAN/HSCAN during iteration
@ -540,7 +559,7 @@ Fixed - NPE with empty sentinel servers
Fixed - unable to read `clientName` config param in Master\Slave and Sentinel modes
Fixed - "Too many open files" error in cluster mode
####15-Aug-2015 - version 2.1.1 released
### 15-Aug-2015 - version 2.1.1 released
Feature - all keys operations extracted to `RKeys` interface
Feature - `RKeys.getKeys`, `RKeys.getKeysByPattern` and `RKeys.randomKey`methods added
Feature - `RBlockingQueueAsync.drainToAsync` method added
@ -554,7 +573,7 @@ Fixed - skip disconnected sentinels during startup
Fixed - slave node discovery in sentinel mode which has been disconnected since start
__Deprecated__ - Redisson methods `deleteAsync`, `delete`, `deleteByPatternAsync`, `deleteByPattern`, `findKeysByPatternAsync`, `findKeysByPattern`. Use same methods with `RKeys` interface
####03-Aug-2015 - version 2.1.0 released
### 03-Aug-2015 - version 2.1.0 released
Feature - `RTopic` subscribtion/unsubscription status listener added
Feature - `RSet`: `removeRandom` and `removeRandomAsync` methods added
Improvement - `RList`: `retainAll`,`containsAll`, `indexOf`, `lastIndexOf` optimization
@ -565,7 +584,7 @@ Fixed - timeout timer interval calculation
Fixed - `RBatch` NPE's with very big commands list
Fixed - `RBucket.set` with timeout
####26-Jul-2015 - version 2.0.0 released
### 26-Jul-2015 - version 2.0.0 released
Starting from 2.0.0 version Redisson has a new own async and lock-free Redis client under the hood. Thanks to the new architecture pipline (command batches) support has been implemented and a lot of code has gone.
Feature - new `RObject` methods: `move`, `moveAsync`, `migrate`, `migrateAsync`
@ -574,12 +593,12 @@ Feature - multiple commands batch (Redis pipelining) support via `Redisson.creat
Feature - new methods `flushall`, `deleteAsync`, `delete`, `deleteByPatternAsync`, `deleteByPattern`, `findKeysByPatternAsync`, `findKeysByPattern` added to `RedissonClient` interface
Improvement - closed channel detection speedup
####22-Jul-2015 - version 1.3.1 released
### 22-Jul-2015 - version 1.3.1 released
Fixed - requests state sync during shutdown
Fixed - netty-transport-native-epoll is now has a provided scope
Fixed - NPE during `BlockingQueue.poll` invocation
####04-Jul-2015 - version 1.3.0 released
### 04-Jul-2015 - version 1.3.0 released
Feature - `RQueue.pollLastAndOfferFirstTo` method added
Feature - `RObject.rename`, `RObject.renameAsync`, `RObject.renamenx`, `RObject.renamenxAsync` methods added
Feature - `RList.getAsync`, `RList.addAsync`, `RList.addAllAsync` methods added
@ -600,7 +619,7 @@ Fixed - `RedissonClient.getScript` method added
Fixed - `BlockingQueue.poll` method
Fixed - Incorrect map key encoding makes hmget return no fields when string keys are used (thanks to sammiq)
####02-Apr-2015 - version 1.2.1 released
### 02-Apr-2015 - version 1.2.1 released
Feature - all redis-script commands via 'RScript' object
Feature - implementation of `java.util.concurrent.BlockingQueue` (thanks to pdeschen)
Feature - buckets load by pattern (thanks to mathieucarbou)
@ -614,13 +633,13 @@ Fixed - `RMap.replace` concurrency issue (thanks to AndrewKolpakov)
Fixed - `RLock` subscription timeout units fixed (thanks to AndrewKolpakov)
Fixed - Re-throw async exceptions (thanks to AndrewKolpakov)
####09-Jan-2015 - version 1.2.0 released
### 09-Jan-2015 - version 1.2.0 released
Feature - cluster mode support
Fixed - `RList` iterator race conditions
Fixed - `RDeque.addFirst` `RDeque.addLast` methods
Fixed - OSGi support
####16-Dec-2014 - version 1.1.7 released
### 16-Dec-2014 - version 1.1.7 released
Improvement - `RAtomicLong` optimization
Fixed - `RMap.fastRemove` and `RMap.getAll` methods
Fixed - `RTopic` listeners re-subscribing in sentinel mode
@ -631,7 +650,7 @@ Fixed - `RAtomicLong` NPE
Fixed - infinity loop during master/slave connection acquiring
Fixed - `RedissonList.addAll` result
####18-Nov-2014 - version 1.1.6 released
### 18-Nov-2014 - version 1.1.6 released
Feature - `RBucket.exists` and `RBucket.existsAsync` methods added
Feature - `RMap.addAndGet` method added
Feature - database index via `database` and operation timeout via `timeout` config params added
@ -644,40 +663,40 @@ Fixed - HashedWheelTimer shutdown
Fixed - `RLock` race conditions (thanks to jsotuyod and AndrewKolpakov)
Fixed - `RCountDownLatch` race conditions
####23-Jul-2014 - version 1.1.5 released
### 23-Jul-2014 - version 1.1.5 released
Feature - operations auto-retry. `retryAttempts` and `retryInterval` params added for each connection type
Feature - `RMap.filterEntries`, `RMap.getAll`, `RMap.filterKeys`, `RMap.filterValues` methods added
Feature - `RMap.fastRemove`, `RMap.fastRemoveAsync`, `RMap.fastPut` & `RMap.fastPutAsync` methods added
Fixed - async operations timeout handling
Fixed - sorting algorithm used in `RSortedSet`.
####15-Jul-2014 - version 1.1.4 released
### 15-Jul-2014 - version 1.1.4 released
Feature - new `RLock.lockInterruptibly`, `RLock.tryLock`, `RLock.lock` methods with TTL support
Fixed - pub/sub connections reattach then slave/master down
Fixed - turn off connection watchdog then slave/master down
Fixed - sentinel master switch
Fixed - slave down connection closing
####13-Jul-2014 - version 1.1.3 released
### 13-Jul-2014 - version 1.1.3 released
Improvement - RedissonCountDownLatch optimization
Improvement - RedissonLock optimization
Fixed - RedissonLock thread-safety
Fixed - master/slave auth using Sentinel servers
Fixed - slave down handling using Sentinel servers
####03-Jul-2014 - version 1.1.2 released
### 03-Jul-2014 - version 1.1.2 released
Improvement - RedissonSet.iterator implemented with sscan
Improvement - RedissonSortedSet.iterator optimization
Feature - `RSortedSet.removeAsync`, `RSortedSet.addAsync`, `RSet.removeAsync`, RSet.addAsync methods added
Feature - slave up/down detection in Sentinel servers connection mode
Feature - new-slave automatic discovery in Sentinel servers connection mode
####17-June-2014 - version 1.1.1 released
### 17-June-2014 - version 1.1.1 released
Feature - sentinel servers support
Fixed - connection leak in `RTopic`
Fixed - setted password not used in single server connection
####07-June-2014 - version 1.1.0 released
### 07-June-2014 - version 1.1.0 released
Feature - master/slave connection management
Feature - simple set/get object support via `org.redisson.core.RBucket`
Feature - hyperloglog support via `org.redisson.core.RHyperLogLog`
@ -691,24 +710,24 @@ Fixed - `RTopic.publish` now returns the number of clients that received the mes
Fixed - reconnection handling (thanks to renzihui)
Improvement - `org.redisson.core.RTopic` now use lazy apporach for subscribe/unsubscribe
####04-May-2014 - version 1.0.4 released
### 04-May-2014 - version 1.0.4 released
Feature - distributed implementation of `java.util.Deque`
Feature - some objects implements `org.redisson.core.RExpirable`
Fixed - JsonJacksonCodec lazy init
####26-Mar-2014 - version 1.0.3 released
### 26-Mar-2014 - version 1.0.3 released
Fixed - RedissonAtomicLong state format
Fixed - Long serialization in JsonJacksonCodec
####05-Feb-2014 - version 1.0.2 released
### 05-Feb-2014 - version 1.0.2 released
Feature - distributed implementation of `java.util.SortedSet`
Fixed - OSGi compability
####17-Jan-2014 - version 1.0.1 released
### 17-Jan-2014 - version 1.0.1 released
Improvement - forceUnlock, isLocked, isHeldByCurrentThread and getHoldCount methods added to RLock
Feature - connection load balancer to use multiple Redis servers
Feature - published in maven central repo
####11-Jan-2014 - version 1.0.0 released
### 11-Jan-2014 - version 1.0.0 released
First stable release.

@ -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>2.8.2-SNAPSHOT</version>
<version>2.8.3-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Redisson</name>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-parent</artifactId>
<version>2.8.2-SNAPSHOT</version>
<version>2.8.3-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
@ -156,7 +156,12 @@
<version>[3.1,)</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<version>[1.4,)</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

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

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

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

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

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-parent</artifactId>
<version>2.8.2-SNAPSHOT</version>
<version>2.8.3-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>
@ -258,6 +258,13 @@
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<version>[1.4,)</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
<build>

@ -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,12 +18,10 @@ 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;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
@ -35,20 +33,19 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
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.BaseStatusListener;
import org.redisson.api.listener.MessageListener;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.LongCodec;
@ -62,14 +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.util.Timeout;
import io.netty.util.TimerTask;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.ThreadLocalRandom;
/**
*
@ -92,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;
@ -125,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);
@ -143,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() {
@ -186,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
@ -200,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);
@ -243,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 "
@ -338,7 +372,7 @@ public class RedissonExecutorService implements RScheduledExecutorService {
}
@Override
public <T> Future<T> submit(Callable<T> task) {
public <T> RFuture<T> submit(Callable<T> task) {
RemotePromise<T> promise = (RemotePromise<T>) submitAsync(task);
execute(promise);
return promise;
@ -411,7 +445,7 @@ public class RedissonExecutorService implements RScheduledExecutorService {
}
@Override
public Future<?> submit(Runnable task) {
public RFuture<?> submit(Runnable task) {
RemotePromise<Void> promise = (RemotePromise<Void>) submitAsync(task);
execute(promise);
return promise;
@ -428,7 +462,7 @@ public class RedissonExecutorService implements RScheduledExecutorService {
}
@Override
public ScheduledFuture<?> schedule(Runnable task, long delay, TimeUnit unit) {
public RScheduledFuture<?> schedule(Runnable task, long delay, TimeUnit unit) {
RedissonScheduledFuture<?> future = (RedissonScheduledFuture<?>) scheduleAsync(task, delay, unit);
execute((RemotePromise<?>)future.getInnerPromise());
return future;
@ -446,7 +480,7 @@ public class RedissonExecutorService implements RScheduledExecutorService {
}
@Override
public <V> ScheduledFuture<V> schedule(Callable<V> task, long delay, TimeUnit unit) {
public <V> RScheduledFuture<V> schedule(Callable<V> task, long delay, TimeUnit unit) {
RedissonScheduledFuture<V> future = (RedissonScheduledFuture<V>) scheduleAsync(task, delay, unit);
execute((RemotePromise<V>)future.getInnerPromise());
return future;
@ -464,7 +498,7 @@ public class RedissonExecutorService implements RScheduledExecutorService {
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
public RScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
RedissonScheduledFuture<?> future = (RedissonScheduledFuture<?>) scheduleAtFixedRateAsync(task, initialDelay, period, unit);
execute((RemotePromise<?>)future.getInnerPromise());
return future;
@ -505,7 +539,7 @@ public class RedissonExecutorService implements RScheduledExecutorService {
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit) {
public RScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit) {
RedissonScheduledFuture<?> future = (RedissonScheduledFuture<?>) scheduleWithFixedDelayAsync(task, initialDelay, delay, unit);
execute((RemotePromise<?>)future.getInnerPromise());
return future;

@ -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);
}

@ -17,7 +17,6 @@ package org.redisson;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@ -26,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;
@ -132,14 +133,45 @@ public class RedissonKeys implements RKeys {
}
@Override
public Long isExists(String... names) {
return commandExecutor.get(isExistsAsync(names));
public long touch(String... names) {
return commandExecutor.get(touchAsync(names));
}
@Override
public RFuture<Long> isExistsAsync(String... names) {
Object[] params = Arrays.copyOf(names, names.length, Object[].class);
return commandExecutor.readAsync((String)null, null, RedisCommands.EXISTS_LONG, params);
public RFuture<Long> touchAsync(String... names) {
return commandExecutor.writeAllAsync(RedisCommands.TOUCH_LONG, new SlotCallback<Long, Long>() {
AtomicLong results = new AtomicLong();
@Override
public void onSlotResult(Long result) {
results.addAndGet(result);
}
@Override
public Long onFinish() {
return results.get();
}
}, names);
}
@Override
public long countExists(String... names) {
return commandExecutor.get(countExistsAsync(names));
}
@Override
public RFuture<Long> countExistsAsync(String... names) {
return commandExecutor.readAllAsync(RedisCommands.EXISTS_LONG, new SlotCallback<Long, Long>() {
AtomicLong results = new AtomicLong();
@Override
public void onSlotResult(Long result) {
results.addAndGet(result);
}
@Override
public Long onFinish() {
return results.get();
}
}, names);
}
@ -227,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()) {
@ -338,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,35 +522,72 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
throw new NullPointerException();
}
List<Object> params = new ArrayList<Object>();
params.add(invalidateEntryOnChange);
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());
}
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());
for (K k : keys) {
byte[] keyEncoded = encodeMapKey(k);
params.add(keyEncoded);
CacheKey cacheKey = toCacheKey(keyEncoded);
cache.remove(cacheKey);
if (invalidateEntryOnChange == 1) {
byte[] msgEncoded = encode(new LocalCachedMapInvalidate(instanceId, cacheKey.getKeyHash()));
params.add(msgEncoded);
} else {
params.add(null);
}
}
return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_LONG,
"local counter = 0; " +
"for j = 2, #ARGV, 2 do "
+ "if redis.call('hdel', KEYS[1], ARGV[j]) == 1 then "
+ "if ARGV[1] == '1' then "
+ "redis.call('publish', KEYS[2], ARGV[j+1]); "
+ "end; "
+ "counter = counter + 1;"
+ "end;"
+ "end;"
+ "return counter;",
Arrays.<Object>asList(getName(), invalidationTopic.getChannelNames().get(0)),
params.toArray());
return commandExecutor.writeAsync(getName(), codec, RedisCommands.HDEL, params.toArray());
}
@ -426,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);
}
@ -742,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
@ -774,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
@ -867,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>>();
@ -875,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 "
@ -897,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
@ -922,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
@ -961,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,
@ -968,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
@ -997,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

@ -33,6 +33,9 @@ import java.util.UUID;
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;
@ -46,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;
/**
@ -66,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
@ -83,10 +95,16 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
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);
return "{" + getName() + "}:" + Hash.hashToBase64(keyState) + ":key";
return suffixName(getName(), Hash.hashToBase64(keyState) + ":key");
} catch (IOException e) {
throw new IllegalStateException(e);
}
@ -260,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));
@ -385,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();

@ -136,6 +136,16 @@ public abstract class RedissonObject implements RObject {
return commandExecutor.writeAsync(getName(), RedisCommands.DEL_BOOL, getName());
}
@Override
public boolean touch() {
return get(touchAsync());
}
@Override
public RFuture<Boolean> touchAsync() {
return commandExecutor.writeAsync(getName(), codec, RedisCommands.TOUCH, getName());
}
@Override
public boolean isExists() {
return get(isExistsAsync());

@ -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);
}

@ -15,16 +15,59 @@
*/
package org.redisson.api;
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
* Future's {@code get} method will return the task's result upon
* successful completion.
*
* @param task the task to submit
* @param <T> the type of the task's result
* @return a Future representing pending completion of the task
*/
@Override
<T> RFuture<T> submit(Callable<T> task);
/**
* Submits a Runnable task for execution and returns a Future
* representing that task. The Future's {@code get} method will
* return the given result upon successful completion.
*
* @param task the task to submit
* @param result the result to return
* @param <T> the type of the result
* @return a Future representing pending completion of the task
*/
@Override
<T> RFuture<T> submit(Runnable task, T result);;
/**
* Submits a Runnable task for execution and returns a Future
* representing that task. The Future's {@code get} method will
* return {@code null} upon <em>successful</em> completion.
*
* @param task the task to submit
* @return a Future representing pending completion of the task
*/
@Override
RFuture<?> submit(Runnable task);
/**
* Returns executor name
*
@ -47,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,13 +25,98 @@ 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);
/**
* Checks if provided keys exist
*
* @param names of keys
* @return amount of existing keys
*/
Long isExists(String... names);
long countExists(String... names);
/**
* Get Redis object type by key
@ -128,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,13 +25,98 @@ 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);
/**
* Checks if provided keys exist
*
* @param names of keys
* @return amount of existing keys
*/
RFuture<Long> isExistsAsync(String... names);
RFuture<Long> countExistsAsync(String... names);
/**
* Get Redis object type by key
@ -84,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,23 @@ 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
*
* @param key - map key
* @return readWriteLock
*/
RReadWriteLock getReadWriteLock(K key);
/**
* Returns <code>RLock</code> instance associated with key
*
@ -120,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();
}

@ -25,6 +25,13 @@ import org.redisson.client.codec.Codec;
*/
public interface RObject extends RObjectAsync {
/**
* Update the last access time of an object.
*
* @return <code>true</code> if object was touched else <code>false</code>
*/
boolean touch();
/**
* Transfer an object from source Redis instance to destination Redis instance
*

@ -23,6 +23,13 @@ package org.redisson.api;
*/
public interface RObjectAsync {
/**
* Update the last access time of an object in async mode.
*
* @return <code>true</code> if object was touched else <code>false</code>
*/
RFuture<Boolean> touchAsync();
/**
* Transfer an object from source Redis instance to destination Redis instance
* in async mode

@ -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>

@ -15,17 +15,99 @@
*/
package org.redisson.api;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledExecutorService;
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
*
*/
public interface RScheduledExecutorService extends RExecutorService, ScheduledExecutorService, RScheduledExecutorServiceAsync {
/**
* Creates and executes a one-shot action that becomes enabled
* after the given delay.
*
* @param command the task to execute
* @param delay the time from now to delay execution
* @param unit the time unit of the delay parameter
* @return a ScheduledFuture representing pending completion of
* the task and whose {@code get()} method will return
* {@code null} upon completion
*/
@Override
RScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
/**
* Creates and executes a ScheduledFuture that becomes enabled after the
* given delay.
*
* @param callable the function to execute
* @param delay the time from now to delay execution
* @param unit the time unit of the delay parameter
* @param <V> the type of the callable's result
* @return a ScheduledFuture that can be used to extract result or cancel
*/
@Override
<V> RScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
/**
* Creates and executes a periodic action that becomes enabled first
* after the given initial delay, and subsequently with the given
* period; that is executions will commence after
* {@code initialDelay} then {@code initialDelay+period}, then
* {@code initialDelay + 2 * period}, and so on.
* If any execution of the task
* encounters an exception, subsequent executions are suppressed.
* Otherwise, the task will only terminate via cancellation or
* termination of the executor. If any execution of this task
* takes longer than its period, then subsequent executions
* may start late, but will not concurrently execute.
*
* @param command the task to execute
* @param initialDelay the time to delay first execution
* @param period the period between successive executions
* @param unit the time unit of the initialDelay and period parameters
* @return a ScheduledFuture representing pending completion of
* the task, and whose {@code get()} method will throw an
* exception upon cancellation
*/
@Override
RScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
/**
* Creates and executes a periodic action that becomes enabled first
* after the given initial delay, and subsequently with the
* given delay between the termination of one execution and the
* commencement of the next. If any execution of the task
* encounters an exception, subsequent executions are suppressed.
* Otherwise, the task will only terminate via cancellation or
* termination of the executor.
*
* @param command the task to execute
* @param initialDelay the time to delay first execution
* @param delay the delay between the termination of one
* execution and the commencement of the next
* @param unit the time unit of the initialDelay and delay parameters
* @return a ScheduledFuture representing pending completion of
* the task, and whose {@code get()} method will throw an
* exception upon cancellation
*/
@Override
RScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
/**
* Cancels scheduled task by id
*

@ -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();

@ -385,7 +385,7 @@ public interface RedissonClient {
*
* @param <V> type of value
* @param name - name of object
* @return Lock object
* @return Set object
*/
<V> RSet<V> getSet(String name);

@ -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

@ -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);
@ -280,6 +281,8 @@ public interface RedisCommands {
RedisCommand<Boolean> SETNX = new RedisCommand<Boolean>("SETNX", new BooleanReplayConvertor(), 2);
RedisCommand<Void> PSETEX = new RedisCommand<Void>("PSETEX", new VoidReplayConvertor(), 3);
RedisStrictCommand<Long> TOUCH_LONG = new RedisStrictCommand<Long>("TOUCH");
RedisStrictCommand<Boolean> TOUCH = new RedisStrictCommand<Boolean>("TOUCH", new BooleanReplayConvertor());
RedisStrictCommand<Long> EXISTS_LONG = new RedisStrictCommand<Long>("EXISTS");
RedisStrictCommand<Boolean> EXISTS = new RedisStrictCommand<Boolean>("EXISTS", new BooleanReplayConvertor());
RedisStrictCommand<Boolean> NOT_EXISTS = new RedisStrictCommand<Boolean>("EXISTS", new BooleanNumberReplayConvertor(1L));

@ -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
*

@ -26,6 +26,8 @@ import org.redisson.client.codec.Codec;
import org.redisson.codec.CodecProvider;
import org.redisson.codec.DefaultCodecProvider;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.connection.ConnectionManager;
import org.redisson.connection.ReplicatedConnectionManager;
import org.redisson.liveobject.provider.DefaultResolverProvider;
import org.redisson.liveobject.provider.ResolverProvider;
@ -50,6 +52,8 @@ public class Config {
private ElasticacheServersConfig elasticacheServersConfig;
private ReplicatedServersConfig replicatedServersConfig;
private ConnectionManager connectionManager;
/**
* Threads amount shared between all redis node clients
@ -122,6 +126,9 @@ public class Config {
if (oldConf.getReplicatedServersConfig() != null) {
setReplicatedServersConfig(new ReplicatedServersConfig(oldConf.getReplicatedServersConfig()));
}
if (oldConf.getConnectionManager() != null) {
useCustomServers(oldConf.getConnectionManager());
}
}
@ -296,6 +303,28 @@ public class Config {
void setReplicatedServersConfig(ReplicatedServersConfig replicatedServersConfig) {
this.replicatedServersConfig = replicatedServersConfig;
}
/**
* Returns the connection manager if supplied via
* {@link #useCustomServers(ConnectionManager)}
*
* @return ConnectionManager
*/
ConnectionManager getConnectionManager() {
return connectionManager;
}
/**
* This is an extension point to supply custom connection manager.
*
* @see ReplicatedConnectionManager on how to implement a connection
* manager.
* @param connectionManager for supply
*/
public void useCustomServers(ConnectionManager connectionManager) {
this.connectionManager = connectionManager;
}
/**
* Init single server configuration.
@ -447,6 +476,7 @@ public class Config {
throw new IllegalStateException("Replication servers config already used!");
}
}
/**
* Activates an unix socket if servers binded to loopback interface.

@ -265,7 +265,9 @@ public class ConfigSupport {
} else if (configCopy.getReplicatedServersConfig() != null) {
validate(configCopy.getReplicatedServersConfig());
return new ReplicatedConnectionManager(configCopy.getReplicatedServersConfig(), configCopy);
} else {
} else if (configCopy.getConnectionManager() != null) {
return configCopy.getConnectionManager();
}else {
throw new IllegalArgumentException("server(s) address(es) not defined!");
}
}

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

Loading…
Cancel
Save