Merge branch 'master' into issue-1507-mapcache-expiration2

pull/2160/head
Mitchell Ackerman 6 years ago
commit 6c7e6fc684

@ -4,12 +4,32 @@ Redisson Releases History
Сonsider __[Redisson PRO](https://redisson.pro)__ version for advanced features and support by SLA.
### 29-Apr-2019 - versions 3.10.7 released
### 28-May-2019 - 3.11.0 released
Feature - `radiusStoreSortedTo` methods added to `RGeo`, `RGeoAsync`, `RGeoRx`, `RGeoReactive` interfaces
Feature - Local cache for `JCache` added. Read the [documentation](https://github.com/redisson/redisson/wiki/14.-Integration-with-frameworks#1442-jcache-api-local-cache) for more details
Feature - `Async`, `Reactive`, `RxJava2` interfaces added to `JCache`. Read the [documentation](https://github.com/redisson/redisson/wiki/14.-Integration-with-frameworks#1441-jcache-api-asynchronous-reactive-and-rxjava2-interfaces) for more details
Feature - `RRingBuffer` object added. Read the [documentation](https://github.com/redisson/redisson/wiki/7.-distributed-collections/#721-ring-buffer) for more details
Improvement - reduced memory consumption by ClusterConnectionManager
Improvement - UpdateValve needs to execute manager.store only once at the end of the request (thanks to [jchobantonov](https://github.com/jchobantonov))
Fixed - `HttpSessionListener.sessionDestoyed` method isn't invoked if session wasn't loaded by Tomcat instance
Fixed - redisson-spring-data `ReactiveSubscription.receive` method throws NPE
Fixed - Redis response isn't fully consumed after decoding error
Fixed - Spring Session PRINCIPAL_NAME_INDEX_NAME session attribute has incorrect name
Fixed - internal `AsyncSemaphore` object doesn't notify sleeping threads with permits more than one
Fixed - `RedisTemplate.radius` and `RedisConnection.geoRadius` methods throws `IllegalArgumentException` during response decoding
Fixed - `RedissonNodeConfig.mapReduceWorkers` setting couldn't be set (thanks to xiaoxuan.bai)
### 29-Apr-2019 - 3.10.7 released
Feature - Add support for [Reactive and RxJava2 interfaces](https://github.com/redisson/redisson/wiki/9.-distributed-services#913-remote-service-asynchronous-reactive-and-rxjava2-calls) to RemoteService object
Feature - MILLISECONDS option added to RRateLimiter.RateIntervalUnit object
Feature - range method added to RList, RListReactive and RListRx interfaces
Improvement - `JCache.getAll` execution optimization for non-existing keys
Improvement - 10X Performance boost for `JCache.putAll` method
Fixed - disconnected sentinels shouldn't be used in sentinel list
Fixed - Apache Tomcat `RedissonSessionManager` doesn't use classloader aware codec for session Map object (thanks to [jchobantonov](https://github.com/jchobantonov))
Fixed - LiveObject field with Map type couldn't be persisted
@ -23,15 +43,17 @@ Fixed - Redis Sentinel prior 5.0.1 version doesn't require password. Regression
Fixed - Redisson tries to renewed Lock expiration even if lock doesn't exist. Regression since 3.10.5 version
Fixed - FstCodec can't deserialize ConcurrentHashMap based object with package visibility
### 05-Apr-2019 - versions 3.10.6 released
### 05-Apr-2019 - 3.10.6 released
Feature - `broadcastSessionEvents` setting added to Tomcat Session Manager
Feature - `remainTimeToLive` method added to `RLock`, `RLockAsync`, `RLockRx` and `RLockReactive` interfaces
Feature - NAT mapping support for cluster mode
Feature - `isLock` method added to `RLockAsync`, `RLockRx`, `RLockReactive` interfaces
Feature - `writeBehindDelay` and `writeBehindBatchSize` settings added to `MapOptions` object
Improvement - Eviction task logging added
Improvement - `MapWriter` interface retains only two methods for handling batch updates
Improvement - `MapOptions.writeBehindThreads` parameter removed
Fixed - `RBitSet.asBitSet` methods throws NPE if RBitSet object doesn't exist
Fixed - `JCache.getAll` method throws `RedisException: too many results to unpack`
Fixed - `RLock.lock` method can be interrupted with `Thread.interrupt` method
@ -43,14 +65,16 @@ Fixed - `CommandPubSubDecoder.decodeResult` throws `IllegalStateException` in JB
Fixed - NullValue object shouldn't be stored if `RedissonSpringCacheManager.allowNullValues = false`
Fixed - `removeListener` method of `RTopicReactive` and `RTopicRx` interfaces throws NoSuchMethodException
### 20-Mar-2019 - versions 3.10.5 released
### 20-Mar-2019 - 3.10.5 released
Feature - `getMultiLock`, `getRedLock` methods added to `RedissonClient`, `RedissonRxClient` and `RedissonReactiveClient` interfaces
Feature - `getInfo`, `listGroups`, `listConsumers` methods added to `RStream`, `RStreamRx`, `RStreamReactive` interfaces
Feature - `RPatternTopic.removeListenerAsync` method added
Feature - `getAndSet` method with TTL support added `RBucket`, `RBucketAsync`, `RBucketReactive`, `RBucketRx` interfaces
Feature - `addListener` and `removeListener` methods added to `RObject`, `RObjectAsync`, `RObjectReactive`, `RObjectRx` objects. It allows to add and remove listeners for Deleted and Expired keyspace events published by Redis
Improvement - shuffle list of sentinels to distribute load (thanks to [hrakaroo](https://github.com/hrakaroo))
Improvement - methods of RxJava2 interfaces should use full set of result objects: `Single`, `Maybe` or `Completable`
Fixed - compatibility with hibernate 5.2.0 - 5.2.4 versions
Fixed - ClassCastException during `RBatchReactive` and `RBatchRx` execution in `exectionMode` = `REDIS_WRITE_ATOMIC` or `REDIS_READ_ATOMIC`
Fixed - sentinel mode doesn't support AUTH command
@ -60,9 +84,10 @@ Fixed - RStream.readGroup method doesn't handle empty result properly with Redis
Fixed - AUTH password included in exceptions
Fixed - locked lock isn't unlocked after disconnect to Redis
### 08-Mar-2019 - versions 3.10.4 released
### 08-Mar-2019 - version 3.10.4 released
Feature - `getCountDownLatch`, `getPermitExpirableSemaphore`, `getSemaphore`, `getFairLock` methods added to `RMultimap` object
Feature - `maxCleanUpDelay` and `minCleanUpDelay` settings added to `Config` object
Fixed - `RLocalCachedMap.destroy` method doesn't clear local cache
Fixed - HP NONSTOP OS is not supported by MapCacheEventCodec
Fixed - `RedissonLocalCachedMap.readAll*` methods throw NPE if `evictionPolicy = WEAK` used
@ -73,14 +98,16 @@ Fixed - negative delay shouldn't be allowed to define for `RDelayedQueue.offer`
Fixed - eviction delay for `RMapCache` object is not decreased
Fixed - `RMultiLock.tryLockAsync` and `RMultiLock.lockAsync` methods may hang during invcation
### 26-Feb-2019 - versions 3.10.3 released
### 26-Feb-2019 - 3.10.3 released
Feature - `RTransaction.getBuckets` method added
Feature - ability to redefine `CacheKeysFactory` in Hibernate Cache Factory
Feautre - ability to specify default values for environment variable (thanks to [aaabramov](https://github.com/aaabramov))
Feature - `RRateLimiter.getConfig` method added
Feature - `ping` and `pingAll` methods with timeout added to `Node` object
Improvement - create Redis stream group if it doesn't exist
Improvement - response decoding optimization
Fixed - `RStream.listPending` throws `IndexOutOfBoundsException`
Fixed - `LocalCacheView.toCacheKey` method throws `StackOverflowError`
Fixed - `RedissonSessionManager` doesn't support async servlets (thanks to [dnouls](https://github.com/dnouls))
@ -93,8 +120,10 @@ Fixed - Transactional RBucket object doesn't respect transaction timeout
Feature - `StreamMessageId.NEVER_DELIVERED` added
Feature - [decodeInExecutor](https://github.com/redisson/redisson/wiki/2.-Configuration#decodeinexecutor) setting added
Feature - `lockAsync` and `tryLockAsync` methods with threadId param added to RedissonMultiLock object
Improvement - default values of `nettyThreads` and `threads` settings set to `32` and `16` respectively
Improvement - Redis nodes with empty names in cluster config should be skipped
Fixed - `RFuture.cause()` method should return CancellationException and not throw it
Fixed - continues handling of RedisLoadingException leads to excessive load
Fixed - slave's connection pools are not initialized when it back from failover
@ -118,10 +147,12 @@ Feature - `cachedKeySet`, `cachedValues`, `cachedEntrySet` and `getCachedMap` me
Feature - __Hibernate 5.4__ support
Feature - [search LiveObjects](https://github.com/redisson/redisson/wiki/9.-Distributed-services#rindex) by field
Feature - allow checking if lock is held by a thread (thanks to [hackworks](https://github.com/hackworks))
Improvement - return `null` if Tomcat session attribute couldn't be parsed
Improvement - Tomcat Session key codec changed to StringCodec
Improvement - Spring Session key codec changed to StringCodec
Improvement - Tomcat Session recycle method implementation (thanks to [jchobantonov](https://github.com/jchobantonov))
Fixed - RRateLimiter RateType checking (thanks to [shengjie8329](https://github.com/shengjie8329))
Fixed - implementation of workaround for DNS name resolver bug
Fixed - running scheduleWithFixedDelay Job couldn't be canceled
@ -157,10 +188,12 @@ Feature - Spring Data Redis 2.1.x support added
Feature - Spring Boot Starter 2.1.x support added
Feature - Spring Data Redis 2.0.x and 2.1.x integrations support `ReactiveRedisTemplate`
Feature - Support of [Different monitoring systems](https://github.com/redisson/redisson/wiki/14.-Integration-with-frameworks#1410-statistics-monitoring-jmx-and-other-systems)
Improvement - RGeo.radius methods use GEORADIUS_RO and GEORADIUSBYMEMBER_RO commands
Improvement - restored implementation of DnsAddressResolverGroupFactory
Improvement - RedisConnectionClosedException removed
Improvement - __default codec changed to FSTCodec__
Fixed - `RMap.getAll` throws `ClassCastException` during transaction execution
Fixed - `pingConnectionInterval` and `lock-watchdog-timeout` parameters added to `redisson.xsd`
Fixed - zRevRangeWithScores does not work properly in Spring RedisTemplate
@ -193,8 +226,10 @@ Feature - `takeFirstElements` and `takeLastElements` added to `RScoredSortedSetR
Feature - `takeFirst` and `takeLast` methods added to `RScoredSortedSet` object
Feature - `readGroup` method variations added to `RStream` object
Feature - `remove`, `trim`, `fastClaim`, `removeGroup`, `removeConsumer`, `updateGroupMessageId` methods added to `RStream` object
Improvement - JCache performance optimization up to 2x times
Improvement - Redis url validation
Fixed - Exception serialization by Jackson codec
Fixed - DNS channels aren't pooled
Fixed - RStream commands don't work with Redis 5.0.1 version
@ -218,7 +253,9 @@ Feature - multi-type listeners support for `RTopic` and `RPatternTopic` objects.
Feature - `useScriptCache` setting added. Manages by Lua-script caching on Redis side. Please refer to [documentation](https://github.com/redisson/redisson/wiki/2.-Configuration#usescriptcache) for more details
Feature - added `RMap.putAllAsync` method with batch size
Feature - added `RSet.random` method limited by count
Improvement - memory allocation optimization during ExecutorService task execution
Fixed - `keepAlive` is not being set
Fixed - Redisson can't resolve short DNS name
Fixed - Redisson shuts down executor it doesn't own
@ -232,7 +269,9 @@ Fixed - `UnsupportedOperationException` thrown when using Spring Actuator with r
### 06-Oct-2018 - versions 2.13.2 and 3.8.2 released
Feature - `RPermitExpirableSemaphore.updateLeaseTime` method added
Improvements - cluster state check
Fixed - DNS resolver fails to find valid DNS record
Fixed - MultiLock should attempt to release locks if request was sent successfully
Fixed - `RRateLimiter.tryAcquire` with timeout blocks forever
@ -246,6 +285,7 @@ Fixed - tomcat session replication in IN_MEMORY mode (thanks to Yasin Koyuncu)
### 18-Sep-2018 - versions 2.13.1 and 3.8.1 released
Feature - __RStream__ object added. Please refer to [documentation](https://github.com/redisson/redisson/wiki/7.-Distributed-collections#720-stream) for more details
Fixed - `failedSlaveCheckInterval` setting is not applied under some conditions
Fixed - `spring.factories` and `spring.provides` files added to Spring Boot module
Fixed - `pingConnectionInterval` setting affects re-connection process with password enabled setting
@ -260,8 +300,10 @@ Feature - __Spring Data Redis__ integration. Please refer to [documentation](htt
Feature - __Spring Boot Starter__ implementation. Please refer to [documentation](https://github.com/redisson/redisson/tree/master/redisson-spring-boot-starter#spring-boot-starter) for more details
Feature - `RBlockingDequeReactive` object added
Feature - `sharedSession` setting for Tomcat Session Manager. Appropriate solution for migration of EAR based application with multiple WARs hosted previously on WebLogic or other servers. Please refer to [documentation](https://github.com/redisson/redisson/tree/master/redisson-tomcat) for more details
Improvement - Redis request/response handling performance improvement
Improvement - CompositeIterator decoupled from CompositeIterable (thanks to [Pepe-Lu](https://github.com/Pepe-Lu))
Fixed - task scheduled with time more than 1 hour is not executed
Fixed - RScheduledExecutorService doesn't handle delayed tasks correctly
Fixed - `RMapCache` and `RSetCache` objects should implement `RDestroyable`
@ -279,13 +321,16 @@ Feature - `RScoredSortedSetReactive`, `RSetReactive`, `RListReactive` objects im
Feature - `RGeoReactive` object added
Feature - reactive version of FairLock added
Feature - `RRateLimiterReactive` object added
Improvement - RObjectReactive and RScoredSortedSetReactive interfaces synced with `RObjectAsync` and `RScoredSortedSetAsync`
Fixed - readonly command shouldn't be executed on master node used as slave
Fixed - connection is closed per command execution for master node used as slave in `readMode=MASTER_SLAVE`
Fixed - `RLiveObjectService` should use entityClass's classloader
### 16-Jul-2018 - versions 2.12.4 and 3.7.4 released
Feature - dump and restore methods added to `RObject` interface
Fixed - Redis response hangs if `RLiveObject` stored as nested object
Fixed - slow Redisson initialization in Sentinel
Fixed - missing PubSub messages when pingConnectionInterval setting is specified
@ -298,6 +343,7 @@ Fixed - `RHyperLogLog.addAll` method doesn't add all elements
### 27-Jun-2018 - versions 2.12.3 and 3.7.3 released
Feature - added `RKeys.getKeys` method with batch size
Feature - added `SnappyCodecV2` codec
Fixed - `SerializationCodec` doesn't support proxied classes
Fixed - NPE if `RScheduledExecutorService`'s task scheduled with cron expression for finite number of execution
Fixed - validation of cron expression parameter of `RScheduledExecutorService.schedule` method
@ -309,6 +355,7 @@ Fixed - `RRateLimiter.acquire` method throws NPE
### 14-Jun-2018 - versions 2.12.2 and 3.7.2 released
Feature - `RBatchOptions.executionMode` setting added. Please refer to [documentation](https://github.com/redisson/redisson/wiki/10.-additional-features#103-execution-batches-of-commands) for more details
Fixed - NPE in JCacheManager.close method
Fixed - ExecutorService tasks aren't reloaded properly
Fixed - removed unnecessary creation of HashMap instances in cluster mode
@ -327,8 +374,10 @@ Feature - ExecutorService task failover. Default failover interval is 60 seconds
Feature - `RScoredSortedSet.pollFirst` and `pollLast` methods with count parameter added
Feature - `RScoredSortedSet.pollFirst` and `pollLast` methods with timeout added
Feature - `RScoredSortedSet.pollFirstFromAny` and `pollLastFromAny` methods added
Improvement - `Node.time()` method returns `Time` object
Improvement - RListReactive, RMapCacheReactive, RSetCacheReactive and RSetReactive are up-to-date to Async interfaces
Fixed - setPingConnectionInterval is not propagated for single server configuration
Fixed - ClusterConnectionManager should use shared resolverGroup
Fixed - value can't be added to BloomFilter
@ -347,8 +396,10 @@ Feature - __XA Transaction API implementation__ Please refer to [documentation]
Feature - `RPermitExpirableSemaphoreReactive` object added
Feature - `RMap.fastReplace` method added
Feature - PING support for Pub/Sub connections
Improvement - `RBatch` object settings extracted as BatchOptions object
Improvement - `RBitSet.set` method should return boolean
Fixed - New IP discovery through DNS doesn't work for cluster mode
Fixed - replication for Tomcat Session Manager with `readMode=INMEMORY`
Fixed - `pingConnectionInterval` is not applied
@ -367,6 +418,7 @@ Fixed - `RedisTimeoutException` arise during blocking command execution on RBloc
Feature - `RKeys.copy` method added
Feature - `RObject.copy` method added
Feature - `RSetCache.getLock` method added
Fixed - `ClusterConnectionManager` throws `IllegalArgumentException`
Fixed - `CommandDecoder` doesn't remove command from commands queue when response was decoded with error
Fixed - `RSetMultimap.get()` doesn't create multimap entry in case of absence
@ -377,6 +429,7 @@ Fixed - hdel comand wasn't used during remote service task removal
### 27-Mar-2018 - versions 2.11.4 and 3.6.4 released
Feature - `RSet.getLock` method added
Fixed - race condition with load balancer node selection
Fixed - `READONLY can't write against a read only slave` error during failover
Fixed - NPE during failover in Sentinel mode
@ -388,6 +441,7 @@ Fixed - `ClassCastException` in `RListMultimapCache.removeAll` method
### 14-Mar-2018 - versions 2.11.3 and 3.6.3 released
Feature - DNS monitoring for Sentinel nodes
Fixed - Old/stale nodes not removed from NodesGroup
Fixed - CertificateException while connecting over SSL to Azure or AWS Elasticache config endpoint
Fixed - publish subscribe connections couldn't be resubscribed during failover
@ -410,8 +464,10 @@ Feature - new values added to `performanceMode` setting
Feature - `lockAsync` and `unlockAsync` methods added to `RedissonMultiLock`
Feature - `RMapCache.remainTimeToLive` method added
Feature - `Config.addressResolverGroupFactory` setting added (thanks @Hai Saadon)
Improvement - `UpdateMode.AFTER_REQUEST` update mode optimization in tomcat session manager
Improvement - removed ByteBuf object usage during collection iteration
Fixed - `Unable to send command` error under heavy load using Redisson PRO
Fixed - `expire`, `expireAt` and `clearExpire` commands aren't implemented properly for `RBloomFilter`, `RDelayedQueue`, `RFairLock`, `RLocalCachedMap` and `RPermitExpirableSemaphore` object
Fixed - Redis clients duplication during discovering ip change of hostname
@ -425,6 +481,7 @@ Feature - `rangeTailReversed`, `rangeHeadReversed` and `rangeReversed` methods a
Feature - `RBucketsAsync` interface added
Feature - `scanInterval` setting added for Sentinel mode
Feature - `RLocalCachedMap.clearLocalCache` method added
Fixed - remove `hset` command invocation during `RMapCache` entry loading
Fixed - buffer leak in `replace` and `remove` methods of `RLocalCachedMap` object
Fixed - `RRemoteService` object throws NPE
@ -451,8 +508,10 @@ Feature - __`DoubleAdder` object added__ Please read [documentation](https://git
Feature - `RBucket.getAndDelete`, `RAtomicLong.getAndDelete` and `RAtomicDouble.getAndDelete` methods added
Feature - __`RAtomicDoubleReactive` object added__
Feature - `RPriorityQueue.pollLastAndOfferFirstTo` method added
Improvement - support single config endpoint node for cluster mode
Improvement - hash functions replaced with https://github.com/google/highwayhash
Fixed - JDK 1.6+ compatibility for RemoteService
Fixed - `setDnsMonitoringInterval(-1)` doesn't disable DNS monitoring
Fixed - `RLocalCachedMap.putAll` gets stuck if map passed as parameter contains > 10000 elements
@ -467,6 +526,7 @@ Fixed - JsonJacksonCoded shouldn't override provided objectMapper settings (than
### 25-Dec-2017 - versions 2.10.7 and 3.5.7 released
Feature - __`RClusteredBitSet` object added__ Please read [documentation](https://github.com/redisson/redisson/wiki/6.-Distributed-objects/#641-bitset-data-partitioning) for more details
Improvement - Memory consumption optimization. `RExecutorFuture` and `RScheduledFuture` shouldn't be tracked if they weren't stored
Improvement - Config settings `codecProvider` and `redissonReferenceEnabled` renamed to `referenceCodecProvider` and `referenceEnabled`
Fixed - `RedissonRedLock` couldn't be locked in some cases
@ -480,7 +540,9 @@ Feature - `RSetMultimapReactive` object added
Feature - `RListMultimapReactive` object added
Feature - `ReconnectionStrategy` and `SyncStrategy` added to `LocalCachedMapOptions`
Feature - `pingConnectionInterval` setting added
Improvement - added sync on key for `JCache.invoke` method
Fixed - arguments encoding for `RScript`
Fixed - `MapLoader` hangs if loaded value is null
Fixed - OutOfMemory during `RExecutorService` usage
@ -501,9 +563,11 @@ Fixed - handling connection to Redis nodes returned to online state
Feature - Added pingConnection, keepAlive, tcpNoDelay settings
Feature - Slaves synchronization support for `RBatch`/`RBatchReactive` objects
Improvement - Data encoding should be executed on client thread only
Improvement - Handling Redis redirect optimization
Improvement - Better collection handling for RedissonReference (thanks to Rui Gu)
Fixed - `RedisLoadingException` handling during re-connection process
Fixed - `RedisClient` can't be shutdown properly
Fixed - timeout drift for `RFairLock`
@ -518,8 +582,10 @@ Fixed - `RMapCache.getAll` doesn't support large keySet
### 28-Sep-2017 - versions 2.10.4 and 3.5.4 released
Feature - added `maxSize` setting for `SpringCacheManager`
Feature - allow `LiveObjectService` to work with classes that inherit from REntities (thanks to @sdjacobs)
Improvement - `RMapCache` entires eviction process optimized
Improvement - handling of slave down process
Fixed - operation on slave coldn't be executed after its restart (thanks to @xavierfacq)
Fixed - `ArrayIndexOutOfBoundsException` in RedissonSessionRepository
Fixed - storing Live Objects in Redisson's collection objects (thanks to Rui Gu)
@ -552,6 +618,7 @@ Fixed - connection could be in closed state during reconnection process for bloc
Feature - DNS monitoring support for Sentinel, Master/Slave and Replicated mode
Feature - `org.redisson.codec.CompositeCodec` added
Feature - added `readMode` property for Tomcat RedissonSessionManager
Fixed - `RMapCache.putAll` couldn't handle map with entries amount larger than 5000
Fixed - master entry should be shutdown in slaveConnectionPool during master change process
Fixed - Redisson's Tomcat Session attributes should be read first to avoid invalidated session exception
@ -566,6 +633,7 @@ Feature - __`RLockReactive` object added__
Feature - __`RReadWriteLockReactive` object added__
Feature - __`RSemaphoreReactive` object added__
Feature - `unlink`, `flushdbParallel`, `flushallParallel` methods added
Fixed - ContextClassLoader should be used by Redisson Codec for Tomcat session's object serialization
Fixed - Spring Cache `NullValue` does not implement Serializable
Fixed - `RLocalCachedMap` doesn't work with non-json and non-binary codecs
@ -584,8 +652,10 @@ Feature - `RExecutorService` should return RExecutorFuture object with taskId
Feature - added `RList.get` method to load elements in a batch
Feature - ability to submit few tasks atomically (in batch) through `RExecutorService` interface
Feature - [Config.keepPubSubOrder](https://github.com/redisson/redisson/wiki/2.-Configuration#keeppubsuborder) setting added
Improvement - make `RMapReactive` and `RMapCacheReactive` interfaces match with `RMap` and `RMapCache`
Improvement - `RLexSortedSet` should extend `RSortedSet`
Fixed - connection listener is not invoked in some cases
Fixed - `RMapCache` `remove`, `put`, `putIfAbsent` and `replace` methods aren't respect entry expiration
Fixed - `SCAN` command should be used in `RKeys.deleteByPattern` method
@ -614,8 +684,10 @@ Feature - `RedissonCacheManager.setAllowNullValues` method added
Feature - `RedissonSpringCacheManager.setCacheNames` method added
Feature - Map Entry listeners support added for `RMapCache` object
Feature - `Config.lockWatchdogTimeout` parameter added
Improvement - NPE checking for key and value added for RedissonMapCache
Improvement - `RKeys.deleteByPatternAsync` uses scan command
Fixed - `RBoundedBlockingQueue.pollAsync` blocks if timeout is less than 1 second
Fixed - unlocking of nested `RReadWriteLock.readLock` deletes current acquired `RReadWriteLock.writeLock`
Fixed - wrong null values checking in RDelayedQueue methods
@ -628,6 +700,7 @@ Fixed - JSR107 cache implementation should throw `javax.cache.CacheException` in
Feature - __Dropwizard metrics integration__ More details [here](https://github.com/redisson/redisson/wiki/14.-Integration-with-frameworks#147-dropwizard-metrics)
Feature - `RLocalCachedMap.preloadCache` method added (thanks to Steve Draper)
Feature - `RGeo.radiusStoreTo` methods added (thanks to Cory Sherman)
Fixed - NoClassDefFoundError exception during using `redisson-all` module
### 27-Apr-2017 - versions 2.9.1 and 3.4.1 released
@ -647,7 +720,9 @@ Feature - `RMapCache.fastPutIfAbsent` with ttl added (thanks to Dobi)
Feature - `EvictionPolicy.WEAK` added for `RLocalCachedMap`
Feature - `LocalCachedMapOptions.invalidationPolicy` introduced for `RLocalCachedMap`
Feature - `expire`, `expireAt`, `move`, `migrate`, `clearExpire`, `renamenx`, `rename`, `remainTimeToLive` methods added to RKey
Improvement - `EvictionPolicy.LRU` optimization for `RLocalCachedMap`
Fixed - `RTopic.onSubscribe` should be invoked after failover process
Fixed - Spring boot with redisson 3.3.2 fails without optional actuator dependency (thanks to Rick Perkowski)
Fixed - `RedissonCacheMap.putIfAbsentAsync` doesn't take in account ttl and minIdleTime params (thanks to Dobi)
@ -668,6 +743,7 @@ Feature - autoconfigured Spring Boot CacheStatisticsProvider implementation (tha
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
@ -681,7 +757,9 @@ Fixed - `Config.fromJson(file)` method, throws StackOverflowError
Feature - Cache with SoftReference support added for `RLocalCachedMap`
Feature - `Config.subscriptionMode` setting added
Improvement - errors handling during RBatch execution
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)
@ -695,7 +773,9 @@ Feature - __`RPriorityQueue` object added__ More details [here](https://github.c
Feature - __`RPriorityDeque` object added__ More details [here](https://github.com/redisson/redisson/wiki/7.-distributed-collections/#716-priority-deque)
Feature - `removeAllListeners` and `removeListener` by instance methods added for `RTopic` and `RPatternTopic`
Feature - `RLockAsync` interface added
Improvement - `RRemoteService` is now able to support method overload
Fixed - `RLocalCachedMap` is not Redis cluster compatible
Fixed - cascade slaves are not supported in cluster mode
Fixed - shutdown checking during master change state check added
@ -704,6 +784,7 @@ Fixed - master isn't checked during new slave discovery in Sentinel mode
### 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`
Fixed - AssertionError during Redisson shutdown
Fixed - `RReadWriteLock.readLock` couldn't be acquired by same thread which has already acquired `writeLock`
@ -719,8 +800,10 @@ 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)
Feature - `RMap.getLock(key)` and `RMultimap.getLock(key)` methods added
Improvement - `RedissonSpringCacheManager` constructor with Redisson instance only added
Improvement - `CronSchedule` moved to `org.redisson.api` package
Fixed - RedissonBaseIterator.hasNext() doesn't return false in some cases
Fixed - NoSuchFieldError exception in `redisson-tomcat` modules
Fixed - ConnectionPool size not respected during redirect of cluster request
@ -733,6 +816,7 @@ Fixed - CommandDecoder should print all replay body on error
Feature - `RList`, `RSet` and `RScoredSortedSet` implements `RSortable` interface with SORT command support
Feature - `NodeAsync` interface
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
@ -743,6 +827,7 @@ Url format used in config files __has changed__. For example:
"//127.0.0.1:6739" now should be written as "redis://127.0.0.1:6739"
Feature - `RSet.removeRandom` allows to remove several members at once
Fixed - exceptions during shutdown
Fixed - redis url couldn't contain underscore in host name
Fixed - IndexOutOfBoundsException during response decoding

@ -51,7 +51,7 @@ JDK compatibility: 1.8 - 12, Android
* Supports OSGi
* Supports SSL
* Supports many popular codecs ([Jackson JSON](https://github.com/FasterXML/jackson), [Avro](http://avro.apache.org/), [Smile](http://wiki.fasterxml.com/SmileFormatSpec), [CBOR](http://cbor.io/), [MsgPack](http://msgpack.org/), [Kryo](https://github.com/EsotericSoftware/kryo), [Amazon Ion](https://amzn.github.io/ion-docs/), [FST](https://github.com/RuedigerMoeller/fast-serialization), [LZ4](https://github.com/jpountz/lz4-java), [Snappy](https://github.com/xerial/snappy-java) and JDK Serialization)
* With over 1800 unit tests
* With over 2000 unit tests
<!--
Used by
================================
@ -103,12 +103,12 @@ Used by
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.10.7</version>
<version>3.11.0</version>
</dependency>
#### Gradle
compile 'org.redisson:redisson:3.10.7'
compile 'org.redisson:redisson:3.11.0'
#### Java
@ -166,8 +166,8 @@ Consider __[Redisson PRO](https://redisson.pro)__ version for advanced features
## Downloads
[Redisson 3.10.7](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=3.10.7&e=jar),
[Redisson node 3.10.7](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.10.7&e=jar)
[Redisson 3.11.0](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=3.11.0&e=jar),
[Redisson node 3.11.0](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.11.0&e=jar)
## FAQs

@ -3,7 +3,7 @@
<groupId>org.redisson</groupId>
<artifactId>redisson-parent</artifactId>
<version>3.11.0.1507-SNAPSHOT</version>
<version>3.11.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Redisson</name>
@ -22,13 +22,14 @@
<test.source.version>1.8</test.source.version>
<release.version>8</release.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<checkstyle.skip>true</checkstyle.skip>
</properties>
<scm>
<url>scm:git:git@github.com:redisson/redisson.git</url>
<connection>scm:git:git@github.com:redisson/redisson.git</connection>
<developerConnection>scm:git:git@github.com:redisson/redisson.git</developerConnection>
<tag>redisson-3.11.0.1507-SNAPSHOT</tag>
<tag>HEAD</tag>
</scm>
<prerequisites>
@ -56,19 +57,6 @@
</developer>
</developers>
<distributionManagement>
<repository>
<id>deployment</id>
<name>Internal Releases</name>
<url>http://maven.bobdev.pgi.com:9090/nexus/content/repositories/release/</url>
</repository>
<snapshotRepository>
<id>deployment</id>
<name>Internal snapshots</name>
<url>http://maven.bobdev.pgi.com:9090/nexus/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>
<modules>
<module>redisson</module>
<module>redisson-all</module>
@ -161,7 +149,7 @@
<testSourceDirectory>${basedir}/src/test/java</testSourceDirectory>
<plugins>
<!--
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
@ -173,7 +161,7 @@
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
-->
<!--
<plugin>
<groupId>com.github.spotbugs</groupId>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-parent</artifactId>
<version>3.11.0.1507-SNAPSHOT</version>
<version>3.11.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
@ -26,7 +26,7 @@
<url>scm:git:git@github.com:mrniko/redisson.git</url>
<connection>scm:git:git@github.com:mrniko/redisson.git</connection>
<developerConnection>scm:git:git@github.com:mrniko/redisson.git</developerConnection>
<tag>redisson-3.11.0.1507-SNAPSHOT</tag>
<tag>redisson-parent-0.9.0</tag>
</scm>
<prerequisites>

@ -1,7 +1,7 @@
# Redis based Hibernate Cache implementation
Implements [Hibernate 2nd level Cache](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#caching) provider based on Redis.
Supports all Hibernate cache strategies: `READ_ONLY`, `NONSTRICT_READ_WRITE`, `READ_WRITE` and `TRANSACTIONAL`.
Implements [Hibernate 2nd level Cache](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#caching) provider based on Redis.
Supports all Hibernate cache strategies: `READ_ONLY`, `NONSTRICT_READ_WRITE`, `READ_WRITE` and `TRANSACTIONAL`.
It's recommended to use FST or Snappy as [codec](https://github.com/redisson/redisson/wiki/4.-data-serialization).
Compatible with Hibernate 4.x, 5.x. (5.3.0, 5.3.1 and 5.3.2 versions aren't supported)
@ -38,7 +38,7 @@ Maven
<artifactId>redisson-hibernate-52</artifactId>
<!-- for Hibernate v5.3.3+ - v5.4.x -->
<artifactId>redisson-hibernate-53</artifactId>
<version>3.10.7</version>
<version>3.11.0</version>
</dependency>
```
@ -46,13 +46,13 @@ Gradle
```groovy
// for Hibernate v4.x
compile 'org.redisson:redisson-hibernate-4:3.10.7'
compile 'org.redisson:redisson-hibernate-4:3.11.0'
// for Hibernate v5.0.x - v5.1.x
compile 'org.redisson:redisson-hibernate-5:3.10.7'
compile 'org.redisson:redisson-hibernate-5:3.11.0'
// for Hibernate v5.2.x
compile 'org.redisson:redisson-hibernate-52:3.10.7'
compile 'org.redisson:redisson-hibernate-52:3.11.0'
// for Hibernate v5.3.3+ - v5.4.x
compile 'org.redisson:redisson-hibernate-53:3.10.7'
compile 'org.redisson:redisson-hibernate-53:3.11.0'
```
### 2. Specify hibernate cache settings

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

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-hibernate</artifactId>
<version>3.11.0.1507-SNAPSHOT</version>
<version>3.11.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-hibernate</artifactId>
<version>3.11.0.1507-SNAPSHOT</version>
<version>3.11.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-hibernate</artifactId>
<version>3.11.0.1507-SNAPSHOT</version>
<version>3.11.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-hibernate</artifactId>
<version>3.11.0.1507-SNAPSHOT</version>
<version>3.11.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -16,14 +16,14 @@ Maven
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.10.7</version>
<version>3.11.0</version>
</dependency>
```
Gradle
```groovy
compile 'org.redisson:redisson-spring-boot-starter:3.10.7'
compile 'org.redisson:redisson-spring-boot-starter:3.11.0'
```
### 2. Add settings into `application.settings` file

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

@ -25,7 +25,7 @@ Maven
<artifactId>redisson-spring-data-20</artifactId>
<!-- for Spring Data Redis v.2.1.x -->
<artifactId>redisson-spring-data-21</artifactId>
<version>3.10.7</version>
<version>3.11.0</version>
</dependency>
```
@ -33,15 +33,15 @@ Gradle
```groovy
// for Spring Data Redis v.1.6.x
compile 'org.redisson:redisson-spring-data-16:3.10.7'
compile 'org.redisson:redisson-spring-data-16:3.11.0'
// for Spring Data Redis v.1.7.x
compile 'org.redisson:redisson-spring-data-17:3.10.7'
compile 'org.redisson:redisson-spring-data-17:3.11.0'
// for Spring Data Redis v.1.8.x
compile 'org.redisson:redisson-spring-data-18:3.10.7'
compile 'org.redisson:redisson-spring-data-18:3.11.0'
// for Spring Data Redis v.2.0.x
compile 'org.redisson:redisson-spring-data-20:3.10.7'
compile 'org.redisson:redisson-spring-data-20:3.11.0'
// for Spring Data Redis v.2.1.x
compile 'org.redisson:redisson-spring-data-21:3.10.7'
compile 'org.redisson:redisson-spring-data-21:3.11.0'
```
### 2. Register `RedissonConnectionFactory` in Spring context

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

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data</artifactId>
<version>3.11.0.1507-SNAPSHOT</version>
<version>3.11.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data</artifactId>
<version>3.11.0.1507-SNAPSHOT</version>
<version>3.11.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data</artifactId>
<version>3.11.0.1507-SNAPSHOT</version>
<version>3.11.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data</artifactId>
<version>3.11.0.1507-SNAPSHOT</version>
<version>3.11.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data</artifactId>
<version>3.11.0.1507-SNAPSHOT</version>
<version>3.11.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -26,7 +26,7 @@ Add `RedissonSessionManager` into `tomcat/conf/context.xml`
* `REDIS` - stores attributes into Redis only. Default mode.
<br/>
`broadcastSessionEvents` - if `true` then `sessionCreated` and `sessionDestroyed` events broadcasted across all Tomcat instances and causes all registered HttpSessionListeners to be triggered. Default is `false`.
`broadcastSessionEvents` - if `true` then `sessionCreated` and `sessionDestroyed` events are broadcasted across all Tomcat instances and cause all registered HttpSessionListeners to be triggered. Default is `false`.
`updateMode` - Session attributes update mode. Two modes are available:
* `DEFAULT` - session attributes are stored into Redis only through setAttribute method. Default mode.
@ -66,14 +66,14 @@ Add `RedissonSessionManager` into `tomcat/conf/context.xml`
### 2. Copy two jars into `TOMCAT_BASE/lib` directory:
[redisson-all-3.10.7.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.10.7&e=jar)
[redisson-all-3.11.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.11.0&e=jar)
for Tomcat 6.x
[redisson-tomcat-6-3.10.7.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-6&v=3.10.7&e=jar)
[redisson-tomcat-6-3.10.7.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-6&v=3.11.0&e=jar)
for Tomcat 7.x
[redisson-tomcat-7-3.10.7.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-7&v=3.10.7&e=jar)
[redisson-tomcat-7-3.11.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-7&v=3.11.0&e=jar)
for Tomcat 8.x
[redisson-tomcat-8-3.10.7.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-8&v=3.10.7&e=jar)
[redisson-tomcat-8-3.11.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-8&v=3.11.0&e=jar)
for Tomcat 9.x
[redisson-tomcat-9-3.10.7.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-9&v=3.10.7&e=jar)
[redisson-tomcat-9-3.11.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-9&v=3.11.0&e=jar)

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

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

@ -29,6 +29,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.apache.catalina.session.StandardSession;
import org.redisson.api.RSet;
import org.redisson.api.RMap;
import org.redisson.api.RTopic;
import org.redisson.tomcat.RedissonSessionManager.ReadMode;
@ -48,10 +49,15 @@ public class RedissonSession extends StandardSession {
private static final String MAX_INACTIVE_INTERVAL_ATTR = "session:maxInactiveInterval";
private static final String LAST_ACCESSED_TIME_ATTR = "session:lastAccessedTime";
private static final String CREATION_TIME_ATTR = "session:creationTime";
private static final String IS_EXPIRATION_LOCKED = "session:isExpirationLocked";
public static final Set<String> ATTRS = new HashSet<String>(Arrays.asList(IS_NEW_ATTR, IS_VALID_ATTR,
THIS_ACCESSED_TIME_ATTR, MAX_INACTIVE_INTERVAL_ATTR, LAST_ACCESSED_TIME_ATTR, CREATION_TIME_ATTR));
public static final Set<String> ATTRS = new HashSet<String>(Arrays.asList(
IS_NEW_ATTR, IS_VALID_ATTR,
THIS_ACCESSED_TIME_ATTR, MAX_INACTIVE_INTERVAL_ATTR,
LAST_ACCESSED_TIME_ATTR, CREATION_TIME_ATTR, IS_EXPIRATION_LOCKED
));
private boolean isExpirationLocked;
private boolean loaded;
private final RedissonSessionManager redissonManager;
private final Map<String, Object> attrs;
@ -147,6 +153,10 @@ public class RedissonSession extends StandardSession {
}
if (broadcastSessionEvents) {
RSet<String> set = redissonManager.getNotifiedNodes(id);
set.add(redissonManager.getNodeId());
set.expire(60, TimeUnit.SECONDS);
map.fastPut(IS_EXPIRATION_LOCKED, true);
map.expire(60, TimeUnit.SECONDS);
} else {
map.delete();
@ -190,6 +200,9 @@ public class RedissonSession extends StandardSession {
}
protected void expireSession() {
if (isExpirationLocked) {
return;
}
if (maxInactiveInterval >= 0) {
map.expire(maxInactiveInterval + 60, TimeUnit.SECONDS);
}
@ -307,7 +320,10 @@ public class RedissonSession extends StandardSession {
newMap.put(MAX_INACTIVE_INTERVAL_ATTR, maxInactiveInterval);
newMap.put(IS_VALID_ATTR, isValid);
newMap.put(IS_NEW_ATTR, isNew);
if (broadcastSessionEvents) {
newMap.put(IS_EXPIRATION_LOCKED, isExpirationLocked);
}
if (attrs != null) {
for (Entry<String, Object> entry : attrs.entrySet()) {
newMap.put(entry.getKey(), entry.getValue());
@ -356,6 +372,10 @@ public class RedissonSession extends StandardSession {
if (isNew != null) {
this.isNew = isNew;
}
Boolean isExpirationLocked = (Boolean) attrs.remove(IS_EXPIRATION_LOCKED);
if (isExpirationLocked != null) {
this.isExpirationLocked = isExpirationLocked;
}
for (Entry<String, Object> entry : attrs.entrySet()) {
super.setAttribute(entry.getKey(), entry.getValue(), false);

@ -37,6 +37,7 @@ import org.apache.catalina.util.LifecycleSupport;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.redisson.Redisson;
import org.redisson.api.RSet;
import org.redisson.api.RMap;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
@ -166,6 +167,12 @@ public class RedissonSessionManager extends ManagerBase implements Lifecycle {
return session;
}
public RSet<String> getNotifiedNodes(String sessionId) {
String separator = keyPrefix == null || keyPrefix.isEmpty() ? "" : ":";
String name = keyPrefix + separator + "redisson:tomcat_notified_nodes:" + sessionId;
return redisson.getSet(name, StringCodec.INSTANCE);
}
public RMap<String, Object> getMap(String sessionId) {
String separator = keyPrefix == null || keyPrefix.isEmpty() ? "" : ":";
String name = keyPrefix + separator + "redisson:tomcat_session:" + sessionId;
@ -194,7 +201,7 @@ public class RedissonSessionManager extends ManagerBase implements Lifecycle {
log.error("Can't read session object by id: " + id, e);
}
if (attrs.isEmpty()) {
if (attrs.isEmpty() || (broadcastSessionEvents && getNotifiedNodes(id).contains(nodeId))) {
log.info("Session " + id + " can't be found");
return null;
}
@ -322,6 +329,8 @@ public class RedissonSessionManager extends ManagerBase implements Lifecycle {
throw new IllegalStateException("Unable to find session: " + msg.getSessionId());
}
s.expire();
RSet<String> set = getNotifiedNodes(msg.getSessionId());
set.add(nodeId);
}
}

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

@ -30,6 +30,7 @@ import java.util.concurrent.TimeUnit;
import org.apache.catalina.session.StandardSession;
import org.redisson.api.RMap;
import org.redisson.api.RSet;
import org.redisson.api.RTopic;
import org.redisson.tomcat.RedissonSessionManager.ReadMode;
import org.redisson.tomcat.RedissonSessionManager.UpdateMode;
@ -48,10 +49,15 @@ public class RedissonSession extends StandardSession {
private static final String MAX_INACTIVE_INTERVAL_ATTR = "session:maxInactiveInterval";
private static final String LAST_ACCESSED_TIME_ATTR = "session:lastAccessedTime";
private static final String CREATION_TIME_ATTR = "session:creationTime";
private static final String IS_EXPIRATION_LOCKED = "session:isExpirationLocked";
public static final Set<String> ATTRS = new HashSet<String>(Arrays.asList(IS_NEW_ATTR, IS_VALID_ATTR,
THIS_ACCESSED_TIME_ATTR, MAX_INACTIVE_INTERVAL_ATTR, LAST_ACCESSED_TIME_ATTR, CREATION_TIME_ATTR));
public static final Set<String> ATTRS = new HashSet<String>(Arrays.asList(
IS_NEW_ATTR, IS_VALID_ATTR,
THIS_ACCESSED_TIME_ATTR, MAX_INACTIVE_INTERVAL_ATTR,
LAST_ACCESSED_TIME_ATTR, CREATION_TIME_ATTR, IS_EXPIRATION_LOCKED
));
private boolean isExpirationLocked;
private boolean loaded;
private final RedissonSessionManager redissonManager;
private final Map<String, Object> attrs;
@ -147,6 +153,10 @@ public class RedissonSession extends StandardSession {
}
if (broadcastSessionEvents) {
RSet<String> set = redissonManager.getNotifiedNodes(id);
set.add(redissonManager.getNodeId());
set.expire(60, TimeUnit.SECONDS);
map.fastPut(IS_EXPIRATION_LOCKED, true);
map.expire(60, TimeUnit.SECONDS);
} else {
map.delete();
@ -190,6 +200,9 @@ public class RedissonSession extends StandardSession {
}
protected void expireSession() {
if (isExpirationLocked) {
return;
}
if (maxInactiveInterval >= 0) {
map.expire(maxInactiveInterval + 60, TimeUnit.SECONDS);
}
@ -307,6 +320,9 @@ public class RedissonSession extends StandardSession {
newMap.put(MAX_INACTIVE_INTERVAL_ATTR, maxInactiveInterval);
newMap.put(IS_VALID_ATTR, isValid);
newMap.put(IS_NEW_ATTR, isNew);
if (broadcastSessionEvents) {
newMap.put(IS_EXPIRATION_LOCKED, isExpirationLocked);
}
if (attrs != null) {
for (Entry<String, Object> entry : attrs.entrySet()) {
@ -356,6 +372,10 @@ public class RedissonSession extends StandardSession {
if (isNew != null) {
this.isNew = isNew;
}
Boolean isExpirationLocked = (Boolean) attrs.remove(IS_EXPIRATION_LOCKED);
if (isExpirationLocked != null) {
this.isExpirationLocked = isExpirationLocked;
}
for (Entry<String, Object> entry : attrs.entrySet()) {
super.setAttribute(entry.getKey(), entry.getValue(), false);

@ -35,6 +35,7 @@ import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.redisson.Redisson;
import org.redisson.api.RMap;
import org.redisson.api.RSet;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.api.listener.MessageListener;
@ -144,6 +145,12 @@ public class RedissonSessionManager extends ManagerBase {
return session;
}
public RSet<String> getNotifiedNodes(String sessionId) {
String separator = keyPrefix == null || keyPrefix.isEmpty() ? "" : ":";
String name = keyPrefix + separator + "redisson:tomcat_notified_nodes:" + sessionId;
return redisson.getSet(name, StringCodec.INSTANCE);
}
public RMap<String, Object> getMap(String sessionId) {
String separator = keyPrefix == null || keyPrefix.isEmpty() ? "" : ":";
String name = keyPrefix + separator + "redisson:tomcat_session:" + sessionId;
@ -172,7 +179,7 @@ public class RedissonSessionManager extends ManagerBase {
log.error("Can't read session object by id: " + id, e);
}
if (attrs.isEmpty()) {
if (attrs.isEmpty() || (broadcastSessionEvents && getNotifiedNodes(id).contains(nodeId))) {
log.info("Session " + id + " can't be found");
return null;
}
@ -302,6 +309,8 @@ public class RedissonSessionManager extends ManagerBase {
throw new IllegalStateException("Unable to find session: " + msg.getSessionId());
}
s.expire();
RSet<String> set = getNotifiedNodes(msg.getSessionId());
set.add(nodeId);
}
}

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

@ -29,6 +29,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.apache.catalina.session.StandardSession;
import org.redisson.api.RSet;
import org.redisson.api.RMap;
import org.redisson.api.RTopic;
import org.redisson.tomcat.RedissonSessionManager.ReadMode;
@ -48,10 +49,15 @@ public class RedissonSession extends StandardSession {
private static final String MAX_INACTIVE_INTERVAL_ATTR = "session:maxInactiveInterval";
private static final String LAST_ACCESSED_TIME_ATTR = "session:lastAccessedTime";
private static final String CREATION_TIME_ATTR = "session:creationTime";
private static final String IS_EXPIRATION_LOCKED = "session:isExpirationLocked";
public static final Set<String> ATTRS = new HashSet<String>(Arrays.asList(IS_NEW_ATTR, IS_VALID_ATTR,
THIS_ACCESSED_TIME_ATTR, MAX_INACTIVE_INTERVAL_ATTR, LAST_ACCESSED_TIME_ATTR, CREATION_TIME_ATTR));
public static final Set<String> ATTRS = new HashSet<String>(Arrays.asList(
IS_NEW_ATTR, IS_VALID_ATTR,
THIS_ACCESSED_TIME_ATTR, MAX_INACTIVE_INTERVAL_ATTR,
LAST_ACCESSED_TIME_ATTR, CREATION_TIME_ATTR, IS_EXPIRATION_LOCKED
));
private boolean isExpirationLocked;
private boolean loaded;
private final RedissonSessionManager redissonManager;
private final Map<String, Object> attrs;
@ -147,6 +153,10 @@ public class RedissonSession extends StandardSession {
}
if (broadcastSessionEvents) {
RSet<String> set = redissonManager.getNotifiedNodes(id);
set.add(redissonManager.getNodeId());
set.expire(60, TimeUnit.SECONDS);
map.fastPut(IS_EXPIRATION_LOCKED, true);
map.expire(60, TimeUnit.SECONDS);
} else {
map.delete();
@ -190,6 +200,9 @@ public class RedissonSession extends StandardSession {
}
protected void expireSession() {
if (isExpirationLocked) {
return;
}
if (maxInactiveInterval >= 0) {
map.expire(maxInactiveInterval + 60, TimeUnit.SECONDS);
}
@ -307,6 +320,9 @@ public class RedissonSession extends StandardSession {
newMap.put(MAX_INACTIVE_INTERVAL_ATTR, maxInactiveInterval);
newMap.put(IS_VALID_ATTR, isValid);
newMap.put(IS_NEW_ATTR, isNew);
if (broadcastSessionEvents) {
newMap.put(IS_EXPIRATION_LOCKED, isExpirationLocked);
}
if (attrs != null) {
for (Entry<String, Object> entry : attrs.entrySet()) {
@ -356,6 +372,10 @@ public class RedissonSession extends StandardSession {
if (isNew != null) {
this.isNew = isNew;
}
Boolean isExpirationLocked = (Boolean) attrs.remove(IS_EXPIRATION_LOCKED);
if (isExpirationLocked != null) {
this.isExpirationLocked = isExpirationLocked;
}
for (Entry<String, Object> entry : attrs.entrySet()) {
super.setAttribute(entry.getKey(), entry.getValue(), false);

@ -33,6 +33,7 @@ import org.apache.catalina.session.ManagerBase;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.redisson.Redisson;
import org.redisson.api.RSet;
import org.redisson.api.RMap;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
@ -142,6 +143,12 @@ public class RedissonSessionManager extends ManagerBase {
}
return session;
}
public RSet<String> getNotifiedNodes(String sessionId) {
String separator = keyPrefix == null || keyPrefix.isEmpty() ? "" : ":";
String name = keyPrefix + separator + "redisson:tomcat_notified_nodes:" + sessionId;
return redisson.getSet(name, StringCodec.INSTANCE);
}
public RMap<String, Object> getMap(String sessionId) {
String separator = keyPrefix == null || keyPrefix.isEmpty() ? "" : ":";
@ -171,7 +178,7 @@ public class RedissonSessionManager extends ManagerBase {
log.error("Can't read session object by id: " + id, e);
}
if (attrs.isEmpty()) {
if (attrs.isEmpty() || (broadcastSessionEvents && getNotifiedNodes(id).contains(nodeId))) {
log.info("Session " + id + " can't be found");
return null;
}
@ -300,6 +307,8 @@ public class RedissonSessionManager extends ManagerBase {
throw new IllegalStateException("Unable to find session: " + msg.getSessionId());
}
s.expire();
RSet<String> set = getNotifiedNodes(msg.getSessionId());
set.add(nodeId);
}
}

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

@ -29,6 +29,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.apache.catalina.session.StandardSession;
import org.redisson.api.RSet;
import org.redisson.api.RMap;
import org.redisson.api.RTopic;
import org.redisson.tomcat.RedissonSessionManager.ReadMode;
@ -48,10 +49,15 @@ public class RedissonSession extends StandardSession {
private static final String MAX_INACTIVE_INTERVAL_ATTR = "session:maxInactiveInterval";
private static final String LAST_ACCESSED_TIME_ATTR = "session:lastAccessedTime";
private static final String CREATION_TIME_ATTR = "session:creationTime";
private static final String IS_EXPIRATION_LOCKED = "session:isExpirationLocked";
public static final Set<String> ATTRS = new HashSet<String>(Arrays.asList(IS_NEW_ATTR, IS_VALID_ATTR,
THIS_ACCESSED_TIME_ATTR, MAX_INACTIVE_INTERVAL_ATTR, LAST_ACCESSED_TIME_ATTR, CREATION_TIME_ATTR));
public static final Set<String> ATTRS = new HashSet<String>(Arrays.asList(
IS_NEW_ATTR, IS_VALID_ATTR,
THIS_ACCESSED_TIME_ATTR, MAX_INACTIVE_INTERVAL_ATTR,
LAST_ACCESSED_TIME_ATTR, CREATION_TIME_ATTR, IS_EXPIRATION_LOCKED
));
private boolean isExpirationLocked;
private boolean loaded;
private final RedissonSessionManager redissonManager;
private final Map<String, Object> attrs;
@ -147,6 +153,10 @@ public class RedissonSession extends StandardSession {
}
if (broadcastSessionEvents) {
RSet<String> set = redissonManager.getNotifiedNodes(id);
set.add(redissonManager.getNodeId());
set.expire(60, TimeUnit.SECONDS);
map.fastPut(IS_EXPIRATION_LOCKED, true);
map.expire(60, TimeUnit.SECONDS);
} else {
map.delete();
@ -190,6 +200,9 @@ public class RedissonSession extends StandardSession {
}
protected void expireSession() {
if (isExpirationLocked) {
return;
}
if (maxInactiveInterval >= 0) {
map.expire(maxInactiveInterval + 60, TimeUnit.SECONDS);
}
@ -307,6 +320,9 @@ public class RedissonSession extends StandardSession {
newMap.put(MAX_INACTIVE_INTERVAL_ATTR, maxInactiveInterval);
newMap.put(IS_VALID_ATTR, isValid);
newMap.put(IS_NEW_ATTR, isNew);
if (broadcastSessionEvents) {
newMap.put(IS_EXPIRATION_LOCKED, isExpirationLocked);
}
if (attrs != null) {
for (Entry<String, Object> entry : attrs.entrySet()) {
@ -356,6 +372,10 @@ public class RedissonSession extends StandardSession {
if (isNew != null) {
this.isNew = isNew;
}
Boolean isExpirationLocked = (Boolean) attrs.remove(IS_EXPIRATION_LOCKED);
if (isExpirationLocked != null) {
this.isExpirationLocked = isExpirationLocked;
}
for (Entry<String, Object> entry : attrs.entrySet()) {
super.setAttribute(entry.getKey(), entry.getValue(), false);

@ -33,6 +33,7 @@ import org.apache.catalina.session.ManagerBase;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.redisson.Redisson;
import org.redisson.api.RSet;
import org.redisson.api.RMap;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
@ -142,6 +143,12 @@ public class RedissonSessionManager extends ManagerBase {
}
return session;
}
public RSet<String> getNotifiedNodes(String sessionId) {
String separator = keyPrefix == null || keyPrefix.isEmpty() ? "" : ":";
String name = keyPrefix + separator + "redisson:tomcat_notified_nodes:" + sessionId;
return redisson.getSet(name, StringCodec.INSTANCE);
}
public RMap<String, Object> getMap(String sessionId) {
String separator = keyPrefix == null || keyPrefix.isEmpty() ? "" : ":";
@ -171,7 +178,7 @@ public class RedissonSessionManager extends ManagerBase {
log.error("Can't read session object by id: " + id, e);
}
if (attrs.isEmpty()) {
if (attrs.isEmpty() || (broadcastSessionEvents && getNotifiedNodes(id).contains(nodeId))) {
log.info("Session " + id + " can't be found");
return null;
}
@ -300,6 +307,8 @@ public class RedissonSessionManager extends ManagerBase {
throw new IllegalStateException("Unable to find session: " + msg.getSessionId());
}
s.expire();
RSet<String> set = getNotifiedNodes(msg.getSessionId());
set.add(nodeId);
}
}

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-parent</artifactId>
<version>3.11.0.1507-SNAPSHOT</version>
<version>3.11.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
@ -353,7 +353,7 @@
<version>3.0.0</version>
<executions>
<execution>
<phase>none</phase>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>

@ -15,7 +15,6 @@
*/
package org.redisson;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@ -37,7 +36,7 @@ import org.redisson.connection.ConnectionListener;
import org.redisson.connection.ConnectionManager;
import org.redisson.connection.MasterSlaveEntry;
import org.redisson.connection.RedisClientEntry;
import org.redisson.misc.URIBuilder;
import org.redisson.misc.RedisURI;
/**
*
@ -56,15 +55,15 @@ public class RedisNodes<N extends Node> implements NodesGroup<N> {
@Override
public N getNode(String address) {
Collection<MasterSlaveEntry> entries = connectionManager.getEntrySet();
URI addr = URIBuilder.create(address);
RedisURI addr = new RedisURI(address);
for (MasterSlaveEntry masterSlaveEntry : entries) {
if (masterSlaveEntry.getAllEntries().isEmpty()
&& URIBuilder.compare(masterSlaveEntry.getClient().getAddr(), addr)) {
&& RedisURI.compare(masterSlaveEntry.getClient().getAddr(), addr)) {
return (N) new RedisClientEntry(masterSlaveEntry.getClient(), connectionManager.getCommandExecutor(), NodeType.MASTER);
}
for (ClientConnectionsEntry entry : masterSlaveEntry.getAllEntries()) {
if (URIBuilder.compare(entry.getClient().getAddr(), addr)
if (RedisURI.compare(entry.getClient().getAddr(), addr)
&& entry.getFreezeReason() != FreezeReason.MANAGER) {
return (N) new RedisClientEntry(entry.getClient(), connectionManager.getCommandExecutor(), entry.getNodeType());
}

@ -126,7 +126,7 @@ public class RedissonRateLimiter extends RedissonObject implements RRateLimiter
public RFuture<Boolean> tryAcquireAsync(long permits, long timeout, TimeUnit unit) {
RPromise<Boolean> promise = new RedissonPromise<Boolean>();
long timeoutInMillis = -1;
if (timeout > 0) {
if (timeout >= 0) {
timeoutInMillis = unit.toMillis(timeout);
}
tryAcquireAsync(permits, promise, timeoutInMillis);

@ -72,7 +72,8 @@ public class RedissonReadLock extends RedissonLock implements RLock {
"local key = KEYS[2] .. ':' .. ind;" +
"redis.call('set', key, 1); " +
"redis.call('pexpire', key, ARGV[1]); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"local remainTime = redis.call('pttl', KEYS[1]); " +
"redis.call('pexpire', KEYS[1], math.max(remainTime, ARGV[1])); " +
"return nil; " +
"end;" +
"return redis.call('pttl', KEYS[1]);",

@ -136,7 +136,7 @@ public class RedissonRemoteService extends BaseRemoteService implements RRemoteS
@Override
public int getFreeWorkers(Class<?> remoteInterface) {
Entry entry = remoteMap.remove(remoteInterface);
Entry entry = remoteMap.get(remoteInterface);
if (entry == null) {
return 0;
}

@ -115,14 +115,24 @@ public class RedissonStream<K, V> extends RedissonExpirable implements RStream<K
@Override
public RFuture<PendingResult> listPendingAsync(String groupName) {
return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.XPENDING, getName(), groupName);
return getPendingInfoAsync(groupName);
}
@Override
public PendingResult listPending(String groupName) {
return get(listPendingAsync(groupName));
return getPendingInfo(groupName);
}
@Override
public RFuture<PendingResult> getPendingInfoAsync(String groupName) {
return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.XPENDING, getName(), groupName);
}
@Override
public PendingResult getPendingInfo(String groupName) {
return get(listPendingAsync(groupName));
}
@Override
public RFuture<List<PendingEntry>> listPendingAsync(String groupName, String consumerName, StreamMessageId startId, StreamMessageId endId, int count) {
return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.XPENDING_ENTRIES, getName(), groupName, startId, endId, count, consumerName);
@ -978,5 +988,49 @@ public class RedissonStream<K, V> extends RedissonExpirable implements RStream<K
public RFuture<List<StreamConsumer>> listConsumersAsync(String groupName) {
return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.XINFO_CONSUMERS, getName(), groupName);
}
private static final RedisCommand<Map<StreamMessageId, Map<Object, Object>>> EVAL_XRANGE = new RedisCommand("EVAL", RedisCommands.XRANGE.getReplayMultiDecoder());
@Override
public RFuture<Map<StreamMessageId, Map<K, V>>> pendingRangeAsync(String groupName, StreamMessageId startId,
StreamMessageId endId, int count) {
return commandExecutor.evalReadAsync(getName(), codec, EVAL_XRANGE,
"local pendingData = redis.call('xpending', KEYS[1], ARGV[1], ARGV[2], ARGV[3], ARGV[4]);" +
"local result = {}; " +
"for i = 1, #pendingData, 1 do " +
"local value = redis.call('xrange', KEYS[1], pendingData[i][1], pendingData[i][1]);" +
"table.insert(result, value[1]);" +
"end; " +
"return result;",
Collections.<Object>singletonList(getName()),
groupName, startId, endId, count);
}
@Override
public RFuture<Map<StreamMessageId, Map<K, V>>> pendingRangeAsync(String groupName, String consumerName,
StreamMessageId startId, StreamMessageId endId, int count) {
return commandExecutor.evalReadAsync(getName(), codec, EVAL_XRANGE,
"local pendingData = redis.call('xpending', KEYS[1], ARGV[1], ARGV[2], ARGV[3], ARGV[4], ARGV[5]);" +
"local result = {}; " +
"for i = 1, #pendingData, 1 do " +
"local value = redis.call('xrange', KEYS[1], pendingData[i][1], pendingData[i][1]);" +
"table.insert(result, value[1]);" +
"end; " +
"return result;",
Collections.<Object>singletonList(getName()),
groupName, startId, endId, count, consumerName);
}
@Override
public Map<StreamMessageId, Map<K, V>> pendingRange(String groupName, String consumerName, StreamMessageId startId,
StreamMessageId endId, int count) {
return get(pendingRangeAsync(groupName, consumerName, startId, endId, count));
}
@Override
public Map<StreamMessageId, Map<K, V>> pendingRange(String groupName, StreamMessageId startId, StreamMessageId endId,
int count) {
return get(pendingRangeAsync(groupName, startId, endId, count));
}
}

@ -264,7 +264,7 @@ public interface RMapRx<K, V> extends RExpirableRx {
*
* @return iterator
*/
Single<Map.Entry<K, V>> entryIterator();
Flowable<Map.Entry<K, V>> entryIterator();
/**
* Returns iterator over map entries collection.
@ -275,7 +275,7 @@ public interface RMapRx<K, V> extends RExpirableRx {
* @param count - size of entries batch
* @return iterator
*/
Single<Map.Entry<K, V>> entryIterator(int count);
Flowable<Map.Entry<K, V>> entryIterator(int count);
/**
* Returns iterator over map entries collection.
@ -295,7 +295,7 @@ public interface RMapRx<K, V> extends RExpirableRx {
* @param pattern - key pattern
* @return iterator
*/
Single<Map.Entry<K, V>> entryIterator(String pattern);
Flowable<Map.Entry<K, V>> entryIterator(String pattern);
/**
* Returns iterator over map entries collection.
@ -316,7 +316,7 @@ public interface RMapRx<K, V> extends RExpirableRx {
* @param count - size of entries batch
* @return iterator
*/
Single<Map.Entry<K, V>> entryIterator(String pattern, int count);
Flowable<Map.Entry<K, V>> entryIterator(String pattern, int count);
/**
* Returns iterator over values collection of this map.

@ -83,22 +83,30 @@ public interface RStream<K, V> extends RStreamAsync<K, V>, RExpirable {
* @return marked messages amount
*/
long ack(String groupName, StreamMessageId... ids);
/**
* Returns pending messages by group name
* Returns common info about pending messages by group name.
*
* @param groupName - name of group
* @return result object
*/
PendingResult getPendingInfo(String groupName);
/*
* Use #getPendingInfo method
*/
@Deprecated
PendingResult listPending(String groupName);
/**
* Returns list of pending messages by group name.
* Returns list of common info about pending messages by group name.
* Limited by start Stream Message ID and end Stream Message ID and count.
* <p>
* {@link StreamMessageId#MAX} is used as max Stream Message ID
* {@link StreamMessageId#MIN} is used as min Stream Message ID
*
* @see #pendingRangeAsync
*
* @param groupName - name of group
* @param startId - start Stream Message ID
* @param endId - end Stream Message ID
@ -108,12 +116,14 @@ public interface RStream<K, V> extends RStreamAsync<K, V>, RExpirable {
List<PendingEntry> listPending(String groupName, StreamMessageId startId, StreamMessageId endId, int count);
/**
* Returns list of pending messages by group name and consumer name.
* Returns list of common info about pending messages by group and consumer name.
* Limited by start Stream Message ID and end Stream Message ID and count.
* <p>
* {@link StreamMessageId#MAX} is used as max Stream Message ID
* {@link StreamMessageId#MIN} is used as min Stream Message ID
*
* @see #pendingRangeAsync
*
* @param consumerName - name of consumer
* @param groupName - name of group
* @param startId - start Stream Message ID
@ -123,6 +133,41 @@ public interface RStream<K, V> extends RStreamAsync<K, V>, RExpirable {
*/
List<PendingEntry> listPending(String groupName, String consumerName, StreamMessageId startId, StreamMessageId endId, int count);
/**
* Returns stream data of pending messages by group name.
* Limited by start Stream Message ID and end Stream Message ID and count.
* <p>
* {@link StreamMessageId#MAX} is used as max Stream Message ID
* {@link StreamMessageId#MIN} is used as min Stream Message ID
*
* @see #listPending
*
* @param groupName - name of group
* @param startId - start Stream Message ID
* @param endId - end Stream Message ID
* @param count - amount of messages
* @return map
*/
Map<StreamMessageId, Map<K, V>> pendingRange(String groupName, StreamMessageId startId, StreamMessageId endId, int count);
/**
* Returns stream data of pending messages by group and customer name.
* Limited by start Stream Message ID and end Stream Message ID and count.
* <p>
* {@link StreamMessageId#MAX} is used as max Stream Message ID
* {@link StreamMessageId#MIN} is used as min Stream Message ID
*
* @see #listPending
*
* @param consumerName - name of consumer
* @param groupName - name of group
* @param startId - start Stream Message ID
* @param endId - end Stream Message ID
* @param count - amount of messages
* @return map
*/
Map<StreamMessageId, Map<K, V>> pendingRange(String groupName, String consumerName, StreamMessageId startId, StreamMessageId endId, int count);
/**
* Transfers ownership of pending messages by id to a new consumer
* by name if idle time of messages is greater than defined value.
@ -655,7 +700,7 @@ public interface RStream<K, V> extends RStreamAsync<K, V>, RExpirable {
Map<String, Map<StreamMessageId, Map<K, V>>> read(int count, long timeout, TimeUnit unit, StreamMessageId id, Map<String, StreamMessageId> nameToId);
/**
* Read stream data in range by specified start Stream Message ID (included) and end Stream Message ID (included).
* Returns stream data in range by specified start Stream Message ID (included) and end Stream Message ID (included).
*
* @param startId - start Stream Message ID
* @param endId - end Stream Message ID
@ -664,7 +709,7 @@ public interface RStream<K, V> extends RStreamAsync<K, V>, RExpirable {
Map<StreamMessageId, Map<K, V>> range(StreamMessageId startId, StreamMessageId endId);
/**
* Read stream data in range by specified start Stream Message ID (included) and end Stream Message ID (included).
* Returns stream data in range by specified start Stream Message ID (included) and end Stream Message ID (included).
*
* @param count - stream data size limit
* @param startId - start Stream Message ID
@ -674,7 +719,7 @@ public interface RStream<K, V> extends RStreamAsync<K, V>, RExpirable {
Map<StreamMessageId, Map<K, V>> range(int count, StreamMessageId startId, StreamMessageId endId);
/**
* Read stream data in reverse order in range by specified start Stream Message ID (included) and end Stream Message ID (included).
* Returns stream data in reverse order in range by specified start Stream Message ID (included) and end Stream Message ID (included).
*
* @param startId - start Stream Message ID
* @param endId - end Stream Message ID
@ -683,7 +728,7 @@ public interface RStream<K, V> extends RStreamAsync<K, V>, RExpirable {
Map<StreamMessageId, Map<K, V>> rangeReversed(StreamMessageId startId, StreamMessageId endId);
/**
* Read stream data in reverse order in range by specified start Stream Message ID (included) and end Stream Message ID (included).
* Returns stream data in reverse order in range by specified start Stream Message ID (included) and end Stream Message ID (included).
*
* @param count - stream data size limit
* @param startId - start Stream Message ID
@ -724,14 +769,14 @@ public interface RStream<K, V> extends RStreamAsync<K, V>, RExpirable {
StreamInfo<K, V> getInfo();
/**
* Returns list of objects with information about groups belonging to this stream.
* Returns list of common info about groups belonging to this stream.
*
* @return list of info objects
*/
List<StreamGroup> listGroups();
/**
* Returns list of objects with information about group customers for specified <code>groupName</code>.
* Returns list of common info about group customers for specified <code>groupName</code>.
*
* @param groupName - name of group
* @return list of info objects

@ -85,46 +85,91 @@ public interface RStreamAsync<K, V> extends RExpirableAsync {
* @return marked messages amount
*/
RFuture<Long> ackAsync(String groupName, StreamMessageId... ids);
/**
* Returns pending messages by group name
* Returns common info about pending messages by group name.
*
* @param groupName - name of group
* @return result object
*/
RFuture<PendingResult> getPendingInfoAsync(String groupName);
/*
* Use #getPendingInfoAsync method
*/
@Deprecated
RFuture<PendingResult> listPendingAsync(String groupName);
/**
* Returns list of pending messages by group name.
* Limited by start stream id and end stream id and count.
* Returns list of common info about pending messages by group name.
* Limited by start Stream Message ID and end Stream Message ID and count.
* <p>
* {@link StreamMessageId#MAX} is used as max stream id
* {@link StreamMessageId#MIN} is used as min stream id
* {@link StreamMessageId#MAX} is used as max Stream Message ID
* {@link StreamMessageId#MIN} is used as min Stream Message ID
*
* @see #pendingRangeAsync
*
* @param groupName - name of group
* @param startId - start stream id
* @param endId - end stream id
* @param startId - start Stream Message ID
* @param endId - end Stream Message ID
* @param count - amount of messages
* @return list
*/
RFuture<List<PendingEntry>> listPendingAsync(String groupName, StreamMessageId startId, StreamMessageId endId, int count);
/**
* Returns list of pending messages by group name and consumer name.
* Limited by start stream id and end stream id and count.
* Returns list of common info about pending messages by group and consumer name.
* Limited by start Stream Message ID and end Stream Message ID and count.
* <p>
* {@link StreamMessageId#MAX} is used as max stream id
* {@link StreamMessageId#MIN} is used as min stream id
* {@link StreamMessageId#MAX} is used as max Stream Message ID
* {@link StreamMessageId#MIN} is used as min Stream Message ID
*
* @see #pendingRangeAsync
*
* @param consumerName - name of consumer
* @param groupName - name of group
* @param startId - start stream id
* @param endId - end stream id
* @param startId - start Stream Message ID
* @param endId - end Stream Message ID
* @param count - amount of messages
* @return list
*/
RFuture<List<PendingEntry>> listPendingAsync(String groupName, String consumerName, StreamMessageId startId, StreamMessageId endId, int count);
/**
* Returns stream data of pending messages by group name.
* Limited by start Stream Message ID and end Stream Message ID and count.
* <p>
* {@link StreamMessageId#MAX} is used as max Stream Message ID
* {@link StreamMessageId#MIN} is used as min Stream Message ID
*
* @see #listPendingAsync
*
* @param groupName - name of group
* @param startId - start Stream Message ID
* @param endId - end Stream Message ID
* @param count - amount of messages
* @return map
*/
RFuture<Map<StreamMessageId, Map<K, V>>> pendingRangeAsync(String groupName, StreamMessageId startId, StreamMessageId endId, int count);
/**
* Returns stream data of pending messages by group and customer name.
* Limited by start Stream Message ID and end Stream Message ID and count.
* <p>
* {@link StreamMessageId#MAX} is used as max Stream Message ID
* {@link StreamMessageId#MIN} is used as min Stream Message ID
*
* @see #listPendingAsync
*
* @param consumerName - name of consumer
* @param groupName - name of group
* @param startId - start Stream Message ID
* @param endId - end Stream Message ID
* @param count - amount of messages
* @return map
*/
RFuture<Map<StreamMessageId, Map<K, V>>> pendingRangeAsync(String groupName, String consumerName, StreamMessageId startId, StreamMessageId endId, int count);
/**
* Transfers ownership of pending messages by id to a new consumer
* by name if idle time of messages is greater than defined value.
@ -645,7 +690,7 @@ public interface RStreamAsync<K, V> extends RExpirableAsync {
RFuture<Map<String, Map<StreamMessageId, Map<K, V>>>> readAsync(int count, long timeout, TimeUnit unit, StreamMessageId id, Map<String, StreamMessageId> nameToId);
/**
* Read stream data in range by specified start Stream ID (included) and end Stream ID (included).
* Returns stream data in range by specified start Stream ID (included) and end Stream ID (included).
*
* @param startId - start Stream ID
* @param endId - end Stream ID
@ -654,7 +699,7 @@ public interface RStreamAsync<K, V> extends RExpirableAsync {
RFuture<Map<StreamMessageId, Map<K, V>>> rangeAsync(StreamMessageId startId, StreamMessageId endId);
/**
* Read stream data in range by specified start Stream ID (included) and end Stream ID (included).
* Returns stream data in range by specified start Stream ID (included) and end Stream ID (included).
*
* @param count - stream data size limit
* @param startId - start Stream ID
@ -664,7 +709,7 @@ public interface RStreamAsync<K, V> extends RExpirableAsync {
RFuture<Map<StreamMessageId, Map<K, V>>> rangeAsync(int count, StreamMessageId startId, StreamMessageId endId);
/**
* Read stream data in reverse order in range by specified start Stream ID (included) and end Stream ID (included).
* Returns stream data in reverse order in range by specified start Stream ID (included) and end Stream ID (included).
*
* @param startId - start Stream ID
* @param endId - end Stream ID
@ -673,7 +718,7 @@ public interface RStreamAsync<K, V> extends RExpirableAsync {
RFuture<Map<StreamMessageId, Map<K, V>>> rangeReversedAsync(StreamMessageId startId, StreamMessageId endId);
/**
* Read stream data in reverse order in range by specified start Stream ID (included) and end Stream ID (included).
* Returns stream data in reverse order in range by specified start Stream ID (included) and end Stream ID (included).
*
* @param count - stream data size limit
* @param startId - start Stream ID

@ -89,11 +89,17 @@ public interface RStreamReactive<K, V> extends RExpirableReactive {
Mono<Long> ack(String groupName, StreamMessageId... ids);
/**
* Returns pending messages by group name
* Returns common info about pending messages by group name.
*
* @param groupName - name of group
* @return result object
*/
Mono<PendingResult> getPendingInfo(String groupName);
/*
* Use #getPendingInfo method
*/
@Deprecated
Mono<PendingResult> listPending(String groupName);
/**
@ -634,7 +640,7 @@ public interface RStreamReactive<K, V> extends RExpirableReactive {
Mono<Map<String, Map<StreamMessageId, Map<K, V>>>> read(int count, long timeout, TimeUnit unit, StreamMessageId id, Map<String, StreamMessageId> nameToId);
/**
* Read stream data in range by specified start Stream ID (included) and end Stream ID (included).
* Returns stream data in range by specified start Stream ID (included) and end Stream ID (included).
*
* @param startId - start Stream ID
* @param endId - end Stream ID
@ -643,7 +649,7 @@ public interface RStreamReactive<K, V> extends RExpirableReactive {
Mono<Map<StreamMessageId, Map<K, V>>> range(StreamMessageId startId, StreamMessageId endId);
/**
* Read stream data in range by specified start Stream ID (included) and end Stream ID (included).
* Returns stream data in range by specified start Stream ID (included) and end Stream ID (included).
*
* @param count - stream data size limit
* @param startId - start Stream ID
@ -653,7 +659,7 @@ public interface RStreamReactive<K, V> extends RExpirableReactive {
Mono<Map<StreamMessageId, Map<K, V>>> range(int count, StreamMessageId startId, StreamMessageId endId);
/**
* Read stream data in reverse order in range by specified start Stream ID (included) and end Stream ID (included).
* Returns stream data in reverse order in range by specified start Stream ID (included) and end Stream ID (included).
*
* @param startId - start Stream ID
* @param endId - end Stream ID
@ -662,7 +668,7 @@ public interface RStreamReactive<K, V> extends RExpirableReactive {
Mono<Map<StreamMessageId, Map<K, V>>> rangeReversed(StreamMessageId startId, StreamMessageId endId);
/**
* Read stream data in reverse order in range by specified start Stream ID (included) and end Stream ID (included).
* Returns stream data in reverse order in range by specified start Stream ID (included) and end Stream ID (included).
*
* @param count - stream data size limit
* @param startId - start Stream ID
@ -717,4 +723,39 @@ public interface RStreamReactive<K, V> extends RExpirableReactive {
*/
Mono<List<StreamConsumer>> listConsumers(String groupName);
/**
* Returns stream data of pending messages by group name.
* Limited by start Stream Message ID and end Stream Message ID and count.
* <p>
* {@link StreamMessageId#MAX} is used as max Stream Message ID
* {@link StreamMessageId#MIN} is used as min Stream Message ID
*
* @see #listPending
*
* @param groupName - name of group
* @param startId - start Stream Message ID
* @param endId - end Stream Message ID
* @param count - amount of messages
* @return map
*/
Mono<Map<StreamMessageId, Map<K, V>>> pendingRange(String groupName, StreamMessageId startId, StreamMessageId endId, int count);
/**
* Returns stream data of pending messages by group and customer name.
* Limited by start Stream Message ID and end Stream Message ID and count.
* <p>
* {@link StreamMessageId#MAX} is used as max Stream Message ID
* {@link StreamMessageId#MIN} is used as min Stream Message ID
*
* @see #listPending
*
* @param consumerName - name of consumer
* @param groupName - name of group
* @param startId - start Stream Message ID
* @param endId - end Stream Message ID
* @param count - amount of messages
* @return map
*/
Mono<Map<StreamMessageId, Map<K, V>>> pendingRange(String groupName, String consumerName, StreamMessageId startId, StreamMessageId endId, int count);
}

@ -90,11 +90,17 @@ public interface RStreamRx<K, V> extends RExpirableRx {
Single<Long> ack(String groupName, StreamMessageId... ids);
/**
* Returns pending messages by group name
* Returns common info about pending messages by group name.
*
* @param groupName - name of group
* @return result object
*/
Single<PendingResult> getPendingInfo(String groupName);
/*
* Use #getPendingInfo method
*/
@Deprecated
Single<PendingResult> listPending(String groupName);
/**
@ -635,7 +641,7 @@ public interface RStreamRx<K, V> extends RExpirableRx {
Single<Map<String, Map<StreamMessageId, Map<K, V>>>> read(int count, long timeout, TimeUnit unit, StreamMessageId id, Map<String, StreamMessageId> nameToId);
/**
* Read stream data in range by specified start Stream ID (included) and end Stream ID (included).
* Returns stream data in range by specified start Stream ID (included) and end Stream ID (included).
*
* @param startId - start Stream ID
* @param endId - end Stream ID
@ -644,7 +650,7 @@ public interface RStreamRx<K, V> extends RExpirableRx {
Single<Map<StreamMessageId, Map<K, V>>> range(StreamMessageId startId, StreamMessageId endId);
/**
* Read stream data in range by specified start Stream ID (included) and end Stream ID (included).
* Returns stream data in range by specified start Stream ID (included) and end Stream ID (included).
*
* @param count - stream data size limit
* @param startId - start Stream ID
@ -654,7 +660,7 @@ public interface RStreamRx<K, V> extends RExpirableRx {
Single<Map<StreamMessageId, Map<K, V>>> range(int count, StreamMessageId startId, StreamMessageId endId);
/**
* Read stream data in reverse order in range by specified start Stream ID (included) and end Stream ID (included).
* Returns stream data in reverse order in range by specified start Stream ID (included) and end Stream ID (included).
*
* @param startId - start Stream ID
* @param endId - end Stream ID
@ -663,7 +669,7 @@ public interface RStreamRx<K, V> extends RExpirableRx {
Single<Map<StreamMessageId, Map<K, V>>> rangeReversed(StreamMessageId startId, StreamMessageId endId);
/**
* Read stream data in reverse order in range by specified start Stream ID (included) and end Stream ID (included).
* Returns stream data in reverse order in range by specified start Stream ID (included) and end Stream ID (included).
*
* @param count - stream data size limit
* @param startId - start Stream ID
@ -718,4 +724,39 @@ public interface RStreamRx<K, V> extends RExpirableRx {
*/
Single<List<StreamConsumer>> listConsumers(String groupName);
/**
* Returns stream data of pending messages by group name.
* Limited by start Stream Message ID and end Stream Message ID and count.
* <p>
* {@link StreamMessageId#MAX} is used as max Stream Message ID
* {@link StreamMessageId#MIN} is used as min Stream Message ID
*
* @see #listPending
*
* @param groupName - name of group
* @param startId - start Stream Message ID
* @param endId - end Stream Message ID
* @param count - amount of messages
* @return map
*/
Single<Map<StreamMessageId, Map<K, V>>> pendingRange(String groupName, StreamMessageId startId, StreamMessageId endId, int count);
/**
* Returns stream data of pending messages by group and customer name.
* Limited by start Stream Message ID and end Stream Message ID and count.
* <p>
* {@link StreamMessageId#MAX} is used as max Stream Message ID
* {@link StreamMessageId#MIN} is used as min Stream Message ID
*
* @see #listPending
*
* @param consumerName - name of consumer
* @param groupName - name of group
* @param startId - start Stream Message ID
* @param endId - end Stream Message ID
* @param count - amount of messages
* @return map
*/
Single<Map<StreamMessageId, Map<K, V>>> pendingRange(String groupName, String consumerName, StreamMessageId startId, StreamMessageId endId, int count);
}

@ -17,7 +17,6 @@ package org.redisson.client;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -28,6 +27,7 @@ import org.redisson.api.RFuture;
import org.redisson.client.handler.RedisChannelInitializer;
import org.redisson.client.handler.RedisChannelInitializer.Type;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedisURI;
import org.redisson.misc.RedissonPromise;
import io.netty.bootstrap.Bootstrap;
@ -63,7 +63,7 @@ public final class RedisClient {
private final AtomicReference<RFuture<InetSocketAddress>> resolvedAddrFuture = new AtomicReference<RFuture<InetSocketAddress>>();
private final Bootstrap bootstrap;
private final Bootstrap pubSubBootstrap;
private final URI uri;
private final RedisURI uri;
private InetSocketAddress resolvedAddr;
private final ChannelGroup channels;

@ -20,7 +20,7 @@ import java.net.URI;
import java.util.concurrent.ExecutorService;
import org.redisson.config.SslProvider;
import org.redisson.misc.URIBuilder;
import org.redisson.misc.RedisURI;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.SocketChannel;
@ -35,7 +35,7 @@ import io.netty.util.Timer;
*/
public class RedisClientConfig {
private URI address;
private RedisURI address;
private InetSocketAddress addr;
private Timer timer;
@ -105,23 +105,23 @@ public class RedisClientConfig {
}
public RedisClientConfig setAddress(String host, int port) {
this.address = URIBuilder.create("redis://" + host + ":" + port);
this.address = new RedisURI("redis://" + host + ":" + port);
return this;
}
public RedisClientConfig setAddress(String address) {
this.address = URIBuilder.create(address);
this.address = new RedisURI(address);
return this;
}
public RedisClientConfig setAddress(InetSocketAddress addr, URI address) {
public RedisClientConfig setAddress(InetSocketAddress addr, RedisURI address) {
this.addr = addr;
this.address = address;
return this;
}
public RedisClientConfig setAddress(URI address) {
public RedisClientConfig setAddress(RedisURI address) {
this.address = address;
return this;
}
public URI getAddress() {
public RedisURI getAddress() {
return address;
}
public InetSocketAddress getAddr() {

@ -15,9 +15,7 @@
*/
package org.redisson.client;
import java.net.URI;
import org.redisson.misc.URIBuilder;
import org.redisson.misc.RedisURI;
/**
*
@ -29,18 +27,18 @@ public class RedisRedirectException extends RedisException {
private static final long serialVersionUID = 181505625075250011L;
private final int slot;
private final URI url;
private final RedisURI url;
public RedisRedirectException(int slot, String url) {
this.slot = slot;
this.url = URIBuilder.create("redis://" + url);
this.url = new RedisURI("redis://" + url);
}
public int getSlot() {
return slot;
}
public URI getUrl() {
public RedisURI getUrl() {
return url;
}

@ -177,14 +177,12 @@ public class ConnectionWatchdog extends ChannelInboundHandlerAdapter {
return;
}
log.debug("blocking queue sent " + connection);
ChannelFuture future = connection.send(currentCommand);
final CommandData<?, ?> cd = currentCommand;
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
log.error("Can't reconnect blocking queue to new connection. {}", cd);
log.error("Can't reconnect blocking queue by command: {} using connection: {}", currentCommand, connection);
}
}
});

@ -16,7 +16,6 @@
package org.redisson.cluster;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
@ -36,6 +35,7 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.redisson.api.NodeType;
import org.redisson.api.RFuture;
@ -58,8 +58,8 @@ import org.redisson.connection.MasterSlaveConnectionManager;
import org.redisson.connection.MasterSlaveEntry;
import org.redisson.connection.SingleEntry;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedisURI;
import org.redisson.misc.RedissonPromise;
import org.redisson.misc.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -82,7 +82,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
private ScheduledFuture<?> monitorFuture;
private volatile URI lastClusterNode;
private volatile RedisURI lastClusterNode;
private RedisStrictCommand<List<ClusterNodeInfo>> clusterNodesCommand;
@ -103,7 +103,8 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
Throwable lastException = null;
List<String> failedMasters = new ArrayList<String>();
for (URI addr : cfg.getNodeAddresses()) {
for (String address : cfg.getNodeAddresses()) {
RedisURI addr = new RedisURI(address);
RFuture<RedisConnection> connectionFuture = connectToNode(cfg, addr, null, addr.getHost());
try {
RedisConnection connection = connectionFuture.syncUninterruptibly().getNow();
@ -180,7 +181,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
@Override
protected RedisClientConfig createRedisConfig(NodeType type, URI address, int timeout, int commandTimeout, String sslHostname) {
protected RedisClientConfig createRedisConfig(NodeType type, RedisURI address, int timeout, int commandTimeout, String sslHostname) {
RedisClientConfig result = super.createRedisConfig(type, address, timeout, commandTimeout, sslHostname);
result.setReadOnly(type == NodeType.SLAVE && config.getReadMode() != ReadMode.MASTER);
return result;
@ -226,14 +227,15 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
MasterSlaveServersConfig config = create(cfg);
config.setMasterAddress(partition.getMasterAddress());
config.setMasterAddress(partition.getMasterAddress().toString());
MasterSlaveEntry e;
List<RFuture<Void>> futures = new ArrayList<RFuture<Void>>();
if (config.checkSkipSlavesInit()) {
e = new SingleEntry(ClusterConnectionManager.this, config);
} else {
config.setSlaveAddresses(partition.getSlaveAddresses());
Set<String> slaveAddresses = partition.getSlaveAddresses().stream().map(r -> r.toString()).collect(Collectors.toSet());
config.setSlaveAddresses(slaveAddresses);
e = new MasterSlaveEntry(ClusterConnectionManager.this, config);
@ -247,7 +249,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
}
RFuture<RedisClient> f = e.setupMasterEntry(config.getMasterAddress());
RFuture<RedisClient> f = e.setupMasterEntry(new RedisURI(config.getMasterAddress()));
RPromise<Void> initFuture = new RedissonPromise<Void>();
futures.add(initFuture);
f.onComplete((res, ex3) -> {
@ -275,12 +277,13 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
return result;
}
private void scheduleClusterChangeCheck(ClusterServersConfig cfg, Iterator<URI> iterator) {
private void scheduleClusterChangeCheck(ClusterServersConfig cfg, Iterator<RedisURI> iterator) {
monitorFuture = group.schedule(new Runnable() {
@Override
public void run() {
if (configEndpointHostName != null) {
URI uri = cfg.getNodeAddresses().iterator().next();
String address = cfg.getNodeAddresses().iterator().next();
RedisURI uri = new RedisURI(address);
AddressResolver<InetSocketAddress> resolver = resolverGroup.getResolver(getGroup().next());
Future<List<InetSocketAddress>> allNodes = resolver.resolveAll(InetSocketAddress.createUnresolved(uri.getHost(), uri.getPort()));
allNodes.addListener(new FutureListener<List<InetSocketAddress>>() {
@ -288,34 +291,34 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
public void operationComplete(Future<List<InetSocketAddress>> future) throws Exception {
AtomicReference<Throwable> lastException = new AtomicReference<Throwable>(future.cause());
if (!future.isSuccess()) {
checkClusterState(cfg, Collections.<URI>emptyList().iterator(), lastException);
checkClusterState(cfg, Collections.<RedisURI>emptyList().iterator(), lastException);
return;
}
List<URI> nodes = new ArrayList<URI>();
List<RedisURI> nodes = new ArrayList<>();
for (InetSocketAddress addr : future.getNow()) {
URI node = URIBuilder.create(uri.getScheme() + "://" + addr.getAddress().getHostAddress() + ":" + addr.getPort());
URI address = applyNatMap(node);
RedisURI node = new RedisURI(uri.getScheme() + "://" + addr.getAddress().getHostAddress() + ":" + addr.getPort());
RedisURI address = applyNatMap(node);
nodes.add(address);
}
Iterator<URI> nodesIterator = nodes.iterator();
Iterator<RedisURI> nodesIterator = nodes.iterator();
checkClusterState(cfg, nodesIterator, lastException);
}
});
} else {
AtomicReference<Throwable> lastException = new AtomicReference<Throwable>();
Iterator<URI> nodesIterator = iterator;
Iterator<RedisURI> nodesIterator = iterator;
if (nodesIterator == null) {
List<URI> nodes = new ArrayList<URI>();
List<URI> slaves = new ArrayList<URI>();
List<RedisURI> nodes = new ArrayList<>();
List<RedisURI> slaves = new ArrayList<>();
for (ClusterPartition partition : getLastPartitions()) {
if (!partition.isMasterFail()) {
nodes.add(partition.getMasterAddress());
}
Set<URI> partitionSlaves = new HashSet<URI>(partition.getSlaveAddresses());
Set<RedisURI> partitionSlaves = new HashSet<>(partition.getSlaveAddresses());
partitionSlaves.removeAll(partition.getFailedSlaveAddresses());
slaves.addAll(partitionSlaves);
}
@ -332,7 +335,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}, cfg.getScanInterval(), TimeUnit.MILLISECONDS);
}
private void checkClusterState(ClusterServersConfig cfg, Iterator<URI> iterator, AtomicReference<Throwable> lastException) {
private void checkClusterState(ClusterServersConfig cfg, Iterator<RedisURI> iterator, AtomicReference<Throwable> lastException) {
if (!iterator.hasNext()) {
if (lastException.get() != null) {
log.error("Can't update cluster state", lastException.get());
@ -343,7 +346,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
if (!getShutdownLatch().acquire()) {
return;
}
URI uri = iterator.next();
RedisURI uri = iterator.next();
RFuture<RedisConnection> connectionFuture = connectToNode(cfg, uri, null, configEndpointHostName);
connectionFuture.onComplete((connection, e) -> {
if (e != null) {
@ -358,7 +361,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
private void updateClusterState(ClusterServersConfig cfg, RedisConnection connection,
Iterator<URI> iterator, URI uri, AtomicReference<Throwable> lastException) {
Iterator<RedisURI> iterator, RedisURI uri, AtomicReference<Throwable> lastException) {
RFuture<List<ClusterNodeInfo>> future = connection.async(clusterNodesCommand);
future.onComplete((nodes, e) -> {
if (e != null) {
@ -402,7 +405,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
MasterSlaveEntry entry = getEntry(currentPart.slots().nextSetBit(0));
// should be invoked first in order to remove stale failedSlaveAddresses
Set<URI> addedSlaves = addRemoveSlaves(entry, currentPart, newPart);
Set<RedisURI> addedSlaves = addRemoveSlaves(entry, currentPart, newPart);
// Do some slaves have changed state from failed to alive?
upDownSlaves(entry, currentPart, newPart, addedSlaves);
@ -411,20 +414,20 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
}
private void upDownSlaves(MasterSlaveEntry entry, ClusterPartition currentPart, ClusterPartition newPart, Set<URI> addedSlaves) {
Set<URI> aliveSlaves = new HashSet<URI>(currentPart.getFailedSlaveAddresses());
private void upDownSlaves(MasterSlaveEntry entry, ClusterPartition currentPart, ClusterPartition newPart, Set<RedisURI> addedSlaves) {
Set<RedisURI> aliveSlaves = new HashSet<>(currentPart.getFailedSlaveAddresses());
aliveSlaves.removeAll(addedSlaves);
aliveSlaves.removeAll(newPart.getFailedSlaveAddresses());
for (URI uri : aliveSlaves) {
for (RedisURI uri : aliveSlaves) {
currentPart.removeFailedSlaveAddress(uri);
if (entry.hasSlave(uri) && entry.slaveUp(uri, FreezeReason.MANAGER)) {
log.info("slave: {} has up for slot ranges: {}", uri, currentPart.getSlotRanges());
}
}
Set<URI> failedSlaves = new HashSet<URI>(newPart.getFailedSlaveAddresses());
Set<RedisURI> failedSlaves = new HashSet<>(newPart.getFailedSlaveAddresses());
failedSlaves.removeAll(currentPart.getFailedSlaveAddresses());
for (URI uri : failedSlaves) {
for (RedisURI uri : failedSlaves) {
currentPart.addFailedSlaveAddress(uri);
if (entry.slaveDown(uri, FreezeReason.MANAGER)) {
log.warn("slave: {} has down for slot ranges: {}", uri, currentPart.getSlotRanges());
@ -432,11 +435,11 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
}
private Set<URI> addRemoveSlaves(MasterSlaveEntry entry, ClusterPartition currentPart, ClusterPartition newPart) {
Set<URI> removedSlaves = new HashSet<URI>(currentPart.getSlaveAddresses());
private Set<RedisURI> addRemoveSlaves(MasterSlaveEntry entry, ClusterPartition currentPart, ClusterPartition newPart) {
Set<RedisURI> removedSlaves = new HashSet<>(currentPart.getSlaveAddresses());
removedSlaves.removeAll(newPart.getSlaveAddresses());
for (URI uri : removedSlaves) {
for (RedisURI uri : removedSlaves) {
currentPart.removeSlaveAddress(uri);
if (entry.slaveDown(uri, FreezeReason.MANAGER)) {
@ -444,9 +447,9 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
}
Set<URI> addedSlaves = new HashSet<URI>(newPart.getSlaveAddresses());
Set<RedisURI> addedSlaves = new HashSet<>(newPart.getSlaveAddresses());
addedSlaves.removeAll(currentPart.getSlaveAddresses());
for (URI uri : addedSlaves) {
for (RedisURI uri : addedSlaves) {
RFuture<Void> future = entry.addSlave(uri);
future.onComplete((res, ex) -> {
if (ex != null) {
@ -499,8 +502,8 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
ClusterPartition newMasterPart = find(newPartitions, slot);
// does partition has a new master?
if (!newMasterPart.getMasterAddress().equals(currentPart.getMasterAddress())) {
URI newUri = newMasterPart.getMasterAddress();
URI oldUri = currentPart.getMasterAddress();
RedisURI newUri = newMasterPart.getMasterAddress();
RedisURI oldUri = currentPart.getMasterAddress();
RFuture<RedisClient> future = changeMaster(slot, newUri);
future.onComplete((res, e) -> {
@ -693,10 +696,10 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
return result;
}
public URI applyNatMap(URI address) {
public RedisURI applyNatMap(RedisURI address) {
String mappedAddress = natMap.get(address.getHost() + ":" + address.getPort());
if (mappedAddress != null) {
return URIBuilder.create(address.getScheme() + "://" + mappedAddress);
return new RedisURI(address.getScheme() + "://" + mappedAddress);
}
return address;
}
@ -718,7 +721,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
ClusterPartition partition = getPartition(partitions, id);
URI address = applyNatMap(clusterNodeInfo.getAddress());
RedisURI address = applyNatMap(clusterNodeInfo.getAddress());
if (clusterNodeInfo.containsFlag(Flag.SLAVE)) {
slavePartition.setParent(partition);
@ -751,10 +754,10 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
if (cp.getParent() != null && cp.getParent().getType() == Type.MASTER) {
ClusterPartition parent = cp.getParent();
for (URI addr : cp.getSlaveAddresses()) {
for (RedisURI addr : cp.getSlaveAddresses()) {
parent.addSlaveAddress(addr);
}
for (URI addr : cp.getFailedSlaveAddresses()) {
for (RedisURI addr : cp.getFailedSlaveAddresses()) {
parent.addFailedSlaveAddress(addr);
}
}
@ -787,7 +790,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager {
}
@Override
public URI getLastClusterNode() {
public RedisURI getLastClusterNode() {
return lastClusterNode;
}

@ -15,11 +15,11 @@
*/
package org.redisson.cluster;
import java.net.URI;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import org.redisson.misc.URIBuilder;
import org.redisson.misc.RedisURI;
/**
*
@ -33,7 +33,7 @@ public class ClusterNodeInfo {
private final String nodeInfo;
private String nodeId;
private URI address;
private RedisURI address;
private final Set<Flag> flags = EnumSet.noneOf(Flag.class);
private String slaveOf;
@ -50,11 +50,11 @@ public class ClusterNodeInfo {
this.nodeId = nodeId;
}
public URI getAddress() {
public RedisURI getAddress() {
return address;
}
public void setAddress(String address) {
this.address = URIBuilder.create(address);
this.address = new RedisURI(address);
}
public void addSlotRange(ClusterSlotRange range) {

@ -15,12 +15,13 @@
*/
package org.redisson.cluster;
import java.net.URI;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.redisson.misc.RedisURI;
/**
*
* @author Nikita Koksharov
@ -34,9 +35,9 @@ public class ClusterPartition {
private final String nodeId;
private boolean masterFail;
private URI masterAddress;
private final Set<URI> slaveAddresses = new HashSet<URI>();
private final Set<URI> failedSlaves = new HashSet<URI>();
private RedisURI masterAddress;
private final Set<RedisURI> slaveAddresses = new HashSet<>();
private final Set<RedisURI> failedSlaves = new HashSet<>();
private final BitSet slots = new BitSet();
private final Set<ClusterSlotRange> slotRanges = new HashSet<ClusterSlotRange>();
@ -123,30 +124,30 @@ public class ClusterPartition {
return slots.cardinality();
}
public URI getMasterAddress() {
public RedisURI getMasterAddress() {
return masterAddress;
}
public void setMasterAddress(URI masterAddress) {
public void setMasterAddress(RedisURI masterAddress) {
this.masterAddress = masterAddress;
}
public void addFailedSlaveAddress(URI address) {
public void addFailedSlaveAddress(RedisURI address) {
failedSlaves.add(address);
}
public Set<URI> getFailedSlaveAddresses() {
public Set<RedisURI> getFailedSlaveAddresses() {
return Collections.unmodifiableSet(failedSlaves);
}
public void removeFailedSlaveAddress(URI uri) {
public void removeFailedSlaveAddress(RedisURI uri) {
failedSlaves.remove(uri);
}
public void addSlaveAddress(URI address) {
public void addSlaveAddress(RedisURI address) {
slaveAddresses.add(address);
}
public Set<URI> getSlaveAddresses() {
public Set<RedisURI> getSlaveAddresses() {
return Collections.unmodifiableSet(slaveAddresses);
}
public void removeSlaveAddress(URI uri) {
public void removeSlaveAddress(RedisURI uri) {
slaveAddresses.remove(uri);
failedSlaves.remove(uri);
}

@ -40,14 +40,22 @@ public class SerializationCodec extends BaseCodec {
@Override
public Object decode(ByteBuf buf, State state) throws IOException {
try {
ByteBufInputStream in = new ByteBufInputStream(buf);
ObjectInputStream inputStream;
if (classLoader != null) {
inputStream = new CustomObjectInputStream(classLoader, in);
} else {
inputStream = new ObjectInputStream(in);
//set thread context class loader to be the classLoader variable as there could be reflection
//done while reading from input stream which reflection will use thread class loader to load classes on demand
ClassLoader currentThreadClassLoader = Thread.currentThread().getContextClassLoader();
try {
ByteBufInputStream in = new ByteBufInputStream(buf);
ObjectInputStream inputStream;
if (classLoader != null) {
Thread.currentThread().setContextClassLoader(classLoader);
inputStream = new CustomObjectInputStream(classLoader, in);
} else {
inputStream = new ObjectInputStream(in);
}
return inputStream.readObject();
} finally {
Thread.currentThread().setContextClassLoader(currentThreadClassLoader);
}
return inputStream.readObject();
} catch (IOException e) {
throw e;
} catch (Exception e) {

@ -15,15 +15,13 @@
*/
package org.redisson.config;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.redisson.misc.URIBuilder;
/**
*
* @author Nikita Koksharov
@ -36,7 +34,7 @@ public class ClusterServersConfig extends BaseMasterSlaveServersConfig<ClusterSe
/**
* Redis cluster node urls list
*/
private List<URI> nodeAddresses = new ArrayList<URI>();
private List<String> nodeAddresses = new ArrayList<>();
/**
* Redis cluster scan interval in milliseconds
@ -60,15 +58,13 @@ public class ClusterServersConfig extends BaseMasterSlaveServersConfig<ClusterSe
* @return config
*/
public ClusterServersConfig addNodeAddress(String... addresses) {
for (String address : addresses) {
nodeAddresses.add(URIBuilder.create(address));
}
nodeAddresses.addAll(Arrays.asList(addresses));
return this;
}
public List<URI> getNodeAddresses() {
public List<String> getNodeAddresses() {
return nodeAddresses;
}
void setNodeAddresses(List<URI> nodeAddresses) {
void setNodeAddresses(List<String> nodeAddresses) {
this.nodeAddresses = nodeAddresses;
}

@ -28,7 +28,6 @@ import org.redisson.connection.AddressResolverGroupFactory;
import org.redisson.connection.ConnectionManager;
import org.redisson.connection.DnsAddressResolverGroupFactory;
import org.redisson.connection.ReplicatedConnectionManager;
import org.redisson.misc.URIBuilder;
import io.netty.channel.EventLoopGroup;
@ -95,10 +94,6 @@ public class Config {
public Config() {
}
static {
URIBuilder.patchUriObject();
}
public Config(Config oldConf) {
setExecutor(oldConf.getExecutor());

@ -21,9 +21,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URL;
import java.util.List;
import java.util.Scanner;
import java.util.UUID;
import java.util.regex.Matcher;
@ -42,7 +40,6 @@ import org.redisson.connection.SingleConnectionManager;
import org.redisson.connection.balancer.LoadBalancer;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
@ -68,38 +65,6 @@ public class ConfigSupport {
}
public abstract static class SingleSeverConfigMixIn {
@JsonProperty
List<URI> address;
@JsonIgnore
abstract SingleServerConfig setAddress(String address);
@JsonIgnore
abstract URI getAddress();
@JsonIgnore
abstract void setAddress(URI address);
}
public abstract static class MasterSlaveServersConfigMixIn {
@JsonProperty
List<URI> masterAddress;
@JsonIgnore
abstract MasterSlaveServersConfig setMasterAddress(String masterAddress);
@JsonIgnore
abstract URI getMasterAddress();
@JsonIgnore
abstract void setMasterAddress(URI masterAddress);
}
@JsonIgnoreProperties({"clusterConfig", "sentinelConfig"})
public static class ConfigMixIn {
@ -264,8 +229,6 @@ public class ConfigSupport {
private ObjectMapper createMapper(JsonFactory mapping, ClassLoader classLoader) {
ObjectMapper mapper = new ObjectMapper(mapping);
mapper.addMixIn(MasterSlaveServersConfig.class, MasterSlaveServersConfigMixIn.class);
mapper.addMixIn(SingleServerConfig.class, SingleSeverConfigMixIn.class);
mapper.addMixIn(Config.class, ConfigMixIn.class);
mapper.addMixIn(ReferenceCodecProvider.class, ClassMixIn.class);
mapper.addMixIn(AddressResolverGroupFactory.class, ClassMixIn.class);

@ -15,10 +15,9 @@
*/
package org.redisson.config;
import java.net.URI;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.redisson.misc.URIBuilder;
/**
*
@ -30,12 +29,12 @@ public class MasterSlaveServersConfig extends BaseMasterSlaveServersConfig<Maste
/**
* Redis slave servers addresses
*/
private Set<URI> slaveAddresses = new HashSet<URI>();
private Set<String> slaveAddresses = new HashSet<String>();
/**
* Redis master server address
*/
private URI masterAddress;
private String masterAddress;
/**
* Database index used for Redis connection
@ -60,22 +59,11 @@ public class MasterSlaveServersConfig extends BaseMasterSlaveServersConfig<Maste
* @return config
*/
public MasterSlaveServersConfig setMasterAddress(String masterAddress) {
if (masterAddress != null) {
this.masterAddress = URIBuilder.create(masterAddress);
}
this.masterAddress = masterAddress;
return this;
}
public URI getMasterAddress() {
if (masterAddress != null) {
return masterAddress;
}
return null;
}
public MasterSlaveServersConfig setMasterAddress(URI masterAddress) {
if (masterAddress != null) {
this.masterAddress = masterAddress;
}
return this;
public String getMasterAddress() {
return masterAddress;
}
/**
@ -85,19 +73,17 @@ public class MasterSlaveServersConfig extends BaseMasterSlaveServersConfig<Maste
* @return config
*/
public MasterSlaveServersConfig addSlaveAddress(String... addresses) {
for (String address : addresses) {
slaveAddresses.add(URIBuilder.create(address));
}
slaveAddresses.addAll(Arrays.asList(addresses));
return this;
}
public MasterSlaveServersConfig addSlaveAddress(URI slaveAddress) {
public MasterSlaveServersConfig addSlaveAddress(String slaveAddress) {
slaveAddresses.add(slaveAddress);
return this;
}
public Set<URI> getSlaveAddresses() {
public Set<String> getSlaveAddresses() {
return slaveAddresses;
}
public void setSlaveAddresses(Set<URI> readAddresses) {
public void setSlaveAddresses(Set<String> readAddresses) {
this.slaveAddresses = readAddresses;
}

@ -15,10 +15,9 @@
*/
package org.redisson.config;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.redisson.misc.URIBuilder;
/**
* Configuration for an Azure Redis Cache or AWS ElastiCache servers.
@ -32,7 +31,7 @@ public class ReplicatedServersConfig extends BaseMasterSlaveServersConfig<Replic
/**
* Replication group node urls list
*/
private List<URI> nodeAddresses = new ArrayList<URI>();
private List<String> nodeAddresses = new ArrayList<>();
/**
* Replication group scan interval in milliseconds
@ -61,15 +60,13 @@ public class ReplicatedServersConfig extends BaseMasterSlaveServersConfig<Replic
* @return config
*/
public ReplicatedServersConfig addNodeAddress(String... addresses) {
for (String address : addresses) {
nodeAddresses.add(URIBuilder.create(address));
}
nodeAddresses.addAll(Arrays.asList(addresses));
return this;
}
public List<URI> getNodeAddresses() {
public List<String> getNodeAddresses() {
return nodeAddresses;
}
void setNodeAddresses(List<URI> nodeAddresses) {
void setNodeAddresses(List<String> nodeAddresses) {
this.nodeAddresses = nodeAddresses;
}

@ -15,10 +15,12 @@
*/
package org.redisson.config;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import org.redisson.misc.URIBuilder;
import java.util.Map;
/**
*
@ -27,7 +29,9 @@ import org.redisson.misc.URIBuilder;
*/
public class SentinelServersConfig extends BaseMasterSlaveServersConfig<SentinelServersConfig> {
private List<URI> sentinelAddresses = new ArrayList<URI>();
private List<String> sentinelAddresses = new ArrayList<>();
private Map<String, String> natMap = Collections.emptyMap();
private String masterName;
@ -50,6 +54,7 @@ public class SentinelServersConfig extends BaseMasterSlaveServersConfig<Sentinel
setMasterName(config.getMasterName());
setDatabase(config.getDatabase());
setScanInterval(config.getScanInterval());
setNatMap(new HashMap<>(config.getNatMap()));
}
/**
@ -73,15 +78,13 @@ public class SentinelServersConfig extends BaseMasterSlaveServersConfig<Sentinel
* @return config
*/
public SentinelServersConfig addSentinelAddress(String... addresses) {
for (String address : addresses) {
sentinelAddresses.add(URIBuilder.create(address));
}
sentinelAddresses.addAll(Arrays.asList(addresses));
return this;
}
public List<URI> getSentinelAddresses() {
public List<String> getSentinelAddresses() {
return sentinelAddresses;
}
void setSentinelAddresses(List<URI> sentinelAddresses) {
void setSentinelAddresses(List<String> sentinelAddresses) {
this.sentinelAddresses = sentinelAddresses;
}
@ -114,4 +117,19 @@ public class SentinelServersConfig extends BaseMasterSlaveServersConfig<Sentinel
return this;
}
public Map<String, String> getNatMap() {
return natMap;
}
/**
* Defines NAT mapping. Address as a map key is replaced with mapped address as value.
*
* @param natMap - nat mapping
* @return config
*/
public SentinelServersConfig setNatMap(Map<String, String> natMap) {
this.natMap = natMap;
return this;
}
}

@ -15,9 +15,6 @@
*/
package org.redisson.config;
import java.net.URI;
import org.redisson.misc.URIBuilder;
/**
*
* @author Nikita Koksharov
@ -29,7 +26,7 @@ public class SingleServerConfig extends BaseConfig<SingleServerConfig> {
* Redis server address
*
*/
private URI address;
private String address;
/**
* Minimum idle subscription connection amount
@ -116,20 +113,12 @@ public class SingleServerConfig extends BaseConfig<SingleServerConfig> {
*/
public SingleServerConfig setAddress(String address) {
if (address != null) {
this.address = URIBuilder.create(address);
this.address = address;
}
return this;
}
public URI getAddress() {
if (address != null) {
return address;
}
return null;
}
void setAddress(URI address) {
if (address != null) {
this.address = address;
}
public String getAddress() {
return address;
}
/**

@ -16,7 +16,6 @@
package org.redisson.connection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.Collection;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
@ -32,6 +31,7 @@ import org.redisson.command.CommandSyncService;
import org.redisson.config.Config;
import org.redisson.config.MasterSlaveServersConfig;
import org.redisson.misc.InfinitySemaphoreLatch;
import org.redisson.misc.RedisURI;
import org.redisson.pubsub.PublishSubscribeService;
import io.netty.channel.EventLoopGroup;
@ -46,7 +46,7 @@ import io.netty.util.concurrent.Future;
*/
public interface ConnectionManager {
URI applyNatMap(URI address);
RedisURI applyNatMap(RedisURI address);
UUID getId();
@ -56,7 +56,7 @@ public interface ConnectionManager {
ExecutorService getExecutor();
URI getLastClusterNode();
RedisURI getLastClusterNode();
Config getCfg();
@ -92,11 +92,11 @@ public interface ConnectionManager {
RFuture<RedisConnection> connectionWriteOp(NodeSource source, RedisCommand<?> command);
RedisClient createClient(NodeType type, URI address, int timeout, int commandTimeout, String sslHostname);
RedisClient createClient(NodeType type, RedisURI address, int timeout, int commandTimeout, String sslHostname);
RedisClient createClient(NodeType type, InetSocketAddress address, URI uri, String sslHostname);
RedisClient createClient(NodeType type, InetSocketAddress address, RedisURI uri, String sslHostname);
RedisClient createClient(NodeType type, URI address, String sslHostname);
RedisClient createClient(NodeType type, RedisURI address, String sslHostname);
MasterSlaveEntry getEntry(RedisClient redisClient);

@ -16,7 +16,6 @@
package org.redisson.connection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@ -27,6 +26,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.redisson.api.RFuture;
import org.redisson.client.RedisClient;
import org.redisson.connection.ClientConnectionsEntry.FreezeReason;
import org.redisson.misc.RedisURI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -48,19 +48,19 @@ public class DNSMonitor {
private final AddressResolver<InetSocketAddress> resolver;
private final ConnectionManager connectionManager;
private final Map<URI, InetSocketAddress> masters = new HashMap<URI, InetSocketAddress>();
private final Map<URI, InetSocketAddress> slaves = new HashMap<URI, InetSocketAddress>();
private final Map<RedisURI, InetSocketAddress> masters = new HashMap<>();
private final Map<RedisURI, InetSocketAddress> slaves = new HashMap<>();
private ScheduledFuture<?> dnsMonitorFuture;
private long dnsMonitoringInterval;
public DNSMonitor(ConnectionManager connectionManager, RedisClient masterHost, Collection<URI> slaveHosts, long dnsMonitoringInterval, AddressResolverGroup<InetSocketAddress> resolverGroup) {
public DNSMonitor(ConnectionManager connectionManager, RedisClient masterHost, Collection<RedisURI> slaveHosts, long dnsMonitoringInterval, AddressResolverGroup<InetSocketAddress> resolverGroup) {
this.resolver = resolverGroup.getResolver(connectionManager.getGroup().next());
masterHost.resolveAddr().syncUninterruptibly();
masters.put(masterHost.getConfig().getAddress(), masterHost.getAddr());
for (URI host : slaveHosts) {
for (RedisURI host : slaveHosts) {
Future<InetSocketAddress> resolveFuture = resolver.resolve(InetSocketAddress.createUnresolved(host.getHost(), host.getPort()));
resolveFuture.syncUninterruptibly();
slaves.put(host, resolveFuture.getNow());
@ -97,7 +97,7 @@ public class DNSMonitor {
}
private void monitorMasters(AtomicInteger counter) {
for (Entry<URI, InetSocketAddress> entry : masters.entrySet()) {
for (Entry<RedisURI, InetSocketAddress> entry : masters.entrySet()) {
Future<InetSocketAddress> resolveFuture = resolver.resolve(InetSocketAddress.createUnresolved(entry.getKey().getHost(), entry.getKey().getPort()));
resolveFuture.addListener(new FutureListener<InetSocketAddress>() {
@Override
@ -134,7 +134,7 @@ public class DNSMonitor {
}
private void monitorSlaves(AtomicInteger counter) {
for (Entry<URI, InetSocketAddress> entry : slaves.entrySet()) {
for (Entry<RedisURI, InetSocketAddress> entry : slaves.entrySet()) {
Future<InetSocketAddress> resolveFuture = resolver.resolve(InetSocketAddress.createUnresolved(entry.getKey().getHost(), entry.getKey().getPort()));
resolveFuture.addListener(new FutureListener<InetSocketAddress>() {
@Override

@ -16,18 +16,19 @@
package org.redisson.connection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.stream.Collectors;
import org.redisson.Version;
import org.redisson.api.NodeType;
@ -48,8 +49,8 @@ import org.redisson.config.TransportMode;
import org.redisson.misc.CountableListener;
import org.redisson.misc.InfinitySemaphoreLatch;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedisURI;
import org.redisson.misc.RedissonPromise;
import org.redisson.misc.URIBuilder;
import org.redisson.pubsub.PublishSubscribeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -241,7 +242,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
}
}
protected RFuture<RedisConnection> connectToNode(BaseMasterSlaveServersConfig<?> cfg, URI addr, RedisClient client, String sslHostname) {
protected RFuture<RedisConnection> connectToNode(BaseMasterSlaveServersConfig<?> cfg, RedisURI addr, RedisClient client, String sslHostname) {
final Object key;
if (client != null) {
key = client;
@ -342,7 +343,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
} else {
entry = createMasterSlaveEntry(config);
}
RFuture<RedisClient> f = entry.setupMasterEntry(config.getMasterAddress());
RFuture<RedisClient> f = entry.setupMasterEntry(new RedisURI(config.getMasterAddress()));
f.syncUninterruptibly();
for (int slot = singleSlotRange.getStartSlot(); slot < singleSlotRange.getEndSlot() + 1; slot++) {
@ -358,15 +359,16 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
protected void startDNSMonitoring(RedisClient masterHost) {
if (config.getDnsMonitoringInterval() != -1) {
Set<RedisURI> slaveAddresses = config.getSlaveAddresses().stream().map(r -> new RedisURI(r)).collect(Collectors.toSet());
dnsMonitor = new DNSMonitor(this, masterHost,
config.getSlaveAddresses(), config.getDnsMonitoringInterval(), resolverGroup);
slaveAddresses, config.getDnsMonitoringInterval(), resolverGroup);
dnsMonitor.start();
}
}
protected MasterSlaveEntry createMasterSlaveEntry(MasterSlaveServersConfig config) {
MasterSlaveEntry entry = new MasterSlaveEntry(this, config);
List<RFuture<Void>> fs = entry.initSlaveBalancer(java.util.Collections.<URI>emptySet());
List<RFuture<Void>> fs = entry.initSlaveBalancer(java.util.Collections.<RedisURI>emptySet());
for (RFuture<Void> future : fs) {
future.syncUninterruptibly();
}
@ -412,31 +414,31 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
}
@Override
public RedisClient createClient(NodeType type, URI address, String sslHostname) {
public RedisClient createClient(NodeType type, RedisURI address, String sslHostname) {
RedisClient client = createClient(type, address, config.getConnectTimeout(), config.getTimeout(), sslHostname);
return client;
}
@Override
public RedisClient createClient(NodeType type, InetSocketAddress address, URI uri, String sslHostname) {
public RedisClient createClient(NodeType type, InetSocketAddress address, RedisURI uri, String sslHostname) {
RedisClient client = createClient(type, address, uri, config.getConnectTimeout(), config.getTimeout(), sslHostname);
return client;
}
@Override
public RedisClient createClient(NodeType type, URI address, int timeout, int commandTimeout, String sslHostname) {
public RedisClient createClient(NodeType type, RedisURI address, int timeout, int commandTimeout, String sslHostname) {
RedisClientConfig redisConfig = createRedisConfig(type, address, timeout, commandTimeout, sslHostname);
return RedisClient.create(redisConfig);
}
private RedisClient createClient(NodeType type, InetSocketAddress address, URI uri, int timeout, int commandTimeout, String sslHostname) {
private RedisClient createClient(NodeType type, InetSocketAddress address, RedisURI uri, int timeout, int commandTimeout, String sslHostname) {
RedisClientConfig redisConfig = createRedisConfig(type, null, timeout, commandTimeout, sslHostname);
redisConfig.setAddress(address, uri);
return RedisClient.create(redisConfig);
}
protected RedisClientConfig createRedisConfig(NodeType type, URI address, int timeout, int commandTimeout, String sslHostname) {
protected RedisClientConfig createRedisConfig(NodeType type, RedisURI address, int timeout, int commandTimeout, String sslHostname) {
RedisClientConfig redisConfig = new RedisClientConfig();
redisConfig.setAddress(address)
.setTimer(timer)
@ -489,9 +491,9 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
return null;
}
private MasterSlaveEntry getEntry(URI addr) {
private MasterSlaveEntry getEntry(RedisURI addr) {
for (MasterSlaveEntry entry : client2entry.values()) {
if (URIBuilder.compare(entry.getClient().getAddr(), addr)) {
if (RedisURI.compare(entry.getClient().getAddr(), addr)) {
return entry;
}
if (entry.hasSlave(addr)) {
@ -521,7 +523,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
return slot2entry.get(slot);
}
protected final RFuture<RedisClient> changeMaster(int slot, URI address) {
protected final RFuture<RedisClient> changeMaster(int slot, RedisURI address) {
final MasterSlaveEntry entry = getEntry(slot);
final RedisClient oldClient = entry.getClient();
RFuture<RedisClient> future = entry.changeMaster(address);
@ -559,7 +561,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
}
// fix for https://github.com/redisson/redisson/issues/1548
if (source.getRedirect() != null
&& !URIBuilder.compare(entry.getClient().getAddr(), source.getAddr())
&& !RedisURI.compare(entry.getClient().getAddr(), source.getAddr())
&& entry.hasSlave(source.getAddr())) {
return entry.redirectedConnectionWriteOp(command, source.getAddr());
}
@ -714,12 +716,12 @@ public class MasterSlaveConnectionManager implements ConnectionManager {
return executor;
}
public URI getLastClusterNode() {
public RedisURI getLastClusterNode() {
return null;
}
@Override
public URI applyNatMap(URI address) {
public RedisURI applyNatMap(RedisURI address) {
return address;
}
}

@ -16,7 +16,6 @@
package org.redisson.connection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
@ -40,9 +39,9 @@ import org.redisson.connection.pool.MasterConnectionPool;
import org.redisson.connection.pool.MasterPubSubConnectionPool;
import org.redisson.misc.CountableListener;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedisURI;
import org.redisson.misc.RedissonPromise;
import org.redisson.misc.TransferListener;
import org.redisson.misc.URIBuilder;
import org.redisson.pubsub.PubSubConnectionEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -92,28 +91,29 @@ public class MasterSlaveEntry {
return config;
}
public List<RFuture<Void>> initSlaveBalancer(Collection<URI> disconnectedNodes) {
public List<RFuture<Void>> initSlaveBalancer(Collection<RedisURI> disconnectedNodes) {
boolean freezeMasterAsSlave = !config.getSlaveAddresses().isEmpty()
&& !config.checkSkipSlavesInit()
&& disconnectedNodes.size() < config.getSlaveAddresses().size();
List<RFuture<Void>> result = new LinkedList<RFuture<Void>>();
RFuture<Void> f = addSlave(config.getMasterAddress(), freezeMasterAsSlave, NodeType.MASTER);
RFuture<Void> f = addSlave(new RedisURI(config.getMasterAddress()), freezeMasterAsSlave, NodeType.MASTER);
result.add(f);
for (URI address : config.getSlaveAddresses()) {
f = addSlave(address, disconnectedNodes.contains(address), NodeType.SLAVE);
for (String address : config.getSlaveAddresses()) {
RedisURI uri = new RedisURI(address);
f = addSlave(uri, disconnectedNodes.contains(uri), NodeType.SLAVE);
result.add(f);
}
return result;
}
public RFuture<RedisClient> setupMasterEntry(InetSocketAddress address, URI uri) {
public RFuture<RedisClient> setupMasterEntry(InetSocketAddress address, RedisURI uri) {
RedisClient client = connectionManager.createClient(NodeType.MASTER, address, uri, sslHostname);
return setupMasterEntry(client);
}
public RFuture<RedisClient> setupMasterEntry(URI address) {
public RFuture<RedisClient> setupMasterEntry(RedisURI address) {
RedisClient client = connectionManager.createClient(NodeType.MASTER, address, sslHostname);
return setupMasterEntry(client);
}
@ -173,7 +173,7 @@ public class MasterSlaveEntry {
return slaveDown(entry);
}
public boolean slaveDown(URI address, FreezeReason freezeReason) {
public boolean slaveDown(RedisURI address, FreezeReason freezeReason) {
ClientConnectionsEntry entry = slaveBalancer.freeze(address, freezeReason);
if (entry == null) {
return false;
@ -276,7 +276,7 @@ public class MasterSlaveEntry {
return slaveBalancer.contains(addr);
}
public boolean hasSlave(URI addr) {
public boolean hasSlave(RedisURI addr) {
return slaveBalancer.contains(addr);
}
@ -284,11 +284,11 @@ public class MasterSlaveEntry {
return slaveBalancer.getAvailableClients();
}
public RFuture<Void> addSlave(URI address) {
public RFuture<Void> addSlave(RedisURI address) {
return addSlave(address, false, NodeType.SLAVE);
}
public RFuture<Void> addSlave(InetSocketAddress address, URI uri) {
public RFuture<Void> addSlave(InetSocketAddress address, RedisURI uri) {
return addSlave(address, uri, false, NodeType.SLAVE);
}
@ -323,12 +323,12 @@ public class MasterSlaveEntry {
return result;
}
private RFuture<Void> addSlave(InetSocketAddress address, URI uri, boolean freezed, NodeType nodeType) {
private RFuture<Void> addSlave(InetSocketAddress address, RedisURI uri, boolean freezed, NodeType nodeType) {
RedisClient client = connectionManager.createClient(NodeType.SLAVE, address, uri, sslHostname);
return addSlave(client, freezed, nodeType);
}
private RFuture<Void> addSlave(URI address, boolean freezed, NodeType nodeType) {
private RFuture<Void> addSlave(RedisURI address, boolean freezed, NodeType nodeType) {
RedisClient client = connectionManager.createClient(nodeType, address, sslHostname);
return addSlave(client, freezed, nodeType);
}
@ -361,11 +361,11 @@ public class MasterSlaveEntry {
return true;
}
public boolean isSlaveUnfreezed(URI address) {
public boolean isSlaveUnfreezed(RedisURI address) {
return slaveBalancer.isUnfreezed(address);
}
public boolean slaveUp(URI address, FreezeReason freezeReason) {
public boolean slaveUp(RedisURI address, FreezeReason freezeReason) {
if (!slaveBalancer.unfreeze(address, freezeReason)) {
return false;
}
@ -373,7 +373,7 @@ public class MasterSlaveEntry {
InetSocketAddress addr = masterEntry.getClient().getAddr();
// exclude master from slaves
if (!config.checkSkipSlavesInit()
&& !URIBuilder.compare(addr, address)) {
&& !RedisURI.compare(addr, address)) {
if (slaveDown(addr, FreezeReason.SYSTEM)) {
log.info("master {} excluded from slaves", addr);
}
@ -406,21 +406,21 @@ public class MasterSlaveEntry {
* @param address of Redis
* @return client
*/
public RFuture<RedisClient> changeMaster(URI address) {
public RFuture<RedisClient> changeMaster(RedisURI address) {
ClientConnectionsEntry oldMaster = masterEntry;
RFuture<RedisClient> future = setupMasterEntry(address);
changeMaster(address, oldMaster, future);
return future;
}
public void changeMaster(InetSocketAddress address, URI uri) {
public void changeMaster(InetSocketAddress address, RedisURI uri) {
ClientConnectionsEntry oldMaster = masterEntry;
RFuture<RedisClient> future = setupMasterEntry(address, uri);
changeMaster(uri, oldMaster, future);
}
private void changeMaster(URI address, ClientConnectionsEntry oldMaster,
private void changeMaster(RedisURI address, ClientConnectionsEntry oldMaster,
RFuture<RedisClient> future) {
future.onComplete((newMasterClient, e) -> {
if (e != null) {
@ -471,7 +471,7 @@ public class MasterSlaveEntry {
return writeConnectionPool.get(command);
}
public RFuture<RedisConnection> redirectedConnectionWriteOp(RedisCommand<?> command, URI addr) {
public RFuture<RedisConnection> redirectedConnectionWriteOp(RedisCommand<?> command, RedisURI addr) {
return slaveBalancer.getConnection(command, addr);
}
@ -482,7 +482,7 @@ public class MasterSlaveEntry {
return slaveBalancer.nextConnection(command);
}
public RFuture<RedisConnection> connectionReadOp(RedisCommand<?> command, URI addr) {
public RFuture<RedisConnection> connectionReadOp(RedisCommand<?> command, RedisURI addr) {
return slaveBalancer.getConnection(command, addr);
}

@ -15,9 +15,8 @@
*/
package org.redisson.connection;
import java.net.URI;
import org.redisson.client.RedisClient;
import org.redisson.misc.RedisURI;
/**
*
@ -29,7 +28,7 @@ public class NodeSource {
public enum Redirect {MOVED, ASK}
private Integer slot;
private URI addr;
private RedisURI addr;
private RedisClient redisClient;
private Redirect redirect;
private MasterSlaveEntry entry;
@ -52,7 +51,7 @@ public class NodeSource {
this.redisClient = redisClient;
}
public NodeSource(Integer slot, URI addr, Redirect redirect) {
public NodeSource(Integer slot, RedisURI addr, Redirect redirect) {
this.slot = slot;
this.addr = addr;
this.redirect = redirect;
@ -74,7 +73,7 @@ public class NodeSource {
return redisClient;
}
public URI getAddr() {
public RedisURI getAddr() {
return addr;
}

@ -15,7 +15,6 @@
*/
package org.redisson.connection;
import java.net.URI;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@ -34,6 +33,7 @@ import org.redisson.config.MasterSlaveServersConfig;
import org.redisson.config.ReadMode;
import org.redisson.config.ReplicatedServersConfig;
import org.redisson.connection.ClientConnectionsEntry.FreezeReason;
import org.redisson.misc.RedisURI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -53,7 +53,7 @@ public class ReplicatedConnectionManager extends MasterSlaveConnectionManager {
private final Logger log = LoggerFactory.getLogger(getClass());
private AtomicReference<URI> currentMaster = new AtomicReference<URI>();
private AtomicReference<RedisURI> currentMaster = new AtomicReference<>();
private ScheduledFuture<?> monitorFuture;
@ -68,7 +68,8 @@ public class ReplicatedConnectionManager extends MasterSlaveConnectionManager {
this.config = create(cfg);
initTimer(this.config);
for (URI addr : cfg.getNodeAddresses()) {
for (String address : cfg.getNodeAddresses()) {
RedisURI addr = new RedisURI(address);
RFuture<RedisConnection> connectionFuture = connectToNode(cfg, addr, null, addr.getHost());
connectionFuture.awaitUninterruptibly();
RedisConnection connection = connectionFuture.getNow();
@ -84,10 +85,10 @@ public class ReplicatedConnectionManager extends MasterSlaveConnectionManager {
}
currentMaster.set(addr);
log.info("{} is the master", addr);
this.config.setMasterAddress(addr);
this.config.setMasterAddress(addr.toString());
} else {
log.info("{} is a slave", addr);
this.config.addSlaveAddress(addr);
this.config.addSlaveAddress(addr.toString());
}
}
@ -123,11 +124,12 @@ public class ReplicatedConnectionManager extends MasterSlaveConnectionManager {
return;
}
URI master = currentMaster.get();
RedisURI master = currentMaster.get();
log.debug("Current master: {}", master);
AtomicInteger count = new AtomicInteger(cfg.getNodeAddresses().size());
for (URI addr : cfg.getNodeAddresses()) {
for (String address : cfg.getNodeAddresses()) {
RedisURI addr = new RedisURI(address);
RFuture<RedisConnection> connectionFuture = connectToNode(cfg, addr, null, addr.getHost());
connectionFuture.onComplete((connection, exc) -> {
if (exc != null) {
@ -180,7 +182,7 @@ public class ReplicatedConnectionManager extends MasterSlaveConnectionManager {
}, cfg.getScanInterval(), TimeUnit.MILLISECONDS);
}
private void slaveUp(URI uri) {
private void slaveUp(RedisURI uri) {
MasterSlaveEntry entry = getEntry(singleSlotRange.getStartSlot());
if (entry.slaveUp(uri, FreezeReason.MANAGER)) {
log.info("slave: {} has up", uri);

@ -16,7 +16,6 @@
package org.redisson.connection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -52,8 +51,8 @@ import org.redisson.config.SentinelServersConfig;
import org.redisson.connection.ClientConnectionsEntry.FreezeReason;
import org.redisson.misc.CountableListener;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedisURI;
import org.redisson.misc.RedissonPromise;
import org.redisson.misc.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -72,13 +71,15 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Set<URI> sentinelHosts = new HashSet<>();
private final ConcurrentMap<URI, RedisClient> sentinels = new ConcurrentHashMap<>();
private final Set<RedisURI> sentinelHosts = new HashSet<>();
private final ConcurrentMap<RedisURI, RedisClient> sentinels = new ConcurrentHashMap<>();
private final AtomicReference<String> currentMaster = new AtomicReference<>();
private final Set<URI> disconnectedSlaves = new HashSet<>();
private final Set<RedisURI> disconnectedSlaves = new HashSet<>();
private ScheduledFuture<?> monitorFuture;
private AddressResolver<InetSocketAddress> sentinelResolver;
private final Map<String, String> natMap;
private boolean usePassword = false;
@ -95,9 +96,12 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
this.config = create(cfg);
initTimer(this.config);
this.natMap=cfg.getNatMap();
this.sentinelResolver = resolverGroup.getResolver(getGroup().next());
for (URI addr : cfg.getSentinelAddresses()) {
for (String address : cfg.getSentinelAddresses()) {
RedisURI addr = new RedisURI(address);
RedisClient client = createClient(NodeType.SENTINEL, addr, this.config.getConnectTimeout(), this.config.getRetryInterval() * this.config.getRetryAttempts(), null);
try {
RedisConnection c = client.connect();
@ -113,7 +117,8 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
}
}
for (URI addr : cfg.getSentinelAddresses()) {
for (String address : cfg.getSentinelAddresses()) {
RedisURI addr = new RedisURI(address);
if (NetUtil.createByteArrayFromIpAddressString(addr.getHost()) == null && !addr.getHost().equals("localhost")) {
sentinelHosts.add(convert(addr.getHost(), "" + addr.getPort()));
}
@ -152,7 +157,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
log.info("slave: {} added", host);
if (flags.contains("s_down") || flags.contains("disconnected")) {
URI uri = URIBuilder.create(host);
RedisURI uri = new RedisURI(host);
disconnectedSlaves.add(uri);
log.warn("slave: {} is down", host);
}
@ -168,12 +173,12 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
String ip = map.get("ip");
String port = map.get("port");
URI sentinelAddr = convert(ip, port);
RedisURI sentinelAddr = convert(ip, port);
RFuture<Void> future = registerSentinel(sentinelAddr, this.config);
connectionFutures.add(future);
}
URI currentAddr = convert(client.getAddr().getAddress().getHostAddress(), "" + client.getAddr().getPort());
RedisURI currentAddr = convert(client.getAddr().getAddress().getHostAddress(), "" + client.getAddr().getPort());
RFuture<Void> f = registerSentinel(currentAddr, this.config);
connectionFutures.add(f);
@ -217,7 +222,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
}
@Override
protected RedisClientConfig createRedisConfig(NodeType type, URI address, int timeout, int commandTimeout,
protected RedisClientConfig createRedisConfig(NodeType type, RedisURI address, int timeout, int commandTimeout,
String sslHostname) {
RedisClientConfig result = super.createRedisConfig(type, address, timeout, commandTimeout, sslHostname);
if (type == NodeType.SENTINEL && !usePassword) {
@ -240,7 +245,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
}
};
for (URI host : sentinelHosts) {
for (RedisURI host : sentinelHosts) {
Future<List<InetSocketAddress>> allNodes = sentinelResolver.resolveAll(InetSocketAddress.createUnresolved(host.getHost(), host.getPort()));
allNodes.addListener(new FutureListener<List<InetSocketAddress>>() {
@Override
@ -250,11 +255,11 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
return;
}
Set<URI> newUris = future.getNow().stream()
Set<RedisURI> newUris = future.getNow().stream()
.map(addr -> convert(addr.getAddress().getHostAddress(), "" + addr.getPort()))
.collect(Collectors.toSet());
for (URI uri : newUris) {
for (RedisURI uri : newUris) {
if (!sentinels.containsKey(uri)) {
registerSentinel(uri, getConfig());
}
@ -344,7 +349,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
String newMaster = createAddress(master.get(0), master.get(1));
if (!newMaster.equals(current)
&& currentMaster.compareAndSet(current, newMaster)) {
RFuture<RedisClient> changeFuture = changeMaster(singleSlotRange.getStartSlot(), URIBuilder.create(newMaster));
RFuture<RedisClient> changeFuture = changeMaster(singleSlotRange.getStartSlot(), new RedisURI(newMaster));
changeFuture.onComplete((res, ex) -> {
if (ex != null) {
currentMaster.compareAndSet(newMaster, current);
@ -428,7 +433,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
return;
}
Set<URI> newUris = list.stream().filter(m -> {
Set<RedisURI> newUris = list.stream().filter(m -> {
String flags = m.get("flags");
if (!m.isEmpty() && !flags.contains("disconnected") && !flags.contains("s_down")) {
return true;
@ -441,7 +446,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
}).collect(Collectors.toSet());
InetSocketAddress addr = connection.getRedisClient().getAddr();
URI currentAddr = convert(addr.getAddress().getHostAddress(), "" + addr.getPort());
RedisURI currentAddr = convert(addr.getAddress().getHostAddress(), "" + addr.getPort());
newUris.add(currentAddr);
updateSentinels(newUris);
@ -449,17 +454,17 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
sentinelsFuture.onComplete(commonListener);
}
private void updateSentinels(Set<URI> newUris) {
Set<URI> currentUris = new HashSet<>(SentinelConnectionManager.this.sentinels.keySet());
Set<URI> addedUris = new HashSet<>(newUris);
private void updateSentinels(Set<RedisURI> newUris) {
Set<RedisURI> currentUris = new HashSet<>(SentinelConnectionManager.this.sentinels.keySet());
Set<RedisURI> addedUris = new HashSet<>(newUris);
addedUris.removeAll(currentUris);
for (URI uri : addedUris) {
for (RedisURI uri : addedUris) {
registerSentinel(uri, getConfig());
}
currentUris.removeAll(newUris);
for (URI uri : currentUris) {
for (RedisURI uri : currentUris) {
RedisClient sentinel = SentinelConnectionManager.this.sentinels.remove(uri);
if (sentinel != null) {
sentinel.shutdownAsync();
@ -469,8 +474,14 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
}
private String createAddress(String host, Object port) {
if (host.contains(":") && !host.startsWith("[")) {
host = "[" + host + "]";
if (host.contains(":")){
String pureHost = host.replaceAll("[\\[\\]]", "");
host = applyNatMap(pureHost);
if (host.contains(":")) {
host = "[" + host + "]";
}
} else {
host = applyNatMap(host);
}
return "redis://" + host + ":" + port;
}
@ -485,7 +496,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
return entry;
}
private RFuture<Void> registerSentinel(URI addr, MasterSlaveServersConfig c) {
private RFuture<Void> registerSentinel(RedisURI addr, MasterSlaveServersConfig c) {
RedisClient sentinel = sentinels.get(addr);
if (sentinel != null) {
return RedissonPromise.newSucceededFuture(null);
@ -511,9 +522,9 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
RPromise<Void> result = new RedissonPromise<Void>();
// to avoid addition twice
MasterSlaveEntry entry = getEntry(singleSlotRange.getStartSlot());
URI uri = convert(ip, port);
RedisURI uri = convert(ip, port);
if (!entry.hasSlave(uri) && !config.checkSkipSlavesInit()) {
RFuture<Void> future = entry.addSlave(URIBuilder.create(addr));
RFuture<Void> future = entry.addSlave(new RedisURI(addr));
future.onComplete((res, e) -> {
if (e != null) {
result.tryFailure(e);
@ -536,9 +547,9 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
return result;
}
private URI convert(String ip, String port) {
private RedisURI convert(String ip, String port) {
String addr = createAddress(ip, port);
URI uri = URIBuilder.create(addr);
RedisURI uri = new RedisURI(addr);
return uri;
}
@ -547,7 +558,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
log.warn("slave: {}:{} has down", ip, port);
} else {
MasterSlaveEntry entry = getEntry(singleSlotRange.getStartSlot());
URI uri = convert(ip, port);
RedisURI uri = convert(ip, port);
if (entry.slaveDown(uri, FreezeReason.MANAGER)) {
log.warn("slave: {}:{} has down", ip, port);
}
@ -571,7 +582,7 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
return;
}
URI uri = convert(ip, port);
RedisURI uri = convert(ip, port);
if (getEntry(singleSlotRange.getStartSlot()).slaveUp(uri, FreezeReason.MANAGER)) {
String slaveAddr = ip + ":" + port;
log.info("slave: {} has up", slaveAddr);
@ -607,5 +618,13 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager {
super.shutdown();
}
private String applyNatMap(String ip) {
String mappedAddress = natMap.get(ip);
if (mappedAddress != null) {
return mappedAddress;
}
return ip;
}
}

@ -15,12 +15,11 @@
*/
package org.redisson.connection;
import java.net.URI;
import org.redisson.api.RFuture;
import org.redisson.client.RedisConnection;
import org.redisson.client.protocol.RedisCommand;
import org.redisson.config.MasterSlaveServersConfig;
import org.redisson.misc.RedisURI;
/**
*
@ -34,7 +33,7 @@ public class SingleEntry extends MasterSlaveEntry {
}
@Override
public RFuture<RedisConnection> connectionReadOp(RedisCommand<?> command, URI addr) {
public RFuture<RedisConnection> connectionReadOp(RedisCommand<?> command, RedisURI addr) {
return super.connectionWriteOp(command);
}

@ -16,7 +16,6 @@
package org.redisson.connection.balancer;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
@ -39,8 +38,8 @@ import org.redisson.connection.pool.PubSubConnectionPool;
import org.redisson.connection.pool.SlaveConnectionPool;
import org.redisson.misc.CountableListener;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedisURI;
import org.redisson.misc.RedissonPromise;
import org.redisson.misc.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -107,7 +106,7 @@ public class LoadBalancerManager {
return count;
}
public boolean unfreeze(URI address, FreezeReason freezeReason) {
public boolean unfreeze(RedisURI address, FreezeReason freezeReason) {
ClientConnectionsEntry entry = getEntry(address);
if (entry == null) {
throw new IllegalStateException("Can't find " + address + " in slaves!");
@ -145,7 +144,7 @@ public class LoadBalancerManager {
return false;
}
public ClientConnectionsEntry freeze(URI address, FreezeReason freezeReason) {
public ClientConnectionsEntry freeze(RedisURI address, FreezeReason freezeReason) {
ClientConnectionsEntry connectionEntry = getEntry(address);
return freeze(connectionEntry, freezeReason);
}
@ -190,12 +189,12 @@ public class LoadBalancerManager {
return getEntry(addr) != null;
}
public boolean isUnfreezed(URI addr) {
public boolean isUnfreezed(RedisURI addr) {
ClientConnectionsEntry entry = getEntry(addr);
return !entry.isFreezed();
}
public boolean contains(URI addr) {
public boolean contains(RedisURI addr) {
return getEntry(addr) != null;
}
@ -203,10 +202,10 @@ public class LoadBalancerManager {
return getEntry(redisClient) != null;
}
private ClientConnectionsEntry getEntry(URI addr) {
private ClientConnectionsEntry getEntry(RedisURI addr) {
for (ClientConnectionsEntry entry : client2Entry.values()) {
InetSocketAddress entryAddr = entry.getClient().getAddr();
if (URIBuilder.compare(entryAddr, addr)) {
if (RedisURI.compare(entryAddr, addr)) {
return entry;
}
}
@ -227,7 +226,7 @@ public class LoadBalancerManager {
return client2Entry.get(redisClient);
}
public RFuture<RedisConnection> getConnection(RedisCommand<?> command, URI addr) {
public RFuture<RedisConnection> getConnection(RedisCommand<?> command, RedisURI addr) {
ClientConnectionsEntry entry = getEntry(addr);
if (entry != null) {
return slaveConnectionPool.get(command, entry);

@ -16,7 +16,6 @@
package org.redisson.connection.balancer;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@ -29,7 +28,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.redisson.connection.ClientConnectionsEntry;
import org.redisson.misc.URIBuilder;
import org.redisson.misc.RedisURI;
/**
* Weighted Round Robin balancer.
@ -77,7 +76,7 @@ public class WeightedRoundRobinBalancer implements LoadBalancer {
*/
public WeightedRoundRobinBalancer(Map<String, Integer> weights, int defaultWeight) {
for (Entry<String, Integer> entry : weights.entrySet()) {
URI uri = URIBuilder.create(entry.getKey());
RedisURI uri = new RedisURI(entry.getKey());
InetSocketAddress addr = new InetSocketAddress(uri.getHost(), uri.getPort());
if (entry.getValue() <= 0) {
throw new IllegalArgumentException("Weight can't be less than or equal zero");

@ -275,9 +275,18 @@ public class TasksRunnerService implements RemoteExecutorService {
T task;
if (params.getLambdaBody() != null) {
ByteArrayInputStream is = new ByteArrayInputStream(params.getLambdaBody());
ObjectInput oo = new CustomObjectInputStream(classLoaderCodec.getClassLoader(), is);
task = (T) oo.readObject();
oo.close();
//set thread context class loader to be the classLoaderCodec.getClassLoader() variable as there could be reflection
//done while reading from input stream which reflection will use thread class loader to load classes on demand
ClassLoader currentThreadClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(classLoaderCodec.getClassLoader());
ObjectInput oo = new CustomObjectInputStream(classLoaderCodec.getClassLoader(), is);
task = (T) oo.readObject();
oo.close();
} finally {
Thread.currentThread().setContextClassLoader(currentThreadClassLoader);
}
} else {
task = (T) classLoaderCodec.getValueDecoder().decode(stateBuf, null);
}

@ -70,6 +70,7 @@ import org.redisson.liveobject.resolver.NamingScheme;
public class RedissonObjectBuilder {
private static final Map<Class<?>, Class<? extends RObject>> SUPPORTED_CLASS_MAPPING = new LinkedHashMap<>();
private static final Map<Class<?>, CodecMethodRef> REFERENCES = new HashMap<>();
static {
SUPPORTED_CLASS_MAPPING.put(SortedSet.class, RedissonSortedSet.class);
@ -81,6 +82,10 @@ public class RedissonObjectBuilder {
SUPPORTED_CLASS_MAPPING.put(BlockingQueue.class, RedissonBlockingQueue.class);
SUPPORTED_CLASS_MAPPING.put(Queue.class, RedissonQueue.class);
SUPPORTED_CLASS_MAPPING.put(List.class, RedissonList.class);
fillCodecMethods(REFERENCES, RedissonClient.class, RObject.class);
fillCodecMethods(REFERENCES, RedissonReactiveClient.class, RObjectReactive.class);
fillCodecMethods(REFERENCES, RedissonRxClient.class, RObjectRx.class);
}
private final Config config;
@ -98,16 +103,11 @@ public class RedissonObjectBuilder {
}
}
private static final Map<Class<?>, CodecMethodRef> REFERENCES = new HashMap<>();
private final ReferenceCodecProvider codecProvider = new DefaultReferenceCodecProvider();
public RedissonObjectBuilder(Config config) {
super();
this.config = config;
fillCodecMethods(REFERENCES, RedissonClient.class, RObject.class);
fillCodecMethods(REFERENCES, RedissonReactiveClient.class, RObjectReactive.class);
fillCodecMethods(REFERENCES, RedissonRxClient.class, RObjectRx.class);
}
public ReferenceCodecProvider getReferenceCodecProvider() {
@ -177,7 +177,7 @@ public class RedissonObjectBuilder {
return null;
}
private void fillCodecMethods(Map<Class<?>, CodecMethodRef> map, Class<?> clientClazz, Class<?> objectClazz) {
private static void fillCodecMethods(Map<Class<?>, CodecMethodRef> map, Class<?> clientClazz, Class<?> objectClazz) {
for (Method method : clientClazz.getDeclaredMethods()) {
if (!method.getReturnType().equals(Void.TYPE)
&& objectClazz.isAssignableFrom(method.getReturnType())

@ -0,0 +1,127 @@
/**
* Copyright (c) 2013-2019 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.misc;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.URL;
/**
*
* @author Nikita Koksharov
*
*/
public class RedisURI {
private final boolean ssl;
private final String host;
private final int port;
public RedisURI(String uri) {
if (!uri.startsWith("redis://")
&& !uri.startsWith("rediss://")) {
throw new IllegalArgumentException("Redis url should start with redis:// or rediss:// (for SSL connection)");
}
String urlHost = uri.replaceFirst("redis://", "http://").replaceFirst("rediss://", "http://");
String ipV6Host = uri.substring(uri.indexOf("://")+3, uri.lastIndexOf(":"));
if (ipV6Host.contains(":")) {
urlHost = urlHost.replace(ipV6Host, "[" + ipV6Host + "]");
}
try {
URL url = new URL(urlHost);
host = url.getHost();
port = url.getPort();
ssl = uri.startsWith("rediss://");
} catch (MalformedURLException e) {
throw new IllegalArgumentException(e);
}
}
public String getScheme() {
if (ssl) {
return "rediss";
}
return "redis";
}
public boolean isSsl() {
return ssl;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
private static String trimIpv6Brackets(String host) {
if (host.startsWith("[") && host.endsWith("]")) {
return host.substring(1, host.length() - 1);
}
return host;
}
public static boolean compare(InetSocketAddress entryAddr, RedisURI addr) {
if (((entryAddr.getHostName() != null && entryAddr.getHostName().equals(trimIpv6Brackets(addr.getHost())))
|| entryAddr.getAddress().getHostAddress().equals(trimIpv6Brackets(addr.getHost())))
&& entryAddr.getPort() == addr.getPort()) {
return true;
}
return false;
}
@Override
@SuppressWarnings("AvoidInlineConditionals")
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((host == null) ? 0 : host.hashCode());
result = prime * result + port;
result = prime * result + (ssl ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
RedisURI other = (RedisURI) obj;
if (host == null) {
if (other.host != null)
return false;
} else if (!host.equals(other.host))
return false;
if (port != other.port)
return false;
if (ssl != other.ssl)
return false;
return true;
}
@Override
public String toString() {
return getScheme() + "://" + host + ":" + port;
}
}

@ -1,90 +0,0 @@
/**
* Copyright (c) 2013-2019 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.misc;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.InetSocketAddress;
import java.net.URI;
/**
*
* @author Rui Gu (https://github.com/jackygurui)
*/
public class URIBuilder {
public static URI create(String uri) {
if (!uri.startsWith("redis://")
&& !uri.startsWith("rediss://")) {
throw new IllegalArgumentException("Redis url should start with redis:// or rediss:// (for SSL connection)");
}
URI u = URI.create(uri);
// Let's assuming most of the time it is OK.
if (u.getHost() != null) {
return u;
}
String s = uri.substring(0, uri.lastIndexOf(":")).replaceFirst("redis://", "").replaceFirst("rediss://", "");
// Assuming this is an IPv6 format, other situations will be handled by
// Netty at a later stage.
return URI.create(uri.replace(s, "[" + s + "]"));
}
public static void patchUriObject() {
try {
patchUriField(35184372088832L, "L_DASH");
patchUriField(2147483648L, "H_DASH");
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private static void patchUriField(Long maskValue, String fieldName)
throws IOException {
try {
Field field = URI.class.getDeclaredField(fieldName);
Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.setAccessible(true);
field.setLong(null, maskValue);
} catch (NoSuchFieldException e) {
// skip for Android platform
} catch (Exception e) {
throw new IOException(e);
}
}
private static String trimIpv6Brackets(String host) {
if (host.startsWith("[") && host.endsWith("]")) {
return host.substring(1, host.length() - 1);
}
return host;
}
public static boolean compare(InetSocketAddress entryAddr, URI addr) {
if (((entryAddr.getHostName() != null && entryAddr.getHostName().equals(trimIpv6Brackets(addr.getHost())))
|| entryAddr.getAddress().getHostAddress().equals(trimIpv6Brackets(addr.getHost())))
&& entryAddr.getPort() == addr.getPort()) {
return true;
}
return false;
}
}

@ -44,6 +44,7 @@ import org.springframework.session.Session;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.util.StringUtils;
/**
*
@ -242,19 +243,26 @@ public class RedissonSessionRepository implements FindByIndexNameSessionReposito
private String keyPrefix = "spring:session:";
private Integer defaultMaxInactiveInterval;
public RedissonSessionRepository(RedissonClient redissonClient, ApplicationEventPublisher eventPublisher) {
public RedissonSessionRepository(RedissonClient redissonClient, ApplicationEventPublisher eventPublisher, String keyPrefix) {
this.redisson = redissonClient;
this.eventPublisher = eventPublisher;
deletedTopic = redisson.getPatternTopic("__keyevent@*:del", StringCodec.INSTANCE);
if (StringUtils.hasText(keyPrefix)) {
this.keyPrefix = keyPrefix;
}
deletedTopic = this.redisson.getPatternTopic("__keyevent@*:del", StringCodec.INSTANCE);
deletedTopic.addListener(String.class, this);
expiredTopic = redisson.getPatternTopic("__keyevent@*:expired", StringCodec.INSTANCE);
expiredTopic = this.redisson.getPatternTopic("__keyevent@*:expired", StringCodec.INSTANCE);
expiredTopic.addListener(String.class, this);
createdTopic = redisson.getPatternTopic(getEventsChannelPrefix() + "*", StringCodec.INSTANCE);
createdTopic = this.redisson.getPatternTopic(getEventsChannelPrefix() + "*", StringCodec.INSTANCE);
createdTopic.addListener(String.class, this);
}
public RedissonSessionRepository(RedissonClient redissonClient, ApplicationEventPublisher eventPublisher) {
this(redissonClient, eventPublisher, null);
}
@Override
public void onMessage(CharSequence pattern, CharSequence channel, String body) {
if (createdTopic.getPatternNames().contains(pattern.toString())) {

@ -17,6 +17,7 @@ package org.redisson.spring.session.config;
import java.util.Map;
import net.bytebuddy.build.ToStringPlugin;
import org.redisson.api.RedissonClient;
import org.redisson.spring.session.RedissonSessionRepository;
import org.springframework.context.ApplicationEventPublisher;
@ -47,10 +48,7 @@ public class RedissonHttpSessionConfiguration extends SpringHttpSessionConfigura
@Bean
public RedissonSessionRepository sessionRepository(
RedissonClient redissonClient, ApplicationEventPublisher eventPublisher) {
RedissonSessionRepository repository = new RedissonSessionRepository(redissonClient, eventPublisher);
if (StringUtils.hasText(keyPrefix)) {
repository.setKeyPrefix(keyPrefix);
}
RedissonSessionRepository repository = new RedissonSessionRepository(redissonClient, eventPublisher, keyPrefix);
repository.setDefaultMaxInactiveInterval(maxInactiveIntervalInSeconds);
return repository;
}

@ -37,6 +37,39 @@ public class RedissonRateLimiterTest extends BaseTest {
Assertions.assertThatThrownBy(() -> limiter.tryAcquire(20)).hasMessageContaining("Requested permits amount could not exceed defined rate");
assertThat(limiter.tryAcquire()).isTrue();
}
@Test
public void testZeroTimeout() throws InterruptedException {
RRateLimiter limiter = redisson.getRateLimiter("myLimiter");
limiter.trySetRate(RateType.OVERALL, 5, 1, RateIntervalUnit.SECONDS);
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isTrue();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isTrue();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isTrue();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isTrue();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isTrue();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isFalse();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isFalse();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isFalse();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isFalse();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isFalse();
Thread.sleep(1000);
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isTrue();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isTrue();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isTrue();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isTrue();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isTrue();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isFalse();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isFalse();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isFalse();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isFalse();
assertThat(limiter.tryAcquire(1, 0, TimeUnit.SECONDS)).isFalse();
}
@Test(timeout = 1500)
public void testTryAcquire() {

@ -20,6 +20,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.awaitility.Awaitility;
import org.awaitility.Duration;
import org.junit.Assert;
import org.junit.Test;
@ -31,6 +32,48 @@ import org.redisson.config.Config;
public class RedissonReadWriteLockTest extends BaseConcurrentTest {
@Test
public void testReadLockExpiration() throws Exception{
Thread thread1 = new Thread(() -> {
RReadWriteLock rReadWriteLock = redisson.getReadWriteLock("test");
RLock readLock = rReadWriteLock.readLock();
readLock.lock(10, TimeUnit.SECONDS);
try {
Thread.sleep(9100);
} catch (Exception e){}
readLock.unlock();
});
Thread thread2 = new Thread(() -> {
RReadWriteLock rReadWriteLock = redisson.getReadWriteLock("test");
RLock readLock = rReadWriteLock.readLock();
readLock.lock(3, TimeUnit.SECONDS);
try {
Thread.sleep(2800);
} catch (Exception e){}
readLock.unlock();
});
AtomicBoolean flag = new AtomicBoolean();
Thread thread3 = new Thread(() -> {
RReadWriteLock rReadWriteLock = redisson.getReadWriteLock("test");
RLock writeLock = rReadWriteLock.writeLock();
writeLock.lock(10, TimeUnit.SECONDS);
flag.set(true);
writeLock.unlock();
});
thread1.start();
thread1.join(300);
thread2.start();
thread2.join(300);
thread3.start();
thread3.join(300);
Awaitility.await().between(8, TimeUnit.SECONDS, 10, TimeUnit.SECONDS).untilTrue(flag);
}
@Test
public void testReadLockExpirationRenewal() throws InterruptedException {
int threadCount = 50;

@ -293,6 +293,29 @@ public class RedissonRemoteServiceTest extends BaseTest {
remoteService.deregister(RemoteInterface.class);
}
@Test
public void testFreeWorkers() throws InterruptedException, ExecutionException {
RedissonClient r1 = createInstance();
ExecutorService executor = Executors.newSingleThreadExecutor();
RRemoteService rs = r1.getRemoteService();
rs.register(RemoteInterface.class, new RemoteImpl(), 1, executor);
assertThat(rs.getFreeWorkers(RemoteInterface.class)).isEqualTo(1);
RedissonClient r2 = createInstance();
RemoteInterfaceAsync ri = r2.getRemoteService().get(RemoteInterfaceAsync.class);
RFuture<Void> f = ri.timeoutMethod();
Thread.sleep(100);
assertThat(rs.getFreeWorkers(RemoteInterface.class)).isEqualTo(0);
f.get();
assertThat(rs.getFreeWorkers(RemoteInterface.class)).isEqualTo(1);
r1.shutdown();
r2.shutdown();
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
}
@Test
public void testCancelAsync() throws InterruptedException {

@ -196,7 +196,35 @@ public class RedissonStreamTest extends BaseTest {
assertThat(pendingEntry.getLastTimeDelivered()).isOne();
}
}
@Test
public void testPendingRange() {
RStream<String, String> stream = redisson.getStream("test");
stream.add("0", "0");
stream.createGroup("testGroup");
StreamMessageId id1 = stream.add("11", "12");
StreamMessageId id2 = stream.add("21", "22");
Map<StreamMessageId, Map<String, String>> s = stream.readGroup("testGroup", "consumer1");
assertThat(s.size()).isEqualTo(2);
Map<StreamMessageId, Map<String, String>> pres = stream.pendingRange("testGroup", StreamMessageId.MIN, StreamMessageId.MAX, 10);
assertThat(pres.keySet()).containsExactly(id1, id2);
assertThat(pres.get(id1)).isEqualTo(Collections.singletonMap("11", "12"));
assertThat(pres.get(id2)).isEqualTo(Collections.singletonMap("21", "22"));
Map<StreamMessageId, Map<String, String>> pres2 = stream.pendingRange("testGroup", "consumer1", StreamMessageId.MIN, StreamMessageId.MAX, 10);
assertThat(pres2.keySet()).containsExactly(id1, id2);
assertThat(pres2.get(id1)).isEqualTo(Collections.singletonMap("11", "12"));
assertThat(pres2.get(id2)).isEqualTo(Collections.singletonMap("21", "22"));
Map<StreamMessageId, Map<String, String>> pres3 = stream.pendingRange("testGroup", "consumer2", StreamMessageId.MIN, StreamMessageId.MAX, 10);
assertThat(pres3).isEmpty();
}
@Test
public void testAck() {
RStream<String, String> stream = redisson.getStream("test");

Loading…
Cancel
Save