diff --git a/CHANGELOG.md b/CHANGELOG.md index b231b6f54..b4bf8f9e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 7c2cda6c7..94710061a 100644 --- a/README.md +++ b/README.md @@ -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 + redisson-hibernate-53 - 3.10.7 + 3.11.0 ``` @@ -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 diff --git a/redisson-hibernate/pom.xml b/redisson-hibernate/pom.xml index f7cfa8d1b..1c08591d7 100644 --- a/redisson-hibernate/pom.xml +++ b/redisson-hibernate/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-parent - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-hibernate/redisson-hibernate-4/pom.xml b/redisson-hibernate/redisson-hibernate-4/pom.xml index ef1b378da..0c3abf2ce 100644 --- a/redisson-hibernate/redisson-hibernate-4/pom.xml +++ b/redisson-hibernate/redisson-hibernate-4/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-hibernate - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-hibernate/redisson-hibernate-5/pom.xml b/redisson-hibernate/redisson-hibernate-5/pom.xml index c43756035..0d3e97939 100644 --- a/redisson-hibernate/redisson-hibernate-5/pom.xml +++ b/redisson-hibernate/redisson-hibernate-5/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-hibernate - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-hibernate/redisson-hibernate-52/pom.xml b/redisson-hibernate/redisson-hibernate-52/pom.xml index ab1b7bd34..713146a98 100644 --- a/redisson-hibernate/redisson-hibernate-52/pom.xml +++ b/redisson-hibernate/redisson-hibernate-52/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-hibernate - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-hibernate/redisson-hibernate-53/pom.xml b/redisson-hibernate/redisson-hibernate-53/pom.xml index 0aa632af6..6a798aa9f 100644 --- a/redisson-hibernate/redisson-hibernate-53/pom.xml +++ b/redisson-hibernate/redisson-hibernate-53/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-hibernate - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-spring-boot-starter/README.md b/redisson-spring-boot-starter/README.md index b0164aa9a..baa908883 100644 --- a/redisson-spring-boot-starter/README.md +++ b/redisson-spring-boot-starter/README.md @@ -16,14 +16,14 @@ Maven org.redisson redisson-spring-boot-starter - 3.10.7 + 3.11.0 ``` 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 diff --git a/redisson-spring-boot-starter/pom.xml b/redisson-spring-boot-starter/pom.xml index 4bdb19da8..c790079b5 100644 --- a/redisson-spring-boot-starter/pom.xml +++ b/redisson-spring-boot-starter/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-parent - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-spring-data/README.md b/redisson-spring-data/README.md index 5c4349e88..8b95bf70a 100644 --- a/redisson-spring-data/README.md +++ b/redisson-spring-data/README.md @@ -25,7 +25,7 @@ Maven redisson-spring-data-20 redisson-spring-data-21 - 3.10.7 + 3.11.0 ``` @@ -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 diff --git a/redisson-spring-data/pom.xml b/redisson-spring-data/pom.xml index 797bf10b2..0248c8a21 100644 --- a/redisson-spring-data/pom.xml +++ b/redisson-spring-data/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-parent - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-spring-data/redisson-spring-data-16/pom.xml b/redisson-spring-data/redisson-spring-data-16/pom.xml index aa45a02f7..794b2c3f0 100644 --- a/redisson-spring-data/redisson-spring-data-16/pom.xml +++ b/redisson-spring-data/redisson-spring-data-16/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-spring-data - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-spring-data/redisson-spring-data-17/pom.xml b/redisson-spring-data/redisson-spring-data-17/pom.xml index 3cff96253..7c26ca242 100644 --- a/redisson-spring-data/redisson-spring-data-17/pom.xml +++ b/redisson-spring-data/redisson-spring-data-17/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-spring-data - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-spring-data/redisson-spring-data-18/pom.xml b/redisson-spring-data/redisson-spring-data-18/pom.xml index b26908dbf..0273742a5 100644 --- a/redisson-spring-data/redisson-spring-data-18/pom.xml +++ b/redisson-spring-data/redisson-spring-data-18/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-spring-data - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-spring-data/redisson-spring-data-20/pom.xml b/redisson-spring-data/redisson-spring-data-20/pom.xml index 017830754..f5f9d7e9f 100644 --- a/redisson-spring-data/redisson-spring-data-20/pom.xml +++ b/redisson-spring-data/redisson-spring-data-20/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-spring-data - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-spring-data/redisson-spring-data-21/pom.xml b/redisson-spring-data/redisson-spring-data-21/pom.xml index 072f4c717..c8b6cc719 100644 --- a/redisson-spring-data/redisson-spring-data-21/pom.xml +++ b/redisson-spring-data/redisson-spring-data-21/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-spring-data - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-tomcat/README.md b/redisson-tomcat/README.md index 323726b47..c1888d2d8 100644 --- a/redisson-tomcat/README.md +++ b/redisson-tomcat/README.md @@ -26,7 +26,7 @@ Add `RedissonSessionManager` into `tomcat/conf/context.xml` * `REDIS` - stores attributes into Redis only. Default mode.
- `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) diff --git a/redisson-tomcat/pom.xml b/redisson-tomcat/pom.xml index 6cddf2765..39630b8cc 100644 --- a/redisson-tomcat/pom.xml +++ b/redisson-tomcat/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-parent - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-tomcat/redisson-tomcat-6/pom.xml b/redisson-tomcat/redisson-tomcat-6/pom.xml index aa86d0502..a08e2b840 100644 --- a/redisson-tomcat/redisson-tomcat-6/pom.xml +++ b/redisson-tomcat/redisson-tomcat-6/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-tomcat - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-tomcat/redisson-tomcat-6/src/main/java/org/redisson/tomcat/RedissonSession.java b/redisson-tomcat/redisson-tomcat-6/src/main/java/org/redisson/tomcat/RedissonSession.java index 7a679677f..8adcf1a13 100644 --- a/redisson-tomcat/redisson-tomcat-6/src/main/java/org/redisson/tomcat/RedissonSession.java +++ b/redisson-tomcat/redisson-tomcat-6/src/main/java/org/redisson/tomcat/RedissonSession.java @@ -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 ATTRS = new HashSet(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 ATTRS = new HashSet(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 attrs; @@ -147,6 +153,10 @@ public class RedissonSession extends StandardSession { } if (broadcastSessionEvents) { + RSet 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 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 entry : attrs.entrySet()) { super.setAttribute(entry.getKey(), entry.getValue(), false); diff --git a/redisson-tomcat/redisson-tomcat-6/src/main/java/org/redisson/tomcat/RedissonSessionManager.java b/redisson-tomcat/redisson-tomcat-6/src/main/java/org/redisson/tomcat/RedissonSessionManager.java index 125ea8e91..431e6a66c 100644 --- a/redisson-tomcat/redisson-tomcat-6/src/main/java/org/redisson/tomcat/RedissonSessionManager.java +++ b/redisson-tomcat/redisson-tomcat-6/src/main/java/org/redisson/tomcat/RedissonSessionManager.java @@ -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 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 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 set = getNotifiedNodes(msg.getSessionId()); + set.add(nodeId); } } diff --git a/redisson-tomcat/redisson-tomcat-7/pom.xml b/redisson-tomcat/redisson-tomcat-7/pom.xml index 549901668..1d7a3607b 100644 --- a/redisson-tomcat/redisson-tomcat-7/pom.xml +++ b/redisson-tomcat/redisson-tomcat-7/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-tomcat - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-tomcat/redisson-tomcat-7/src/main/java/org/redisson/tomcat/RedissonSession.java b/redisson-tomcat/redisson-tomcat-7/src/main/java/org/redisson/tomcat/RedissonSession.java index 4fe201f19..4f13b69bc 100644 --- a/redisson-tomcat/redisson-tomcat-7/src/main/java/org/redisson/tomcat/RedissonSession.java +++ b/redisson-tomcat/redisson-tomcat-7/src/main/java/org/redisson/tomcat/RedissonSession.java @@ -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 ATTRS = new HashSet(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 ATTRS = new HashSet(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 attrs; @@ -147,6 +153,10 @@ public class RedissonSession extends StandardSession { } if (broadcastSessionEvents) { + RSet 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 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 entry : attrs.entrySet()) { super.setAttribute(entry.getKey(), entry.getValue(), false); diff --git a/redisson-tomcat/redisson-tomcat-7/src/main/java/org/redisson/tomcat/RedissonSessionManager.java b/redisson-tomcat/redisson-tomcat-7/src/main/java/org/redisson/tomcat/RedissonSessionManager.java index 2e39b1f8b..33f4ed3da 100644 --- a/redisson-tomcat/redisson-tomcat-7/src/main/java/org/redisson/tomcat/RedissonSessionManager.java +++ b/redisson-tomcat/redisson-tomcat-7/src/main/java/org/redisson/tomcat/RedissonSessionManager.java @@ -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 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 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 set = getNotifiedNodes(msg.getSessionId()); + set.add(nodeId); } } diff --git a/redisson-tomcat/redisson-tomcat-8/pom.xml b/redisson-tomcat/redisson-tomcat-8/pom.xml index ad9969dd6..7fefeff09 100644 --- a/redisson-tomcat/redisson-tomcat-8/pom.xml +++ b/redisson-tomcat/redisson-tomcat-8/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-tomcat - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-tomcat/redisson-tomcat-8/src/main/java/org/redisson/tomcat/RedissonSession.java b/redisson-tomcat/redisson-tomcat-8/src/main/java/org/redisson/tomcat/RedissonSession.java index 4fe201f19..e1247d70b 100644 --- a/redisson-tomcat/redisson-tomcat-8/src/main/java/org/redisson/tomcat/RedissonSession.java +++ b/redisson-tomcat/redisson-tomcat-8/src/main/java/org/redisson/tomcat/RedissonSession.java @@ -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 ATTRS = new HashSet(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 ATTRS = new HashSet(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 attrs; @@ -147,6 +153,10 @@ public class RedissonSession extends StandardSession { } if (broadcastSessionEvents) { + RSet 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 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 entry : attrs.entrySet()) { super.setAttribute(entry.getKey(), entry.getValue(), false); diff --git a/redisson-tomcat/redisson-tomcat-8/src/main/java/org/redisson/tomcat/RedissonSessionManager.java b/redisson-tomcat/redisson-tomcat-8/src/main/java/org/redisson/tomcat/RedissonSessionManager.java index 9a881d317..dba307955 100644 --- a/redisson-tomcat/redisson-tomcat-8/src/main/java/org/redisson/tomcat/RedissonSessionManager.java +++ b/redisson-tomcat/redisson-tomcat-8/src/main/java/org/redisson/tomcat/RedissonSessionManager.java @@ -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 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 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 set = getNotifiedNodes(msg.getSessionId()); + set.add(nodeId); } } diff --git a/redisson-tomcat/redisson-tomcat-9/pom.xml b/redisson-tomcat/redisson-tomcat-9/pom.xml index bdda53cd8..9bb96ffb6 100644 --- a/redisson-tomcat/redisson-tomcat-9/pom.xml +++ b/redisson-tomcat/redisson-tomcat-9/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-tomcat - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ diff --git a/redisson-tomcat/redisson-tomcat-9/src/main/java/org/redisson/tomcat/RedissonSession.java b/redisson-tomcat/redisson-tomcat-9/src/main/java/org/redisson/tomcat/RedissonSession.java index 4fe201f19..e1247d70b 100644 --- a/redisson-tomcat/redisson-tomcat-9/src/main/java/org/redisson/tomcat/RedissonSession.java +++ b/redisson-tomcat/redisson-tomcat-9/src/main/java/org/redisson/tomcat/RedissonSession.java @@ -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 ATTRS = new HashSet(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 ATTRS = new HashSet(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 attrs; @@ -147,6 +153,10 @@ public class RedissonSession extends StandardSession { } if (broadcastSessionEvents) { + RSet 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 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 entry : attrs.entrySet()) { super.setAttribute(entry.getKey(), entry.getValue(), false); diff --git a/redisson-tomcat/redisson-tomcat-9/src/main/java/org/redisson/tomcat/RedissonSessionManager.java b/redisson-tomcat/redisson-tomcat-9/src/main/java/org/redisson/tomcat/RedissonSessionManager.java index 9a881d317..dba307955 100644 --- a/redisson-tomcat/redisson-tomcat-9/src/main/java/org/redisson/tomcat/RedissonSessionManager.java +++ b/redisson-tomcat/redisson-tomcat-9/src/main/java/org/redisson/tomcat/RedissonSessionManager.java @@ -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 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 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 set = getNotifiedNodes(msg.getSessionId()); + set.add(nodeId); } } diff --git a/redisson/pom.xml b/redisson/pom.xml index c58afdd48..85c005da3 100644 --- a/redisson/pom.xml +++ b/redisson/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-parent - 3.11.0.1507-SNAPSHOT + 3.11.1-SNAPSHOT ../ @@ -353,7 +353,7 @@ 3.0.0 - none + verify check diff --git a/redisson/src/main/java/org/redisson/RedisNodes.java b/redisson/src/main/java/org/redisson/RedisNodes.java index c223f7bdc..c5ca8c6ac 100644 --- a/redisson/src/main/java/org/redisson/RedisNodes.java +++ b/redisson/src/main/java/org/redisson/RedisNodes.java @@ -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 implements NodesGroup { @Override public N getNode(String address) { Collection 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()); } diff --git a/redisson/src/main/java/org/redisson/RedissonRateLimiter.java b/redisson/src/main/java/org/redisson/RedissonRateLimiter.java index d9d7f221d..2ba29a313 100644 --- a/redisson/src/main/java/org/redisson/RedissonRateLimiter.java +++ b/redisson/src/main/java/org/redisson/RedissonRateLimiter.java @@ -126,7 +126,7 @@ public class RedissonRateLimiter extends RedissonObject implements RRateLimiter public RFuture tryAcquireAsync(long permits, long timeout, TimeUnit unit) { RPromise promise = new RedissonPromise(); long timeoutInMillis = -1; - if (timeout > 0) { + if (timeout >= 0) { timeoutInMillis = unit.toMillis(timeout); } tryAcquireAsync(permits, promise, timeoutInMillis); diff --git a/redisson/src/main/java/org/redisson/RedissonReadLock.java b/redisson/src/main/java/org/redisson/RedissonReadLock.java index 8524ab809..36426219a 100644 --- a/redisson/src/main/java/org/redisson/RedissonReadLock.java +++ b/redisson/src/main/java/org/redisson/RedissonReadLock.java @@ -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]);", diff --git a/redisson/src/main/java/org/redisson/RedissonRemoteService.java b/redisson/src/main/java/org/redisson/RedissonRemoteService.java index 035e0c017..4bef90548 100644 --- a/redisson/src/main/java/org/redisson/RedissonRemoteService.java +++ b/redisson/src/main/java/org/redisson/RedissonRemoteService.java @@ -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; } diff --git a/redisson/src/main/java/org/redisson/RedissonStream.java b/redisson/src/main/java/org/redisson/RedissonStream.java index 805d56137..d57ba7b51 100644 --- a/redisson/src/main/java/org/redisson/RedissonStream.java +++ b/redisson/src/main/java/org/redisson/RedissonStream.java @@ -115,14 +115,24 @@ public class RedissonStream extends RedissonExpirable implements RStream 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 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> 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 extends RedissonExpirable implements RStream> listConsumersAsync(String groupName) { return commandExecutor.readAsync(getName(), StringCodec.INSTANCE, RedisCommands.XINFO_CONSUMERS, getName(), groupName); } + + private static final RedisCommand>> EVAL_XRANGE = new RedisCommand("EVAL", RedisCommands.XRANGE.getReplayMultiDecoder()); + @Override + public RFuture>> 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.singletonList(getName()), + groupName, startId, endId, count); + } + + @Override + public RFuture>> 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.singletonList(getName()), + groupName, startId, endId, count, consumerName); + } + + @Override + public Map> pendingRange(String groupName, String consumerName, StreamMessageId startId, + StreamMessageId endId, int count) { + return get(pendingRangeAsync(groupName, consumerName, startId, endId, count)); + } + + @Override + public Map> pendingRange(String groupName, StreamMessageId startId, StreamMessageId endId, + int count) { + return get(pendingRangeAsync(groupName, startId, endId, count)); + } + } diff --git a/redisson/src/main/java/org/redisson/api/RMapRx.java b/redisson/src/main/java/org/redisson/api/RMapRx.java index 74d867e0a..3a696915e 100644 --- a/redisson/src/main/java/org/redisson/api/RMapRx.java +++ b/redisson/src/main/java/org/redisson/api/RMapRx.java @@ -264,7 +264,7 @@ public interface RMapRx extends RExpirableRx { * * @return iterator */ - Single> entryIterator(); + Flowable> entryIterator(); /** * Returns iterator over map entries collection. @@ -275,7 +275,7 @@ public interface RMapRx extends RExpirableRx { * @param count - size of entries batch * @return iterator */ - Single> entryIterator(int count); + Flowable> entryIterator(int count); /** * Returns iterator over map entries collection. @@ -295,7 +295,7 @@ public interface RMapRx extends RExpirableRx { * @param pattern - key pattern * @return iterator */ - Single> entryIterator(String pattern); + Flowable> entryIterator(String pattern); /** * Returns iterator over map entries collection. @@ -316,7 +316,7 @@ public interface RMapRx extends RExpirableRx { * @param count - size of entries batch * @return iterator */ - Single> entryIterator(String pattern, int count); + Flowable> entryIterator(String pattern, int count); /** * Returns iterator over values collection of this map. diff --git a/redisson/src/main/java/org/redisson/api/RStream.java b/redisson/src/main/java/org/redisson/api/RStream.java index f4b3722bd..0366352b4 100644 --- a/redisson/src/main/java/org/redisson/api/RStream.java +++ b/redisson/src/main/java/org/redisson/api/RStream.java @@ -83,22 +83,30 @@ public interface RStream extends RStreamAsync, 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. *

* {@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 extends RStreamAsync, RExpirable { List 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. *

* {@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 extends RStreamAsync, RExpirable { */ List 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. + *

+ * {@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> 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. + *

+ * {@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> 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 extends RStreamAsync, RExpirable { Map>> read(int count, long timeout, TimeUnit unit, StreamMessageId id, Map 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 extends RStreamAsync, RExpirable { Map> 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 extends RStreamAsync, RExpirable { Map> 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 extends RStreamAsync, RExpirable { Map> 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 extends RStreamAsync, RExpirable { StreamInfo 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 listGroups(); /** - * Returns list of objects with information about group customers for specified groupName. + * Returns list of common info about group customers for specified groupName. * * @param groupName - name of group * @return list of info objects diff --git a/redisson/src/main/java/org/redisson/api/RStreamAsync.java b/redisson/src/main/java/org/redisson/api/RStreamAsync.java index 188003b26..b190dfb4f 100644 --- a/redisson/src/main/java/org/redisson/api/RStreamAsync.java +++ b/redisson/src/main/java/org/redisson/api/RStreamAsync.java @@ -85,46 +85,91 @@ public interface RStreamAsync extends RExpirableAsync { * @return marked messages amount */ RFuture 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 getPendingInfoAsync(String groupName); + + /* + * Use #getPendingInfoAsync method + */ + @Deprecated RFuture 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. *

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

- * {@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> 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. + *

+ * {@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>> 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. + *

+ * {@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>> 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 extends RExpirableAsync { RFuture>>> readAsync(int count, long timeout, TimeUnit unit, StreamMessageId id, Map 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 extends RExpirableAsync { RFuture>> 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 extends RExpirableAsync { RFuture>> 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 extends RExpirableAsync { RFuture>> 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 diff --git a/redisson/src/main/java/org/redisson/api/RStreamReactive.java b/redisson/src/main/java/org/redisson/api/RStreamReactive.java index 4f7cfd9a9..69480db39 100644 --- a/redisson/src/main/java/org/redisson/api/RStreamReactive.java +++ b/redisson/src/main/java/org/redisson/api/RStreamReactive.java @@ -89,11 +89,17 @@ public interface RStreamReactive extends RExpirableReactive { Mono 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 getPendingInfo(String groupName); + + /* + * Use #getPendingInfo method + */ + @Deprecated Mono listPending(String groupName); /** @@ -634,7 +640,7 @@ public interface RStreamReactive extends RExpirableReactive { Mono>>> read(int count, long timeout, TimeUnit unit, StreamMessageId id, Map 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 extends RExpirableReactive { Mono>> 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 extends RExpirableReactive { Mono>> 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 extends RExpirableReactive { Mono>> 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 extends RExpirableReactive { */ Mono> 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. + *

+ * {@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>> 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. + *

+ * {@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>> pendingRange(String groupName, String consumerName, StreamMessageId startId, StreamMessageId endId, int count); + } diff --git a/redisson/src/main/java/org/redisson/api/RStreamRx.java b/redisson/src/main/java/org/redisson/api/RStreamRx.java index 9b70b9fc7..12f7dab7d 100644 --- a/redisson/src/main/java/org/redisson/api/RStreamRx.java +++ b/redisson/src/main/java/org/redisson/api/RStreamRx.java @@ -90,11 +90,17 @@ public interface RStreamRx extends RExpirableRx { Single 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 getPendingInfo(String groupName); + + /* + * Use #getPendingInfo method + */ + @Deprecated Single listPending(String groupName); /** @@ -635,7 +641,7 @@ public interface RStreamRx extends RExpirableRx { Single>>> read(int count, long timeout, TimeUnit unit, StreamMessageId id, Map 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 extends RExpirableRx { Single>> 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 extends RExpirableRx { Single>> 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 extends RExpirableRx { Single>> 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 extends RExpirableRx { */ Single> 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. + *

+ * {@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>> 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. + *

+ * {@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>> pendingRange(String groupName, String consumerName, StreamMessageId startId, StreamMessageId endId, int count); + } diff --git a/redisson/src/main/java/org/redisson/client/RedisClient.java b/redisson/src/main/java/org/redisson/client/RedisClient.java index 71935fe67..f3fa9707b 100644 --- a/redisson/src/main/java/org/redisson/client/RedisClient.java +++ b/redisson/src/main/java/org/redisson/client/RedisClient.java @@ -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> resolvedAddrFuture = new AtomicReference>(); private final Bootstrap bootstrap; private final Bootstrap pubSubBootstrap; - private final URI uri; + private final RedisURI uri; private InetSocketAddress resolvedAddr; private final ChannelGroup channels; diff --git a/redisson/src/main/java/org/redisson/client/RedisClientConfig.java b/redisson/src/main/java/org/redisson/client/RedisClientConfig.java index cc6c3842f..50e260a18 100644 --- a/redisson/src/main/java/org/redisson/client/RedisClientConfig.java +++ b/redisson/src/main/java/org/redisson/client/RedisClientConfig.java @@ -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() { diff --git a/redisson/src/main/java/org/redisson/client/RedisRedirectException.java b/redisson/src/main/java/org/redisson/client/RedisRedirectException.java index f5b80e979..f14fb54f9 100644 --- a/redisson/src/main/java/org/redisson/client/RedisRedirectException.java +++ b/redisson/src/main/java/org/redisson/client/RedisRedirectException.java @@ -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; } diff --git a/redisson/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java b/redisson/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java index 3daaad16b..a1c8c77fc 100644 --- a/redisson/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java +++ b/redisson/src/main/java/org/redisson/client/handler/ConnectionWatchdog.java @@ -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); } } }); diff --git a/redisson/src/main/java/org/redisson/cluster/ClusterConnectionManager.java b/redisson/src/main/java/org/redisson/cluster/ClusterConnectionManager.java index e561ebdcc..beb672c62 100644 --- a/redisson/src/main/java/org/redisson/cluster/ClusterConnectionManager.java +++ b/redisson/src/main/java/org/redisson/cluster/ClusterConnectionManager.java @@ -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> clusterNodesCommand; @@ -103,7 +103,8 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { Throwable lastException = null; List failedMasters = new ArrayList(); - for (URI addr : cfg.getNodeAddresses()) { + for (String address : cfg.getNodeAddresses()) { + RedisURI addr = new RedisURI(address); RFuture 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> futures = new ArrayList>(); if (config.checkSkipSlavesInit()) { e = new SingleEntry(ClusterConnectionManager.this, config); } else { - config.setSlaveAddresses(partition.getSlaveAddresses()); + Set 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 f = e.setupMasterEntry(config.getMasterAddress()); + RFuture f = e.setupMasterEntry(new RedisURI(config.getMasterAddress())); RPromise initFuture = new RedissonPromise(); futures.add(initFuture); f.onComplete((res, ex3) -> { @@ -275,12 +277,13 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { return result; } - private void scheduleClusterChangeCheck(ClusterServersConfig cfg, Iterator iterator) { + private void scheduleClusterChangeCheck(ClusterServersConfig cfg, Iterator 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 resolver = resolverGroup.getResolver(getGroup().next()); Future> allNodes = resolver.resolveAll(InetSocketAddress.createUnresolved(uri.getHost(), uri.getPort())); allNodes.addListener(new FutureListener>() { @@ -288,34 +291,34 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { public void operationComplete(Future> future) throws Exception { AtomicReference lastException = new AtomicReference(future.cause()); if (!future.isSuccess()) { - checkClusterState(cfg, Collections.emptyList().iterator(), lastException); + checkClusterState(cfg, Collections.emptyList().iterator(), lastException); return; } - List nodes = new ArrayList(); + List 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 nodesIterator = nodes.iterator(); + Iterator nodesIterator = nodes.iterator(); checkClusterState(cfg, nodesIterator, lastException); } }); } else { AtomicReference lastException = new AtomicReference(); - Iterator nodesIterator = iterator; + Iterator nodesIterator = iterator; if (nodesIterator == null) { - List nodes = new ArrayList(); - List slaves = new ArrayList(); + List nodes = new ArrayList<>(); + List slaves = new ArrayList<>(); for (ClusterPartition partition : getLastPartitions()) { if (!partition.isMasterFail()) { nodes.add(partition.getMasterAddress()); } - Set partitionSlaves = new HashSet(partition.getSlaveAddresses()); + Set 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 iterator, AtomicReference lastException) { + private void checkClusterState(ClusterServersConfig cfg, Iterator iterator, AtomicReference 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 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 iterator, URI uri, AtomicReference lastException) { + Iterator iterator, RedisURI uri, AtomicReference lastException) { RFuture> 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 addedSlaves = addRemoveSlaves(entry, currentPart, newPart); + Set 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 addedSlaves) { - Set aliveSlaves = new HashSet(currentPart.getFailedSlaveAddresses()); + private void upDownSlaves(MasterSlaveEntry entry, ClusterPartition currentPart, ClusterPartition newPart, Set addedSlaves) { + Set 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 failedSlaves = new HashSet(newPart.getFailedSlaveAddresses()); + Set 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 addRemoveSlaves(MasterSlaveEntry entry, ClusterPartition currentPart, ClusterPartition newPart) { - Set removedSlaves = new HashSet(currentPart.getSlaveAddresses()); + private Set addRemoveSlaves(MasterSlaveEntry entry, ClusterPartition currentPart, ClusterPartition newPart) { + Set 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 addedSlaves = new HashSet(newPart.getSlaveAddresses()); + Set addedSlaves = new HashSet<>(newPart.getSlaveAddresses()); addedSlaves.removeAll(currentPart.getSlaveAddresses()); - for (URI uri : addedSlaves) { + for (RedisURI uri : addedSlaves) { RFuture 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 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; } diff --git a/redisson/src/main/java/org/redisson/cluster/ClusterNodeInfo.java b/redisson/src/main/java/org/redisson/cluster/ClusterNodeInfo.java index 551c0c3fd..2b6facb83 100644 --- a/redisson/src/main/java/org/redisson/cluster/ClusterNodeInfo.java +++ b/redisson/src/main/java/org/redisson/cluster/ClusterNodeInfo.java @@ -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 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) { diff --git a/redisson/src/main/java/org/redisson/cluster/ClusterPartition.java b/redisson/src/main/java/org/redisson/cluster/ClusterPartition.java index 63abed343..12e78c547 100644 --- a/redisson/src/main/java/org/redisson/cluster/ClusterPartition.java +++ b/redisson/src/main/java/org/redisson/cluster/ClusterPartition.java @@ -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 slaveAddresses = new HashSet(); - private final Set failedSlaves = new HashSet(); + private RedisURI masterAddress; + private final Set slaveAddresses = new HashSet<>(); + private final Set failedSlaves = new HashSet<>(); private final BitSet slots = new BitSet(); private final Set slotRanges = new HashSet(); @@ -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 getFailedSlaveAddresses() { + public Set 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 getSlaveAddresses() { + public Set getSlaveAddresses() { return Collections.unmodifiableSet(slaveAddresses); } - public void removeSlaveAddress(URI uri) { + public void removeSlaveAddress(RedisURI uri) { slaveAddresses.remove(uri); failedSlaves.remove(uri); } diff --git a/redisson/src/main/java/org/redisson/codec/SerializationCodec.java b/redisson/src/main/java/org/redisson/codec/SerializationCodec.java index 66e0c677a..335dc327d 100644 --- a/redisson/src/main/java/org/redisson/codec/SerializationCodec.java +++ b/redisson/src/main/java/org/redisson/codec/SerializationCodec.java @@ -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) { diff --git a/redisson/src/main/java/org/redisson/config/ClusterServersConfig.java b/redisson/src/main/java/org/redisson/config/ClusterServersConfig.java index 9177499db..d786b58c6 100644 --- a/redisson/src/main/java/org/redisson/config/ClusterServersConfig.java +++ b/redisson/src/main/java/org/redisson/config/ClusterServersConfig.java @@ -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 nodeAddresses = new ArrayList(); + private List nodeAddresses = new ArrayList<>(); /** * Redis cluster scan interval in milliseconds @@ -60,15 +58,13 @@ public class ClusterServersConfig extends BaseMasterSlaveServersConfig getNodeAddresses() { + public List getNodeAddresses() { return nodeAddresses; } - void setNodeAddresses(List nodeAddresses) { + void setNodeAddresses(List nodeAddresses) { this.nodeAddresses = nodeAddresses; } diff --git a/redisson/src/main/java/org/redisson/config/Config.java b/redisson/src/main/java/org/redisson/config/Config.java index 1895aeaaa..c76122cb3 100644 --- a/redisson/src/main/java/org/redisson/config/Config.java +++ b/redisson/src/main/java/org/redisson/config/Config.java @@ -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()); diff --git a/redisson/src/main/java/org/redisson/config/ConfigSupport.java b/redisson/src/main/java/org/redisson/config/ConfigSupport.java index b1304e551..c67935969 100644 --- a/redisson/src/main/java/org/redisson/config/ConfigSupport.java +++ b/redisson/src/main/java/org/redisson/config/ConfigSupport.java @@ -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 address; - - @JsonIgnore - abstract SingleServerConfig setAddress(String address); - - @JsonIgnore - abstract URI getAddress(); - - @JsonIgnore - abstract void setAddress(URI address); - - } - - public abstract static class MasterSlaveServersConfigMixIn { - - @JsonProperty - List 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); diff --git a/redisson/src/main/java/org/redisson/config/MasterSlaveServersConfig.java b/redisson/src/main/java/org/redisson/config/MasterSlaveServersConfig.java index 68e48a398..8bc1bbd88 100644 --- a/redisson/src/main/java/org/redisson/config/MasterSlaveServersConfig.java +++ b/redisson/src/main/java/org/redisson/config/MasterSlaveServersConfig.java @@ -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 slaveAddresses = new HashSet(); + private Set slaveAddresses = new HashSet(); /** * 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 getSlaveAddresses() { + public Set getSlaveAddresses() { return slaveAddresses; } - public void setSlaveAddresses(Set readAddresses) { + public void setSlaveAddresses(Set readAddresses) { this.slaveAddresses = readAddresses; } diff --git a/redisson/src/main/java/org/redisson/config/ReplicatedServersConfig.java b/redisson/src/main/java/org/redisson/config/ReplicatedServersConfig.java index e70badf73..b5847d5dd 100644 --- a/redisson/src/main/java/org/redisson/config/ReplicatedServersConfig.java +++ b/redisson/src/main/java/org/redisson/config/ReplicatedServersConfig.java @@ -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 nodeAddresses = new ArrayList(); + private List nodeAddresses = new ArrayList<>(); /** * Replication group scan interval in milliseconds @@ -61,15 +60,13 @@ public class ReplicatedServersConfig extends BaseMasterSlaveServersConfig getNodeAddresses() { + public List getNodeAddresses() { return nodeAddresses; } - void setNodeAddresses(List nodeAddresses) { + void setNodeAddresses(List nodeAddresses) { this.nodeAddresses = nodeAddresses; } diff --git a/redisson/src/main/java/org/redisson/config/SentinelServersConfig.java b/redisson/src/main/java/org/redisson/config/SentinelServersConfig.java index b1f182e65..e25f4939b 100644 --- a/redisson/src/main/java/org/redisson/config/SentinelServersConfig.java +++ b/redisson/src/main/java/org/redisson/config/SentinelServersConfig.java @@ -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 { - private List sentinelAddresses = new ArrayList(); + private List sentinelAddresses = new ArrayList<>(); + + private Map natMap = Collections.emptyMap(); private String masterName; @@ -50,6 +54,7 @@ public class SentinelServersConfig extends BaseMasterSlaveServersConfig(config.getNatMap())); } /** @@ -73,15 +78,13 @@ public class SentinelServersConfig extends BaseMasterSlaveServersConfig getSentinelAddresses() { + public List getSentinelAddresses() { return sentinelAddresses; } - void setSentinelAddresses(List sentinelAddresses) { + void setSentinelAddresses(List sentinelAddresses) { this.sentinelAddresses = sentinelAddresses; } @@ -114,4 +117,19 @@ public class SentinelServersConfig extends BaseMasterSlaveServersConfig 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 natMap) { + this.natMap = natMap; + return this; + } + } diff --git a/redisson/src/main/java/org/redisson/config/SingleServerConfig.java b/redisson/src/main/java/org/redisson/config/SingleServerConfig.java index e8fc46989..ec190cec7 100644 --- a/redisson/src/main/java/org/redisson/config/SingleServerConfig.java +++ b/redisson/src/main/java/org/redisson/config/SingleServerConfig.java @@ -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 { * Redis server address * */ - private URI address; + private String address; /** * Minimum idle subscription connection amount @@ -116,20 +113,12 @@ public class SingleServerConfig extends BaseConfig { */ 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; } /** diff --git a/redisson/src/main/java/org/redisson/connection/ConnectionManager.java b/redisson/src/main/java/org/redisson/connection/ConnectionManager.java index e36a13876..7b4ef3309 100644 --- a/redisson/src/main/java/org/redisson/connection/ConnectionManager.java +++ b/redisson/src/main/java/org/redisson/connection/ConnectionManager.java @@ -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 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); diff --git a/redisson/src/main/java/org/redisson/connection/DNSMonitor.java b/redisson/src/main/java/org/redisson/connection/DNSMonitor.java index 55a694192..fe5236522 100644 --- a/redisson/src/main/java/org/redisson/connection/DNSMonitor.java +++ b/redisson/src/main/java/org/redisson/connection/DNSMonitor.java @@ -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 resolver; private final ConnectionManager connectionManager; - private final Map masters = new HashMap(); - private final Map slaves = new HashMap(); + private final Map masters = new HashMap<>(); + private final Map slaves = new HashMap<>(); private ScheduledFuture dnsMonitorFuture; private long dnsMonitoringInterval; - public DNSMonitor(ConnectionManager connectionManager, RedisClient masterHost, Collection slaveHosts, long dnsMonitoringInterval, AddressResolverGroup resolverGroup) { + public DNSMonitor(ConnectionManager connectionManager, RedisClient masterHost, Collection slaveHosts, long dnsMonitoringInterval, AddressResolverGroup 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 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 entry : masters.entrySet()) { + for (Entry entry : masters.entrySet()) { Future resolveFuture = resolver.resolve(InetSocketAddress.createUnresolved(entry.getKey().getHost(), entry.getKey().getPort())); resolveFuture.addListener(new FutureListener() { @Override @@ -134,7 +134,7 @@ public class DNSMonitor { } private void monitorSlaves(AtomicInteger counter) { - for (Entry entry : slaves.entrySet()) { + for (Entry entry : slaves.entrySet()) { Future resolveFuture = resolver.resolve(InetSocketAddress.createUnresolved(entry.getKey().getHost(), entry.getKey().getPort())); resolveFuture.addListener(new FutureListener() { @Override diff --git a/redisson/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java b/redisson/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java index d364bd4fb..b90514311 100644 --- a/redisson/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java +++ b/redisson/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java @@ -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 connectToNode(BaseMasterSlaveServersConfig cfg, URI addr, RedisClient client, String sslHostname) { + protected RFuture 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 f = entry.setupMasterEntry(config.getMasterAddress()); + RFuture 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 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> fs = entry.initSlaveBalancer(java.util.Collections.emptySet()); + List> fs = entry.initSlaveBalancer(java.util.Collections.emptySet()); for (RFuture 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 changeMaster(int slot, URI address) { + protected final RFuture changeMaster(int slot, RedisURI address) { final MasterSlaveEntry entry = getEntry(slot); final RedisClient oldClient = entry.getClient(); RFuture 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; } } diff --git a/redisson/src/main/java/org/redisson/connection/MasterSlaveEntry.java b/redisson/src/main/java/org/redisson/connection/MasterSlaveEntry.java index 9de6ac7b2..070e36534 100644 --- a/redisson/src/main/java/org/redisson/connection/MasterSlaveEntry.java +++ b/redisson/src/main/java/org/redisson/connection/MasterSlaveEntry.java @@ -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> initSlaveBalancer(Collection disconnectedNodes) { + public List> initSlaveBalancer(Collection disconnectedNodes) { boolean freezeMasterAsSlave = !config.getSlaveAddresses().isEmpty() && !config.checkSkipSlavesInit() && disconnectedNodes.size() < config.getSlaveAddresses().size(); List> result = new LinkedList>(); - RFuture f = addSlave(config.getMasterAddress(), freezeMasterAsSlave, NodeType.MASTER); + RFuture 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 setupMasterEntry(InetSocketAddress address, URI uri) { + public RFuture setupMasterEntry(InetSocketAddress address, RedisURI uri) { RedisClient client = connectionManager.createClient(NodeType.MASTER, address, uri, sslHostname); return setupMasterEntry(client); } - public RFuture setupMasterEntry(URI address) { + public RFuture 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 addSlave(URI address) { + public RFuture addSlave(RedisURI address) { return addSlave(address, false, NodeType.SLAVE); } - public RFuture addSlave(InetSocketAddress address, URI uri) { + public RFuture addSlave(InetSocketAddress address, RedisURI uri) { return addSlave(address, uri, false, NodeType.SLAVE); } @@ -323,12 +323,12 @@ public class MasterSlaveEntry { return result; } - private RFuture addSlave(InetSocketAddress address, URI uri, boolean freezed, NodeType nodeType) { + private RFuture 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 addSlave(URI address, boolean freezed, NodeType nodeType) { + private RFuture 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 changeMaster(URI address) { + public RFuture changeMaster(RedisURI address) { ClientConnectionsEntry oldMaster = masterEntry; RFuture 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 future = setupMasterEntry(address, uri); changeMaster(uri, oldMaster, future); } - private void changeMaster(URI address, ClientConnectionsEntry oldMaster, + private void changeMaster(RedisURI address, ClientConnectionsEntry oldMaster, RFuture future) { future.onComplete((newMasterClient, e) -> { if (e != null) { @@ -471,7 +471,7 @@ public class MasterSlaveEntry { return writeConnectionPool.get(command); } - public RFuture redirectedConnectionWriteOp(RedisCommand command, URI addr) { + public RFuture redirectedConnectionWriteOp(RedisCommand command, RedisURI addr) { return slaveBalancer.getConnection(command, addr); } @@ -482,7 +482,7 @@ public class MasterSlaveEntry { return slaveBalancer.nextConnection(command); } - public RFuture connectionReadOp(RedisCommand command, URI addr) { + public RFuture connectionReadOp(RedisCommand command, RedisURI addr) { return slaveBalancer.getConnection(command, addr); } diff --git a/redisson/src/main/java/org/redisson/connection/NodeSource.java b/redisson/src/main/java/org/redisson/connection/NodeSource.java index e74e40f0e..c9a0c5dc1 100644 --- a/redisson/src/main/java/org/redisson/connection/NodeSource.java +++ b/redisson/src/main/java/org/redisson/connection/NodeSource.java @@ -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; } diff --git a/redisson/src/main/java/org/redisson/connection/ReplicatedConnectionManager.java b/redisson/src/main/java/org/redisson/connection/ReplicatedConnectionManager.java index c4f116243..f66f77206 100644 --- a/redisson/src/main/java/org/redisson/connection/ReplicatedConnectionManager.java +++ b/redisson/src/main/java/org/redisson/connection/ReplicatedConnectionManager.java @@ -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 currentMaster = new AtomicReference(); + private AtomicReference 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 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 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); diff --git a/redisson/src/main/java/org/redisson/connection/SentinelConnectionManager.java b/redisson/src/main/java/org/redisson/connection/SentinelConnectionManager.java index b4116768a..02b7c1dc2 100755 --- a/redisson/src/main/java/org/redisson/connection/SentinelConnectionManager.java +++ b/redisson/src/main/java/org/redisson/connection/SentinelConnectionManager.java @@ -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 sentinelHosts = new HashSet<>(); - private final ConcurrentMap sentinels = new ConcurrentHashMap<>(); + private final Set sentinelHosts = new HashSet<>(); + private final ConcurrentMap sentinels = new ConcurrentHashMap<>(); private final AtomicReference currentMaster = new AtomicReference<>(); - private final Set disconnectedSlaves = new HashSet<>(); + private final Set disconnectedSlaves = new HashSet<>(); private ScheduledFuture monitorFuture; private AddressResolver sentinelResolver; + + private final Map 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 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 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> allNodes = sentinelResolver.resolveAll(InetSocketAddress.createUnresolved(host.getHost(), host.getPort())); allNodes.addListener(new FutureListener>() { @Override @@ -250,11 +255,11 @@ public class SentinelConnectionManager extends MasterSlaveConnectionManager { return; } - Set newUris = future.getNow().stream() + Set 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 changeFuture = changeMaster(singleSlotRange.getStartSlot(), URIBuilder.create(newMaster)); + RFuture 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 newUris = list.stream().filter(m -> { + Set 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 newUris) { - Set currentUris = new HashSet<>(SentinelConnectionManager.this.sentinels.keySet()); - Set addedUris = new HashSet<>(newUris); + private void updateSentinels(Set newUris) { + Set currentUris = new HashSet<>(SentinelConnectionManager.this.sentinels.keySet()); + Set 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 registerSentinel(URI addr, MasterSlaveServersConfig c) { + private RFuture 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 result = new RedissonPromise(); // 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 future = entry.addSlave(URIBuilder.create(addr)); + RFuture 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; + } } diff --git a/redisson/src/main/java/org/redisson/connection/SingleEntry.java b/redisson/src/main/java/org/redisson/connection/SingleEntry.java index 0e3aa0746..d479890f1 100644 --- a/redisson/src/main/java/org/redisson/connection/SingleEntry.java +++ b/redisson/src/main/java/org/redisson/connection/SingleEntry.java @@ -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 connectionReadOp(RedisCommand command, URI addr) { + public RFuture connectionReadOp(RedisCommand command, RedisURI addr) { return super.connectionWriteOp(command); } diff --git a/redisson/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java b/redisson/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java index 8c67466a4..3f086dcdc 100644 --- a/redisson/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java +++ b/redisson/src/main/java/org/redisson/connection/balancer/LoadBalancerManager.java @@ -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 getConnection(RedisCommand command, URI addr) { + public RFuture getConnection(RedisCommand command, RedisURI addr) { ClientConnectionsEntry entry = getEntry(addr); if (entry != null) { return slaveConnectionPool.get(command, entry); diff --git a/redisson/src/main/java/org/redisson/connection/balancer/WeightedRoundRobinBalancer.java b/redisson/src/main/java/org/redisson/connection/balancer/WeightedRoundRobinBalancer.java index 44b439790..e5b1cd0e9 100644 --- a/redisson/src/main/java/org/redisson/connection/balancer/WeightedRoundRobinBalancer.java +++ b/redisson/src/main/java/org/redisson/connection/balancer/WeightedRoundRobinBalancer.java @@ -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 weights, int defaultWeight) { for (Entry 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"); diff --git a/redisson/src/main/java/org/redisson/executor/TasksRunnerService.java b/redisson/src/main/java/org/redisson/executor/TasksRunnerService.java index d0cd44d49..a47877715 100644 --- a/redisson/src/main/java/org/redisson/executor/TasksRunnerService.java +++ b/redisson/src/main/java/org/redisson/executor/TasksRunnerService.java @@ -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); } diff --git a/redisson/src/main/java/org/redisson/liveobject/core/RedissonObjectBuilder.java b/redisson/src/main/java/org/redisson/liveobject/core/RedissonObjectBuilder.java index cd37e6aa0..eecb33938 100644 --- a/redisson/src/main/java/org/redisson/liveobject/core/RedissonObjectBuilder.java +++ b/redisson/src/main/java/org/redisson/liveobject/core/RedissonObjectBuilder.java @@ -70,6 +70,7 @@ import org.redisson.liveobject.resolver.NamingScheme; public class RedissonObjectBuilder { private static final Map, Class> SUPPORTED_CLASS_MAPPING = new LinkedHashMap<>(); + private static final Map, 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, 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, CodecMethodRef> map, Class clientClazz, Class objectClazz) { + private static void fillCodecMethods(Map, CodecMethodRef> map, Class clientClazz, Class objectClazz) { for (Method method : clientClazz.getDeclaredMethods()) { if (!method.getReturnType().equals(Void.TYPE) && objectClazz.isAssignableFrom(method.getReturnType()) diff --git a/redisson/src/main/java/org/redisson/misc/RedisURI.java b/redisson/src/main/java/org/redisson/misc/RedisURI.java new file mode 100644 index 000000000..ba94508e0 --- /dev/null +++ b/redisson/src/main/java/org/redisson/misc/RedisURI.java @@ -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; + } + +} diff --git a/redisson/src/main/java/org/redisson/misc/URIBuilder.java b/redisson/src/main/java/org/redisson/misc/URIBuilder.java deleted file mode 100644 index 545828f0a..000000000 --- a/redisson/src/main/java/org/redisson/misc/URIBuilder.java +++ /dev/null @@ -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; - } - -} diff --git a/redisson/src/main/java/org/redisson/spring/session/RedissonSessionRepository.java b/redisson/src/main/java/org/redisson/spring/session/RedissonSessionRepository.java index 03d653097..47f62c0a8 100644 --- a/redisson/src/main/java/org/redisson/spring/session/RedissonSessionRepository.java +++ b/redisson/src/main/java/org/redisson/spring/session/RedissonSessionRepository.java @@ -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())) { diff --git a/redisson/src/main/java/org/redisson/spring/session/config/RedissonHttpSessionConfiguration.java b/redisson/src/main/java/org/redisson/spring/session/config/RedissonHttpSessionConfiguration.java index ef7d6459a..9d519cf5e 100644 --- a/redisson/src/main/java/org/redisson/spring/session/config/RedissonHttpSessionConfiguration.java +++ b/redisson/src/main/java/org/redisson/spring/session/config/RedissonHttpSessionConfiguration.java @@ -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; } diff --git a/redisson/src/test/java/org/redisson/RedissonRateLimiterTest.java b/redisson/src/test/java/org/redisson/RedissonRateLimiterTest.java index d86ff5809..fab0becc0 100644 --- a/redisson/src/test/java/org/redisson/RedissonRateLimiterTest.java +++ b/redisson/src/test/java/org/redisson/RedissonRateLimiterTest.java @@ -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() { diff --git a/redisson/src/test/java/org/redisson/RedissonReadWriteLockTest.java b/redisson/src/test/java/org/redisson/RedissonReadWriteLockTest.java index 9d048ffea..c455b8b32 100644 --- a/redisson/src/test/java/org/redisson/RedissonReadWriteLockTest.java +++ b/redisson/src/test/java/org/redisson/RedissonReadWriteLockTest.java @@ -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; diff --git a/redisson/src/test/java/org/redisson/RedissonRemoteServiceTest.java b/redisson/src/test/java/org/redisson/RedissonRemoteServiceTest.java index 8a5d76d38..fed33f5be 100644 --- a/redisson/src/test/java/org/redisson/RedissonRemoteServiceTest.java +++ b/redisson/src/test/java/org/redisson/RedissonRemoteServiceTest.java @@ -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 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 { diff --git a/redisson/src/test/java/org/redisson/RedissonStreamTest.java b/redisson/src/test/java/org/redisson/RedissonStreamTest.java index 52614eb42..cba5e0876 100644 --- a/redisson/src/test/java/org/redisson/RedissonStreamTest.java +++ b/redisson/src/test/java/org/redisson/RedissonStreamTest.java @@ -196,7 +196,35 @@ public class RedissonStreamTest extends BaseTest { assertThat(pendingEntry.getLastTimeDelivered()).isOne(); } } - + + @Test + public void testPendingRange() { + RStream stream = redisson.getStream("test"); + + stream.add("0", "0"); + + stream.createGroup("testGroup"); + + StreamMessageId id1 = stream.add("11", "12"); + StreamMessageId id2 = stream.add("21", "22"); + + Map> s = stream.readGroup("testGroup", "consumer1"); + assertThat(s.size()).isEqualTo(2); + + Map> 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> 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> pres3 = stream.pendingRange("testGroup", "consumer2", StreamMessageId.MIN, StreamMessageId.MAX, 10); + assertThat(pres3).isEmpty(); + } + @Test public void testAck() { RStream stream = redisson.getStream("test");