From 2047a4f9137cbed3a90d14651b24aae72ce7e7e7 Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Fri, 4 Oct 2024 11:29:44 +0300 Subject: [PATCH] markdown docs added --- docs/README.md | 92 + docs/api-models.md | 121 + docs/cache-api-implementations.md | 1094 +++++++ docs/cache/micronaut-cache.md | 492 +++ docs/cache/spring-cache.md | 250 ++ docs/client-tracking.md | 37 + docs/commands-mapping.md | 209 ++ docs/configuration.md | 2635 +++++++++++++++++ docs/data-and-services/collections.md | 2014 +++++++++++++ docs/data-and-services/common-methods.md | 140 + docs/data-and-services/counters.md | 167 ++ docs/data-and-services/data-partitioning.md | 22 + docs/data-and-services/data-serialization.md | 23 + .../locks-and-synchronizers.md | 946 ++++++ docs/data-and-services/object-references.md | 16 + docs/data-and-services/objects.md | 626 ++++ docs/data-and-services/publish-subscribe.md | 262 ++ docs/data-and-services/services.md | 1337 +++++++++ docs/dependency-list.md | 26 + docs/faq.md | 47 + docs/getting-started.md | 81 + docs/integration-with-spring.md | 584 ++++ docs/logo.png | Bin 0 -> 14476 bytes docs/low-level-client.md | 30 + docs/microservices-integration.md | 257 ++ docs/nodes-operations.md | 33 + docs/observability.md | 711 +++++ docs/overview.md | 18 + docs/pipelining.md | 115 + docs/server-side-scripting.md | 107 + docs/standalone-node.md | 125 + docs/transactions.md | 140 + docs/web-session-management.md | 89 + 33 files changed, 12846 insertions(+) create mode 100644 docs/README.md create mode 100644 docs/api-models.md create mode 100644 docs/cache-api-implementations.md create mode 100644 docs/cache/micronaut-cache.md create mode 100644 docs/cache/spring-cache.md create mode 100644 docs/client-tracking.md create mode 100644 docs/commands-mapping.md create mode 100644 docs/configuration.md create mode 100644 docs/data-and-services/collections.md create mode 100644 docs/data-and-services/common-methods.md create mode 100644 docs/data-and-services/counters.md create mode 100644 docs/data-and-services/data-partitioning.md create mode 100644 docs/data-and-services/data-serialization.md create mode 100644 docs/data-and-services/locks-and-synchronizers.md create mode 100644 docs/data-and-services/object-references.md create mode 100644 docs/data-and-services/objects.md create mode 100644 docs/data-and-services/publish-subscribe.md create mode 100644 docs/data-and-services/services.md create mode 100644 docs/dependency-list.md create mode 100644 docs/faq.md create mode 100644 docs/getting-started.md create mode 100644 docs/integration-with-spring.md create mode 100644 docs/logo.png create mode 100644 docs/low-level-client.md create mode 100644 docs/microservices-integration.md create mode 100644 docs/nodes-operations.md create mode 100644 docs/observability.md create mode 100644 docs/overview.md create mode 100644 docs/pipelining.md create mode 100644 docs/server-side-scripting.md create mode 100644 docs/standalone-node.md create mode 100644 docs/transactions.md create mode 100644 docs/web-session-management.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..5f4fedcbf --- /dev/null +++ b/docs/README.md @@ -0,0 +1,92 @@ +# Redisson - Easy Redis Java client
and Real-Time Data Platform + +High-performance async and lock-free Java client for Redis and Valkey based on [Netty](http://netty.io) framework. + + +## Features + +* Thread-safe implementation +* JDK 1.8+ up to the latest version compatible +* Android compatible +* [Redis](https://redis.io) compatible - from 3.0 up to the latest version +* [Valkey](https://valkey.io) compatible - from 7.2.5 up to the latest version +* Supported deployment types + * [Proxy](configuration.md/#proxy-mode) + * [Multi-Cluster](configuration.md/#multi-cluster-mode) + * [Multi-Sentinel](configuration.md/#multi-sentinel-mode) + * [Single](configuration.md/#single-mode) + * [Cluster](configuration.md/#cluster-mode) + * [Sentinel](configuration.md/#sentinel-mode) + * [Replicated](configuration.md/#replicated-mode) + * [Master and Slaves](configuration.md/#master-slave-mode) +* Amazon Web Services compatible + * [AWS Elasticache Serverless](https://aws.amazon.com/elasticache/features/#Serverless) + * [AWS Redis Global Datastore](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Redis-Global-Datastore.html) + * [AWS ElastiCache](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/WhatIs.html) + * [Amazon MemoryDB](https://aws.amazon.com/memorydb) +* Microsoft Azure compatible + * [Azure Redis Cache](https://azure.microsoft.com/en-us/services/cache/) + * [Azure Redis Cache active-passive replication](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-how-to-geo-replication) + * [Azure Redis Cache active-active replication](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-how-to-active-geo-replication) +* Google Cloud Memorystore compatible + * [Google Cloud Redis](https://cloud.google.com/memorystore/docs/redis/) + * [Google Cloud Redis High availability](https://cloud.google.com/memorystore/docs/redis/high-availability) +* Redis Enterprise compatible + * [Redis Enterprise](https://redis.com/redis-enterprise/) + * [Redis Enterprise Active-Active databases](https://docs.redis.com/latest/rs/databases/active-active/get-started/) + * [Redis Enterprise Multiple Active Proxy](https://docs.redis.com/latest/rs/databases/configure/proxy-policy/#about-multiple-active-proxy-support) +* IBM Cloud compatible + * [IBM Cloud Databases for Redis](https://www.ibm.com/cloud/databases-for-redis) +* Aiven compatible + * [Aiven for Redis](https://aiven.io/redis) +* Supports auto-reconnection +* Supports failed to send command auto-retry +* Supports OSGi +* Supports SSL +* Asynchronous connection pool +* Lua scripting +* [RediSearch](data-and-services/services.md/#redisearch-service) +* [JSON datatype](data-and-services/objects.md/#json-object-holder) +* [JSON Store](data-and-services/collections.md/#json-store) +* [Reactive Streams](api-models.md/#reactive-api) API +* [RxJava3](api-models.md/#rxjava-api) API +* [Asynchronous](api-models.md/#synchronous-and-asynchronous-api) API +* Local cache support including [Caffeine](https://github.com/ben-manes/caffeine)-based implementation +* [Cache API implementations](cache-api-implementations.md) + Spring Cache, JCache API (JSR-107), Hibernate Cache, MyBatis Cache, Quarkus Cache, Micronaut Cache +* [Distributed Java objects](data-and-services/objects.md) + Object holder, JSON holder, Binary stream holder, Geospatial holder, BitSet, PublishSubscribe, Bloom filter, HyperLogLog +* [Distributed Java counters](data-and-services/counters.md) + AtomicLong, AtomicDouble, LongAdder, DoubleAdder +* [Distributed Java collections](data-and-services/collections.md) + JSON Store, Map, Multimap, Set, List, SortedSet, ScoredSortedSet, LexSortedSet, Queue, Deque, Blocking Queue, Bounded Blocking Queue, Blocking Deque, Delayed Queue, Priority Queue, Priority Deque +* [Distributed Java locks and synchronizers](data-and-services/locks-and-synchronizers.md) + Lock, FairLock, MultiLock, RedLock, ReadWriteLock, Semaphore, PermitExpirableSemaphore, CountDownLatch +* [Distributed services](data-and-services/services.md) + Remote service, Live Object service, Executor service, Scheduler service, MapReduce service +* [Microservices integration](microservices-integration.md) + Helidon, Micronaut, Quarkus +* [Integration with Spring framework](integration-with-spring.md) + Spring Boot Starter, Spring Cache, Spring Session, Spring Transaction Manager, Spring Cloud Stream, Spring Data Redis +* [Web Session Management](web-session-management.md) + Apache Tomcat Session, Spring Session, Micronaut Session +* [Transactions API](transactions.md) +* [Redis pipelining](pipelining.md) (command batches) +* Supports many popular codecs ([Kryo](https://github.com/EsotericSoftware/kryo), [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/), [Amazon Ion](https://amzn.github.io/ion-docs/), [LZ4](https://github.com/jpountz/lz4-java), [Snappy](https://github.com/xerial/snappy-java), [Protobuf](https://github.com/protocolbuffers/protobuf) and JDK Serialization) +* 2000+ unit tests + +## Comparing solutions +- [Redisson vs Jedis](https://redisson.org/feature-comparison-redisson-vs-jedis.html) +- [Redisson vs Lettuce](https://redisson.org/feature-comparison-redisson-vs-lettuce.html) +- [Redis vs Apache Ignite](https://redisson.org/feature-comparison-redis-vs-ignite.html) +- [Redis vs Hazelcast](https://redisson.org/feature-comparison-redis-vs-hazelcast.html) +- [Redis vs Ehcache](https://redisson.org/feature-comparison-redis-vs-ehcache.html) + +## Success stories + +- [Moving from Hazelcast to Redis / Datorama](https://engineering.datorama.com/moving-from-hazelcast-to-redis-b90a0769d1cb) +- [Migrating from Hazelcast to Redis / Halodoc](https://blogs.halodoc.io/why-and-how-we-move-from-hazelcast-to-redis-2/) +- [Distributed Locking with Redis (Migration from Hazelcast) / ContaAzul](https://carlosbecker.com/posts/distributed-locks-redis/) +- [Migrating from Coherence to Redis](https://www.youtube.com/watch?v=JF5R2ucKTEg) + +Upgrade to __[Redisson PRO](https://redisson.pro)__ with **advanced features**. \ No newline at end of file diff --git a/docs/api-models.md b/docs/api-models.md new file mode 100644 index 000000000..277daed5c --- /dev/null +++ b/docs/api-models.md @@ -0,0 +1,121 @@ +### Synchronous and Asynchronous API + +Redisson instances are fully thread-safe. + +Synchronous and Asynchronous API could be reached via [RedissonClient](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RedissonClient.html) interface. + +Most Redisson objects extend asynchronous interface with asynchronous methods which mirrors synchronous methods. Like below: +```java +// RAtomicLong extends RAtomicLongAsync +RAtomicLong obj = client.getAtomicLong("myLong"); +obj.compareAndSet(1, 401); + +RAtomicLongAsync objAsync = client.getAtomicLong("myLong"); +RFuture future = objAsync.compareAndSetAsync(1, 401); +``` +Asynchronous methods return [RFuture](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RFuture.html) object which extends [CompletionStage](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html) interface. + +```java +future.whenComplete((res, exception) -> { + + // handle both result and exception + +}); + + +// or +future.thenAccept(res -> { + + // handle result + +}).exceptionally(exception -> { + + // handle exception + +}); + +``` +Avoid to use blocking methods in future listeners. Listeners executed by netty-threads and delays in listeners may cause errors in Redis or Valkey request/response processing. Use follow methods to execute blocking methods in listeners: + +```java +future.whenCompleteAsync((res, exception) -> { + + // handle both result and exception + +}, executor); + + +// or +future.thenAcceptAsync(res -> { + + // handle result + +}, executor).exceptionallyAsync(exception -> { + + // handle exception + +}, executor); +``` + +### Reactive API + +Reactive API could be reached via [RedissonReactiveClient](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RedissonReactiveClient.html) interface. + +Redisson's implementation based on [Project Reactor](https://projectreactor.io). + +Usage example: + +```java +RedissonReactiveClient client = redissonClient.reactive(); + +RAtomicLongReactive atomicLong = client.getAtomicLong("myLong"); +Mono cs = longObject.compareAndSet(10, 91); +Mono get = longObject.get(); + +get.doOnSuccess(res -> { + // ... +}).subscribe(); +``` + +### RxJava API + +RxJava API could be reached via [RedissonRxClient](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RedissonRxClient.html) interface. + +Redisson's implementation based on [RxJava3](https://github.com/ReactiveX/RxJava). + +Usage example: + +```java +RedissonRxClient client = redissonClient.rxJava(); + +RAtomicLongRx atomicLong = client.getAtomicLong("myLong"); +Single cs = longObject.compareAndSet(10, 91); +Single get = longObject.get(); + +get.doOnSuccess(res -> { + // ... +}).subscribe(); +``` + +### Retry policy + +Redisson implements auto-retry policy per operation. Retry policy is controlled by [retryAttempts](configuration.md) and [retryInterval](configuration.md) settings. These settings are applied to each Redisson object. [timeout](configuration.md) setting is applied when the Redis or Valkey command was successfully sent. + +Settings above can be overridden per Redisson object instance. These settings apply to each method of a given Redisson object instance. + +Here is an example with `RBucket` object: +```java +RedissonClient client = Redisson.create(config); +RBucket bucket = client.getBucket('myObject'); + +// sync way +bucket.get(); +// async way +RFuture result = bucket.getAsync(); + +// instance with overridden retryInterval and timeout parameters +RBucket bucket = client.getBucket(PlainOptions.name('myObject') + .timeout(Duration.ofSeconds(3)) + .retryInterval(Duration.ofSeconds(5))); + +``` \ No newline at end of file diff --git a/docs/cache-api-implementations.md b/docs/cache-api-implementations.md new file mode 100644 index 000000000..75bb213e3 --- /dev/null +++ b/docs/cache-api-implementations.md @@ -0,0 +1,1094 @@ +{% include 'cache/Spring-cache.md' %} + +## Hibernate Cache + +Redisson implements [Hibernate 2nd level Cache](https://docs.jboss.org/hibernate/orm/6.0/userguide/html_single/Hibernate_User_Guide.html#caching) provider based on Redis. +All Hibernate cache strategies are supported: `READ_ONLY`, `NONSTRICT_READ_WRITE`, `READ_WRITE` and `TRANSACTIONAL`. + +Compatible with Hibernate 4.x, 5.1.x, 5.2.x, 5.3.3+ up to 5.6.x and 6.0.2+ up to 6.x.x + +### Eviction, local cache and data partitioning + +Redisson provides various Hibernate Cache factories including those with features below: + +**local cache** - so called `near cache`, which is useful for use cases when Hibernate Cache used mostly for read operations and/or network roundtrips are undesirable. It caches Map entries on Redisson side and executes read operations up to **5x faster** in comparison with common implementation. Local cache instances with the same name connected to the same pub/sub channel. This channel is used for exchanging of update/invalidate events between all instances. Cache store doesn't use `hashCode()`/`equals()` methods of key object, instead it uses hash of serialized state. + +**data partitioning** - although all implementations are cluster compatible thier content isn't scaled/partitioned across multiple Redis or Valkey master nodes in cluster. Data partitioning allows to scale available memory, read/write operations and entry eviction process for individual Hibernate Cache instance in Redis or Valkey cluster. + +**1. Scripted eviction** + +Allows to define `time to live` or `max idle time` parameters per map entry. Eviction is done on Redisson side through a custom scheduled task which removes expired entries using Lua script. Eviction task is started once per unique object name at the moment of getting Map instance. If instance isn't used and has expired entries it should be get again to start the eviction process. This leads to extra Redis or Valkey calls and eviction task per unique map object name. + +Entries are cleaned time to time by `org.redisson.eviction.EvictionScheduler`. By default, it removes 100 expired entries at a time. This can be changed through [cleanUpKeysAmount](configuration.md) setting. Task launch time tuned automatically and depends on expired entries amount deleted in previous time and varies between 5 second to 30 minutes by default. This time interval can be changed through [minCleanUpDelay](configuration.md) and [maxCleanUpDelay](configuration.md). For example, if clean task deletes 100 entries each time it will be executed every 5 seconds (minimum execution delay). But if current expired entries amount is lower than previous one then execution delay will be increased by 1.5 times and decreased otherwise. + +Available implementations: + +|Class name | Local cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :----------:| :----------:| +|RedissonRegionFactory
open-source version | ❌ | ❌ | ❌ | +|RedissonRegionFactory
[Redisson PRO](http://redisson.pro) version | ❌ | ❌ | ✔️ | +|RedissonLocalCachedRegionFactory
available only in [Redisson PRO](http://redisson.pro) | ✔️ | ❌ | ✔️ | +|RedissonClusteredRegionFactory
available only in [Redisson PRO](http://redisson.pro) | ❌ | ✔️ | ✔️ | +|RedissonClusteredLocalCachedRegionFactory
available only in [Redisson PRO](http://redisson.pro) | ✔️ | ✔️ | ✔️ | + +**2. Advanced eviction** + +Allows to define `time to live` parameter per map entry. Doesn't use an entry eviction task, entries are cleaned on Redis or Valkey side. + +Available implementations: + +|Class name | Local cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :----------:| :----------:| +|RedissonRegionV2Factory
available only in [Redisson PRO](http://redisson.pro) | ❌ | ✔️ | ✔️ | +|RedissonLocalCachedV2RegionFactory
available only in [Redisson PRO](http://redisson.pro) | ✔️ | ✔️ | ✔️ | + +**3. Native eviction** + +Allows to define `time to live` parameter per map entry. Doesn't use an entry eviction task, entries are cleaned on Redis side. +Requires **Redis 7.4+**. + +Available implementations: + +|Class name | Local cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :----------:| :----------:| +|RedissonRegionNativeFactory
open-source version | ❌ | ❌ | ❌ | +|RedissonRegionNativeFactory
[Redisson PRO](http://redisson.pro) version | ❌ | ❌ | ✔️ | +|RedissonLocalCachedNativeRegionFactory
available only in [Redisson PRO](http://redisson.pro) | ✔️ | ❌ | ✔️ | +|RedissonClusteredNativeRegionFactory
available only in [Redisson PRO](http://redisson.pro) | ❌ | ✔️ | ✔️ | + +### Usage + +**1. Add `redisson-hibernate` dependency into your project:** + +Maven + +```xml + + org.redisson + + redisson-hibernate-4 + + redisson-hibernate-5 + + redisson-hibernate-52 + + redisson-hibernate-53 + + redisson-hibernate-6 + xVERSIONx + +``` + +Gradle + +```groovy + // for Hibernate v4.x + compile 'org.redisson:redisson-hibernate-4:xVERSIONx' + // for Hibernate v5.0.x - v5.1.x + compile 'org.redisson:redisson-hibernate-5:xVERSIONx' + // for Hibernate v5.2.x + compile 'org.redisson:redisson-hibernate-52:xVERSIONx' + // for Hibernate v5.3.3+ - v5.6.x + compile 'org.redisson:redisson-hibernate-53:xVERSIONx' + // for Hibernate v6.0.2+ - v6.x.x + compile 'org.redisson:redisson-hibernate-6:xVERSIONx' +``` + +**2. Specify hibernate cache settings** + +Define Redisson Region Cache Factory: + +```xml + + + + + + + + + + + + +``` + +By default each Region Factory creates own Redisson instance. For multiple applications, using the same Redis or Valkey setup and deployed in the same JVM, amount of Redisson instances could be reduced using JNDI registry: + +```xml + + + + + + + + + + + + + + + +``` + +```xml + + + + + + + + + +``` + +**Cache settings** + +Redisson allows to define follow cache settings per entity, collection, naturalid, query and timestamp regions: + +`REGION_NAME` - is a name of region which is defined in @Cache annotation otherwise it's a fully qualified class name. + +| | | +|-|-| +|Parameter| **`hibernate.cache.redisson.[REGION_NAME].eviction.max_entries`** | +|Description| Max size of cache. Superfluous entries in Redis or Valkey are evicted using LRU algorithm.
`0` value means unbounded cache. | +|Default value| `0` | + +| | | +|-|-| +|Parameter| **`hibernate.cache.redisson.[REGION_NAME].expiration.time_to_live`** | +|Description| Time to live per cache entry in Redis. Defined in milliseconds.
`0` value means this setting doesn't affect expiration. | +|Default value| `0` | + +| | | +|-|-| +|Parameter| **`hibernate.cache.redisson.[REGION_NAME].expiration.max_idle_time`** | +|Description| Max idle time per cache entry in Redis. Defined in milliseconds.
`0` value means this setting doesn't affect expiration. | +|Default value| `0` | + +| | | +|-|-| +|Parameter| **`hibernate.cache.redisson.[REGION_NAME].localcache.cache_provider`** | +|Description| Cache provider used as local cache store.
`REDISSON` and `CAFFEINE` providers are available. | +|Default value| `REDISSON` | + +| | | +|-|-| +|Parameter| **`hibernate.cache.redisson.[REGION_NAME].localcache.store_mode`** | +|Description| Store mode of cache data.
`LOCALCACHE` - store data in local cache only and use Redis or Valkey only for data update/invalidation
`LOCALCACHE_REDIS` - store data in both Redis or Valkey and local cache | +|Default value| `LOCALCACHE` | + +| | | +|-|-| +|Parameter| **`hibernate.cache.redisson.[REGION_NAME].localcache.max_idle_time`** | +|Description| Max idle time per entry in local cache. Defined in milliseconds.
`0` value means this setting doesn't affect expiration | +|Default value| `0` | + +| | | +|-|-| +|Parameter| **`hibernate.cache.redisson.[REGION_NAME].localcache.time_to_live`** | +|Description| Time to live per entry in local cache. Defined in milliseconds.
`0` value means this setting doesn't affect expiration | +|Default value| `0` | + +| | | +|-|-| +|Parameter| **`hibernate.cache.redisson.[REGION_NAME].localcache.eviction_policy`** | +|Description| Eviction policy applied to local cache entries when cache size limit reached.
`LFU`, `LRU`, `SOFT`, `WEAK` and `NONE` policies are available. | +|Default value| `NONE` | + +| | | +|-|-| +|Parameter| **`hibernate.cache.redisson.[REGION_NAME].localcache.sync_strategy`** | +|Description| Sync strategy used to synchronize local cache changes across all instances.
`INVALIDATE` - Invalidate cache entry across all LocalCachedMap instances on map entry change
`UPDATE` - Update cache entry across all LocalCachedMap instances on map entry change
`NONE` - No synchronizations on map changes | +|Default value| `INVALIDATE` | + +| | | +|-|-| +|Parameter| **`hibernate.cache.redisson.[REGION_NAME].localcache.reconnection_strategy`** | +|Description| Reconnection strategy used to load missed local cache updates through Hibernate during any connection failures to Redis.
`CLEAR` - Clear local cache if map instance has been disconnected for a while
`LOAD` - Store invalidated entry hash in invalidation log for 10 minutes. Cache keys for stored invalidated entry hashes will be removed if LocalCachedMap instance has been disconnected less than 10 minutes or whole cache will be cleaned otherwise
`NONE` - No reconnection handling | +|Default value| `NONE` | + +| | | +|-|-| +|Parameter| **`hibernate.cache.redisson.[REGION_NAME].localcache.size`** | +|Description| Max size of local cache. Superfluous entries in Redis or Valkey are evicted using defined eviction policy.
`0` value means unbounded cache. | +|Default value| `0` | + +_**NOTE**: `hibernate.cache.redisson.[REGION_NAME].localcache.*` settings are available for `RedissonClusteredLocalCachedRegionFactory` and `RedissonLocalCachedRegionFactory` classes only._ + +**Default cache settings** + +Default region configuration used for all caches not specified in configuration: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +### Overriding the default configuration + +Configuration per entity/collection/naturalid/query region overrides default configuration: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +## JCache API (JSR-107) +Redisson provides an implementation of JCache API ([JSR-107](https://www.jcp.org/en/jsr/detail?id=107)) for Redis. + +Below are examples of JCache API usage. + +**1.** Using default config located at `/redisson-jcache.yaml`: +```java +MutableConfiguration config = new MutableConfiguration<>(); + +CacheManager manager = Caching.getCachingProvider().getCacheManager(); +Cache cache = manager.createCache("namedCache", config); +``` + +**2.** Using config file with custom location: +```java +MutableConfiguration config = new MutableConfiguration<>(); + +// yaml config +URI redissonConfigUri = getClass().getResource("redisson-jcache.yaml").toURI(); +CacheManager manager = Caching.getCachingProvider().getCacheManager(redissonConfigUri, null); +Cache cache = manager.createCache("namedCache", config); +``` + +**3.** Using Redisson's config object: +```java +MutableConfiguration jcacheConfig = new MutableConfiguration<>(); + +Config redissonCfg = ... +Configuration config = RedissonConfiguration.fromConfig(redissonCfg, jcacheConfig); + +CacheManager manager = Caching.getCachingProvider().getCacheManager(); +Cache cache = manager.createCache("namedCache", config); +``` + +**4.** Using Redisson instance object: +```java +MutableConfiguration jcacheConfig = new MutableConfiguration<>(); + +RedissonClient redisson = ... +Configuration config = RedissonConfiguration.fromInstance(redisson, jcacheConfig); + +CacheManager manager = Caching.getCachingProvider().getCacheManager(); +Cache cache = manager.createCache("namedCache", config); +``` + +Read more [here](configuration.md) about Redisson configuration. + +Provided implementation fully passes TCK tests. Here is the [test](https://github.com/cruftex/jsr107-test-zoo/tree/master/redisson-V2-test) module. + +### Asynchronous, Reactive and RxJava3 interfaces + +Along with usual JCache API, Redisson provides Asynchronous, Reactive and RxJava3 API. + +**[Asynchronous interface](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/CacheAsync.html)**. Each method returns `org.redisson.api.RFuture` object. +Example: + +```java +MutableConfiguration config = new MutableConfiguration<>(); + +CacheManager manager = Caching.getCachingProvider().getCacheManager(); +Cache cache = manager.createCache("myCache", config); + +CacheAsync asyncCache = cache.unwrap(CacheAsync.class); +RFuture putFuture = asyncCache.putAsync("1", "2"); +RFuture getFuture = asyncCache.getAsync("1"); +``` + +**[Reactive interface](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/CacheReactive.html)**. Each method returns `reactor.core.publisher.Mono` object. +Example: + +```java +MutableConfiguration config = new MutableConfiguration<>(); + +CacheManager manager = Caching.getCachingProvider().getCacheManager(); +Cache cache = manager.createCache("myCache", config); + +CacheReactive reactiveCache = cache.unwrap(CacheReactive.class); +Mono putFuture = reactiveCache.put("1", "2"); +Mono getFuture = reactiveCache.get("1"); +``` + +**[RxJava3 interface](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/CacheRx.html)**. Each method returns one of the following object: `io.reactivex.Completable`, `io.reactivex.Single`, `io.reactivex.Maybe`. +Example: + +```java +MutableConfiguration config = new MutableConfiguration<>(); + +CacheManager manager = Caching.getCachingProvider().getCacheManager(); +Cache cache = manager.createCache("myCache", config); + +CacheRx rxCache = cache.unwrap(CacheRx.class); +Completable putFuture = rxCache.put("1", "2"); +Maybe getFuture = rxCache.get("1"); +``` +### Local cache and data partitioning + +Redisson provides JCache implementations with two important features: + +**local cache** - so called near cache used to speed up read operations and avoid network roundtrips. It caches JCache entries on Redisson side and executes read operations up to 45x faster in comparison with common implementation. Local cache instances with the same name connected to the same pub/sub channel. This channel is used for exchanging of update/invalidate events between all instances. Local cache store doesn't use hashCode()/equals() methods of key object, instead it uses hash of serialized state. + +**data partitioning** - although JCache instance is cluster compatible its content isn't scaled/partitioned across multiple Redis or Valkey master nodes in cluster. Data partitioning allows to scale available memory, read/write operations and entry eviction process for individual JCache instance in Redis or Valkey cluster. + +**fallback mode** - if set to `true` and Redis or Valkey is down the errors won't be thrown allowing application continue to operate without Redis. + +Below is the complete list of available managers: + +| | Local
cache | Data
partitioning | Ultra-fast
read/write | Fallback
mode | +| ------------- | :-----------: | :----------:| :----------:| :----------:| +|JCache
open-source version | ❌ | ❌ | ❌ | ❌ | +|JCache
[Redisson PRO](https://redisson.pro) version | ❌ | ❌ | ✔️ | ✔️ | +|JCache with local cache
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ❌ | ✔️ | ✔️ | +|JCache with data partitioning
available only in [Redisson PRO](https://redisson.pro) | ❌ | ✔️ | ✔️ | ✔️ | +|JCache with local cache and data partitioning
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ✔️ | ✔️ | ✔️ | + +**Local cache configuration** + +```java + LocalCacheConfiguration configuration = new LocalCacheConfiguration<>() + + // Defines whether to store a cache miss into the local cache. + // Default value is false. + .storeCacheMiss(false); + + // Defines store mode of cache data. + // Follow options are available: + // LOCALCACHE - store data in local cache only and use Redis or Valkey only for data update/invalidation. + // LOCALCACHE_REDIS - store data in both Redis or Valkey and local cache. + .storeMode(StoreMode.LOCALCACHE_REDIS) + + // Defines Cache provider used as local cache store. + // Follow options are available: + // REDISSON - uses Redisson own implementation + // CAFFEINE - uses Caffeine implementation + .cacheProvider(CacheProvider.REDISSON) + + // Defines local cache eviction policy. + // Follow options are available: + // LFU - Counts how often an item was requested. Those that are used least often are discarded first. + // LRU - Discards the least recently used items first + // SOFT - Uses weak references, entries are removed by GC + // WEAK - Uses soft references, entries are removed by GC + // NONE - No eviction + .evictionPolicy(EvictionPolicy.NONE) + + // If cache size is 0 then local cache is unbounded. + .cacheSize(1000) + + // Used to load missed updates during any connection failures to Redis. + // Since, local cache updates can't be get in absence of connection to Redis. + // Follow reconnection strategies are available: + // CLEAR - Clear local cache if map instance has been disconnected for a while. + // LOAD - Store invalidated entry hash in invalidation log for 10 minutes + // Cache keys for stored invalidated entry hashes will be removed + // if LocalCachedMap instance has been disconnected less than 10 minutes + // or whole cache will be cleaned otherwise. + // NONE - Default. No reconnection handling + .reconnectionStrategy(ReconnectionStrategy.NONE) + + // Used to synchronize local cache changes. + // Follow sync strategies are available: + // INVALIDATE - Default. Invalidate cache entry across all LocalCachedMap instances on map entry change + // UPDATE - Insert/update cache entry across all LocalCachedMap instances on map entry change + // NONE - No synchronizations on map changes + .syncStrategy(SyncStrategy.INVALIDATE) + + // time to live for each map entry in local cache + .timeToLive(10000) + // or + .timeToLive(10, TimeUnit.SECONDS) + + // max idle time for each map entry in local cache + .maxIdle(10000) + // or + .maxIdle(10, TimeUnit.SECONDS); +``` + +Usage example: + +```java + +LocalCacheConfiguration config = new LocalCacheConfiguration<>(); + .setEvictionPolicy(EvictionPolicy.LFU) + .setTimeToLive(48, TimeUnit.MINUTES) + .setMaxIdle(24, TimeUnit.MINUTES); + .setCacheSize(1000); + +CacheManager manager = Caching.getCachingProvider().getCacheManager(); +Cache cache = manager.createCache("myCache", config); + +// or + +URI redissonConfigUri = getClass().getResource("redisson-jcache.yaml").toURI(); +CacheManager manager = Caching.getCachingProvider().getCacheManager(redissonConfigUri, null); +Cache cache = manager.createCache("myCache", config); + +// or + +Config redissonCfg = ... +Configuration rConfig = RedissonConfiguration.fromConfig(redissonCfg, config); + +CacheManager manager = Caching.getCachingProvider().getCacheManager(); +Cache cache = manager.createCache("namedCache", rConfig); +``` + +**Data partitioning** + +Usage examples: + +```java + +ClusteredConfiguration config = new ClusteredConfiguration<>(); + +CacheManager manager = Caching.getCachingProvider().getCacheManager(); +Cache cache = manager.createCache("myCache", config); + +// or + +URI redissonConfigUri = getClass().getResource("redisson-jcache.yaml").toURI(); +CacheManager manager = Caching.getCachingProvider().getCacheManager(redissonConfigUri, null); +Cache cache = manager.createCache("myCache", config); + +// or + +Config redissonCfg = ... +Configuration rConfig = RedissonConfiguration.fromConfig(redissonCfg, config); + +CacheManager manager = Caching.getCachingProvider().getCacheManager(); +Cache cache = manager.createCache("namedCache", rConfig); +``` + +**Local cache with data partitioning configuration** + +```java + ClusteredLocalCacheConfiguration configuration = new ClusteredLocalCacheConfiguration<>() + + // Defines whether to store a cache miss into the local cache. + // Default value is false. + .storeCacheMiss(false); + + // Defines store mode of cache data. + // Follow options are available: + // LOCALCACHE - store data in local cache only and use Redis or Valkey only for data update/invalidation. + // LOCALCACHE_REDIS - store data in both Redis or Valkey and local cache. + .storeMode(StoreMode.LOCALCACHE_REDIS) + + // Defines Cache provider used as local cache store. + // Follow options are available: + // REDISSON - uses Redisson own implementation + // CAFFEINE - uses Caffeine implementation + .cacheProvider(CacheProvider.REDISSON) + + // Defines local cache eviction policy. + // Follow options are available: + // LFU - Counts how often an item was requested. Those that are used least often are discarded first. + // LRU - Discards the least recently used items first + // SOFT - Uses weak references, entries are removed by GC + // WEAK - Uses soft references, entries are removed by GC + // NONE - No eviction + .evictionPolicy(EvictionPolicy.NONE) + + // If cache size is 0 then local cache is unbounded. + .cacheSize(1000) + + // Used to load missed updates during any connection failures to Redis. + // Since, local cache updates can't be get in absence of connection to Redis. + // Follow reconnection strategies are available: + // CLEAR - Clear local cache if map instance has been disconnected for a while. + // LOAD - Store invalidated entry hash in invalidation log for 10 minutes + // Cache keys for stored invalidated entry hashes will be removed + // if LocalCachedMap instance has been disconnected less than 10 minutes + // or whole cache will be cleaned otherwise. + // NONE - Default. No reconnection handling + .reconnectionStrategy(ReconnectionStrategy.NONE) + + // Used to synchronize local cache changes. + // Follow sync strategies are available: + // INVALIDATE - Default. Invalidate cache entry across all LocalCachedMap instances on map entry change + // UPDATE - Insert/update cache entry across all LocalCachedMap instances on map entry change + // NONE - No synchronizations on map changes + .syncStrategy(SyncStrategy.INVALIDATE) + + // time to live for each map entry in local cache + .timeToLive(10000) + // or + .timeToLive(10, TimeUnit.SECONDS) + + // max idle time for each map entry in local cache + .maxIdle(10000) + // or + .maxIdle(10, TimeUnit.SECONDS); +``` + +Usage examples: + +```java + +ClusteredLocalCacheConfiguration config = new ClusteredLocalCacheConfiguration<>(); + +CacheManager manager = Caching.getCachingProvider().getCacheManager(); +Cache cache = manager.createCache("myCache", config); + +// or + +URI redissonConfigUri = getClass().getResource("redisson-jcache.yaml").toURI(); +CacheManager manager = Caching.getCachingProvider().getCacheManager(redissonConfigUri, null); +Cache cache = manager.createCache("myCache", config); + +// or + +Config redissonCfg = ... +Configuration rConfig = RedissonConfiguration.fromConfig(redissonCfg, config); + +CacheManager manager = Caching.getCachingProvider().getCacheManager(); +Cache cache = manager.createCache("namedCache", rConfig); +``` + +### Open Liberty or WebSphere Liberty integration + +Distributed Cache configuration example: + +```xml + + + + + + + + + + +``` + +Distributed Session persistence configuration example: + +```xml + + servlet-6.0 + sessionCache-1.0 + + + + + + + + + + + + + + +``` + +_Settings below are available only in [Redisson PRO](https://redisson.pro) edition._ + +Follow settings are available per JCache instance: + +| | | +|-|-| +|Parameter| **`fallback`** | +|Description| Skip errors if Redis or Valkey cache is unavailable | +|Default value| `false` | + +| | | +|-|-| +|Parameter| **`implementation`** | +|Description| Cache implementation.
`cache` - standard implementation
`clustered-local-cache` - data partitioning and local cache support
`local-cache` - local cache support
`clustered-cache` - data partitioning support
| +|Default value| `cache` | + +| | | +|-|-| +|Parameter| **`localcache.store_cache_miss`** | +|Description| Defines whether to store a cache miss into the local cache. | +|Default value| `false` | + +| | | +|-|-| +|Parameter| **`localcache.cache_provider`** | +|Description| Cache provider used as local cache store.
`REDISSON` and `CAFFEINE` providers are available. | +|Default value| `REDISSON` | + +| | | +|-|-| +|Parameter| **`localcache.store_mode`** | +|Description| Store mode of cache data.
`LOCALCACHE` - store data in local cache only and use Redis or Valkey only for data update/invalidation
`LOCALCACHE_REDIS` - store data in both Redis or Valkey and local cache | +|Default value| `LOCALCACHE` | + +| | | +|-|-| +|Parameter| **`localcache.max_idle_time`** | +|Description| Max idle time per entry in local cache. Defined in milliseconds.
`0` value means this setting doesn't affect expiration | +|Default value| `0` | + +| | | +|-|-| +|Parameter| **`localcache.time_to_live`** | +|Description| Time to live per entry in local cache. Defined in milliseconds.
`0` value means this setting doesn't affect expiration | +|Default value| `0` | + +| | | +|-|-| +|Parameter| **`localcache.eviction_policy`** | +|Description| Eviction policy applied to local cache entries when cache size limit reached.
`LFU`, `LRU`, `SOFT`, `WEAK` and `NONE` policies are available. | +|Default value| `NONE` | + +| | | +|-|-| +|Parameter| **`localcache.sync_strategy`** | +|Description| Sync strategy used to synchronize local cache changes across all instances.
`INVALIDATE` - Invalidate cache entry across all LocalCachedMap instances on map entry change
`UPDATE` - Update cache entry across all LocalCachedMap instances on map entry change
`NONE` - No synchronizations on map changes | +|Default value| `INVALIDATE` | + +| | | +|-|-| +|Parameter| **`localcache.reconnection_strategy`** | +|Description| Reconnection strategy used to load missed local cache updates through Hibernate during any connection failures to Redis.
`CLEAR` - Clear local cache if map instance has been disconnected for a while
`LOAD` - Store invalidated entry hash in invalidation log for 10 minutes. Cache keys for stored invalidated entry hashes will be removed if LocalCachedMap instance has been disconnected less than 10 minutes or whole cache will be cleaned otherwise
`NONE` - No reconnection handling | +|Default value| `NONE` | + +| | | +|-|-| +|Parameter| **`localcache.size`** | +|Description| Max size of local cache. Superfluous entries in Redis or Valkey are evicted using defined eviction policy.
`0` value means unbounded cache. | +|Default value| `0` | + +## MyBatis Cache + +Redisson implements [MyBatis Cache](https://mybatis.org/mybatis-3/sqlmap-xml.html#cache) based on Redis. + +Compatible with MyBatis 3.0.0+ + +### Eviction, local cache and data partitioning + +Redisson provides multiple MyBatis Cache implementations which support features below: + +**local cache** - so called `near cache` used to speed up read operations and avoid network roundtrips. It caches Map entries on Redisson side and executes read operations up to **45x faster** in comparison with common implementation. Local cache instances with the same name connected to the same pub/sub channel. This channel is used for exchanging of update/invalidate events between all instances. Local cache store doesn't use `hashCode()`/`equals()` methods of key object, instead it uses hash of serialized state. + +**data partitioning** - although Map object is cluster compatible its content isn't scaled/partitioned across multiple Redis or Valkey master nodes in cluster. Data partitioning allows to scale available memory, read/write operations and entry eviction process for individual Map instance in Redis or Valkey cluster. + +**1. Scripted eviction** + +Allows to define `time to live` or `max idle time` parameters per entry. Eviction is done on Redisson side through a custom scheduled task which removes expired entries using Lua script. Eviction task is started once per unique object name at the moment of getting Map instance. If instance isn't used and has expired entries it should be get again to start the eviction process. This leads to extra Redis or Valkey calls and eviction task per unique map object name. + +Entries are cleaned time to time by `org.redisson.eviction.EvictionScheduler`. By default, it removes 100 expired entries at a time. This can be changed through [cleanUpKeysAmount](configuration.md) setting. Task launch time tuned automatically and depends on expired entries amount deleted in previous time and varies between 5 second to 30 minutes by default. This time interval can be changed through [minCleanUpDelay](configuration.md) and [maxCleanUpDelay](configuration.md). For example, if clean task deletes 100 entries each time it will be executed every 5 seconds (minimum execution delay). But if current expired entries amount is lower than previous one then execution delay will be increased by 1.5 times and decreased otherwise. + +Available implementations: + +|Class name | Local
cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :----------:| :----------:| +|RedissonCache
open-source version | ❌ | ❌ | ❌ | +|RedissonCache
[Redisson PRO](http://redisson.pro) version | ❌ | ❌ | ✔️ | +|RedissonLocalCachedCache
available only in [Redisson PRO](http://redisson.pro) | ✔️ | ❌ | ✔️ | +|RedissonClusteredCache
available only in [Redisson PRO](http://redisson.pro) | ❌ | ✔️ | ✔️ | +|RedissonClusteredLocalCachedCache
available only in [Redisson PRO](http://redisson.pro) | ✔️ | ✔️ | ✔️ | + +**2. Advanced eviction** + +Allows to define `time to live` parameter per map entry. Doesn't use an entry eviction task, entries are cleaned on Redis or Valkey side. + +Available implementations: + +|Class name | Local
cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :----------:| :----------:| +|RedissonCacheV2
available only in [Redisson PRO](http://redisson.pro) | ❌ | ✔️ | ✔️ | +|RedissonLocalCachedCacheV2
available only in [Redisson PRO](http://redisson.pro) | ✔️ | ✔️ | ✔️ | + +**3. Native eviction** + +Allows to define `time to live` parameter per map entry. Doesn't use an entry eviction task, entries are cleaned on Redis side. +Requires **Redis 7.4+**. + +Available implementations: + +|Class name | Local
cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :----------:| :----------:| +|RedissonCacheNative
open-source version | ❌ | ❌ | ❌ | +|RedissonCacheNative
[Redisson PRO](http://redisson.pro) version | ❌ | ❌ | ✔️ | +|RedissonLocalCachedCacheNative
available only in [Redisson PRO](http://redisson.pro) | ✔️ | ❌ | ✔️ | +|RedissonClusteredCacheNative
available only in [Redisson PRO](http://redisson.pro) | ❌ | ✔️ | ✔️ | + +### Usage + +**1. Add `redisson-mybatis` dependency into your project** + +Maven + +```xml + + org.redisson + redisson-mybatis + xVERSIONx + +``` + +Gradle + +```groovy +compile 'org.redisson:redisson-mybatis:xVERSIONx' +``` + +**2. Specify MyBatis cache settings** + +Redisson allows to define follow settings per Cache instance: + +`timeToLive` - defines time to live per cache entry + +`maxIdleTime` - defines max idle time per cache entry + +`maxSize` - defines max size of entries amount stored in Redis + +`localCacheProvider` - cache provider used as local cache store. `REDISSON` and `CAFFEINE` providers are available. Default value: `REDISSON` + +`localCacheEvictionPolicy` - local cache eviction policy. `LFU`, `LRU`, `SOFT`, `WEAK` and `NONE` eviction policies are available. + +`localCacheSize` - local cache size. If size is `0` then local cache is unbounded. + +`localCacheTimeToLive` - time to live in milliseconds for each map entry in local cache. If value equals to `0` then timeout is not applied. + +`localCacheMaxIdleTime` - max idle time in milliseconds for each map entry in local cache. If value equals to `0` then timeout is not applied. + +`localCacheSyncStrategy` - local cache sync strategy. `INVALIDATE`, `UPDATE` and `NONE` eviction policies are available. + +`redissonConfig` - defines path to redisson config in YAML format + +Cache definition examples: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +## Quarkus Cache + +### Eviction, local cache and data partitioning + +Redisson provides various Quarkus Cache implementations with features below: + +**local cache** - so called `near cache` used to speed up read operations and avoid network roundtrips. It caches Map entries on Redisson side and executes read operations up to **45x faster** in comparison with common implementation. Local cache instances with the same name connected to the same pub/sub channel. This channel is used for exchanging of update/invalidate events between all instances. Local cache store doesn't use `hashCode()`/`equals()` methods of key object, instead it uses hash of serialized state. + +**data partitioning** - although Map object is cluster compatible its content isn't scaled/partitioned across multiple Redis or Valkey master nodes in cluster. Data partitioning allows to scale available memory, read/write operations and entry eviction process for individual Map instance in cluster. + +**1. Scripted eviction** + +Allows to define `time to live` or `max idle time` parameters per map entry. Eviction is done on Redisson side through a custom scheduled task which removes expired entries using Lua script. Eviction task is started once per unique object name at the moment of getting Map instance. If instance isn't used and has expired entries it should be get again to start the eviction process. This leads to extra Redis or Valkey calls and eviction task per unique map object name. + +Entries are cleaned time to time by `org.redisson.eviction.EvictionScheduler`. By default, it removes 100 expired entries at a time. This can be changed through [cleanUpKeysAmount](../configuration.md) setting. Task launch time tuned automatically and depends on expired entries amount deleted in previous time and varies between 5 second to 30 minutes by default. This time interval can be changed through [minCleanUpDelay](../configuration.md) and [maxCleanUpDelay](../configuration.md). For example, if clean task deletes 100 entries each time it will be executed every 5 seconds (minimum execution delay). But if current expired entries amount is lower than previous one then execution delay will be increased by 1.5 times and decreased otherwise. + +Available implementations: + +|`impementation`
setting value | Local
cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :-----------:| :----------:| +|`standard`
open-source version | вќЊ | вќЊ | вќЊ | +|`standard`
[Redisson PRO](https://redisson.pro) version | вќЊ | вќЊ | вњ”пёЏ | +|`localcache`
available only in [Redisson PRO](https://redisson.pro) | вњ”пёЏ | вќЊ | вњ”пёЏ | +|`clustered`
available only in [Redisson PRO](https://redisson.pro) | вќЊ | вњ”пёЏ | вњ”пёЏ | +|`clustered_localcache`
available only in [Redisson PRO](https://redisson.pro) | вњ”пёЏ | вњ”пёЏ | вњ”пёЏ | + +**2. Advanced eviction** + +Allows to define `time to live` parameter per map entry. Doesn't use an entry eviction task, entries are cleaned on Redis or Valkey side. + +Available implementations: + +|`impementation`
setting value | Local
cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :-----------:| :----------:| +|`v2`
available only in [Redisson PRO](https://redisson.pro) | вќЊ | вњ”пёЏ | вњ”пёЏ | +|`localcache_v2`
available only in [Redisson PRO](https://redisson.pro) | вњ”пёЏ | вњ”пёЏ | вњ”пёЏ | + +**3. Native eviction** + +Allows to define `time to live` parameter per map entry. Doesn't use an entry eviction task, entries are cleaned on Redis side. +Requires **Redis 7.4+**. + +Available implementations: + +|`impementation`
setting value | Local
cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :-----------:| :----------:| +|`native`
open-source version | вќЊ | вќЊ | вќЊ | +|`native`
[Redisson PRO](https://redisson.pro) version | вќЊ | вќЊ | вњ”пёЏ | +|`localcache_native`
available only in [Redisson PRO](https://redisson.pro) | вњ”пёЏ | вќЊ | вњ”пёЏ | +|`clustered_native`
available only in [Redisson PRO](https://redisson.pro) | вќЊ | вњ”пёЏ | вњ”пёЏ | + + +### Usage + +**1. Add `redisson-quarkus-cache` dependency into your project** + +Maven + +```xml + + org.redisson + + redisson-quarkus-30-cache + xVERSIONx + +``` + +Gradle + +```groovy +// for Quarkus v3.x.x +compile 'org.redisson:redisson-quarkus-30-cache:xVERSIONx' +``` + +**2. Add settings into `application.properties` file** + +* Basic settings + + `expire-after-write` - setting defines time to live of the item stored in the cache. Default value is `0`. + `expire-after-access` - setting defines time to live added to the item after read operation. Default value is `0`. + `implementation` - setting defines the type of cache used. Default value is `standard`. + + Below is the cache configuration example. + + ``` + quarkus.cache.type=redisson + quarkus.cache.redisson.implementation=standard + + # Default configuration for all caches + quarkus.cache.redisson.expire-after-write=5s + quarkus.cache.redisson.expire-after-access=1s + + # Configuration for `sampleCache` cache + quarkus.cache.redisson.sampleCache.expire-after-write=100s + quarkus.cache.redisson.sampleCache.expire-after-access=10s + ``` + +* Local cache settings + + `quarkus.cache.redisson.[CACHE_NAME].max-size` - max size of this cache. Superfluous elements are evicted using LRU algorithm. If 0 the cache is unbounded. Default value is `0`. + + `quarkus.cache.redisson.[CACHE_NAME].cache-size` - local cache size. If size is 0 then local cache is unbounded. Default value is `0`. + + `quarkus.cache.redisson.[CACHE_NAME].reconnection-strategy` - used to load missed updates during any connection failures to Redis. Default value is`CLEAR`. Since, local cache updates can't be executed in absence of connection to Redis. Available values: + * `CLEAR` - Clear local cache if map instance has been disconnected for a while. + * `LOAD` - Store invalidated entry hash in invalidation log for 10 minutes. Cache keys for stored invalidated entry hashes will be removed if LocalCachedMap instance has been disconnected less than 10 minutes or whole cache will be cleaned otherwise. + * `NONE` - No reconnection handling + + `redisson.cache.redisson.[CACHE_NAME].sync-strategy` - used to synchronize local cache changes. Default value is`INVALIDATE`. Available values: + * `INVALIDATE` - Invalidate cache entry across all LocalCachedMap instances on map entry change. + * `UPDATE` - Insert/update cache entry across all LocalCachedMap instances on map entry change. + * `NONE` - No synchronizations on map changes. + + `redisson.cache.redisson.[CACHE_NAME].eviction-policy` - defines local cache eviction policy. Default value is`NONE`. Available values: + * `LRU` - uses local cache with LRU (least recently used) eviction policy. + * `LFU` - uses local cache with LFU (least frequently used) eviction policy. + * `SOFT` - uses local cache with soft references. The garbage collector will evict items from the local cache when the JVM is running out of memory. + * `WEAK` - uses local cache with weak references. The garbage collector will evict items from the local cache when it became weakly reachable. + * `NONE` - doesn't use eviction policy, but timeToLive and maxIdleTime params are still working. + + `redisson.cache.redisson.[CACHE_NAME].time-to-live` - time to live duration of each map entry in local cache. If value equals to 0 then timeout is not applied. Default value is `0`. + + `redisson.cache.redisson.[CACHE_NAME].max-idle` - defines max idle time duration of each map entry in local cache. If value equals to 0 then timeout is not applied. Default value is `0`. + + `redisson.cache.redisson.[CACHE_NAME].store-mode` - defines store mode of cache data. Default value is `LOCALCACHE_REDIS`. Available values: + * `LOCALCACHE` - store data in local cache only and use Redis or Valkey only for data update/invalidation + * `LOCALCACHE_REDIS` - store data in both Redis or Valkey and local cache + + `redisson.cache.redisson.[CACHE_NAME].cache-provider` - defines Cache provider used as local cache store. Default value is `REDISSON`. Available values: + * `REDISSON` - uses Redisson own implementation + * `CAFFEINE` - uses Caffeine implementation + + `redisson.cache.redisson.[CACHE_NAME].store-cache-miss` - defines whether to store a cache miss into the local cache. Default value is `false`. + +Local cache configuration example: + +``` +quarkus.cache.type=redisson +# possible values for localcache: localcache, localcache_v2, clustered_localcache +quarkus.cache.redisson.implementation=localcache + +# Default configuration for all caches +quarkus.cache.redisson.expire-after-write=5s +quarkus.cache.redisson.expire-after-access=1s +quarkus.cache.redisson.cache-size=100 +quarkus.cache.redisson.eviction-policy=LFU +quarkus.cache.redisson.time-to-live=10s +quarkus.cache.redisson.max-idle=5s + +# Configuration for `sampleCache` cache +quarkus.cache.redisson.sampleCache.expire-after-write=100s +quarkus.cache.redisson.sampleCache.expire-after-access=10s +quarkus.cache.redisson.sampleCache.cache-size=100 +quarkus.cache.redisson.sampleCache.eviction-policy=LFU +quarkus.cache.redisson.sampleCache.time-to-live=10s +quarkus.cache.redisson.sampleCache.max-idle=5s +``` + +{% include 'cache/Micronaut-cache.md' %} \ No newline at end of file diff --git a/docs/cache/micronaut-cache.md b/docs/cache/micronaut-cache.md new file mode 100644 index 000000000..497c5cb19 --- /dev/null +++ b/docs/cache/micronaut-cache.md @@ -0,0 +1,492 @@ +## Micronaut Cache + +### Eviction, local cache and data partitioning + +Redisson provides various Micronaut Cache implementations with multiple important features: + +**local cache** - so called `near cache` used to speed up read operations and avoid network roundtrips. It caches Map entries on Redisson side and executes read operations up to **45x faster** in comparison with common implementation. Local cache instances with the same name connected to the same pub/sub channel. This channel is used for exchanging of update/invalidate events between all instances. Local cache store doesn't use `hashCode()`/`equals()` methods of key object, instead it uses hash of serialized state. + +**data partitioning** - although any Cache object is cluster compatible its content isn't scaled/partitioned across multiple Redis or Valkey master nodes in cluster. Data partitioning allows to scale available memory, read/write operations and entry eviction process for individual Map instance in cluster. + +**1. Scripted eviction** + +Allows to define `time to live` or `max idle time` parameters per map entry. Eviction is done on Redisson side through a custom scheduled task which removes expired entries using Lua script. Eviction task is started once per unique object name at the moment of getting Map instance. If instance isn't used and has expired entries it should be get again to start the eviction process. This leads to extra Redis or Valkey calls and eviction task per unique map object name. + +Entries are cleaned time to time by `org.redisson.eviction.EvictionScheduler`. By default, it removes 100 expired entries at a time. This can be changed through [cleanUpKeysAmount](../configuration.md) setting. Task launch time tuned automatically and depends on expired entries amount deleted in previous time and varies between 5 second to 30 minutes by default. This time interval can be changed through [minCleanUpDelay](../configuration.md) and [maxCleanUpDelay](../configuration.md). For example, if clean task deletes 100 entries each time it will be executed every 5 seconds (minimum execution delay). But if current expired entries amount is lower than previous one then execution delay will be increased by 1.5 times and decreased otherwise. + +Available implementations: + +|Setting prefix | Local cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :----------:| :----------:| +|redisson.caches.*
open-source version | ❌ | ❌ | ❌ | +|redisson.caches.*
[Redisson PRO](http://redisson.pro) version | ❌ | ❌ | ✔️ | +|redisson.local-caches.*
available only in [Redisson PRO](http://redisson.pro) | ✔️ | ❌ | ✔️ | +|redisson.clustered-caches.*
available only in [Redisson PRO](http://redisson.pro) | ❌ | ✔️ | ✔️ | +|redisson.clustered-local-caches.*
available only in [Redisson PRO](http://redisson.pro) | ✔️ | ✔️ | ✔️ | + +**2. Advanced eviction** + +Allows to define `time to live` parameter per map entry. Doesn't use an entry eviction task, entries are cleaned on Redis or Valkey side. + +Available implementations: + +|Setting prefix | Local cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :----------:| :----------:| +|redisson.caches-v2.*
available only in [Redisson PRO](http://redisson.pro) | ❌ | ✔️ | ✔️ | +|redisson.local-caches-v2.*
available only in [Redisson PRO](http://redisson.pro) | ✔️ | ✔️ | ✔️ | + +**3. Native eviction** + +Allows to define `time to live` parameter per map entry. Doesn't use an entry eviction task, entries are cleaned on Redis side. +Requires **Redis 7.4+**. + +Available implementations: + +|Setting prefix | Local cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :----------:| :----------:| +|redisson.caches-native.*
open-source version | ❌ | ❌ | ❌ | +|redisson.caches-native.*
[Redisson PRO](http://redisson.pro) version | ❌ | ❌ | ✔️ | +|redisson.local-caches-native.*
available only in [Redisson PRO](http://redisson.pro) | ✔️ | ❌ | ✔️ | +|redisson.clustered-caches-native.*
available only in [Redisson PRO](http://redisson.pro) | ❌ | ✔️ | ✔️ | + + +### Usage + +**1. Add `redisson-micronaut` dependency into your project** + +Maven + +```xml + + org.redisson + + redisson-micronaut-20 + + redisson-micronaut-30 + + redisson-micronaut-40 + xVERSIONx + +``` + +Gradle + +```groovy +// for Micronaut v2.0.x - v2.5.x +compile 'org.redisson:redisson-micronaut-20:xVERSIONx' +// for Micronaut v3.x.x +compile 'org.redisson:redisson-micronaut-30:xVERSIONx' +// for Micronaut v4.x.x +compile 'org.redisson:redisson-micronaut-40:xVERSIONx' +``` + +**2. Add settings into application.yml file** + +Config structure is a Redisson YAML configuration - +([single mode](../configuration.md/#single-settings), +[replicated mode](../configuration.md/#replicated-settings), +[cluster mode](../configuration.md/#cluster-settings), +[sentinel mode](../configuration.md/#sentinel-settings), +[proxy mode](../configuration.md/#proxy-mode-settings), +[multi cluster mode](../configuration.md/#multi-cluster-settings), +[multi sentinel mode](../configuration.md/#multi-sentinel-settings)) + +NOTE: Setting names in camel case should be joined with hyphens (-). + +Config example: + +```yaml +redisson: + single-server-config: + address: "redis://127.0.0.1:6379" + threads: 16 + netty-threads: 32 + caches: + my-cache1: + expire-after-write: 10s + expire-after-access: 3s + max-size: 1000 + codec: org.redisson.codec.Kryo5Codec + my-cache2: + expire-after-write: 200s + expire-after-access: 30s + +``` + +??? note "Map Cache settings. Click to expand" + **Setting name**: `redisson.caches.[CACHE_NAME].max-size` + Type: `java.lang.Integer` + Description: Max size of this cache. Superfluous elements are evicted using LRU algorithm. If `0` the cache is unbounded. + Default value: `0` + + **Setting name**: `redisson.caches.[CACHE_NAME].codec` + Type: `java.lang.Class` + Description: Data codec applied to cache entries. + Default value: `org.redisson.codec.Kryo5Codec` + + **Setting name**: `redisson.caches.[CACHE_NAME].expire-after-write` + Type: `java.time.Duration` + Description: Cache entry time to live duration applied after each write operation. Disabled if value is 0. + Default value: `0` + + **Setting name**: `redisson.caches.[CACHE_NAME].expire-after-access` + Type: `java.time.Duration` + Description: Cache entry time to live duration applied after each read operation. Disabled if value is 0. + Default value: `0` + + + **Setting name**: `redisson.caches.[CACHE_NAME].write-behind-batch-size` + Type: `java.lang.Integer` + Description: Write behind tasks batch size. During `MapWriter` methods execution all updates accumulated into a batch of specified size. + Default value: `50` + + **Setting name**: `redisson.caches.[CACHE_NAME].write-behind-delay` + Type: `java.time.Duration` + Description: Write behind tasks execution delay. All updates would be applied with lag not more than specified delay. + Default value: `1000ms` + + **Setting name**: `redisson.caches.[CACHE_NAME].writer` + Type: `java.lang.Class` + Description: `MapWriter` object used for write-through operations + Default value: `null` + + **Setting name**: `redisson.caches.[CACHE_NAME].write-mode` + Type: `java.lang.String` + Description: Write mode. Default is `WRITE_THROUGH` + Default value: `null` + + **Setting name**: `redisson.caches.[CACHE_NAME].loader` + Type: `java.lang.Class` + Description: `MapLoader` object used to load entries during read-operations execution + Default value: `null` + +```yaml +redisson: + single-server-config: + address: "redis://127.0.0.1:6379" + clustered-caches: + my-cache1: + expire-after-write: 10s + expire-after-access: 3s + max-size: 1000 + codec: org.redisson.codec.Kryo5Codec + my-cache2: + expire-after-write: 200s + expire-after-access: 30s +``` + +??? note "Clustered Map Cache settings. Click to expand" + _These settings are available only in [Redisson PRO](https://redisson.pro)_ + + **Setting name**: `redisson.clustered-caches.[CACHE_NAME].max-size` + Type: `java.lang.Integer` + Description: Max size of this cache. Superfluous elements are evicted using LRU algorithm. If 0 the cache is unbounded. + Default value: `0` + + **Setting name**: `redisson.clustered-caches.[CACHE_NAME].codec` + Type: `java.lang.Class` + Description: Data codec applied to cache entries. + Default value: `Kryo5Codec` + + **Setting name**: `redisson.clustered-caches.[CACHE_NAME].expire-after-write` + Type: `java.time.Duration` + Description: Cache entry time to live duration applied after each write operation. Disabled if value is 0. + Default value: `0` + + **Setting name**: `redisson.clustered-caches.[CACHE_NAME].expire-after-access` + Type: `java.time.Duration` + Description: Cache entry time to live duration applied after each read operation. Disabled if value is 0. + Default value: `0` + + **Setting name**: `redisson.clustered-caches.[CACHE_NAME].write-behind-batch-size` + Type: `java.lang.Integer` + Description: Write behind tasks batch size. During `MapWriter` methods execution all updates accumulated into a batch of specified size. + Default value: `50` + + **Setting name**: `redisson.clustered-caches.[CACHE_NAME].write-behind-delay` + Type: `java.time.Duration` + Description: Write behind tasks execution delay. All updates would be applied with lag not more than specified delay. + Default value: `1000ms` + + **Setting name**: `redisson.clustered-caches.[CACHE_NAME].writer` + Type: `java.lang.Class` + Description: `MapWriter` object used for write-through operations + Default value: `null` + + **Setting name** `redisson.clustered-caches.[CACHE_NAME].write-mode` + Type: `java.lang.String` + Description: Write mode. Default is `WRITE_THROUGH` + Default value: `null` + + **Setting name** `redisson.clustered-caches.[CACHE_NAME].loader` + Type: `java.lang.Class` + Description: `MapLoader` object used to load entries during read-operations execution + Default value: `null` + + +```yaml +redisson: + single-server-config: + address: "redis://127.0.0.1:6379" + clustered-local-caches: + my-cache1: + expire-after-write: 10s + expire-after-access: 3s + max-size: 1000 + codec: org.redisson.codec.Kryo5Codec + store-сache-miss: true + eviction-policy: `LFU` + cache-size: 5000 + time-to-live: 2s + max-idle: 1s + my-cache2: + expire-after-write: 200s + expire-after-access: 30s + time-to-live: 10s + max-idle: 5s +``` + +??? note "Clustered Local Map Cache settings. Click to expand" + _These settings are available only in [Redisson PRO](https://redisson.pro)_ + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].max-size` + Type: `java.lang.Integer` + Description: Max size of this cache. Superfluous elements are evicted using LRU algorithm. If 0 the cache is unbounded. + Default value: `0` | + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].codec` + Type: `java.lang.Class` + Description: Data codec applied to cache entries. + Default value: `Kryo5Codec` + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].expire-after-write` + Type: `java.time.Duration` + Description: Cache entry time to live duration applied after each write operation. Disabled if value is 0. + Default value: `0` + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].expire-after-access` + Type: `java.time.Duration` + Description: Cache entry time to live duration applied after each read operation. Disabled if value is 0. + Default value: `0` + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].write-behind-batch-size` + Type: `java.lang.Integer` + Description: Write behind tasks batch size. During `MapWriter` methods execution all updates accumulated into a batch of specified size. + Default value: `50` + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].write-behind-delay` + Type: `java.time.Duration` + Description: Write behind tasks execution delay. All updates would be applied with lag not more than specified delay. + Default value: `1000ms` + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].writer` + Type: `java.lang.Class` + Description: `MapWriter` object used for write-through operations + Default value: `null` + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].write-mode` + Type: `java.lang.String` + Description: Write mode. Default is `WRITE_THROUGH` + Default value: `null` + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].loader` + Type: `java.lang.Class` + Description: `MapLoader` object used to load entries during read-operations execution + Default value: `null` + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].cache-size` + Type: `java.lang.Integer` + Description: Local cache size. If size is 0 then local cache is unbounded. + Default value: `0` + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].reconnection-strategy` + Type: `java.lang.String` + Description: Used to load missed updates during any connection failures to Redis. Since, local cache updates can't be executed in absence of connection to Redis: + + * `CLEAR` - Clear local cache if map instance has been disconnected for a while. + * `LOAD` - Store invalidated entry hash in invalidation log for 10 minutes. Cache keys for stored invalidated entry hashes will be removed if LocalCachedMap instance has been disconnected less than 10 minutes or whole cache will be cleaned otherwise. + * `NONE` - No reconnection handling + + Default value: `NONE` + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].sync-strategy` + Type: `java.lang.String` + Description: Used to synchronize local cache changes. + + * `INVALIDATE` - Invalidate cache entry across all LocalCachedMap instances on map entry change. + * `UPDATE` - Insert/update cache entry across all LocalCachedMap instances on map entry change. + * `NONE` - No synchronizations on map changes. + + Default value: `NONE` + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].eviction-policy` + Type: `java.lang.String` + Description: Defines local cache eviction policy. + + * `LRU` - uses local cache with LRU (least recently used) eviction policy. + * `LFU` - uses local cache with LFU (least frequently used) eviction policy. + * `SOFT` - uses local cache with soft references. The garbage collector will evict items from the local cache when the JVM is running out of memory. + * `WEAK` - uses local cache with weak references. The garbage collector will evict items from the local cache when it became weakly reachable. + * `NONE` - doesn't use eviction policy, but timeToLive and maxIdleTime params are still working. + + Default value: `NONE` + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].time-to-live` + Type: `java.lang.Integer` + Description: Time to live duration of each map entry in local cache. If value equals to 0 then timeout is not applied. + Default value: `0` + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].max-idle` + Type: `java.lang.Integer` + Description: Defines max idle time duration of each map entry in local cache. If value equals to 0 then timeout is not applied. + Default value: `0` + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].cache-provider` + Type: `java.lang.String` + Description: Defines Cache provider used as local cache store. + + * `REDISSON` - uses Redisson own implementation. + * `CAFFEINE` - uses Caffeine implementation. + + Default value: `REDISSON` + + **Setting name**: `redisson.clustered-local-caches.[CACHE_NAME].store-сache-miss` + Type: `java.lang.Boolean` + Description: Defines whether to store a cache miss into the local cache. + Default value: `false` + +```yaml +redisson: + single-server-config: + address: "redis://127.0.0.1:6379" + local-caches: + my-cache1: + expire-after-write: 10s + expire-after-access: 3s + max-size: 1000 + codec: org.redisson.codec.Kryo5Codec + store-сache-miss: true + eviction-policy: `LFU` + cache-size: 5000 + time-to-live: 1s + max-idle: 1s + my-cache2: + expire-after-write: 200s + expire-after-access: 30s + eviction-policy: `LFU` + cache-size: 5000 + time-to-live: 10s + max-idle: 5s +``` + +??? note "Local Cached Map Cache settings. Click to expand" + _These settings are available only in [Redisson PRO](https://redisson.pro)_ + + **Setting name**: `redisson.local-caches.[CACHE_NAME].max-size` + Type: `java.lang.Integer` + Description: Max size of this cache. Superfluous elements are evicted using LRU algorithm. If 0 the cache is unbounded. + Default value: `0` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].codec` + Type: `java.lang.Class` + Description: Data codec applied to cache entries. + Default value: `Kryo5Codec` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].expire-after-write` + Type: `java.time.Duration` + Description: Cache entry time to live duration applied after each write operation. Disabled if value is 0. + Default value: `0` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].expire-after-access` + Type: `java.time.Duration` + Description: Cache entry time to live duration applied after each read operation. Disabled if value is 0. + Default value: `0` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].write-behind-batch-size` + Type: `java.lang.Integer` + Description: Write behind tasks batch size. During `MapWriter` methods execution all updates accumulated into a batch of specified size. + Default value: `50` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].write-behind-delay` + Type: `java.time.Duration` + Description: Write behind tasks execution delay. All updates would be applied with lag not more than specified delay. + Default value: `1000ms` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].writer` + Type: `java.lang.Class` + Description: `MapWriter` object used for write-through operations + Default value: `null` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].write-mode` + Type: `java.lang.String` + Description: Write mode. Default is `WRITE_THROUGH` + Default value: `null` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].loader` + Type: `java.lang.Class` + Description: `MapLoader` object used to load entries during read-operations execution + Default value: `null` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].cache-size` + Type: `java.lang.Integer` + Description: Local cache size. If size is 0 then local cache is unbounded. + Default value: `0` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].reconnection-strategy` + Type: `java.lang.String` + Description: Used to load missed updates during any connection failures to Redis. Since, local cache updates can't be executed in absence of connection to Redis.
`CLEAR` - Clear local cache if map instance has been disconnected for a while.
`LOAD` - Store invalidated entry hash in invalidation log for 10 minutes.
Cache keys for stored invalidated entry hashes will be removed if LocalCachedMap instance has been disconnected less than 10 minutes or whole cache will be cleaned otherwise.
`NONE` - No reconnection handling + Default value: `NONE` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].sync-strategy` + Type: `java.lang.String` + Description: Used to synchronize local cache changes.
`INVALIDATE` - Invalidate cache entry across all LocalCachedMap instances on map entry change.
`UPDATE` - Insert/update cache entry across all LocalCachedMap instances on map entry change.
`NONE` - No synchronizations on map changes. + Default value: `NONE` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].eviction-policy` + Type: `java.lang.String` + Description: Defines local cache eviction policy.
`LRU` - uses local cache with LRU (least recently used) eviction policy.
`LFU` - uses local cache with LFU (least frequently used) eviction policy.
`SOFT` - uses local cache with soft references. The garbage collector will evict items from the local cache when the JVM is running out of memory.
`WEAK` - uses local cache with weak references. The garbage collector will evict items from the local cache when it became weakly reachable.
`NONE` - doesn't use eviction policy, but timeToLive and maxIdleTime params are still working. + Default value: `NONE` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].time-to-live` + Type: `java.time.Duration` + Description: Time to live duration of each map entry in local cache. If value equals to 0 then timeout is not applied. + Default value: `0` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].max-idle` + Type: `java.time.Duration` + Description: Defines max idle time duration of each map entry in local cache. If value equals to 0 then timeout is not applied. + Default value: `0` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].cache-provider` + Type: `java.lang.String` + Description: Defines Cache provider used as local cache store.
`REDISSON` - uses Redisson own implementation.
`CAFFEINE` - uses Caffeine implementation. + Default value: `0` + + **Setting name**: `redisson.local-caches.[CACHE_NAME].store-сache-miss` + Type: `java.lang.Boolean` + Description: Defines whether to store a cache miss into the local cache. + Default value: `false` + +Code example: + +```java +@Singleton +@CacheConfig("my-cache1") +public class CarsService { + + @Cacheable + public List listAll() { + // ... + } + + @CachePut(parameters = {"type"}) + public List addCar(String type, String description) { + // ... + } + + @CacheInvalidate(parameters = {"type"}) + public void removeCar(String type, String description) { + // ... + } +} +``` diff --git a/docs/cache/spring-cache.md b/docs/cache/spring-cache.md new file mode 100644 index 000000000..0572b3eb1 --- /dev/null +++ b/docs/cache/spring-cache.md @@ -0,0 +1,250 @@ +## Spring Cache + +Redisson provides various Spring Cache implementations. Each Cache instance has two important parameters: `ttl` and `maxIdleTime`. Data is stored infinitely if these settings are not defined or equal to `0`. + +Config example: +```java + @Configuration + @ComponentScan + @EnableCaching + public static class Application { + + @Bean(destroyMethod="shutdown") + RedissonClient redisson() throws IOException { + Config config = new Config(); + config.useClusterServers() + .addNodeAddress("redis://127.0.0.1:7004", "redis://127.0.0.1:7001"); + return Redisson.create(config); + } + + @Bean + CacheManager cacheManager(RedissonClient redissonClient) { + Map config = new HashMap(); + + // create "testMap" cache with ttl = 24 minutes and maxIdleTime = 12 minutes + config.put("testMap", new CacheConfig(24*60*1000, 12*60*1000)); + return new RedissonSpringCacheManager(redissonClient, config); + } + + } +``` + +Cache configuration can be read from YAML configuration files: + +```java + @Configuration + @ComponentScan + @EnableCaching + public static class Application { + + @Bean(destroyMethod="shutdown") + RedissonClient redisson(@Value("classpath:/redisson.yaml") Resource configFile) throws IOException { + Config config = Config.fromYAML(configFile.getInputStream()); + return Redisson.create(config); + } + + @Bean + CacheManager cacheManager(RedissonClient redissonClient) throws IOException { + return new RedissonSpringCacheManager(redissonClient, "classpath:/cache-config.yaml"); + } + + } +``` + +### Eviction, local cache and data partitioning +Redisson provides various Spring Cache managers with two important features: + +**local cache** - so called `near cache` used to speed up read operations and avoid network roundtrips. It caches Map entries on Redisson side and executes read operations up to **45x faster** in comparison with common implementation. Local cache instances with the same name connected to the same pub/sub channel. This channel is used for exchanging of update/invalidate events between all instances. Local cache store doesn't use `hashCode()`/`equals()` methods of key object, instead it uses hash of serialized state. + +**data partitioning** - although Map object is cluster compatible its content isn't scaled/partitioned across multiple Redis or Valkey master nodes in cluster. Data partitioning allows to scale available memory, read/write operations and entry eviction process for individual Map instance in cluster. + +**Scripted eviction** + +Allows to define `time to live` or `max idle time` parameters per map entry. Eviction is done on Redisson side through a custom scheduled task which removes expired entries using Lua script. Eviction task is started once per unique object name at the moment of getting Map instance. If instance isn't used and has expired entries it should be get again to start the eviction process. This leads to extra Redis or Valkey calls and eviction task per unique map object name. + +Entries are cleaned time to time by `org.redisson.eviction.EvictionScheduler`. By default, it removes 100 expired entries at a time. This can be changed through [cleanUpKeysAmount](../configuration.md) setting. Task launch time tuned automatically and depends on expired entries amount deleted in previous time and varies between 5 second to 30 minutes by default. This time interval can be changed through [minCleanUpDelay](../configuration.md) and [maxCleanUpDelay](../configuration.md). For example, if clean task deletes 100 entries each time it will be executed every 5 seconds (minimum execution delay). But if current expired entries amount is lower than previous one then execution delay will be increased by 1.5 times and decreased otherwise. + +Available implementations: + +|Class name | Local
cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :----------:| :----------:| +|RedissonSpringCacheManager
open-source version | ❌ | ❌ | ❌ | +|RedissonSpringCacheManager
[Redisson PRO](https://redisson.pro) version | ❌ | ❌ | ✔️ | +|RedissonSpringLocalCachedCacheManager
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ❌ | ✔️ | +|RedissonClusteredSpringCacheManager
available only in [Redisson PRO](https://redisson.pro) | ❌ | ✔️ | ✔️ | +|RedissonClusteredSpringLocalCachedCacheManager
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ✔️ | ✔️ | + +**Advanced eviction** + +Allows to define `time to live` parameter per map entry. Doesn't use an entry eviction task, entries are cleaned on Redis or Valkey side. + +Available implementations: + +|Class name | Local
cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :----------:| :----------:| +|RedissonSpringCacheV2Manager
available only in [Redisson PRO](https://redisson.pro) | ❌ | ✔️ | ✔️ | +|RedissonSpringLocalCachedCacheV2Manager
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ✔️ | ✔️ | + +**Native eviction** + +Allows to define `time to live` parameter per map entry. Doesn't use an entry eviction task, entries are cleaned on Redis side. +Requires **Redis 7.4+**. + +Available implementations: + +|Class name | Local
cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :----------:| :----------:| +|RedissonSpringCacheNativeManager
open-source version | ❌ | ❌ | ❌ | +|RedissonSpringCacheNativeManager
[Redisson PRO](https://redisson.pro) version | ❌ | ❌ | ✔️ | +|RedissonSpringLocalCachedCacheNativeManager
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ❌ | ✔️ | +|RedissonClusteredSpringCacheNativeManager
available only in [Redisson PRO](https://redisson.pro) | ❌ | ✔️ | ✔️ | + +**Local cache** + +Follow options object can be supplied during local cached managers initialization: +```java +LocalCachedMapOptions options = LocalCachedMapOptions.defaults() + +// Defines whether to store a cache miss into the local cache. +// Default value is false. +.storeCacheMiss(false); + +// Defines store mode of cache data. +// Follow options are available: +// LOCALCACHE - store data in local cache only. +// LOCALCACHE_REDIS - store data in both Redis or Valkey and local cache. +.storeMode(StoreMode.LOCALCACHE_REDIS) + +// Defines Cache provider used as local cache store. +// Follow options are available: +// REDISSON - uses Redisson own implementation +// CAFFEINE - uses Caffeine implementation +.cacheProvider(CacheProvider.REDISSON) + + // Defines local cache eviction policy. + // Follow options are available: + // LFU - Counts how often an item was requested. Those that are used least often are discarded first. + // LRU - Discards the least recently used items first + // SOFT - Uses weak references, entries are removed by GC + // WEAK - Uses soft references, entries are removed by GC + // NONE - No eviction +.evictionPolicy(EvictionPolicy.NONE) + + // If cache size is 0 then local cache is unbounded. +.cacheSize(1000) + + // Used to load missed updates during any connection failures to Redis. + // Since, local cache updates can't be get in absence of connection to Redis. + // Follow reconnection strategies are available: + // CLEAR - Clear local cache if map instance has been disconnected for a while. + // LOAD - Store invalidated entry hash in invalidation log for 10 minutes + // Cache keys for stored invalidated entry hashes will be removed + // if LocalCachedMap instance has been disconnected less than 10 minutes + // or whole cache will be cleaned otherwise. + // NONE - Default. No reconnection handling +.reconnectionStrategy(ReconnectionStrategy.NONE) + + // Used to synchronize local cache changes. + // Follow sync strategies are available: + // INVALIDATE - Default. Invalidate cache entry across all LocalCachedMap instances on map entry change + // UPDATE - Insert/update cache entry across all LocalCachedMap instances on map entry change + // NONE - No synchronizations on map changes +.syncStrategy(SyncStrategy.INVALIDATE) + + // time to live for each map entry in local cache +.timeToLive(10000) + // or +.timeToLive(10, TimeUnit.SECONDS) + + // max idle time for each map entry in local cache +.maxIdle(10000) + // or +.maxIdle(10, TimeUnit.SECONDS); +``` + +Each Spring Cache instance has two important parameters: `ttl` and `maxIdleTime` and stores data infinitely if they are not defined or equal to `0`. + +Complete config example: +```java +@Configuration +@ComponentScan +@EnableCaching +public static class Application { + + @Bean(destroyMethod="shutdown") + RedissonClient redisson() throws IOException { + Config config = new Config(); + config.useClusterServers() + .addNodeAddress("redis://127.0.0.1:7004", "redis://127.0.0.1:7001"); + return Redisson.create(config); + } + + @Bean + CacheManager cacheManager(RedissonClient redissonClient) { + Map config = new HashMap(); + + // define local cache settings for "testMap" cache. + // ttl = 48 minutes and maxIdleTime = 24 minutes for local cache entries + LocalCachedMapOptions options = LocalCachedMapOptions.defaults() + .evictionPolicy(EvictionPolicy.LFU) + .timeToLive(48, TimeUnit.MINUTES) + .maxIdle(24, TimeUnit.MINUTES); + .cacheSize(1000); + + // create "testMap" Redis or Valkey cache with ttl = 24 minutes and maxIdleTime = 12 minutes + LocalCachedCacheConfig cfg = new LocalCachedCacheConfig(24*60*1000, 12*60*1000, options); + // Max size of map stored in Redis + cfg.setMaxSize(2000); + config.put("testMap", cfg); + + return new RedissonSpringLocalCachedCacheManager(redissonClient, config); + // or + return new RedissonSpringLocalCachedCacheNativeManager(redissonClient, config); + // or + return new RedissonSpringLocalCachedCacheV2Manager(redissonClient, config); + // or + return new RedissonClusteredSpringLocalCachedCacheManager(redissonClient, config); + } + +} +``` + +Cache configuration could be read from YAML configuration files: + +```java + @Configuration + @ComponentScan + @EnableCaching + public static class Application { + + @Bean(destroyMethod="shutdown") + RedissonClient redisson(@Value("classpath:/redisson.yaml") Resource configFile) throws IOException { + Config config = Config.fromYAML(configFile.getInputStream()); + return Redisson.create(config); + } + + @Bean + CacheManager cacheManager(RedissonClient redissonClient) throws IOException { + return new RedissonSpringLocalCachedCacheManager(redissonClient, "classpath:/cache-config.yaml"); + } + + } +``` + +### YAML config format +Below is the configuration of Spring Cache with name `testMap` in YAML format: + +```yaml +--- +testMap: + ttl: 1440000 + maxIdleTime: 720000 + localCacheOptions: + invalidationPolicy: "ON_CHANGE" + evictionPolicy: "NONE" + cacheSize: 0 + timeToLiveInMillis: 0 + maxIdleInMillis: 0 +``` + +_Please note: `localCacheOptions` settings are available for `org.redisson.spring.cache.RedissonSpringLocalCachedCacheManager` and `org.redisson.spring.cache.RedissonSpringClusteredLocalCachedCacheManager` classes only._ diff --git a/docs/client-tracking.md b/docs/client-tracking.md new file mode 100644 index 000000000..0f11f0627 --- /dev/null +++ b/docs/client-tracking.md @@ -0,0 +1,37 @@ +Client tracking listener is invoked when an invalidation message is received if the data previously requested has been changed. Next listener invocation will be made only if a new data request has been made and another change has occurred since then. + +Available for [RBucket](data-and-services/objects.md/#object-holder), [RStream](data-and-services/objects.md/#stream), [RSet](data-and-services/objects.md/#set), [RMap](data-and-services/objects.md/#map), [RScoredSortedSet](data-and-services/collections.md/#scoredsortedset), [RList](data-and-services/collections.md/#list), [RQueue](data-and-services/collections.md/#queue), [RDeque](data-and-services/collections.md/#deque), [RBlockingQueue](data-and-services/collections.md/#blocking-queue), [RBlockingDeque](data-and-services/collections.md/#blocking-deque), [RDelayedQueue](data-and-services/collections.md/#delayed-queue), [RRingBuffer](data-and-services/collections.md/#ring-buffer) objects. + +Requires [protocol](configuration.md) setting value set to `RESP3`. + +Code usage example. +```java +RBucket b = redisson.getBucket("test"); +int listenerId = b.addListener(new TrackingListener() { + @Override + public void onChange(String name) { + // ... + } +}); + +// data requested and change is now tracked +b.get(); + +// ... + +// stop tracking +b.removeListener(listenerId); +``` + +**Flush listener** + +Flush listener is executed on flushall/flushdb commands execution. + +```java +redisson.getKeys().addListener(new FlushListener() { + @Override + public void onFlush(InetSocketAddress address) { + // ... + } +}); +``` \ No newline at end of file diff --git a/docs/commands-mapping.md b/docs/commands-mapping.md new file mode 100644 index 000000000..9d025f14e --- /dev/null +++ b/docs/commands-mapping.md @@ -0,0 +1,209 @@ +Redis or Valkey command|Sync / Async API
Redisson.create(config)|Reactive API
redisson.reactive()|RxJava3 API
redisson.rxJava()| +| --- | --- | --- | --- | +AUTH | Config.setPassword() | - | - | +APPEND | RBinaryStream.
getOutputStream().write() | - | - | +BZPOPMAX|RScoredSortedSet.
pollLast()
pollLastAsync() | RScoredSortedSetReactive.
pollLast()| RScoredSortedSetRx.
pollLast() | +BZPOPMIN|RScoredSortedSet.
pollFirst()
pollFirstAsync() | RScoredSortedSetReactive.
pollFirst() | RScoredSortedSetRx.
pollFirst() | +BZMPOP|RScoredSortedSet.
pollLast()
pollLastAsync()
pollLastFromAny()
pollLastEntriesFromAny() | RScoredSortedSetReactive.
pollLast()
pollLastFromAny()
pollLastEntriesFromAny()| RScoredSortedSetRx.
pollLast()
pollLastFromAny()
pollLastEntriesFromAny() | +BITCOUNT|RBitSet.
cardinality()
cardinalityAsync() | RBitSetReactive.
cardinality() | RBitSetRx.
cardinality() | +BITOP|RBitSet.
or()
and()
xor()
orAsync()
andAsync()
xorAsync() | RBitSetReactive.
or()
and()
xor() | RBitSetRx.
or()
and()
xor() | +BITPOS|RBitSet.
length()
lengthAsync() | RBitSetReactive.
length() | RBitSetRx.
length() | +BITFIELD|RBitSet.
getByte()
setByte()
incrementAndGetByte()

getShort()
setShort()
incrementAndGetShort()

getInteger()
setInteger()
incrementAndGetInteger()

getLong()
setLong()
incrementAndGetLong() | RBitSetReactive.
getByte()
setByte()
incrementAndGetByte()

getShort()
setShort()
incrementAndGetShort()

getInteger()
setInteger()
incrementAndGetInteger()

getLong()
setLong()
incrementAndGetLong() | RBitSetRx.
getByte()
setByte()
incrementAndGetByte()

getShort()
setShort()
incrementAndGetShort()

getInteger()
setInteger()
incrementAndGetInteger()

getLong()
setLong()
incrementAndGetLong() | +BLMPOP|RBlockingQueue.
pollLastFromAny()
pollFirstFromAny()
pollLastFromAnyAsync()
pollFirstFromAnyAsync() | RBlockingQueueReactive.
pollLastFromAny()
pollFirstFromAny() |RBlockingQueueRx.
pollLastFromAny()
pollFirstFromAny() | +BLPOP|RBlockingQueue.
take()
poll()
pollFromAny()
takeAsync()
pollAsync()
pollFromAnyAsync() | RBlockingQueueReactive.
take()
poll()
pollFromAny() |RBlockingQueueRx.
take()
poll()
pollFromAny() | +BLMOVE|RBlockingDeque.
move()
moveAsync()| RBlockingDequeReactive.
move()|RBlockingDequeRx.
move()| +BRPOP|RBlockingDeque.
takeLast()
takeLastAsync() | RBlockingDequeReactive.
takeLast() | RBlockingDequeRx.
takeLast() | +BRPOPLPUSH|RBlockingQueue.
pollLastAndOfferFirstTo()
pollLastAndOfferFirstToAsync() | RBlockingQueueReactive.
pollLastAndOfferFirstTo()| RBlockingQueueRx.
pollLastAndOfferFirstTo()| +CONFIG GET|RedisNode.
setConfig()
setConfigAsync() | - | - | +CONFIG SET|RedisNode.
getConfig()
getConfigAsync() | - | - | +COPY|RObject.
copy()
copyAsync() | RObjectReactive.
copy() | RObjectRx.
copy() | +CLIENT SETNAME|Config.setClientName() | - | - | +CLIENT REPLY|BatchOptions.skipResult() | - | - | +CLIENT TRACKING|RBucket
.addListener(TrackingListener)
RStream
.addListener(TrackingListener)
RScoredSortedSet
.addListener(TrackingListener)
RSet
.addListener(TrackingListener)
RMap
.addListener(TrackingListener) | RBucketReactive
.addListener(TrackingListener)
RStreamReactive
.addListener(TrackingListener)
RScoredSortedSetReactive
.addListener(TrackingListener)
RSetReactive
.addListener(TrackingListener)
RMapReactive
.addListener(TrackingListener) | RBucketRx
.addListener(TrackingListener)
RStreamRx
.addListener(TrackingListener)
RScoredSortedSetRx
.addListener(TrackingListener)
RSetRx
.addListener(TrackingListener)
RMapRx
.addListener(TrackingListener) | +CLUSTER INFO| ClusterNode.info() | - | - | +CLUSTER KEYSLOT|RKeys.
getSlot()
getSlotAsync() | RKeysReactive.
getSlot() | RKeysRx.
getSlot() | +CLUSTER NODES|Used in ClusterConnectionManager | +DECRBY|RAtomicLong.
addAndGet()
addAndGetAsync() | RAtomicLongReactive.
addAndGet() |RAtomicLongRx.
addAndGet() | +DUMP|RObject.
dump()
dumpAsync()| RObjectReactive.
dump()| RObjectRx.
dump()| +DBSIZE|RKeys.
count()
countAsync()| RKeysReactive.
count()| RKeysRx.count()| +DECR|RAtomicLong.
decrementAndGet()
decrementAndGetAsync()| RAtomicLongReactive.
decrementAndGet()| RAtomicLongRx.
decrementAndGet()| +DEL|RObject.
delete()
deleteAsync()

RKeys.
delete()
deleteAsync()| RObjectReactive.
delete()

RKeysReactive.
delete()|RObjectRx.
delete()

RKeysRx.
delete()| +STRLEN|RBucket.
size()
sizeAsync()| RBucketReactive.
size()| RBucketRx.
size()| +EVAL|RScript.
eval()
evalAsync()| RScriptReactive.
eval()| RScriptRx.
eval()| +EVALSHA|RScript.
evalSha()
evalShaAsync()| RScriptReactive.
evalSha()| RScriptRx.
evalSha()| +EXEC|RBatch.
execute()
executeAsync()| RBatchReactive.
execute()| RBatchRx.
execute()| +EXISTS|RObject.
isExists()
isExistsAsync()| RObjectReactive.
isExists()| RObjectRx.
isExists()| +FLUSHALL|RKeys.
flushall()
flushallAsync()| RKeysReactive.
flushall()| RKeysRx.
flushall()| +FLUSHDB|RKeys.
flushdb()
flushdbAsync()| RKeysReactive.
flushdb()| RKeysRx.
flushdb()| +GETRANGE|RBinaryStream.
getChannel().read()|RBinaryStreamReactive.
read()|RBinaryStreamRx.
read()| +GEOADD|RGeo.
add()
addAsync()| RGeoReactive.
add()| RGeoRx.
add()| +GEODIST|RGeo.
dist()
distAsync()| RGeoReactive.
dist()| RGeoRx.
dist()| +GEOHASH|RGeo.
hash()
hashAsync()| RGeoReactive.
hash()| RGeoRx.
hash()| +GEOPOS|RGeo.
pos()
posAsync()| RGeoReactive.
pos()| RGeoRx.
pos()| +GEORADIUS|RGeo.
radius()
radiusAsync()
radiusWithDistance()
radiusWithDistanceAsync()
radiusWithPosition()
radiusWithPositionAsync()|RGeoReactive.
radius()
radiusWithDistance()
radiusWithPosition()|RGeoRx.
radius()
radiusWithDistance()
radiusWithPosition()| +GEOSEARCH|RGeo.
search()
searchAsync()| RGeoReactive.
search()| RGeoRx.
search()| +GEOSEARCHSTORE|RGeo.
storeSearchTo()
storeSearchToAsync()| RGeoReactive.
storeSearchTo()| RGeoRx.
storeSearchTo()| +GET|RBucket.
get()
getAsync()

RBinaryStream.
get()
getAsync()| RBucketReactive.
get()

RBinaryStreamReactive.
get()| RBucketRx.
get()

RBinaryStreamRx.
get()| +GETEX|RBucket.
getAndExpire()
getAndExpireAsync()
getAndClearExpire()
getAndClearExpireAsync()| RBucketReactive.
getAndExpire()
getAndClearExpire()| RBucketRx.
getAndExpire()
getAndClearExpire()| +GETBIT|RBitSet.
get()
getAsync()| RBitSetReactive.
get()| RBitSetRx.
get() | +GETSET|RBucket.
getAndSet()
getAndSetAsync()

RAtomicLong.
getAndSet()
getAndSetAsync()

RAtomicDouble.
getAndSet()
getAndSetAsync()|RBucketReactive.
getAndSet()

RAtomicLongReactive.
getAndSet()

RAtomicDoubleReactive.
getAndSet()|RBucketRx.
getAndSet()

RAtomicLongRx.
getAndSet()

RAtomicDoubleRx.
getAndSet() +HDEL|RMap.
fastRemove()
fastRemoveAsync()| RMapReactive.
fastRemove()| RMapRx.
fastRemove()| +HEXISTS|RMap.
containsKey()
containsKeyAsync()| RMapReactive.
containsKey()| RMapRx.
containsKey()| +HGET|RMap.
get()
getAsync()|RMapReactive.
get()|RMapRx.
get()| +HSTRLEN|RMap.
valueSize()
valueSizeAsync()|RMapReactive.
valueSize()| RMapRx.
valueSize()| +HGETALL|RMap.
readAllEntrySet()
readAllEntrySetAsync()|RMapReactive.
readAllEntrySet()| RMapRx.
readAllEntrySet()| +HINCRBY|RMap.
addAndGet()
addAndGetAsync()| RMapReactive.
addAndGet()| RMapRx.
addAndGet()| +HINCRBYFLOAT|RMap.
addAndGet()
addAndGetAsync()| RMapReactive.
addAndGet()| RMapRx.
addAndGet()| +HKEYS|RMap.
readAllKeySet()
readAllKeySetAsync()| RMapReactive.
readAllKeySet()| RMapRx.
readAllKeySet()| +HLEN|RMap.
size()
sizeAsync()| RMapReactive.
size()| RMapRx.
size()| +HMGET|RMap.
getAll()
getAllAsync()| RMapReactive.
getAll()| RMapRx.
getAll()| +HMSET|RMap.
putAll()
putAllAsync()| RMapReactive.
putAll()| RMapRx.
putAll()| +HRANDFIELD|RMap.
putAll()
putAllAsync()| RMapReactive.
putAll()| RMapRx.
putAll()| +HSCAN|RMap.
keySet().iterator()
values().iterator()
entrySet().iterator()|RMapReactive.
keyIterator()
valueIterator()
entryIterator()|RMapRx.
keyIterator()
valueIterator()
entryIterator()| +HSET|RMap.
randomEntries()
randomKeys()
randomEntriesAsync()
randomKeysAsync()| RMapReactive.
randomEntries()
randomKeys()| RMapRx.
randomEntries()
randomKeys()| +HSETNX|RMap.
fastPutIfAbsent()
fastPutIfAbsentAsync()| RMapReactive.
fastPutIfAbsent()| RMapRx.
fastPutIfAbsent()| +HVALS|RMap.
readAllValues()
readAllValuesAsync()| RMapReactive.
readAllValues()| RMapRx.
readAllValues()| +HPEXPIRE|RMapCacheNative.
put()
fastPut()
putIfAbsent()
fastPutIfAbsent()
putAll()
expireEntry()
putAsync()
fastPutAsync()
putIfAbsentAsync()
fastPutIfAbsentAsync()
putAllAsync()
expireEntryAsync()| RMapCacheNativeReactive.

put()
fastPut()
putIfAbsent()
fastPutIfAbsent()
putAll()
expireEntry()| RMapCacheNativeRx.

put()
fastPut()
putIfAbsent()
fastPutIfAbsent()
putAll()
expireEntry()| +HPEXPIREAT|RMapCacheNative.
put()
fastPut()
putIfAbsent()
fastPutIfAbsent()
putAll()
expireEntry()
putAsync()
fastPutAsync()
putIfAbsentAsync()
fastPutIfAbsentAsync()
putAllAsync()
expireEntryAsync()| RMapCacheNativeReactive.

put()
fastPut()
putIfAbsent()
fastPutIfAbsent()
putAll()
expireEntry()| RMapCacheNativeRx.

put()
fastPut()
putIfAbsent()
fastPutIfAbsent()
putAll()
expireEntry()| +HEXPIRE|RMapCacheNative.
put()
fastPut()
putIfAbsent()
fastPutIfAbsent()
putAll()
expireEntry()
putAsync()
fastPutAsync()
putIfAbsentAsync()
fastPutIfAbsentAsync()
putAllAsync()
expireEntryAsync()| RMapCacheNativeReactive.

put()
fastPut()
putIfAbsent()
fastPutIfAbsent()
putAll()
expireEntry()| RMapCacheNativeRx.

put()
fastPut()
putIfAbsent()
fastPutIfAbsent()
putAll()
expireEntry()| +HEXPIREAT|RMapCacheNative.
put()
fastPut()
putIfAbsent()
fastPutIfAbsent()
putAll()
expireEntry()
putAsync()
fastPutAsync()
putIfAbsentAsync()
fastPutIfAbsentAsync()
putAllAsync()
expireEntryAsync()| RMapCacheNativeReactive.

put()
fastPut()
putIfAbsent()
fastPutIfAbsent()
putAll()
expireEntry()| RMapCacheNativeRx.

put()
fastPut()
putIfAbsent()
fastPutIfAbsent()
putAll()
expireEntry()| +HPERSIST|RMapCacheNative.
clearExpire()
clearExpireAsync()| RMapCacheNativeReactive.
clearExpire()| RMapCacheNativeRx.
clearExpire()| +HPTTL|RMapCacheNative.
remainTimeToLive()
remainTimeToLiveAsync()| RMapCacheNativeReactive.
remainTimeToLive()| RMapCacheNativeRx.
remainTimeToLive()| +INCR|RAtomicLong.
incrementAndGet()
incrementAndGetAsync()| RAtomicLongReactive.
incrementAndGet()| RAtomicLongRx.
incrementAndGet()| +INCRBY|RAtomicLong.
addAndGet()
addAndGetAsync()| RAtomicLongReactive.
addAndGet()| RAtomicLongRx.
addAndGet()| +INCRBYFLOAT|RAtomicDouble.
addAndGet()
addAndGetAsync()| RAtomicDoubleReactive.
addAndGet()| RAtomicDoubleRx.
addAndGet()| +JSON.ARRAPPEND|RJsonBucket.
arrayAppend()
arrayAppendAsync()| RJsonBucketReactive.
arrayAppend()| RJsonBucketRx.
arrayAppend()| +JSON.ARRINDEX|RJsonBucket.
arrayIndex()
arrayIndexAsync()| RJsonBucketReactive.
arrayIndex()| RJsonBucketRx.
arrayIndex()| +JSON.ARRINSERT|RJsonBucket.
arrayInsert()
arrayInsertAsync()| RJsonBucketReactive.
arrayInsert()| RJsonBucketRx.
arrayInsert()| +JSON.ARRLEN|RJsonBucket.
arraySize()
arraySizeAsync()| RJsonBucketReactive.
arraySize()| RJsonBucketRx.
arraySize()| +JSON.ARRPOP|RJsonBucket.
arrayPollLast()
arrayPollFirst()
arrayPop()
arrayPollLastAsync()
arrayPollFirstAsync()
arrayPopAsync()| RJsonBucketReactive.
arrayPollLast()
arrayPollFirst()
arrayPop()| RJsonBucketRx.
arrayPollLast()
arrayPollFirst()
arrayPop()| +JSON.ARRTRIM|RJsonBucket.
arrayTrim()
arrayTrimAsync()| RJsonBucketReactive.
arrayTrim()| RJsonBucketRx.
arrayTrim()| +JSON.CLEAR|RJsonBucket.
clear()
clearAsync()| RJsonBucketReactive.
clear()| RJsonBucketRx.
clear()| +JSON.GET|RJsonBucket.
get()
getAsync()| RJsonBucketReactive.
get()| RJsonBucketRx.
get()| +JSON.MERGE|RJsonBucket.
merge()
mergeAsync()| RJsonBucketReactive.
merge()| RJsonBucketRx.
merge()| +JSON.NUMINCRBY|RJsonBucket.
incrementAndGet()
incrementAndGetAsync()| RJsonBucketReactive.
incrementAndGet()| RJsonBucketRx.
incrementAndGet()| +JSON.OBJLEN|RJsonBucket.
countKeys()
countKeysAsync()| RJsonBucketReactive.
countKeys()| RJsonBucketRx.
countKeys()| +JSON.OBJKEYS|RJsonBucket.
getKeys()
getKeysAsync()| RJsonBucketReactive.
getKeys()| RJsonBucketRx.
getKeys()| +JSON.SET|RJsonBucket.
set()
setAsync()| RJsonBucketReactive.
set()| RJsonBucketRx.
set()| +JSON.STRAPPEND|RJsonBucket.
stringAppend()
stringAppendAsync()| RJsonBucketReactive.
stringAppend()| RJsonBucketRx.
stringAppend()| +JSON.STRLEN|RJsonBucket.
stringSize()
stringSizeAsync()| RJsonBucketReactive.
stringSize()| RJsonBucketRx.
stringSize()| +JSON.TOGGLE|RJsonBucket.
toggle()
toggleAsync()| RJsonBucketReactive.
toggle()| RJsonBucketRx.
toggle()| +JSON.TYPE|RJsonBucket.
type()
typeAsync()| RJsonBucketReactive.
type()| RJsonBucketRx.
type()| +KEYS|RKeys.
getKeysByPattern()
getKeysByPatternAsync()| RKeysReactive.
getKeysByPattern()| RKeysRx.
getKeysByPattern()| +LINDEX|RList.
get()
getAsync()| RListReactive.
get()|RListRx.
get()| +LLEN|RList.
size()
sizeAsync()| RListReactive.
size()|RListRx.
size()| +LMOVE|RDeque.
move()
moveAsync()| RDequeReactive.
move()|RDequeRx.
move()| +LPOP|RQueue.
poll()
pollAsync()| RQueueReactive.
poll()|RQueueRx.
poll()| +LPUSH|RDeque.
addFirst()
addFirstAsync()|RDequeReactive.
addFirst()||RDequeRx.
addFirst()| +LRANGE|RList.
readAll()
readAllAsync()|RListReactive.readAll()|RListRx.readAll()| +LPUSHX|RDeque.
addFirstIfExists()
addFirstIfExistsAsync()|RDequeReactive.
addFirstIfExists()|RDequeRx.
addFirstIfExists()| +LREM|RList.
fastRemove()
fastRemoveAsync()|RListReactive.
fastRemove()|RListRx.
fastRemove()| +LSET|RList.
fastSet()
fastSetAsync()|RListReactive.
fastSet()|RListRx.
fastSet()| +LTRIM|RList.
trim()
trimAsync()|RListReactive.
trim()|RListRx.
trim()| +LINSERT|RList.
addBefore()
addAfter()
addBeforeAsync()
addAfterAsync()|RListReactive.
addBefore()
addAfter()|RListRx.
addBefore()
addAfter()| +MULTI|RBatch.
execute()
executeAsync()|RBatchReactive.
execute()|RBatchRx.
execute()| +MGET|RBuckets.
get()
getAsync()|RBucketsReactive.
get()|RBucketsRx.
get()| +MSETNX|RBuckets.
trySet()
trySetAsync()|RBucketsReactive.
trySet()|RBucketsRx.
trySet()| +MIGRATE|RObject.
migrate()
migrateAsync()|RObjectReactive.
migrate()|RObjectRx.
migrate()| +MOVE|RObject.
move()
moveAsync()|RObjectReactive.
move()|RObjectRx.
move()| +MSET|RBuckets.
set()
setAsync()|RBucketsReactive.
set()|RBucketsRx.
set()| +PERSIST|RExpirable.
clearExpire()
clearExpireAsync()|RExpirableReactive.
clearExpire()|RExpirableRx.
clearExpire()| +PEXPIRE|RExpirable.
expire()
expireAsync()|RExpirableReactive.
expire()|RExpirableRx.
expire()| +PEXPIREAT|RExpirable.
expireAt()
expireAtAsync()|RExpirableReactive.
expireAt()|RExpirableRx.
expireAt()| +PEXPIRETIME|RExpirable.
expireTime()
expireTimeAsync()|RExpirableReactive.
expireTime()|RExpirableRx.
expireTime()| +PFADD|RHyperLogLog.
add()
addAsync()
addAll()
addAllAsync()|RHyperLogLogReactive.
add()

addAll()|RHyperLogLogRx.
add()

addAll()| +PFCOUNT|RHyperLogLog.
count()
countAsync()
countWith()
countWithAsync()|RHyperLogLogReactive.
count()
countWith()|RHyperLogLogRx.
count()
countWith()| +PFMERGE|RHyperLogLog.
mergeWith()
mergeWithAsync()|RHyperLogLogReactive.
mergeWith()|RHyperLogLogRx.
mergeWith()| +PING|Node.ping()
NodesGroup.pingAll()| - | - | +PSUBSCRIBE|RPatternTopic.
addListener()|RPatternTopicReactive.
addListener()|RPatternTopicRx.
addListener()| +PSETEX|RBucket.
set()
setAsync()|RBucketReactive.
set()|RBucketRx.
set()| +PTTL|RExpirable.
remainTimeToLive()
remainTimeToLiveAsync()|RExpirableReactive.
remainTimeToLive()|RExpirableRx.
remainTimeToLive()| +PUBLISH|RTopic.
publish()|RTopicReactive.
publish()|RTopicRx.
publish()| +PUBSUB NUMSUB|RTopic.
countSubscribers()
countSubscribersAsync()|RTopicReactive.
countSubscribers()|RTopicRx.
countSubscribers()| +PUNSUBSCRIBE|RPatternTopic.
removeListener()|RPatternTopicReactive.
removeListener()|RPatternTopicRx.
removeListener()| +RANDOMKEY|RKeys.
randomKey()
randomKeyAsync()|RKeysReactive.
randomKey()|RKeysRx.
randomKey()| +RESTORE|RObject.
restore()
restoreAsync()|RObjectReactive.
restore()|RObjectRx.
restore()| +RENAME|RObject.
rename()
renameAsync()|RObjectReactive.
rename()|RObjectRx.
rename()| +RPOP|RDeque.
pollLast()
removeLast()
pollLastAsync()
removeLastAsync()|RDequeReactive.
pollLast()
removeLast()|RDequeRx.
pollLast()
removeLast()| +RPOPLPUSH|RDeque.
pollLastAndOfferFirstTo()
pollLastAndOfferFirstToAsync()|RDequeReactive.
pollLastAndOfferFirstTo()|RDequeRx.
pollLastAndOfferFirstTo()| +RPUSH|RList.
add()
addAsync()|RListReactive.
add()|RListRx.
add()| +RPUSHX|RDeque.
addLastIfExists()
addLastIfExistsAsync()|RListReactive.
addLastIfExists()|RListRx.
addLastIfExists()| +SADD|RSet.
add()
addAsync()|RSetReactive.
add()|RSetRx.
add()| +SETRANGE|RBinaryStream.
getChannel().write()|RBinaryStreamReactive.
write()|RBinaryStreamRx.
write()| +SCAN|RKeys.
getKeys()|RKeysReactive.
getKeys()|RKeysRx.
getKeys()| +SCARD|RSet.
size()
sizeAsync()|RSetReactive.
size()|RSetRx.
size()| +SCRIPT EXISTS|RScript.
scriptExists()
scriptExistsAsync()|RScriptReactive.
scriptExists()|RScriptRx.
scriptExists()| +SCRIPT FLUSH|RScript.
scriptFlush()
scriptFlushAsync()|RScriptReactive.
scriptFlush()|RScriptRx.
scriptFlush()| +SCRIPT KILL|RScript.
scriptKill()
scriptKillAsync()|RScriptReactive.
scriptKill()|RScriptRx.
scriptKill()| +SCRIPT LOAD|RScript.
scriptLoad()
scriptLoadAsync()|RScriptReactive.
scriptLoad()|RScriptRx.
scriptLoad()| +SDIFFSTORE|RSet.
diff()
diffAsync()|RSetReactive.
diff()|RSetRx.
diff()| +SDIFF|RSet.
readDiff()
readDiffAsync()|RSetReactive.
readDiff()|RSetRx.
readDiff()| +SRANDMEMBER|RSet.
random()
randomAsync()|RSetReactive.
random()|RSetRx.
random()| +SELECT|Config.setDatabase()| - | - | +SET|RBucket.
set()
setAsync()|RBucketReactive.
set()|RBucketRx.
set()| +SETBIT|RBitSet.
set()
clear()
setAsync()
clearAsync()|RBitSetReactive.
set()
clear()|RBitSetRx.
set()
clear()| +SETEX|RBucket.
set()
setAsync()|RBucketReactive.
set()|RBucketRx.
set()| +SETNX|RBucket.
setIfAbsent()
setIfAbsentAsync()|RBucketReactive.
setIfAbsent()|RBucketRx.
setIfAbsent()| +SISMEMBER|RSet.
contains()
containsAsync()|RSetReactive.
contains()|RSetRx.
contains()| +SINTERSTORE|RSet.
intersection()
intersectionAsync()|RSetReactive.
intersection()|RSetRx.
intersection()| +SINTER|RSet.
readIntersection()
readIntersectionAsync()|RSetReactive.
readIntersection()|RSetRx.
readIntersection()| +SMEMBERS|RSet.
readAll()
readAllAsync()|RSetReactive.
readAll()|RSetRx.
readAll()| +SMOVE|RSet.
move()
moveAsync()|RSetReactive.
move()|RSetRx.
move()| +SORT|RList.
readSort()
sortTo()
readSortAsync()
sortToAsync()|RListReactive.
readSort()
sortTo()|RListRx.
readSort()
sortTo()| +SPOP|RSet.
removeRandom()
removeRandomAsync()|RSetReactive.
removeRandom()|RSetRx.
removeRandom()| +SREM|RSet.
remove()
removeAsync()|RSetReactive.
remove()|RSetRx.
remove()| +SSCAN|RSet.
iterator()|RSetReactive.
iterator()|RSetRx.
iterator()| +SUBSCRIBE|RTopic.
addListener()|RTopicReactive.
addListener()|RTopicRx.
addListener()| +SUNION|RSet.
readUnion()
readUnionAsync()|RSetReactive.
readUnion()|RSetRx.
readUnion()| +SUNIONSTORE|RSet.
union()
unionAsync()|RSetReactive.
union()|RSetRx.
union()| +SWAPDB|RKeys.
swapdb()
swapdbAsync()|RKeysReactive.
swapdb()|RKeysRx.
swapdb()| +TTL|RExpirable.
remainTimeToLive()
remainTimeToLiveAsync()|RExpirableReactive.
remainTimeToLive()|RExpirableRx.
remainTimeToLive()| +TYPE|RKeys.
getType()
getTypeAsync()|RKeysReactive.
getType()|RKeysRx.
getType()| +TOUCH|RObject.
touch()
touchAsync()|RObjectReactive.
touch()|RObjectRx.
touch()| +UNSUBSCRIBE|RTopic.
removeListener()|RTopicReactive.
removeListener()|RTopicRx.
removeListener()| +UNLINK|RObject.
unlink()
unlinkAsync()|RObjectReactive.
unlink()|RObjectRx.
unlink()| +WAIT|BatchOptions.
sync()|BatchOptions.
sync()|BatchOptions.
sync()| +WAITAOF|BatchOptions.
syncAOF()|BatchOptions.
syncAOF()|BatchOptions.
syncAOF()| +ZADD|RScoredSortedSet.
add()
addAsync()
addAll()
addAllAsync()|RScoredSortedSetReactive.
add()
addAll()|RScoredSortedSetRx.
add()
addAll()| +ZCARD|RScoredSortedSet.
size()
sizeAsync()|RScoredSortedSetReactive.
size()|RScoredSortedSetRx.
size()| +ZCOUNT|RScoredSortedSet.
count()
countAsync()|RScoredSortedSetReactive.
count()|RScoredSortedSetRx.
count()| +ZDIFF|RScoredSortedSet.
readDiff()
readDiffAsync()|RScoredSortedSetReactive.
readDiff()|RScoredSortedSetRx.
readDiff()| +ZDIFFSTORE|RScoredSortedSet.
diff()
diffAsync()|RScoredSortedSetReactive.
diff()|RScoredSortedSetRx.
diff()| +ZINCRBY|RScoredSortedSet.
addScore()
addScoreAsync()|RScoredSortedSetReactive.
addScore()|RScoredSortedSetRx.
addScore()| +ZINTER|RScoredSortedSet.
readIntersection()
readIntersectionAsync()|RScoredSortedSetReactive.
readIntersection()|RScoredSortedSetRx.
readIntersection()| +ZREMRANGEBYRANK|RScoredSortedSet.
removeRangeByRank()
removeRangeByRankAsync()|RScoredSortedSetReactive.
removeRangeByRank()|RScoredSortedSetRx.
removeRangeByRank()| +ZREVRANGEBYLEX|RLexSortedSet.
rangeReversed()
rangeReversedAsync()|RLexSortedSetReactive.
rangeReversed()|RLexSortedSetSetRx.
rangeReversed()| +ZLEXCOUNT|RLexSortedSet.
lexCount()
lexCountHead()
lexCountTail()
lexCountAsync()
lexCountHeadAsync()
lexCountTailAsync()|RLexSortedSetReactive.
lexCount()
lexCountHead()
lexCountTail()|RLexSortedSetRx.
lexCount()
lexCountHead()
lexCountTail()| +ZRANGE|RScoredSortedSet.
valueRange()
valueRangeAsync()|RScoredSortedSetReactive.
valueRange()|RScoredSortedSetRx.
valueRange()| +ZRANDMEMBER|RScoredSortedSet.
random()
randomAsync()|RScoredSortedSetReactive.
random()|RScoredSortedSetRx.
random()| +ZREVRANGE|RScoredSortedSet.
valueRangeReversed()
valueRangeReversedAsync()|RScoredSortedSetReactive.
valueRangeReversed()|RScoredSortedSetRx.
valueRangeReversed()| +ZUNION|RScoredSortedSet.
readUnion()
readUnionAsync()|RScoredSortedSetReactive.
readUnion()|RScoredSortedSetRx.
readUnion()| +ZUNIONSTORE|RScoredSortedSet.
union()
unionAsync()|RScoredSortedSetReactive.
union()|RScoredSortedSetRx.
union()| +ZINTERSTORE|RScoredSortedSet.
intersection()
intersectionAsync()|RScoredSortedSetReactive.
intersection()|RScoredSortedSetRx.
intersection()| +ZPOPMAX|RScoredSortedSet.
pollLast()
pollLastAsync()|RScoredSortedSetReactive.
pollLast()|RScoredSortedSetRx.
pollLast()| +ZPOPMIN|RScoredSortedSet.
pollFirst()
pollFirstAsync()|RScoredSortedSetReactive.
pollFirst()|RScoredSortedSetRx.
pollFirst()| +ZMPOP|RScoredSortedSet.
pollFirstEntriesFromAny()
pollFirstEntriesFromAnyAsync()
pollLastFromAny()
pollLastFromAnyAsync()
pollFirstFromAny()
pollFirstFromAnyAsync()|RScoredSortedSetReactive.
pollFirstEntriesFromAny()
pollLastFromAny()
pollFirstFromAny()|RScoredSortedSetRx.
pollFirstEntriesFromAny()
pollLastFromAny()
pollFirstFromAny()| +ZRANGEBYLEX|RLexSortedSet.
range()
rangeHead()
rangeTail()
rangeAsync()
rangeHeadAsync()
rangeTailAsync()|RLexSortedSetReactive.
range()
rangeHead()
rangeTail()|RLexSortedSetRx.
range()
rangeHead()
rangeTail()| +ZRANGEBYSCORE|RScoredSortedSet.
valueRange()
entryRange()
valueRangeAsync()
entryRangeAsync()|RScoredSortedSetReactive.
valueRange()
entryRange()|RScoredSortedSetRx.
valueRange()
entryRange()| +TIME|RedissonClient.
getNodesGroup().
getNode().time()
getClusterNodesGroup().
getNode().time()| - | - | +ZRANK|RScoredSortedSet.
rank()
rankAsync()|RScoredSortedSetReactive.
rank()|RScoredSortedSetRx.
rank()| +ZREM|RScoredSortedSet.
remove()
removeAll()
removeAsync()
removeAllAsync()|RScoredSortedSetReactive.
remove()
removeAll()|RScoredSortedSetRx.
remove()
removeAll()| +ZREMRANGEBYLEX|RLexSortedSet.
removeRange()
removeRangeHead()
removeRangeTail()
removeRangeAsync()
removeRangeHeadAsync()
removeRangeTailAsync()|RLexSortedSetReactive.
removeRange()
removeRangeHead()
removeRangeTail()|RLexSortedSetRx.
removeRange()
removeRangeHead()
removeRangeTail()| +ZREMRANGEBYSCORE|RScoredSortedSet.
removeRangeByScore()
removeRangeByScoreAsync()|RScoredSortedSetReactive.
removeRangeByScore()|RScoredSortedSetRx.
removeRangeByScore()| +ZREVRANGEBYSCORE|RScoredSortedSet.
valueRangeReversed()
entryRangeReversed()
valueRangeReversedAsync()
entryRangeReversedAsync()|RScoredSortedSetReactive.
entryRangeReversed()
valueRangeReversed()|RScoredSortedSetRx.
entryRangeReversed()
valueRangeReversed()| +ZREVRANK|RScoredSortedSet.
revRank()
revRankAsync()|RScoredSortedSetReactive.
revRank()|RScoredSortedSetRx.
revRank()| +ZSCORE|RScoredSortedSet.
getScore()
getScoreAsync()|RScoredSortedSetReactive.
getScore()|RScoredSortedSetRx.
getScore()| +XACK|RStream.
ack()
ackAsync()|RStreamReactive.
ack()|RStreamRx.
ack()| +XADD|RStream.
add()
addAsync()|RStreamReactive.
add()|RStreamRx.
add()| +XAUTOCLAIM|RStream.
autoClaim()
autoClaimAsync()|RStreamReactive.
autoClaim()|RStreamRx.
autoClaim()| +XCLAIM|RStream.
claim()
claimAsync()|RStreamReactive.
claim()|RStreamRx.
claim()| +XDEL|RStream.
remove()
removeAsync()|RStreamReactive.
remove()|RStreamRx.
remove()| +XGROUP|RStream.
createGroup()
removeGroup()
updateGroup()
createGroupAsync()
removeGroupAsync()
updateGroupAsync()|RStreamReactive.
createGroup()
removeGroup()
updateGroup()|RStreamRx.
createGroup()
removeGroup()
updateGroup()| +XINFO|RStream.
getInfo()
listGroups()
listConsumers()
getInfoAsync()
listGroupsAsync()
listConsumersAsync()|RStreamReactive.
getInfo()
listGroups()
listConsumers()|RStreamRx.
getInfo()
listGroups()
listConsumers()| +XLEN|RStream.
size()
sizeAsync()|RStreamReactive.
size()|RStreamRx.
size()| +XPENDING|RStream.
listPending()
listPendingAsync()|RStreamReactive.
listPending()|RStreamRx.
listPending()| +XRANGE|RStream.
range()
rangeAsync()|RStreamReactive.
range()|RStreamRx.
range()| +XREAD|RStream.
read()
readAsync()|RStreamReactive.
read()|RStreamRx.
read()| +XREADGROUP|RStream.
readGroup()
readGroupAsync()|RStreamReactive.
readGroup()|RStreamRx.
readGroup()| +XREVRANGE|RStream.
rangeReversed()
rangeReversedAsync()|RStreamReactive.
rangeReversed()|RStreamRx.
rangeReversed()| +XTRIM|RStream.
trim()
trimAsync()|RStreamReactive.
trim()|RStreamRx.
trim()| \ No newline at end of file diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 000000000..e538c8ec4 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,2635 @@ +## Using Redisson API +Programmatic configuration is performed by the `Config` object instance. For example: +```java +Config config = new Config(); +config.setTransportMode(TransportMode.EPOLL); +config.useClusterServers() + // use "rediss://" for SSL connection + .addNodeAddress("perredis://127.0.0.1:7181"); + +RedissonClient redisson = Redisson.create(config); +``` +## Using YAML +Redisson can also be configured in a declarative way by using a user-supplied text file in YAML format. + +### YAML config +Redisson configuration could be stored in YAML format. +Use the `config.fromYAML()` method to read the configuration stored in YAML format: +```java +Config config = Config.fromYAML(new File("config-file.yaml")); +RedissonClient redisson = Redisson.create(config); +``` +Use the `config.toYAML()` method to write a configuration in YAML format: +```java +Config config = new Config(); +// ... many settings are set here +String yamlFormat = config.toYAML(); +``` +### Variable syntax + +Variables are used to parameterize the Redisson configuration and are then substituted with the actual values upon Redisson startup. Variables are defined using the `${variable_name}` syntax. To resolve variable references to their values, the following sources are consulted in sequence of precedence, with later sources preceding earlier ones: + +* environment variables +* Java system properties + +Definition example: +```yaml +singleServerConfig: + address: "redis://127.0.0.1:${REDIS_PORT}" +``` + +Default values complies with shell format. Example: +```yaml +singleServerConfig: + address: "redis://127.0.0.1:${REDIS_PORT:-6379}" +``` + +### Passwords encryption + +_This feature is available only in [Redisson PRO](https://redisson.pro) edition._ + +Redisson supports Advanced Encryption Standard (AES) encryption for passwords defined in the configuration file, with the secret key stored in the file. + +The `org.redisson.config.PasswordCipher` class is used to encrypt passwords. The secret key file may contain any characters. The encrypted password has the `{aes}` prefix. + +Syntax: +``` +java -cp redisson-all.jar org.redisson.config.PasswordCipher encode +``` +Usage example: +``` +java -cp redisson-all.jar org.redisson.config.PasswordCipher encode pass123 secret_key.txt +``` +Output: +``` +{aes}M+TfpT4T6psLCfS+RHKT7Fx0j6r5wOX535G3NMnaphY= +``` + +The secret key file is defined through the `secretKey` setting in the Redisson configuration YAML file and applied to all encrypted passwords. + +Configuration YAML file example: + +```yaml +singleServerConfig: + address: "rediss://127.0.0.1:6379" + password: "{aes}M+TfpT4T6psLCfS+RHKT7Fx0j6r5wOX535G3NMnaphY=" + sslTruststore: file:truststore + sslTruststorePassword: "{aes}31paDOrhnyPfDxXPgqyLZF8QR5yJU3U1bZfhsuM4Ruo=" + secretKey: file:secret_key +``` +## Common settings + +The following settings belong to the `org.redisson.Config` object and are common for all modes: + +**codec** + +Default value: `org.redisson.codec.Kryo5Codec` + +Redis or Valkey data codec. Used during read/write data operations. Several implementations are [available](data-and-services/data-serialization.md). + +**connectionListener** + +Default value: `null` + +The connection listener, which is triggered when Redisson is connected/disconnected to a Redis or Valkey server. + +**lazyInitialization** +Default value: `false` + +Defines whether Redisson connects to Redis or Valkey only when the first Redis or Valkey call is made or if Redisson connects during creation of the Redisson instance. + +`true` - connects to Redis or Valkey only when the first Redis or Valkey call is made +`false` - connects to Redis or Valkey upon Redisson instance creation + +**nettyThreads** +Default value: 32 + +Defines the number of threads shared between all internal Redis or Valkey clients used by Redisson. Netty threads are used for Redis or Valkey response decoding and command sending. `0` = `cores_amount * 2` + +**nettyHook** +Default value: empty object + +Netty hook applied to Netty Bootstrap and Channel objects. + +**nettyExecutor** +Default value: `null` + +Use the external `ExecutorService' which is used by Netty for Redis or Valkey response decoding and command sending. + +**executor** +Default value: `null` + +Use the external `ExecutorService` which processes all listeners of `RTopic,` `RRemoteService` invocation handlers, and `RExecutorService` tasks. + +**eventLoopGroup** +Use external EventLoopGroup. EventLoopGroup processes all Netty connections tied with Redis or Valkey servers using its own threads. By default, each Redisson client creates its own EventLoopGroup. So, if there are multiple Redisson instances in the same JVM, it would be useful to share one EventLoopGroup among them. + +Only `io.netty.channel.epoll.EpollEventLoopGroup`, `io.netty.channel.kqueue.KQueueEventLoopGroup` and `io.netty.channel.nio.NioEventLoopGroup` are allowed for usage. + +**transportMode** +Default value: `TransportMode.NIO` + +Available values: + +* `TransportMode.NIO`, +* `TransportMode.EPOLL` - requires `netty-transport-native-epoll` lib in the classpath +* `TransportMode.KQUEUE` - requires `netty-transport-native-kqueue` lib in the classpath + +**threads** +Default value: 16 + +Threads are used to execute the listener's logic of the `RTopic` object, invocation handlers of the `RRemoteService`, the `RTopic` object and `RExecutorService` tasks. + +**protocol** +Default value: RESP2 + +Defines the Redis or Valkey protocol version. Available values: `RESP2`, `RESP3` + +**lockWatchdogTimeout** +Default value: `30000` + +RLock object watchdog timeout in milliseconds. This parameter is only used if an RLock object is acquired without the `leaseTimeout` parameter. The lock expires after `lockWatchdogTimeout` if the watchdog didn’t extend it to the next `lockWatchdogTimeout` time interval. This prevents infinity-locked locks due to a Redisson client crash, or any other reason why a lock can’t be released properly. + +**checkLockSyncedSlaves** +Default value: `true` + +Defines whether to check the synchronized slaves amount with the actual slaves amount after lock acquisition. + +**slavesSyncTimeout** +Default value: `1000` + +Defines the slaves synchronization timeout in milliseconds, applied to each operation of the RLock, RSemaphore, RPermitExpirableSemaphore objects. + +**reliableTopicWatchdogTimeout** +Default value: `600000` + +Reliable Topic watchdog timeout in milliseconds. Reliable Topic subscriber expires after `timeout` if the watchdog didn’t extend it to the next `timeout` time interval. This prevents the infinite growing of stored messages in a topic, due to a Redisson client crush or any other reason when a subscriber can’t consume messages anymore. + +**addressResolverGroupFactory** +Default value: `org.redisson.connection.SequentialDnsAddressResolverFactory` + +Allows for specifying a customized implementation of [DnsAddressResolverGroup](https://github.com/netty/netty/blob/4.1/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressResolverGroup.java). + +Available implementations: + +* `org.redisson.connection.DnsAddressResolverGroupFactory` - uses the default DNS servers list provided by OS. +* `org.redisson.connection.SequentialDnsAddressResolverFactory` - uses the default DNS servers list provided by OS and allows to control the concurrency level of requests to DNS servers. +* `org.redisson.connection.RoundRobinDnsAddressResolverGroupFactory` - uses the default DNS servers list provided by OS in round robin mode. + +**useScriptCache** +Default value: `false` + +Defines whether to use the Lua-script cache on the Redis or Valkey side. Most Redisson methods are Lua-script-based, and turning this setting on could increase the speed of such methods' execution and save network traffic. + +**keepPubSubOrder** +Default value: `true` + +Defines whether to keep PubSub messages handling in arrival order, or to handle messages concurrently. This setting is applied only for PubSub messages per channel. + +**minCleanUpDelay** +Default value: `5` + +Defines the minimum delay in seconds for the cleanup process of expired entries. Applied to `JCache`, `RSetCache`, `RClusteredSetCache`, `RMapCache`, `RListMultimapCache`, `RSetMultimapCache`, `RLocalCachedMapCache`, `RClusteredLocalCachedMapCache` objects. + +**maxCleanUpDelay** +Default value: `1800` + +Defines maximum delay in seconds for clean up process of expired entries. Applied to `JCache`, `RSetCache`, `RClusteredSetCache`, `RMapCache`, `RListMultimapCache`, `RSetMultimapCache`, +`RLocalCachedMapCache`, `RClusteredLocalCachedMapCache` objects. + +**cleanUpKeysAmount** +Default value: `100` + +Defines the amount of expired keys deleted per single operation during the cleanup process of expired entries. Applied to `JCache`, `RSetCache`, `RClusteredSetCache`, `RMapCache`, `RListMultimapCache`, `RSetMultimapCache`, `RLocalCachedMapCache`, +`RClusteredLocalCachedMapCache` objects. + +**meterMode** +Default value: `ALL` + +Defines the Micrometer statistics collection mode. + +_This setting is available only in [Redisson PRO](https://redisson.pro) edition._ + +Available values: + +* `ALL` - collect both Redis or Valkey and Redisson objects statistics +* `REDIS` - collect only Redis or Valkey statistics +* `OBJECTS` - collect only Redisson objects statistics + + +**meterRegistryProvider** +Default value: `null` + +Defines the Micrometer registry provider used to collect various statistics for Redisson objects. Please refer to the [statistics monitoring](observability.md) sections for list of all available providers. + +_This setting is available only in [Redisson PRO](https://redisson.pro) edition._ + +**useThreadClassLoader** +Default value: `true` + +Defines whether to supply ContextClassLoader of the current Thread to Codec. + +Usage of `Thread.getContextClassLoader()` may resolve `ClassNotFoundException` errors arising during Redis or Valkey response decoding. This error might occurr if Redisson is used in both Tomcat and deployed application. + +**performanceMode** +Default value: `LOWER_LATENCY_MODE_2` + +Defines the command processing engine performance mode. Since all values are application-specific (except for the `NORMAL` value) it’s recommended to try all of them. + +_This setting is available only in [Redisson PRO](https://redisson.pro) edition._ + +Available values: + +* `HIGHER_THROUGHPUT` - switches command processor engine to *higher +throughput* mode +* `LOWER_LATENCY_AUTO` - switches command processor engine to *lower latency* mode and detects optimal settings automatically +* `LOWER_LATENCY_MODE_3` - switches command processor engine to *lower +latency* mode with predefined settings set #3 +* `LOWER_LATENCY_MODE_2` - switches command processor engine to *lower latency* mode with predefined settings set #2 +* `LOWER_LATENCY_MODE_1` - switches command +processor engine to *lower latency* mode with predefined settings set #1 +* `NORMAL` - switches command processor engine to normal mode + +## Cluster mode +Cluster mode could be used with any hosting. + +Compatible with: + +* [AWS ElastiCache Cluster](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/WhatIs.html#WhatIs.Clusters) +* [Amazon MemoryDB](https://aws.amazon.com/memorydb) +* [Azure Redis Cache](https://azure.microsoft.com/en-us/services/cache/) + +Programmatic config example: +```java +Config config = new Config(); +config.useClusterServers() + .setScanInterval(2000) // cluster state scan interval in milliseconds + // use "rediss://" for SSL connection + .addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001") + .addNodeAddress("redis://127.0.0.1:7002"); + +RedissonClient redisson = Redisson.create(config); +``` +### Cluster settings +Documentation covering Redis server cluster configuration is [here](https://redis.io/topics/cluster-tutorial). +Cluster connection mode is activated by the following line: + +`ClusterServersConfig clusterConfig = config.useClusterServers();` + +`ClusterServersConfig` settings listed below: + +**checkSlotsCoverage** +Default value: `true` + +Enables cluster slots check during Redisson startup. + +**nodeAddresses** +Add a Redis or Valkey cluster node or endpoint address in `host:port` format. Redisson automatically discovers the cluster topology. Use the `rediss://` protocol for SSL connections. + +**scanInterval** +Default value: `1000` + +Scan interval in milliseconds. Applied to Redis or Valkey clusters topology scans. + +**slots** +Default value: `231` + +Partitions amount used for data partitioning. Data partitioning supported by [Set](data-and-services/collections.md/#eviction-and-data-partitioning), [Map](data-and-services/collections.md/#eviction-local-cache-and-data-partitioning), [BitSet](data-and-services/objects.md/#data-partitioning), [Bloom filter](data-and-services/objects.md/#data-partitioning_1), [Spring Cache](cache-api-implementations.md/#eviction-local-cache-and-data-partitioning), [JCache](cache-api-implementations.md/#local-cache-and-data-partitioning), [Micronaut Cache](cache-api-implementations.md/#eviction-local-cache-and-data-partitioning_4), [Quarkus Cache](cache-api-implementations.md/#eviction-local-cache-and-data-partitioning_3) and [Hibernate Cache](cache-api-implementations.md/#eviction-local-cache-and-data-partitioning_1) structures. + +_This setting is available only in [Redisson PRO](https://redisson.pro) edition._ + +**readMode** +Default value: `SLAVE` + +Set node type used for read operation. +Available values: + +* `SLAVE` - Read from slave nodes, uses `MASTER` if no `SLAVES` are available, +* `MASTER` - Read from master node, +* `MASTER_SLAVE` - Read from master and slave nodes + +**subscriptionMode** +Default value: `MASTER` + +Set node type used for subscription operation. +Available values: + +* `SLAVE` - Subscribe to slave nodes, +* `MASTER` - Subscribe to master node, + +**loadBalancer** +Default value: `org.redisson.connection.balancer.RoundRobinLoadBalancer` + +Сonnection load balancer for multiple Redis or Valkey servers. +Available implementations: + +* `org.redisson.connection.balancer.CommandsLoadBalancer` +* `org.redisson.connection.balancer.WeightedRoundRobinBalancer` +* `org.redisson.connection.balancer.RoundRobinLoadBalancer` +* `org.redisson.connection.balancer.RandomLoadBalancer` + +**subscriptionConnectionMinimumIdleSize** +Default value: `1` + +Minimum idle connection pool size for subscription (pub/sub) channels. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**subscriptionConnectionPoolSize** +Default value: `50` + +Maximum connection pool size for subscription (pub/sub) channels. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**shardedSubscriptionMode** +Default value: `AUTO` + +Defines whether to use the sharded subscription feature available in Valkey or Redis 7.0 and higher. Used by `RMapCache`, `RLocalCachedMap`, `RCountDownLatch`, `RLock`, `RPermitExpirableSemaphore`, `RSemaphore`, `RLongAdder`, `RDoubleAdder`, `Micronaut Session`, `Apache Tomcat Manager` objects. + +**slaveConnectionMinimumIdleSize** +Default value: `24` + +Redis or Valkey `slave` node minimum idle connection amount for each slave node. + +**slaveConnectionPoolSize** +Default value: `64` + +Redis or Valkey `slave` node maximum connection pool size for each slave node. + +**masterConnectionMinimumIdleSize** +Default value: `24` + +Minimum idle connections amount per Redis or Valkey master node. + +**masterConnectionPoolSize** +Default value: `64` + +Redis or Valkey `master` node maximum connection pool size. + +**idleConnectionTimeout** +Default value: `10000` + +If a pooled connection is not used for a timeout time and the current connections amount is bigger than the minimum idle connections pool size, then it will be closed and removed from the pool. Value in milliseconds. + +**connectTimeout** +Default value: `10000` + +Timeout in milliseconds during connecting to any Redis or Valkey server. + +**timeout** +Default value: `3000` + +Redis or Valkey server response timeout in milliseconds. Starts countdown after a Redis or Valkey command is successfully sent. + +**retryAttempts** +Default value: `3` + +Error will be thrown if Redis or Valkey command can’t be sent to server +after *retryAttempts*. But if it sent successfully then *timeout* will be started. + +**retryInterval** +Default value: `1500` + +Time interval in milliseconds, after which another attempt to send a Redis or Valkey command will be executed. + +**failedSlaveReconnectionInterval** +Default value: `3000` + +Interval of Redis or Valkey Slave reconnection attempts, when it was excluded from an internal list of available servers. On each timeout event, Redisson tries to connect to the disconnected Redis or Valkey server. Value in milliseconds. + +**failedSlaveNodeDetector** +Default value: `org.redisson.client.FailedConnectionDetector` + +Defines the failed Redis or Valkey Slave node detector object which implements failed node detection logic via the `org.redisson.client.FailedNodeDetector` interface. + +Available implementations: + +* `org.redisson.client.FailedConnectionDetector` - marks the Redis or Valkey node as failed if it has ongoing connection errors in the defined `checkInterval` interval (in milliseconds). Default is 180000 milliseconds. +* `org.redisson.client.FailedCommandsDetector` - marks the Redis or Valkey node as failed if it has certain amount of command execution errors defined by `failedCommandsLimit` in the defined `checkInterval` interval (in milliseconds). +* `org.redisson.client.FailedCommandsTimeoutDetector` - marks the Redis or Valkey node as failed if it has a certain amount of command execution timeout errors defined by `failedCommandsLimit` in the defined `checkInterval` interval in milliseconds. + +**password** +Default value: `null` + +Password for Redis or Valkey server authentication. + +**username** +Default value: `null` + +Username for Redis or Valkey server authentication. Requires Redis 6.0 and higher. + +**credentialsResolver** + +Default value: empty + +Defines Credentials resolver, which is invoked during connection for Redis or Valkey server authentication. Returns Credentials object per Redis or Valkey node +address, it contains `username` and `password` fields. Allows you to specify dynamically changing Redis or Valkey credentials. + +**subscriptionsPerConnection** +Default value: `5` + +Subscriptions per subscriber connection limit. Used by `RTopic`, +`RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, +`RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, +`RLocalCachedMap`, `RLocalCachedMapCache` objectsi, and Hibernate Local +Cached Region Factories. + +**clientName** +Default value: `null` + +Name of client connection. + +**sslProtocols** +Default value: `null` + +Defines array of allowed SSL protocols. +Example values: `TLSv1.3`, `TLSv1.2`, `TLSv1.1`, `TLSv1` + +**sslEnableEndpointIdentification** +Default value: `true` + +Enables SSL endpoint identification during handshaking, which prevents +man-in-the-middle attacks. + +**sslProvider** +Default value: `JDK` + +Defines the SSL provider (JDK or OPENSSL) used to handle SSL connections. OPENSSL is considered as a faster implementation and requires [netty-tcnative-boringssl-static](https://repo1.maven.org/maven2/io/netty/netty-tcnative-boringssl-static/) to be added to the classpath. + +**sslTruststore** +Default value: `null` + +Defines the path to the SSL truststore. It stores certificates which is used to identify the server side of an SSL connection. SSL truststore is read on each new connection creation and can be dynamically reloaded. + + +**sslTruststorePassword** +Default value: `null` + +Defines password for SSL truststore + +**sslKeystoreType** +Default value: `null` + +Defines the SSL keystore type. + +**sslKeystore** +Default value: `null` + +Defines the path to the SSL truststore. It stores certificates which are used to identify the server side of an SSL connections. The SSL truststore is read on each new connection creation and can be dynamically reloaded. + +**sslKeystorePassword** +Default value: `null` + +Defines password for the SSL keystore + +**pingConnectionInterval** +Default value: `30000` + +This setting allows for detecting and reconnecting broken connections, using the PING command. PING command send interval is defined in milliseconds. Useful in cases when the netty lib doesn’t invoke `channelInactive` method for closed connections. Set to `0` to disable. + +**keepAlive** +Default value: `false` + +Enables TCP keepAlive for connection. + +**tcpKeepAliveCount** +Default value: 0 + +This defines the maximum number of keepalive probes TCP should send before dropping the connection. A `0` value means to use the system's default setting. + + +**tcpKeepAliveIdle** +Default value: 0 + +Defines the time in seconds the connection needs to remain idle before +TCP starts sending keepalive probes. A 0 value means use the system's default setting. + +**tcpKeepAliveInterval** +Default value: 0 + +Defines the time in seconds between individual keepalive probes. `0` value means use the system's default setting. + +**tcpUserTimeout** +Default value: 0 + +Defines the maximum amount of time in milliseconds that transmitted data may remain unacknowledged or buffered data may remain untransmitted (due to zero window size) before TCP will forcibly close the connection. A 0 value means use the system's default setting. + +**tcpNoDelay** +Default value: `true` + +Enables TCP noDelay for connections. + +**subscriptionTimeout** +Default value: 7500 + +Defines subscription timeout in milliseconds applied per channel +subscription. + +**natMapper** +Default value: no mapper + +Defines NAT mapper interface, which maps Redis or Valkey URI objects and applies to all connections. It can be used to map internal Redis or Valkey server IPs to external ones. + +Available implementations: + +* `org.redisson.api.HostPortNatMapper` +* `org.redisson.api.HostNatMapper` + +**nameMapper** +Default value: no mapper + +Defines Name mapper which maps Redisson object name to a custom name. +Applied to all Redisson objects. + +**commandMapper** +Default value: no mapper + +Defines Command mapper, which maps Redis or Valkey command name to a custom name. +Applied to all Redis or Valkey commands. + + +### Cluster YAML config format +Below is a cluster configuration example in YAML format. All property +names matched with `ClusterServersConfig` and `Config` object property names. +```yaml +--- +clusterServersConfig: + idleConnectionTimeout: 10000 + connectTimeout: 10000 + timeout: 3000 + retryAttempts: 3 + retryInterval: 1500 + failedSlaveReconnectionInterval: 3000 + failedSlaveNodeDetector: ! {} + password: null + subscriptionsPerConnection: 5 + clientName: null + loadBalancer: ! {} + subscriptionConnectionMinimumIdleSize: 1 + subscriptionConnectionPoolSize: 50 + slaveConnectionMinimumIdleSize: 24 + slaveConnectionPoolSize: 64 + masterConnectionMinimumIdleSize: 24 + masterConnectionPoolSize: 64 + readMode: "SLAVE" + subscriptionMode: "SLAVE" + nodeAddresses: + - "redis://127.0.0.1:7004" + - "redis://127.0.0.1:7001" + - "redis://127.0.0.1:7000" + scanInterval: 1000 + pingConnectionInterval: 30000 + keepAlive: false + tcpNoDelay: true +threads: 16 +nettyThreads: 32 +codec: ! {} +transportMode: "NIO" +``` + +## Replicated mode +With Replicated mode the role of each node is polled to determine if a failover has occurred resulting in a new master. + +Compatible with: + +* [AWS ElastiCache](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Replication.html) (non-clustered) +* [Azure Redis Cache](https://azure.microsoft.com/en-us/services/cache/) (non-clustered) +* [Google Cloud Memorystore for Redis High availability](https://cloud.google.com/memorystore/docs/redis/high-availability) + +_Use [Redisson PRO](https://redisson.pro) if a single host bounded to multiple slaves or master and slave nodes. Compatible with [Aiven Redis](https://aiven.io/redis) hosting._ + +Programmatic config example: +```java +Config config = new Config(); +config.useReplicatedServers() + .setScanInterval(2000) // master node change scan interval + // use "rediss://" for SSL connection + .addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001") + .addNodeAddress("redis://127.0.0.1:7002"); + +RedissonClient redisson = Redisson.create(config); +``` +### Replicated settings +Replicated connection mode is activated by follow line: + +`ReplicatedServersConfig replicatedConfig = config.useReplicatedServers();` + +`Replicated ServersConfig` settings listed below: + +**nodeAddresses** +Add Redis or Valkey node address in `host:port` format. Multiple nodes could be added at once. All nodes (master and slaves) should be defined. For Aiven Redis hosting single hostname is enough. Use `rediss://` protocol for SSL connection. + +**scanInterval** +Default value: `1000` + +Replicated nodes scan interval in milliseconds. + +**loadBalancer** +Default value: `org.redisson.connection.balancer.RoundRobinLoadBalancer` + +Сonnection load balancer for multiple Redis or Valkey servers. + +Available implementations: + +* `org.redisson.connection.balancer.CommandsLoadBalancer` +* `org.redisson.connection.balancer.WeightedRoundRobinBalancer` +* `org.redisson.connection.balancer.RoundRobinLoadBalancer` +* `org.redisson.connection.balancer.RandomLoadBalancer` + +**monitorIPChanges** +Default value: `false` + +Check each Redis or Valkey hostname defined in the configuration for IP address +changes during the scan process. + +**subscriptionConnectionMinimumIdleSize** +Default value: `1` + +Minimum idle connection pool size for subscription (pub/sub) channels. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**subscriptionConnectionPoolSize** +Default value: `50` + +Maximum connection pool size for subscription (pub/sub) channels. Used +by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, +`RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, +`RLocalCachedMap`, `RLocalCachedMapCache` objects, and Hibernate Local +Cached Region Factories. + +**slaveConnectionMinimumIdleSize** +Default value: `24` + +Redis or Valkey `slave` node minimum idle connection amount for each slave node. + +**slaveConnectionPoolSize** +Default value: `64` + +Redis or Valkey `slave` node maximum connection pool size for each slave node. + +**masterConnectionMinimumIdleSize** +Default value: `24` + +The minimum idle connection amount is per Redis or Valkey master node. + +**masterConnectionPoolSize** +Default value: `64` + +Redis or Valkey `master` node maximum connection pool size. + +**idleConnectionTimeout** +Default value: `10000` + +If a pooled connection is not used for a timeout time and current connections amount bigger than minimum idle connections pool size, then it will be closed and removed from the pool. Value in milliseconds. + +**readMode** +Default value: `SLAVE` + +Set node type used for read operation. +Available values: + +* `SLAVE` - Read from slave nodes, uses MASTER if no SLAVES are available, +* `MASTER` - Read from the master node, +* `MASTER_SLAVE` - Read from master and slave nodes + +**subscriptionMode** +Default value: `MASTER` + +Set node type used for subscription operation. +Available values: + +* `SLAVE` - Subscribe to slave nodes, +* `MASTER` - Subscribe to master node, + +**connectTimeout** +Default value: `10000` + +Timeout during connecting to any Redis or Valkey server. + +**timeout** +Default value: `3000` + +Redis or Valkey server response timeout. It starts to count down after a Redis or Valkey command is successfully sent. Value in milliseconds. + +**retryAttempts** +Default value: `3` + +An error will be thrown if a Redis or Valkey command can’t be sent to Redis or Valkey server +after *retryAttempts*. But if it is sent successfully, then *timeout* will be +started. + +**retryInterval** +Default value: `1500` + +Time interval after which another attempt to send a Redis or Valkey command will be executed. Value in milliseconds. + +**failedSlaveReconnectionInterval** +Default value: `3000` + +The interval of Redis or Valkey Slave reconnection attempts when excluded from +the internal list of available servers. On each timeout event, Redisson tries to connect to a disconnected Redis or Valkey server. Value in milliseconds. + +**failedSlaveNodeDetector** +Default value: `org.redisson.client.FailedConnectionDetector` + +Defines failed Redis or Valkey Slave node detector object, which implements failed +node detection logic via `org.redisson.client.FailedNodeDetector` +interface. + +Available implementations: + +* `org.redisson.client.FailedConnectionDetector` - marks the Redis or Valkey node as failed if it has ongoing connection errors in the defined `checkInterval` interval in milliseconds. The default is 180000 milliseconds. +* `org.redisson.client.FailedCommandsDetector` - marks the Redis or Valkey node as failed if it has a certain amount of command execution errors defined by the `failedCommandsLimit` in the defined `checkInterval` interval in milliseconds. +* `org.redisson.client.FailedCommandsTimeoutDetector` - marks the Redis or Valkey node as failed if it has a certain amount of command execution timeout errors defined by the `failedCommandsLimit` in the defined `checkInterval` interval in milliseconds. + +**database** +Default value: `0` + +Database index used for Redis or Valkey connection. + +**password** +Default value: `null` + +Password for Redis or Valkey server authentication. + +**username** +Default value: `null` + +Username for Redis or Valkey server authentication. Requires Redis 6.0 and higher. + +**credentialsResolver** +Default value: empty + +Defines Credentials resolver, which is invoked during connection for Redis or Valkey server authentication. Returns Credentials object per Redis or Valkey node address, it contains `username` and `password` fields. Allows to specify dynamically changing Redis credentials. + +**subscriptionsPerConnection** +Default value: `5` + +Subscriptions per subscribe connection limit. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**clientName** +Default value: `null` + +Name of client connection. + +**sslProtocols** +Default value: `null` + +Defines array of allowed SSL protocols. +Example values: `TLSv1.3`, `TLSv1.2`, `TLSv1.1`, `TLSv1` + +**sslEnableEndpointIdentification** +Default value: `true` + +Enables SSL endpoint identification during handshaking, which prevents man-in-the-middle attacks. + +**sslProvider** +Default value: `JDK` + +Defines the SSL provider (JDK or OPENSSL) used to handle SSL connections. OPENSSL considered as a faster implementation and requires [netty-tcnative-boringssl-static](https://repo1.maven.org/maven2/io/netty/netty-tcnative-boringssl-static/) to be added in the classpath. + +**sslTruststore** +Default value: `null` + +Defines the path to the SSL truststore. It stores certificates which are used to identify the server side of an SSL connections. The SSL truststore is read on each new connection creation and can be dynamically reloaded. + +**sslTruststorePassword** +Default value: `null` + +Defines password for SSL truststore. + +**sslKeystoreType** +Default value: `null` + +Defines SSL keystore type. + +**sslKeystore** +Default value: `null` + +Defines path to the SSL keystore. It stores private key and certificates corresponding to their public keys. Used if the server side of an SSL connection requires client authentication. SSL keystore is read on each new connection creation and can be dynamically reloaded. + +**sslKeystorePassword** +Default value: `null` + +Defines password for SSL keystore. + +**pingConnectionInterval** +Default value: `30000` + +PING command sending interval, per connection to Redis. Defined in milliseconds. Set to `0` to disable. + +**keepAlive** +Default value: `false` + +Enables TCP keepAlive for connections. + +**tcpNoDelay** +Default value: `true` + +Enables TCP noDelay for connections. + +**nameMapper** +Default value: no mapper + +Defines Name mapper which maps Redisson object name to a custom name. Applied to all Redisson objects. + +**commandMapper** +Default value: no mapper + +Defines Command mapper which maps Redis or Valkey command name to a custom name. Applied to all commands. + +### Replicated YAML config format +Below is a replicated configuration example in YAML format. All property +names are matched with `ReplicatedServersConfig` and `Config` object property names. + +```yaml +--- +replicatedServersConfig: + idleConnectionTimeout: 10000 + connectTimeout: 10000 + timeout: 3000 + retryAttempts: 3 + retryInterval: 1500 + failedSlaveReconnectionInterval: 3000 + failedSlaveNodeDetector: ! {} + password: null + subscriptionsPerConnection: 5 + clientName: null + loadBalancer: ! {} + subscriptionConnectionMinimumIdleSize: 1 + subscriptionConnectionPoolSize: 50 + slaveConnectionMinimumIdleSize: 24 + slaveConnectionPoolSize: 64 + masterConnectionMinimumIdleSize: 24 + masterConnectionPoolSize: 64 + readMode: "SLAVE" + subscriptionMode: "SLAVE" + nodeAddresses: + - "redis://redishost1:2812" + - "redis://redishost2:2815" + - "redis://redishost3:2813" + scanInterval: 1000 + monitorIPChanges: false +threads: 16 +nettyThreads: 32 +codec: ! {} +transportMode: "NIO" +``` + +## Single mode + +Single mode could be used with any hosting. + +Compatible with: + + * [Azure Redis Cache](https://azure.microsoft.com/en-us/services/cache/) + * [Google Cloud Memorystore for Redis](https://cloud.google.com/memorystore/docs/redis/). + +Programmatic config example: +```java +// connects to 127.0.0.1:6379 by default +RedissonClient redisson = Redisson.create(); + +Config config = new Config(); +config.useSingleServer().setAddress("redis://myredisserver:6379"); +RedissonClient redisson = Redisson.create(config); +``` +### Single settings + +Documentation covering Redis or Valkey single server configuration is [here](https://redis.io/topics/config). +Multiple IP bindings for a single hostname are supported in [Proxy mode](#proxy-mode) + +Single server connection mode is activated by the following line: +`SingleServerConfig singleConfig = config.useSingleServer();` + +`SingleServerConfig` settings listed below: + +**address** +Redis or Valkey server address in `host:port` format. Use `rediss://` protocol for SSL connection. + +**subscriptionConnectionMinimumIdleSize** +Default value: `1` + +Minimum idle connection pool size for subscription (pub/sub) channels. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**subscriptionConnectionPoolSize** +Default value: `50` + +Maximum connection pool size for subscription (pub/sub) channels. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**connectionMinimumIdleSize** +Default value: `24` + +Minimum idle Redis or Valkey connection amount. + +**connectionPoolSize** +Default value: `64` + +Redis or Valkey connection maximum pool size. + +**dnsMonitoringInterval** +Default value: `5000` + +DNS change monitoring interval. Set `-1` to disable. Multiple IP bindings for a single hostname are supported in [Proxy mode](#proxy-mode). + +**idleConnectionTimeout** +Default value: `10000` + +If a pooled connection is not used for a timeout time and current connections amount bigger than minimum idle connection pool size, then it will be closed and removed from the pool. Value in milliseconds. + +**connectTimeout** +Default value: `10000` + +Timeout during connecting to any Redis or Valkey server + +**timeout** +Default value: `3000` + +Redis or Valkey server response timeout. It starts to count down once a Redis or Valkey command is successfully sent. Value in milliseconds. + +**retryAttempts** +Default value: `3` + +Error will be thrown if Redis or Valkey command can’t be sent to Redis or Valkey server +after the defined *retryAttempts*. But if it is sent successfully, then *timeout* will be started. + +**retryInterval** +Default value: `1500` + +Time interval after which another attempt to send the Redis or Valkey command will be executed. Value in milliseconds. + +**database** +Default value: `0` + +Database index used for Redis or Valkey connection. + +**password** +Default value: `null` + +Password for Redis or Valkey server authentication. + +**username** +Default value: `null` + +Username for Redis or Valkey server authentication. Requires Redis 6.0 and higher. + +**credentialsResolver** +Default value: empty + +Defines Credentials resolver, which is invoked during connection for Redis or Valkey server authentication. Returns Credentials object per Redis or Valkey node address, it contains `username` and `password` fields. Allows you to specify dynamically changing Redis credentials. + +**subscriptionsPerConnection** +Default value: `5` + +Subscriptions per subscriber connection limit. Used by `RTopic`, +`RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**subscriptionTimeout** +Default value: 7500 + +Defines subscription timeout in milliseconds, applied per channel +subscription. + +**clientName** +Default value: `null` + +Name of client connection + +**sslProtocols** +Default value: `null` + +Defines array of allowed SSL protocols. +Example values: `TLSv1.3`, `TLSv1.2`, `TLSv1.1`, `TLSv1` + +**sslEnableEndpointIdentification** +Default value: `true` + +Enables SSL endpoint identification during handshaking, which prevents man-in-the-middle attacks. + +**sslProvider** +Default value: `JDK` + +Defines SSL provider (JDK or OPENSSL) used to handle SSL connections. +OPENSSL is considered as the faster implementation and requires [netty-tcnative-boringssl-static](https://repo1.maven.org/maven2/io/netty/netty-tcnative-boringssl-static/) to be added in classpath. + +**sslTruststore** +Default value: `null` + +Defines the path to the SSL truststore. It stores certificates which are used to identify the server side of an SSL connections. The SSL truststore is read on each new connection creation and can be dynamically reloaded. + +**sslTruststorePassword** +Default value: `null` + +Defines password for SSL truststore. + +**sslKeystoreType** +Default value: `null` + +Defines SSL keystore type. + +**sslKeystore** +Default value: `null` + +Defines the path to the SSL truststore. It stores certificates which are used to identify the server side of an SSL connections. The SSL truststore is read on each new connection creation and can be dynamically reloaded. + +**sslKeystorePassword** +Default value: `null` + +Defines password for SSL keystore + +**pingConnectionInterval** +Default value: `30000` + +PING command sending interval, per connection to Redis. Defined in +milliseconds. Set `0` to disable. + +**keepAlive** +Default value: `false` + +Enables TCP keepAlive for connections. + +**tcpNoDelay** +Default value: `true` + +Enables TCP noDelay for connections. + +**nameMapper** +Default value: no mapper + +Defines Name mapper which maps Redisson object name to a custom name. +Applied to all Redisson objects. + +**commandMapper** +Default value: no mapper + +Defines Command mapper which maps Redis or Valkey command name to a custom name. Applied to all commands. + +### Single YAML config format + +Below is a single configuration example in YAML format. All property names are matched with `SingleServerConfig` and `Config` object property names. +```yaml +--- +singleServerConfig: + idleConnectionTimeout: 10000 + connectTimeout: 10000 + timeout: 3000 + retryAttempts: 3 + retryInterval: 1500 + password: null + subscriptionsPerConnection: 5 + clientName: null + address: "redis://127.0.0.1:6379" + subscriptionConnectionMinimumIdleSize: 1 + subscriptionConnectionPoolSize: 50 + connectionMinimumIdleSize: 24 + connectionPoolSize: 64 + database: 0 + dnsMonitoringInterval: 5000 +threads: 16 +nettyThreads: 32 +codec: ! {} +transportMode: "NIO" +``` + +## Sentinel mode + +Programmatic config example: + +```java +Config config = new Config(); +config.useSentinelServers() + .setMasterName("mymaster") + // use "rediss://" for SSL connection + .addSentinelAddress("redis://127.0.0.1:26389", "redis://127.0.0.1:26379") + .addSentinelAddress("redis://127.0.0.1:26319"); + +RedissonClient redisson = Redisson.create(config); +``` +### Sentinel settings + +Documentation covers Redis server sentinel configuration is [here](https://redis.io/topics/sentinel). + +Sentinel connection mode is activated by follow line: +`SentinelServersConfig sentinelConfig = config.useSentinelServers();` + +`SentinelServersConfig` settings listed below: + +**checkSentinelsList** +Default value: `true` + +Enables sentinels list check during Redisson startup. + +**dnsMonitoringInterval** +Default value: `5000` + +Interval in milliseconds to check the endpoint's DNS. Set `-1` to disable. + +**checkSlaveStatusWithSyncing** +Default value: `true` + +Check if slave node `master-link-status` field has status `ok`. + +**masterName** + +Master server name used by Redis or Valkey Sentinel servers and master change monitoring task. + +**addSentinelAddress** +Add Redis or Valkey Sentinel node address in `host:port` format. Multiple nodes at once could be added. + +**readMode** +Default value: `SLAVE` + +Set node type used for read operation. +Available values: + +* `SLAVE` - Read from slave nodes, uses MASTER if no SLAVES are available, +* `MASTER` - Read from master node, +* `MASTER_SLAVE` - Read from master and slave nodes + +**subscriptionMode** +Default value: `SLAVE` + +Set node type used for subscription operation. +Available values: + +* `SLAVE` - Subscribe to slave nodes, +* `MASTER` - Subscribe to master node, + +**loadBalancer** +Default value: `org.redisson.connection.balancer.RoundRobinLoadBalancer` + +Сonnection load balancer for multiple Redis or Valkey servers. +Available implementations: + +* `org.redisson.connection.balancer.CommandsLoadBalancer` +* `org.redisson.connection.balancer.WeightedRoundRobinBalancer` +* `org.redisson.connection.balancer.RoundRobinLoadBalancer` +* `org.redisson.connection.balancer.RandomLoadBalancer` + +**subscriptionConnectionMinimumIdleSize** +Default value: `1` + +Minimum idle connection pool size for subscription (pub/sub) channels. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**subscriptionConnectionPoolSize** +Default value: `50` + +Maximum connection pool size for subscription (pub/sub) channels. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**slaveConnectionMinimumIdleSize** +Default value: `24` + +Redis or Valkey `slave` node minimum idle connection amount for each slave node. + +**slaveConnectionPoolSize** +Default value: `64` + +Redis or Valkey `slave` node maximum connection pool size for each slave node + + +**masterConnectionMinimumIdleSize** +Default value: `24` + +Minimum idle connections amount per Redis or Valkey master node. + +**masterConnectionPoolSize** +Default value: `64` + +Redis or Valkey `master` node maximum connection pool size. + +**idleConnectionTimeout** +Default value: `10000` + +If a pooled connection is not used for a timeout time and current connections amount bigger than minimum idle connections pool size, then it will be closed and removed from the pool. Value in milliseconds. + +**connectTimeout** +Default value: `10000` + +Timeout during connecting to any Redis or Valkey server. + +**timeout** +Default value: `3000` + +Redis or Valkey server response timeout. Starts to count down when a command was successfully sent. Value in milliseconds. + +**retryAttempts** +Default value: `3` + +Error will be thrown if Redis or Valkey command can’t be sent to Redis server after *retryAttempts*. But if it sent successfully then *timeout* will be started. + +**retryInterval** +Default value: `1500` + +Time interval after which another one attempt to send Redis or Valkey command will be executed. Value in milliseconds. + +**failedSlaveReconnectionInterval** + +Default value: `3000` + +The interval of Redis or Valkey Slave reconnection attempts when it was excluded from internal list of available servers. On each timeout event Redisson tries to connect to disconnected server. Value in milliseconds. + +**failedSlaveNodeDetector** +Default value: `org.redisson.client.FailedConnectionDetector` + +Defines failed Redis or Valkey Slave node detector object which implements failed node detection logic via `org.redisson.client.FailedNodeDetector` +interface. + +Available implementations: + +* `org.redisson.client.FailedConnectionDetector` - marks the Redis or Valkey node as failed if it has ongoing connection errors in defined `checkInterval` interval in milliseconds. Default is 180000 milliseconds. +* `org.redisson.client.FailedCommandsDetector` - marks the Redis or Valkey node as failed if it has certain amount of command execution errors defined by `failedCommandsLimit` in defined `checkInterval` interval in milliseconds. +* `org.redisson.client.FailedCommandsTimeoutDetector` - marks the Redis or Valkey node as failed if it has certain amount of command execution timeout errors defined by `failedCommandsLimit` in defined `checkInterval` interval in milliseconds. + +**database** +Default value: `0` + +Database index used for Redis or Valkey connection. + +**password** +Default value: `null` + +Password for Redis or Valkey servers authentication. + +**username** +Default value: `null` + +Username for Redis or Valkey servers authentication. Requires Redis 6.0 and higher. + +**sentinelPassword** +Default value: `null` + +Password for Redis or Valkey Sentinel servers authentication. Used only if +Sentinel password differs from master’s and slave’s. + +**sentinelUsername** +Default value: `null` + +Username for Redis or Valkey Sentinel servers for authentication. Used only if +Sentinel username differs from master’s and slave’s. Requires Redis 6.0 and higher. + +**credentialsResolver** +Default value: empty + +Defines Credentials resolver, which is invoked during connection for Redis or Valkey server authentication. Returns Credentials object per node address, it contains `username` and `password` fields. Allows you to specify dynamically changing credentials. + +**sentinelsDiscovery** +Default value: `true` + +Enables sentinels discovery. + +**subscriptionsPerConnection** +Default value: `5` + +Subscriptions per subscribe connection limit. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**subscriptionTimeout** +Default value: 7500 + +Defines subscription timeout in milliseconds applied per channel +subscription. + +**clientName** +Default value: `null` + +Name of client connection. + +**sslProtocols** +Default value: `null` + +Defines array of allowed SSL protocols. +Example values: `TLSv1.3`, `TLSv1.2`, `TLSv1.1`, `TLSv1` + +**sslEnableEndpointIdentification** +Default value: `true` + +Enables SSL endpoint identification during handshaking, which prevents man-in-the-middle attacks. + +**sslProvider** +Default value: `JDK` + +Defines SSL provider (JDK or OPENSSL) used to handle SSL connections. +OPENSSL is considered as a faster implementation and requires[netty-tcnative-boringssl-static](https://repo1.maven.org/maven2/io/netty/netty-tcnative-boringssl-static/) to be added in classpath. + +**sslTruststore** +Default value: `null` + +Defines path to the SSL truststore. It stores certificates which is used to identify the server side of an SSL connection. SSL truststore is read on each new connection creation and can be dynamically reloaded. + +**sslTruststorePassword** +Default value: `null` + +Defines password for SSL truststore. + +**sslKeystoreType** +Default value: `null` + +Defines SSL keystore type. + +**sslKeystore** +Default value: `null` + +Defines path to the SSL keystore. It stores private key and certificates corresponding to their public keys. Used if the server side of an SSL connection requires client authentication. SSL keystore is read on each new connection creation and can be dynamically reloaded. + +**sslKeystorePassword** +Default value: `null` + +Defines password for SSL keystore. + +**pingConnectionInterval** +Default value: `30000` + +PING command sending interval per connection to Redis. Defined in +milliseconds. Set `0` to disable. + +**keepAlive** +Default value: `false` + +Enables TCP keepAlive for connections. + +**tcpNoDelay** +Default value: `true` + +Enables TCP noDelay for connections. + +**natMapper** +Default value: no mapper + +Defines NAT mapper interface which maps Redis or Valkey URI object and applied to all connections. Can be used to map internal Redis server IPs to external ones. + +Available implementations: + +* `org.redisson.api.HostPortNatMapper` +* `org.redisson.api.HostNatMapper` + +**nameMapper** +Default value: no mapper + +Defines Name mapper which maps Redisson object name to a custom name. +Applied to all Redisson objects. + +**commandMapper** +Default value: no mapper + +Defines Command mapper which maps Redis or Valkey command name to a custom name. Applied to all commands. + +### Sentinel YAML config format + +Below is a sentinel configuration example in YAML format. All property +names are matched with `SentinelServersConfig` and `Config` object property names. + +```yaml +--- +sentinelServersConfig: + idleConnectionTimeout: 10000 + connectTimeout: 10000 + timeout: 3000 + retryAttempts: 3 + retryInterval: 1500 + failedSlaveReconnectionInterval: 3000 + failedSlaveNodeDetector: ! {} + password: null + subscriptionsPerConnection: 5 + clientName: null + loadBalancer: ! {} + subscriptionConnectionMinimumIdleSize: 1 + subscriptionConnectionPoolSize: 50 + slaveConnectionMinimumIdleSize: 24 + slaveConnectionPoolSize: 64 + masterConnectionMinimumIdleSize: 24 + masterConnectionPoolSize: 64 + readMode: "SLAVE" + subscriptionMode: "SLAVE" + sentinelAddresses: + - "redis://127.0.0.1:26379" + - "redis://127.0.0.1:26389" + masterName: "mymaster" + database: 0 +threads: 16 +nettyThreads: 32 +codec: ! {} +transportMode: "NIO" +``` + + +## Master slave mode + +Programmatic config example: +```java +Config config = new Config(); +config.useMasterSlaveServers() + // use "rediss://" for SSL connection + .setMasterAddress("redis://127.0.0.1:6379") + .addSlaveAddress("redis://127.0.0.1:6389", "redis://127.0.0.1:6332", "redis://127.0.0.1:6419") + .addSlaveAddress("redis://127.0.0.1:6399"); + +RedissonClient redisson = Redisson.create(config); +``` +### Master slave settings + +Documentation covering Redis or Valkey server master/slave configuration is [here](https://redis.io/topics/replication). + +Master slave connection mode is activated by the following line: +`MasterSlaveServersConfig masterSlaveConfig = config.useMasterSlaveServers();` + +`MasterSlaveServersConfig` settings listed below: + + +**dnsMonitoringInterval** +Default value: `5000` + +Interval in milliseconds to check the endpoint’s DNS. Set `-1` to +disable. + +**masterAddress** +Redis or Valkey master node address in `host:port` format. Use `rediss://` protocol for SSL connection. + +**addSlaveAddress** +Add Redis or Valkey slave node address in `host:port` format. Multiple nodes at +once could be added. Use `rediss://` protocol for SSL connection. + +**readMode** +Default value: `SLAVE` + +Set node type used for read operation. +Available values: + +* `SLAVE` - Read from slave nodes, uses MASTER if no SLAVES are +available, +* `MASTER` - Read from master node, +* `MASTER_SLAVE` - Read from master and slave nodes + +**subscriptionMode** +Default value: `SLAVE` + +Set node type used for subscription operation. +Available values: + +* `SLAVE` - Subscribe to slave nodes, +* `MASTER` - Subscribe to master node + +**loadBalancer** +Default value: `org.redisson.connection.balancer.RoundRobinLoadBalancer` + +Сonnection load balancer for multiple Redis or Valkey servers. +Available implementations: + +* `org.redisson.connection.balancer.CommandsLoadBalancer` +* `org.redisson.connection.balancer.WeightedRoundRobinBalancer` +* `org.redisson.connection.balancer.RoundRobinLoadBalancer` +* `org.redisson.connection.balancer.RandomLoadBalancer` + +**subscriptionConnectionMinimumIdleSize** +Default value: `1` + +Minimum idle connection pool size for subscription (pub/sub) channels. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**subscriptionConnectionPoolSize** +Default value: `50` + +Maximum connection pool size for subscription (pub/sub) channels. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**slaveConnectionMinimumIdleSize** +Default value: `24` + +Redis or Valkey `slave` node minimum idle connection amount for each slave node. + +**slaveConnectionPoolSize** +Default value: `64` + +Redis or Valkey `slave` node maximum connection pool size for each slave node. + +**masterConnectionMinimumIdleSize** +Default value: `24` + +Minimum idle connections amount per Redis or Valkey master node. + +**masterConnectionPoolSize** +Default value: `64` + +Redis or Valkey `master` node maximum connection pool size. + +**idleConnectionTimeout** +Default value: `10000` + +If a pooled connection is not used for a timeout time and current connections amount bigger than minimum idle connections pool size, then it will be closed and removed from the pool. Value in milliseconds. + +**connectTimeout** +Default value: `10000` + +Timeout during connecting to any Redis or Valkey server. + +**timeout** +Default value: `3000` + +Redis or Valkey server response timeout. Starts to count down when a command was successfully sent. Value in milliseconds. + +**retryAttempts** +Default value: `3` + +Error will be thrown if Redis or Valkey command can’t be sent to server after *retryAttempts*. But if it sent successfully then *timeout* will be started. + +**retryInterval** +Default value: `1500` + +Time interval after which another one attempt to send Redis or Valkey command will be executed. Value in milliseconds. + +**failedSlaveReconnectionInterval** +Default value: `3000` + +Interval of Redis or Valkey Slave reconnection attempts when it was excluded from internal list of available servers. On each timeout event Redisson tries to connect to disconnected server. Value in milliseconds. + +**failedSlaveNodeDetector** +Default value: `org.redisson.client.FailedConnectionDetector` + +Defines failed Redis or Valkey Slave node detector object which implements failed node detection logic via `org.redisson.client.FailedNodeDetector` interface. + +Available implementations: + +* `org.redisson.client.FailedConnectionDetector` - marks the Redis or Valkey node as failed if it has ongoing connection errors in defined `checkInterval` interval in milliseconds. Default is 180000 milliseconds. +* `org.redisson.client.FailedCommandsDetector` - marks the Redis or Valkey node as failed if it has certain amount of command execution errors defined by `failedCommandsLimit` in defined `checkInterval` interval in milliseconds. +* `org.redisson.client.FailedCommandsTimeoutDetector` - marks the Redis or Valkey node as failed if it has certain amount of command execution timeout errors defined by `failedCommandsLimit` in defined `checkInterval` interval in milliseconds. + +**database** +Default value: `0` + +Database index used for Redis or Valkey connection. + +**password** +Default value: `null` + +Password for Redis or Valkey server authentication. + +**username** +Default value: `null` + +Username for Redis or Valkey server authentication. Requires Redis 6.0 and higher. + +**credentialsResolver** +Default value: empty + +Defines Credentials resolver, which is invoked during connection for Redis or Valkey server authentication. Returns Credentials object per address, it contains `username` and `password` fields. Allows you to specify dynamically changing credentials. + +**subscriptionsPerConnection** +Default value: `5` + +Subscriptions per subscriber connection limit. Used by `RTopic`, +`RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, +`RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, +`RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local +Cached Region Factories. + +**subscriptionTimeout** +Default value: 7500 + +Defines subscription timeout in milliseconds applied per channel +subscription. + +**clientName** +Default value: `null` + +Name of client connection. + +**sslProtocols** +Default value: `null` + +Defines array of allowed SSL protocols. +Example values: `TLSv1.3`, `TLSv1.2`, `TLSv1.1`, `TLSv1` + +**sslEnableEndpointIdentification** +Default value: `true` + +Enables SSL endpoint identification during handshaking, which prevents man-in-the-middle attacks. + +**sslProvider** +Default value: `JDK` + +Defines SSL provider (JDK or OPENSSL) used to handle SSL connections. +OPENSSL considered as a faster implementation and requires[netty-tcnative-boringssl-static](https://repo1.maven.org/maven2/io/netty/netty-tcnative-boringssl-static/) to be added in the classpath. + +**sslTruststore** +Default value: `null` + +Defines path to the SSL truststore. It stores certificates which is used to identify the server side of an SSL connection. SSL truststore is read on each new connection creation and can be dynamically reloaded. + +**sslTruststorePassword** +Default value: `null` + +Defines password for SSL truststore. + +**sslKeystoreType** +Default value: `null` + +Defines SSL keystore type. + +**sslKeystore** +Default value: `null` + +Defines path to the SSL keystore. It stores private key and certificates corresponding to their public keys. Used if the server side of an SSL connection requires client authentication. SSL keystore is read on each new connection creation and can be dynamically reloaded. + +**sslKeystorePassword** +Default value: `null` + +Defines password for SSL keystore. + +**pingConnectionInterval** +Default value: `30000` + +PING command sending interval per connection to Redis. Defined in +milliseconds. Set `0` to disable. + +**keepAlive** +Default value: `false` + +Enables TCP keepAlive for connections. + +**tcpNoDelay** +Default value: `true` + +Enables TCP noDelay for connection. + +**nameMapper** +Default value: no mapper + +Defines Name mapper which maps Redisson object name to a custom name. Applied to all Redisson objects. + +**commandMapper** +Default value: no mapper + +Defines Command mapper which maps Redis or Valkey command name to a custom name. Applied to all commands. + +### Master slave YAML config format + +Below is master slave configuration example in YAML format. All property names are matched with `MasterSlaveServersConfig` and `Config` object property names. +```yaml +--- +masterSlaveServersConfig: + idleConnectionTimeout: 10000 + connectTimeout: 10000 + timeout: 3000 + retryAttempts: 3 + retryInterval: 1500 + failedSlaveReconnectionInterval: 3000 + failedSlaveNodeDetector: ! {} + password: null + subscriptionsPerConnection: 5 + clientName: null + loadBalancer: ! {} + subscriptionConnectionMinimumIdleSize: 1 + subscriptionConnectionPoolSize: 50 + slaveConnectionMinimumIdleSize: 24 + slaveConnectionPoolSize: 64 + masterConnectionMinimumIdleSize: 24 + masterConnectionPoolSize: 64 + readMode: "SLAVE" + subscriptionMode: "SLAVE" + slaveAddresses: + - "redis://127.0.0.1:6381" + - "redis://127.0.0.1:6380" + masterAddress: "redis://127.0.0.1:6379" + database: 0 +threads: 16 +nettyThreads: 32 +codec: ! {} +transportMode: "NIO" +``` + +## Proxy mode + +Proxy mode supports single or multiple Redis or Valkey databases (including synced with active-active replication) used for read/write operations. Each Redis or Valkey hostname might be resolved to more than one IP address. + +Depending on value of [proxyMode](#proxy-mode) setting there are two modes: +1. all nodes are primary and used for read/write operation with load balancer +2. single primary for read/write operation and the rest are idle secondary nodes + +Failed nodes detection is managed by `scanMode` setting. + +Compatible with: + +* [AWS Elasticache Serverless](https://aws.amazon.com/elasticache/features/#Serverless) +* [Azure Redis Cache active-active replication](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-how-to-active-geo-replication) +* [Redis Enterprise Multiple Active Proxy](https://docs.redis.com/latest/rs/databases/configure/proxy-policy/#about-multiple-active-proxy-support) +* [Redis Enterprise Active-Active databases](https://docs.redis.com/latest/rs/databases/active-active/get-started/) + +_This feature is available only in [Redisson PRO](https://redisson.pro) edition._ + +Programmatic config example: +```java +Config config = new Config(); +// use "rediss://" for SSL connection +config.useProxyServers().addAddress("redis://myredisserver1:6379", "redis://myredisserver2:6379"); + +RedissonClient redisson = Redisson.create(config); +``` + +### Proxy mode settings + +Proxy servers connection mode is activated by the following line: + +`ProxyServersConfig proxyConfig = config.useProxyServers();` + +`ProxyServersConfig` settings listed below: + +**addresses** +Redis or Valkey proxy servers addresses in `host:port` format. If single hostname is defined and DNS monitoring is enabled then all resolved ips are considered as proxy nodes and used by load balancer. Use `rediss://` protocol for SSL connection. + +**subscriptionConnectionMinimumIdleSize** +Default value: `1` + +Minimum idle connection pool size for subscription (pub/sub) channels. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**subscriptionConnectionPoolSize** +Default value: `50` + +Maximum connection pool size for subscription (pub/sub) channels. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**connectionMinimumIdleSize** +Default value: `24` + +Minimum idle Redis or Valkey connection amount. + +**connectionPoolSize** +Default value: `64` + +Redis or Valkey connection maximum pool size. + +**scanMode** +Default value: `PING` + +Defines scan mode to detect failed Redis or Valkey nodes. +Available values: + +* `PING` - Each Redis or Valkey node is checked using PING command. If node unable to response then it considered as a failed node. +* `PUBSUB` - Messages are sent over pubsub channel per Redis or Valkey node and should be received by all other nodes. If node unable to subscribe or receive message then it considered as a failed node. + +**proxyMode** +Default value: `ALL_ACTIVE` + +Defines proxy mode. +Available values: + +* `FIRST_ACTIVE` - Primary (active) database is a first address in the list of addresses and the rest are idle secondary nodes used after failover. +* `ALL_ACTIVE` - All databases are primary (active) and used for read/write operations. + +**scanInterval** +Default value: `5000` + +Defines proxy nodes scan interval in milliseconds. `0` means disable. + +**scanTimeout** +Default value: `3000` + +Defines proxy nodes scan timeout in milliseconds applied per Redis or Valkey node. + +**dnsMonitoringInterval** +Default value: `5000` + +DNS change monitoring interval. Set `-1` to disable. + +**idleConnectionTimeout** +Default value: `10000` + +If a pooled connection is not used for a timeout time and current connections amount bigger than minimum idle connections pool size, then it will be closed and removed from the pool. Value in milliseconds. + +**connectTimeout** +Default value: `10000` + +Timeout during connecting to any Redis or Valkey server. + +**timeout** +Default value: `3000` + +Redis or Valkey server response timeout. Starts to count down when a command was successfully sent. Value in milliseconds. + +**retryAttempts** +Default value: `3` + +Error will be thrown if Redis or Valkey ommand can’t be sent to a server after *retryAttempts*. But if it sent successfully then *timeout* will be started. + +**retryInterval** +Default value: `1500` + +Time interval after which another one attempt to send Redis or Valkey command will be executed. Value in milliseconds. + +**database** +Default value: `0` + +Database index used for Redis or Valkey connection. + +**failedNodeReconnectionInterval** +When the retry interval reached Redisson tries to connect to the disconnected Redis or Valkey node. After successful reconnection Redis node is become available for read/write operations execution. + +Default value: `3000` + +**failedSlaveNodeDetector** +Default value: `org.redisson.client.FailedConnectionDetector` + +Defines failed Redis or Valkey Slave node detector object which implements failed node detection logic via `org.redisson.client.FailedNodeDetector` interface. + +Available implementations: + +* `org.redisson.client.FailedConnectionDetector` - marks the Redis or Valkey node as failed if it has ongoing connection errors in defined `checkInterval` interval in milliseconds. Default is 180000 milliseconds. +* `org.redisson.client.FailedCommandsDetector` - marks the Redis or Valkey node as failed if it has certain amount of command execution errors defined by `failedCommandsLimit` in defined `checkInterval` interval in +milliseconds. +* `org.redisson.client.FailedCommandsTimeoutDetector` - marks the Redis or Valkey node as failed if it has certain amount of command execution timeout errors defined by `failedCommandsLimit` in defined `checkInterval` interval in milliseconds. + +**password** +Default value: `null` + +Password for Redis or Valkey server authentication. + +**username** +Default value: `null` + +Username for Redis or Valkey server authentication. Requires Redis 6.0 and higher. + +**credentialsResolver** + +Default value: empty + +Defines Credentials resolver, which is invoked during connection for Redis or Valkey server authentication. Returns Credentials object per address, it contains `username` and `password` fields. Allows you to specify dynamically changing credentials. + +**subscriptionsPerConnection** +Default value: `5` + +Subscriptions per subscribe connection limit. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**subscriptionTimeout** +Default value: 7500 + +Defines subscription timeout in milliseconds applied per channel subscription. + +**clientName** +Default value: `null` + +Name of client connection + +**sslProtocols** +Default value: `null` + +Defines array of allowed SSL protocols. +Example values: `TLSv1.3`, `TLSv1.2`, `TLSv1.1`, `TLSv1` + +**sslEnableEndpointIdentification** +Default value: `true` + +Enables SSL endpoint identification during handshaking, which prevents man-in-the-middle attacks. + +**sslProvider** +Default value: `JDK` + +Defines SSL provider (JDK or OPENSSL) used to handle SSL connections. +OPENSSL considered as a faster implementation and requires[netty-tcnative-boringssl-static](https://repo1.maven.org/maven2/io/netty/netty-tcnative-boringssl-static/) to be added in the classpath. + +**sslTruststore** +Default value: `null` + +Defines path to the SSL truststore. It stores certificates which is used to identify the server side of an SSL connection. SSL truststore is read on each new connection creation and can be dynamically reloaded. + +**sslTruststorePassword** +Default value: `null` + +Defines password for SSL truststore. + +**sslKeystoreType** +Default value: `null` + +Defines SSL keystore type. + +**sslKeystore** +Default value: `null` + +Defines path to the SSL keystore. It stores private key and certificates corresponding to their public keys. Used if the server side of an SSL connection requires client authentication. SSL keystore is read on each new connection creation and can be dynamically reloaded. + +**sslKeystorePassword** +Default value: `null` + +Defines password for SSL keystore. + +**pingConnectionInterval** +Default value: `30000` + +PING command sending interval per connection to Redis. Defined in milliseconds. Set `0` to disable. + +**keepAlive** +Default value: `false` + +Enables TCP keepAlive for connection. + +**tcpNoDelay** +Default value: `true` + +Enables TCP noDelay for connection. + +**loadBalancer** +Default value: `org.redisson.connection.balancer.RoundRobinLoadBalancer` + +Сonnection load balancer for multiple Redis or Valkey servers. +Available implementations: + +* `org.redisson.connection.balancer.CommandsLoadBalancer` +* `org.redisson.connection.balancer.WeightedRoundRobinBalancer` +* `org.redisson.connection.balancer.RoundRobinLoadBalancer` +* `org.redisson.connection.balancer.RandomLoadBalancer` + +**nameMapper** +Default value: no mapper + +Defines Name mapper which maps Redisson object name to a custom name. +Applied to all Redisson objects. + +**commandMapper** +Default value: no mapper + +Defines Command mapper which maps Redis or Valkey command name to a custom name. Applied to all commands. + +### Proxy mode YAML config format + +Below is proxy mode configuration example in YAML format. All property +names are matched with `ProxyServersConfig` and `Config` object property names. + +```yaml +--- +proxyServersConfig: + idleConnectionTimeout: 10000 + connectTimeout: 10000 + timeout: 3000 + retryAttempts: 3 + retryInterval: 1500 + password: null + subscriptionsPerConnection: 5 + clientName: null + addresses: "redis://127.0.0.1:6379" + subscriptionConnectionMinimumIdleSize: 1 + subscriptionConnectionPoolSize: 50 + connectionMinimumIdleSize: 24 + connectionPoolSize: 64 + database: 0 + dnsMonitoringInterval: 5000 + loadBalancer: ! {} +threads: 16 +nettyThreads: 32 +codec: ! {} +transportMode: "NIO" +``` + +## Multi cluster mode + +Supports multiple Redis or Valkey Cluster setups with active-passive data replication relationship. Replication of the primary Cluster with secondary Redis Cluster is managed by `replicationMode` setting. + +Cluster with all available master nodes becomes the primary. Master nodes availability scan interval is defined by `scanInterval` setting. + +Compatible with: + +* [AWS Redis Global Datastore](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Redis-Global-Datastore.html). + +_This feature is available only in [Redisson PRO](https://redisson.pro) edition._ + +Programmatic config example: +```java +Config config = new Config(); +config.useMultiClusterServers() + .setScanInterval(2000) // cluster state scan interval in milliseconds + // use "rediss://" for SSL connection + .addAddress("redis://cluster1:7000", "redis://cluster2:70002"); + +RedissonClient redisson = Redisson.create(config); +``` +### Multi Cluster settings + +Multi clusters connection mode is activated by follow line: + +`ClusterServersConfig clusterConfig = config.useMultiClusterServers();` +`ClusterServersConfig` settings listed below: + +**checkSlotsCoverage** +Default value: `true` + +Enables cluster slots check during Redisson startup. + +**addresses** +Each entry is a Redis or Valkey cluster setup, which is defined by the hostname of any of nodes in cluster or endpoint. Addresses should be in `redis://host:port` format. Use `rediss://` protocol for SSL connection. + +**scanInterval** +Default value: `5000` + +Scan interval in milliseconds. Applied to clusters topology scan and primary and secondary clusters scan process. Handles failover between primary and secondary clusters. Cluster with all available master nodes becomes the primary. + +**slots** +Default value: `231` + +Partitions amount used for data partitioning. Data partitioning supported by [Set](data-and-services/collections.md/#eviction-and-data-partitioning), [Map](data-and-services/collections.md/#eviction-local-cache-and-data-partitioning), [BitSet](data-and-services/objects.md/#data-partitioning), [Bloom filter](data-and-services/objects.md/#data-partitioning_1), [Spring Cache](cache-api-implementations.md/#eviction-local-cache-and-data-partitioning), [JCache](cache-api-implementations.md/#local-cache-and-data-partitioning), [Micronaut Cache](cache-api-implementations.md/#eviction-local-cache-and-data-partitioning_4), [Quarkus Cache](cache-api-implementations.md/#eviction-local-cache-and-data-partitioning_3) and [Hibernate Cache](cache-api-implementations.md/#eviction-local-cache-and-data-partitioning_1) structures. + + +**readMode** +Default value: `SLAVE` + +Set node type used for read operation. +Available values: + +* `SLAVE` - Read from slave nodes, uses MASTER if no SLAVES are available, +* `MASTER` - Read from master node, +* `MASTER_SLAVE` - Read from master and slave nodes + +**datastoreMode** +Default value: `ACTIVE_PASSIVE` + +Defines Datastore mode. +Available values: + +* `ACTIVE` - only primary (active) cluster is used +* `ACTIVE_PASSIVE` - primary (active) cluster is used for read/write operations and secondary (passive) clusters are used for read operations only +* `WRITE_ACTIVE_READ_PASSIVE` - Primary (active) cluster is used for write operations and secondary (passive) clusters are used for read operations only + +**subscriptionMode** +Default value: `SLAVE` + +Set node type used for subscription operation. +Available values: + +* `SLAVE` - Subscribe to slave nodes, +* `MASTER` - Subscribe to master node, + +**shardedSubscriptionMode** +Default value: `AUTO` + +Defines whether to use sharded subscription feature available in Valkey or Redis 7.0 and higher. Used by `RMapCache`, `RLocalCachedMap`, `RCountDownLatch`, `RLock`, `RPermitExpirableSemaphore`, `RSemaphore`, `RLongAdder`, `RDoubleAdder`, `Micronaut Session`, `Apache Tomcat Manager` objects. + +**replicationMode** +Default value: `NONE` + +Defines replication of the primary Cluster with secondary Redis Clusters. + +Available values: + +* `NONE` - No replication executed by Redisson. Replication should be executed on Redis or Valkey side, +* `SYNC` - Each Redisson method invocation which modifies data is completed only if it has been replicated to all Redis or Valkey deployments, +* `ASYNC` - Each Redisson method invocation which modifies data doesn't wait for replication to complete on other Redis or Valkey deployments + +**loadBalancer** +Default value: `org.redisson.connection.balancer.RoundRobinLoadBalancer` + +Сonnection load balancer for multiple Redis or Valkey servers. +Available implementations: + +* `org.redisson.connection.balancer.CommandsLoadBalancer` +* `org.redisson.connection.balancer.WeightedRoundRobinBalancer` +* `org.redisson.connection.balancer.RoundRobinLoadBalancer` +* `org.redisson.connection.balancer.RandomLoadBalancer` + +**primaryDiscoveryMode** +Default value: `AUTO` + +Defines primary Cluster selection mode. + +Available values: + +* `FIRST_PRIMARY` - Primary database is the first address in the list of addresses +* `AUTO` - Primary database is selected if all master nodes are available + +**subscriptionConnectionMinimumIdleSize** +Default value: `1` + +Minimum idle connection pool size for subscription (pub/sub) channels. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**subscriptionConnectionPoolSize** +Default value: `50` + +Maximum connection pool size for subscription (pub/sub) channels. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories + +**slaveConnectionMinimumIdleSize** +Default value: `24` + +Redis or Valkey `slave` node minimum idle connection amount for each slave node. + +**slaveConnectionPoolSize** +Default value: `64` + +Redis or Valkey `slave` node maximum connection pool size for each slave node. + +**masterConnectionMinimumIdleSize** +Default value: `24` + +Minimum idle connections amount per Redis or Valkey master node. + +**masterConnectionPoolSize** +Default value: `64` + +Redis or Valkey `master' node maximum connection pool size. + +**idleConnectionTimeout** +Default value: `10000` + +If a pooled connection is not used for a timeout time and current connections amount bigger than minimum idle connections pool size, then it will be closed and removed from the pool. Value in milliseconds. + +**connectTimeout** +Default value: `10000` + +Timeout in milliseconds during connecting to any Redis or Valkey server + +**timeout** +Default value: `3000` + +Redis or Valkey server response timeout in milliseconds. Starts to count down when a command was successfully sent. + +**retryAttempts** +Default value: `3` + +Error will be thrown if Redis or Valkey command can’t be sent to a server after *retryAttempts*. But if it sent successfully then *timeout* will be started. + +**retryInterval** +Default value: `1500` + +Time interval in milliseconds after which another one attempt to send Redis or Valkey command will be executed. + +**failedSlaveReconnectionInterval** +Default value: `3000` + +Interval of Redis or Valkey Slave reconnection attempts when it was excluded from internal list of available servers. On each timeout event Redisson tries to connect to disconnected server. Value in milliseconds. + +**failedSlaveNodeDetector** +Default value: `org.redisson.client.FailedConnectionDetector` + +Defines failed Redis or Valkey Slave node detector object which implements failed node detection logic via `org.redisson.client.FailedNodeDetector` interface. + +Available implementations: + +* `org.redisson.client.FailedConnectionDetector` - marks the Redis or Valkey node as failed if it has ongoing connection errors in defined `checkInterval` interval in milliseconds. Default is 180000 milliseconds. +* `org.redisson.client.FailedCommandsDetector` - marks the Redis or Valkey node as failed if it has certain amount of command execution errors defined by `failedCommandsLimit` in defined `checkInterval` interval in milliseconds. +* `org.redisson.client.FailedCommandsTimeoutDetector` - marks the Redis node as failed if it has certain amount of command execution timeout errors defined by `failedCommandsLimit` in defined `checkInterval` interval in milliseconds. + +**password** +Default value: `null` + +Password for Redis or Valkey server authentication. + +**username** +Default value: `null` + +Username for Redis or Valkey server authentication. Requires Redis 6.0 and higher. + +**credentialsResolver** + +Default value: empty + +Defines Credentials resolver, which is invoked during connection for Redis or Valkey server authentication. Returns Credentials object per node address, it contains `username` and `password` fields. Allows you to specify dynamically changing credentials. + +**subscriptionsPerConnection** +Default value: `5` + +Subscriptions per subscribe connection limit. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**subscriptionTimeout** +Default value: 7500 + +Defines subscription timeout in milliseconds applied per channel subscription. + +**clientName** +Default value: `null` + +Name of client connection. + +**sslProtocols** +Default value: `null` + +Defines array of allowed SSL protocols. +Example values: `TLSv1.3`, `TLSv1.2`, `TLSv1.1`, `TLSv1` + +**sslEnableEndpointIdentification** +Default value: `true` + +Enables SSL endpoint identification during handshaking, which prevents man-in-the-middle attacks. + +**sslProvider** +Default value: `JDK` + +Defines SSL provider (JDK or OPENSSL) used to handle SSL connections. +OPENSSL is considered the faster implementation and requires [netty-tcnative-boringssl-static](https://repo1.maven.org/maven2/io/netty/netty-tcnative-boringssl-static/) to be added in the classpath. + +**sslTruststore** +Default value: `null` + +Defines the path to the SSL truststore. It stores certificates which are used to identify the server side of an SSL connections. The SSL truststore is read on each new connection creation and can be dynamically reloaded. + +**sslTruststorePassword** +Default value: `null` + +Defines password for SSL truststore. + +**sslKeystoreType** +Default value: `null` + +Defines SSL keystore type. + +**sslKeystore** +Default value: `null` + +Defines the path to the SSL truststore. It stores certificates which are used to identify the server side of an SSL connections. The SSL truststore is read on each new connection creation and can be dynamically reloaded. + +**sslKeystorePassword** +Default value: `null` + +Defines password for SSL keystore. + +**pingConnectionInterval** +Default value: `30000` + +This setting allows to detect and reconnect broken connections using +PING command. PING command sending interval defined in milliseconds. +Useful in cases when netty lib doesn’t invoke `channelInactive` method +for closed connections. Set `0` to disable. + +**keepAlive** +Default value: `false` + +Enables TCP keepAlive for connection. + +**tcpNoDelay** +Default value: `true` + +Enables TCP noDelay for connection. + +**natMapper** +Default value: no mapper + +Defines NAT mapper interface which maps Redis or Valkey URI object and applied to all connections. Can be used to map internal Redis server IPs to external ones. Available implementations: + +* `org.redisson.api.HostPortNatMapper` +* `org.redisson.api.HostNatMapper` + +**nameMapper** +Default value: no mapper + +Defines Name mapper which maps Redisson object name to a custom name. Applied to all Redisson objects. + +**commandMapper** +Default value: no mapper + +Defines Command mapper which maps Redis or Valkey command name to a custom name. Applied to all commands. + +### Multi Cluster YAML config format + +Below is cluster configuration example in YAML format. All property +names are matched with `ClusterServersConfig` and `Config` object property names. + +```yaml +--- +multiClusterServersConfig: + idleConnectionTimeout: 10000 + connectTimeout: 10000 + timeout: 3000 + retryAttempts: 3 + retryInterval: 1500 + failedSlaveReconnectionInterval: 3000 + failedSlaveNodeDetector: ! {} + password: null + subscriptionsPerConnection: 5 + clientName: null + loadBalancer: ! {} + subscriptionConnectionMinimumIdleSize: 1 + subscriptionConnectionPoolSize: 50 + slaveConnectionMinimumIdleSize: 24 + slaveConnectionPoolSize: 64 + masterConnectionMinimumIdleSize: 24 + masterConnectionPoolSize: 64 + readMode: "SLAVE" + datastoreMode: "ACTIVE_PASSIVE" + subscriptionMode: "SLAVE" + addresses: + - "redis://cluster1:7004" + - "redis://cluster2:7001" + - "redis://cluster3:7000" + scanInterval: 5000 + pingConnectionInterval: 30000 + keepAlive: false + tcpNoDelay: true +threads: 16 +nettyThreads: 32 +codec: ! {} +transportMode: "NIO" +``` + +## Multi Sentinel mode + +Supports multiple Redis or Valkey Sentinel setups with active-passive data replication. + +Replication of primary Sentinel deployment with secondary Sentinel deployments is managed by `replicationMode` setting. First sentinel host belongs to the active Sentinel setup and others to Passive Sentinel Setups. + +_This feature is available only in [Redisson PRO](https://redisson.pro) edition._ + +Programmatic config example: +```java +Config config = new Config(); +config.useMultiSentinelServers() + .setReplicationMode(ReplicationMode.ASYNC) + .setMasterName("mymaster") + // use "rediss://" for SSL connection + .addSentinelAddress("redis://sentinel_primary_cluster:26389", + "redis://sentinel_secondary_cluster1:26379", + "redis://sentinel_secondary_cluster2:26379") + +RedissonClient redisson = Redisson.create(config); +``` +### Multi Sentinel settings + +Documentation covering Redis or Valkey server sentinel configuration is [here](https://redis.io/topics/sentinel). + +Multi Sentinel connection mode is activated by follow line: +`MultiSentinelServersConfig sentinelConfig = config.useMultiSentinelServers();` + +`MultiSentinelServersConfig` settings listed below: + +**replicationMode** +Default value: `NONE` + +Defines replication of primary Sentinel deployment with secondary Redis Sentinel deployments. + +Available values: + +* `NONE` - No replication executed by Redisson. Replication should be executed on Redis or Valkey side, +* `SYNC` - Each Redisson method invocation which modifies data is completed only if it has been replicated to all Redis or Valkey deployments, +* `ASYNC` - Each Redisson method invocation which modifies data doesn't wait for replication to complete on other Redis or Valkey deployments + +**checkSentinelsList** +Default value: `true` + +Enables sentinels list check during Redisson startup. + +**dnsMonitoringInterval** +Default value: `5000` + +Interval in milliseconds to check the endpoint's DNS. Set `-1` to disable. + +**checkSlaveStatusWithSyncing** +Default value: `true` + +Check if slave node `master-link-status` field has status `ok`. + +**masterName** +Master server name used by Sentinel servers and master change monitoring task. + +**addSentinelAddress** +Add Sentinel node address in `host:port` format. Multiple nodes at once could be added. + +**readMode** +Default value: `SLAVE` + +Set node type used for read operation. +Available values: + +* `SLAVE` - Read from slave nodes, uses MASTER if no SLAVES are available, +* `MASTER` - Read from master node, +* `MASTER_SLAVE` - Read from master and slave nodes + +**subscriptionMode** +Default value: `SLAVE` + +Set node type used for subscription operation. +Available values: + +* `SLAVE` - Subscribe to slave nodes, +* `MASTER` - Subscribe to master node, + +**loadBalancer** +Default value: `org.redisson.connection.balancer.RoundRobinLoadBalancer` + +Сonnection load balancer for multiple Redis or Valkey servers. +Available implementations: + +* `org.redisson.connection.balancer.CommandsLoadBalancer` +* `org.redisson.connection.balancer.WeightedRoundRobinBalancer` +* `org.redisson.connection.balancer.RoundRobinLoadBalancer` +* `org.redisson.connection.balancer.RandomLoadBalancer` + +**subscriptionConnectionMinimumIdleSize** +Default value: `1` + +Minimum idle connection pool size for subscription (pub/sub) channels. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**subscriptionConnectionPoolSize** +Default value: `50` + +Maximum connection pool size for subscription (pub/sub) channels. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**slaveConnectionMinimumIdleSize** +Default value: `24` + +Redis or Valkey `slave` node minimum idle connection amount for each slave node. + +**slaveConnectionPoolSize** +Default value: `64` + +Redis or Valkey `slave` node maximum connection pool size for each slave node. + +**masterConnectionMinimumIdleSize** +Default value: `24` + +Minimum idle connections amount per Redis or Valkey master node. + +**masterConnectionPoolSize** +Default value: `64` + +Redis or Valkey `master` node maximum connection pool size. + +**idleConnectionTimeout** +Default value: `10000` + +If a pooled connection is not used for a timeout time and current connections amount bigger than minimum idle connections pool size, then it will be closed and removed from the pool. Value in milliseconds. + +**connectTimeout** +Default value: `10000` + +Timeout during connecting to any Redis or Valkey server. + +**timeout** +Default value: `3000` + +Redis or Valkey server response timeout. Starts to count down when a command was successfully sent. Value in milliseconds. + +**retryAttempts** +Default value: `3` + +Error will be thrown if Redis or Valkey command can’t be sent to a server after *retryAttempts*. But if it sent successfully then *timeout* will be started. + +**retryInterval** +Default value: `1500` + +Time interval after which another one attempt to send Redis or Valkey command will be executed. Value in milliseconds. + + +**failedSlaveReconnectionInterval** + +Default value: `3000` + +Interval of Redis or Valkey Slave reconnection attempts when it was excluded from internal list of available servers. On each timeout event Redisson tries to connect to disconnected server. Value in milliseconds. + +**failedSlaveNodeDetector** +Default value: `org.redisson.client.FailedConnectionDetector` + +Defines failed Redis or Valkey Slave node detector object which implements failed node detection logic via `org.redisson.client.FailedNodeDetector` interface. + +Available implementations: + +* `org.redisson.client.FailedConnectionDetector` - marks the Redis or Valkey node as failed if it has ongoing connection errors in defined `checkInterval` interval in milliseconds. Default is 180000 milliseconds. +* `org.redisson.client.FailedCommandsDetector` - marks the Redis or Valkey node as failed if it has certain amount of command execution errors defined by `failedCommandsLimit` in defined `checkInterval` interval in milliseconds. +* `org.redisson.client.FailedCommandsTimeoutDetector` - marks the Redis or Valkey node as failed if it has certain amount of command execution timeout errors defined by `failedCommandsLimit` in defined `checkInterval` interval in milliseconds. + +**database** +Default value: `0` + +Database index used for Redis or Valkey connections. + +**password** +Default value: `null` + +Password for Redis or Valkey servers authentication. + +**username** +Default value: `null` + +Username for Redis or Valkey servers authentication. Requires Redis 6.0 and higher. + +**sentinelPassword** +Default value: `null` + +Password for Sentinel servers authentication. Used only if Sentinel password differs from master's and slave's. + +**sentinelUsername** +Default value: `null` + +Username for Sentinel servers for authentication. Used only if Sentinel username differs from master's and slave's. Requires Redis 6.0 and higher. + +**credentialsResolver** +Default value: empty + +Defines Credentials resolver, which is invoked during connection for Redis or Valkey server authentication. Returns Credentials object per node address, it contains `username` and `password` fields. Allows you to specify dynamically changing credentials. + +**sentinelsDiscovery** +Default value: `true` + +Enables sentinels discovery. + +**subscriptionsPerConnection** +Default value: `5` + +Subscriptions per subscribe connection limit. Used by `RTopic`, `RPatternTopic`, `RLock`, `RSemaphore`, `RCountDownLatch`, `RClusteredLocalCachedMap`, `RClusteredLocalCachedMapCache`, `RLocalCachedMap`, `RLocalCachedMapCache` objects and Hibernate Local Cached Region Factories. + +**subscriptionTimeout** +Default value: 7500 + +Defines subscription timeout in milliseconds applied per channel subscription. + +**clientName** +Default value: `null` + +Name of client connection. + +**sslProtocols** +Default value: `null` + +Defines array of allowed SSL protocols. +Example values: `TLSv1.3`, `TLSv1.2`, `TLSv1.1`, `TLSv1` + +**sslEnableEndpointIdentification** +Default value: `true` + +Enables SSL endpoint identification during handshaking, which prevents man-in-the-middle attacks. + +**sslProvider** +Default value: `JDK` + +Defines SSL provider (JDK or OPENSSL) used to handle SSL connections. +OPENSSL considered as a faster implementation and requires [netty-tcnative-boringssl-static](https://repo1.maven.org/maven2/io/netty/netty-tcnative-boringssl-static/) to be added in the classpath. + +**sslTruststore** +Default value: `null` + +Defines the path to the SSL truststore. It stores certificates which are used to identify the server side of an SSL connections. The SSL truststore is read on each new connection creation and can be dynamically reloaded. + +**sslTruststorePassword** +Default value: `null` + +Defines password for SSL truststore. + +**sslKeystoreType** +Default value: `null` + +Defines SSL keystore type. + +**sslKeystore** +Default value: `null` + +Defines the path to the SSL keystore. It stores certificates which are used to identify the server side of an SSL connections. The SSL keystore is read on each new connection creation and can be dynamically reloaded. + +**sslKeystorePassword** +Default value: `null` + +Defines password for SSL keystore. + +**pingConnectionInterval** +Default value: `30000` + +PING command sending interval per connection to Redis. Defined in milliseconds. Set `0` to disable. + +**keepAlive** +Default value: `false` + +Enables TCP keepAlive for connection. + +**tcpNoDelay** +Default value: `true` + +Enables TCP noDelay for connection. + +**natMapper** +Default value: no mapper + +Defines NAT mapper interface which maps Redis or Valkey URI object and applied to all connections. Can be used to map internal Redis or Valkey server IPs to external ones. Available implementations: + +* `org.redisson.api.HostPortNatMapper` +* `org.redisson.api.HostNatMapper` + +**nameMapper** +Default value: no mapper + +Defines Name mapper which maps Redisson object name to a custom name. Applied to all Redisson objects. + +**commandMapper** +Default value: no mapper + +Defines Command mapper which maps Redis or Valkey command name to a custom name. Applied to all commands. + +### Multi Sentinel YAML config format + +Below is a sentinel configuration example in YAML format. All property +names are matched with `MultiSentinelServersConfig` and `Config` object +property names. + +```yaml +--- +multiSentinelServersConfig: + replicationMode: "ASYNC" + idleConnectionTimeout: 10000 + connectTimeout: 10000 + timeout: 3000 + retryAttempts: 3 + retryInterval: 1500 + failedSlaveReconnectionInterval: 3000 + failedSlaveNodeDetector: ! {} + password: null + subscriptionsPerConnection: 5 + clientName: null + loadBalancer: ! {} + subscriptionConnectionMinimumIdleSize: 1 + subscriptionConnectionPoolSize: 50 + slaveConnectionMinimumIdleSize: 24 + slaveConnectionPoolSize: 64 + masterConnectionMinimumIdleSize: 24 + masterConnectionPoolSize: 64 + readMode: "SLAVE" + subscriptionMode: "SLAVE" + sentinelAddresses: + - "redis://127.0.0.1:26379" + - "redis://127.0.0.1:26389" + masterName: "mymaster" + database: 0 +threads: 16 +nettyThreads: 32 +codec: ! {} +transportMode: "NIO" +``` diff --git a/docs/data-and-services/collections.md b/docs/data-and-services/collections.md new file mode 100644 index 000000000..2415ab2a3 --- /dev/null +++ b/docs/data-and-services/collections.md @@ -0,0 +1,2014 @@ +## Map +Redis or Valkey based distributed [Map](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMap.html) object for Java implements [ConcurrentMap](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentMap.html) interface. This object is thread-safe. Consider to use [Live Object service](services.md/#live-object-service) to store POJO object as Redis or Valkey Map. Redis or Valkey uses serialized state to check key uniqueness instead of key's `hashCode()`/`equals()` methods. + +If Map used mostly for read operations and/or network roundtrips are undesirable use Map with [Local cache](#eviction-local-cache-and-data-partitioning) support. + +Code examples: + +```java +RMap map = redisson.getMap("anyMap"); +SomeObject prevObject = map.put("123", new SomeObject()); +SomeObject currentObject = map.putIfAbsent("323", new SomeObject()); +SomeObject obj = map.remove("123"); + +// use fast* methods when previous value is not required +map.fastPut("a", new SomeObject()); +map.fastPutIfAbsent("d", new SomeObject()); +map.fastRemove("b"); + +RFuture putAsyncFuture = map.putAsync("321"); +RFuture fastPutAsyncFuture = map.fastPutAsync("321"); + +map.fastPutAsync("321", new SomeObject()); +map.fastRemoveAsync("321"); +``` +RMap object allows to bind a [Lock](locks-and-synchronizers.md/#lock)/[ReadWriteLock](locks-and-synchronizers.md/#readwritelock)/[Semaphore](locks-and-synchronizers.md/#semaphore)/[CountDownLatch](locks-and-synchronizers.md/#countdownlatch) object per key: +```java +RMap map = redisson.getMap("anyMap"); +MyKey k = new MyKey(); +RLock keyLock = map.getLock(k); +keyLock.lock(); +try { + MyValue v = map.get(k); + // process value ... +} finally { + keyLock.unlock(); +} + +RReadWriteLock rwLock = map.getReadWriteLock(k); +rwLock.readLock().lock(); +try { + MyValue v = map.get(k); + // process value ... +} finally { + keyLock.readLock().unlock(); +} +``` + +### Eviction, local cache and data partitioning + +Redisson provides various Map structure implementations with multiple important features: + +**local cache** - so called `near cache` used to speed up read operations and avoid network roundtrips. It caches Map entries on Redisson side and executes read operations up to **45x faster** in comparison with common implementation. Local cache instances with the same name connected to the same pub/sub channel. This channel is used for exchanging of update/invalidate events between all instances. Local cache store doesn't use `hashCode()`/`equals()` methods of key object, instead it uses hash of serialized state. It's recommended to use each local cached instance as a singleton per unique name since it has own state for local cache. + +**data partitioning** - although any Map object is cluster compatible its content isn't scaled/partitioned across multiple master nodes in cluster. Data partitioning allows to scale available memory, read/write operations and entry eviction process for individual Map instance in cluster. + +**1. No eviction** + +Each object implements [RMap](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMap.html), [Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMapAsync.html), [Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMapReactive.html) and [RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMapRx.html) interfaces. + +Available implementations: + +|RedissonClient
method name | Local
cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :-----------:| :---------:| +|getMap()
open-source version | ❌ | ❌ | ❌ | +|getLocalCachedMap()
open-source version | ✔️ | ❌ | ❌ | +|getMap()
[Redisson PRO](https://redisson.pro) version | ❌ | ❌ | ✔️ | +|getLocalCachedMap()
[Redisson PRO](https://redisson.pro) version | ✔️ | ❌ | ✔️ | +|getClusteredMap()
available only in [Redisson PRO](https://redisson.pro) | ❌ | ✔️ | ✔️ | +|getClusteredLocalCachedMap()
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ✔️ | ✔️ | +
+ +**2. Scripted eviction** + +Allows to define `time to live` or `max idle time` parameters per map entry. Eviction is done on Redisson side through a custom scheduled task which removes expired entries using Lua script. Eviction task is started once per unique object name at the moment of getting Map instance. If instance isn't used and has expired entries it should be get again to start the eviction process. This leads to extra Redis or Valkey calls and eviction task per unique map object name. + +Entries are cleaned time to time by `org.redisson.eviction.EvictionScheduler`. By default, it removes 100 expired entries at a time. This can be changed through [cleanUpKeysAmount](../configuration.md) setting. Task launch time tuned automatically and depends on expired entries amount deleted in previous time and varies between 5 second to 30 minutes by default. This time interval can be changed through [minCleanUpDelay](../configuration.md) and [maxCleanUpDelay](../configuration.md). For example, if clean task deletes 100 entries each time it will be executed every 5 seconds (minimum execution delay). But if current expired entries amount is lower than previous one then execution delay will be increased by 1.5 times and decreased otherwise. + +Each object implements [RMapCache](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMapCache.html), [Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMapCacheAsync.html), [Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMapCacheReactive.html) and [RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMapCacheRx.html) interfaces. + +Available implementations: + +|RedissonClient
method name | Local
cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :-----------:| :---------:| +|getMapCache()
open-source version | ❌ | ❌ | ❌ | +|getMapCache()
[Redisson PRO](https://redisson.pro) version | ❌ | ❌ | ✔️ | +|getLocalCachedMapCache()
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ❌ | ✔️ | +|getClusteredMapCache()
available only in [Redisson PRO](https://redisson.pro) | ❌ | ✔️ | ✔️ | +|getClusteredLocalCachedMapCache()
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ✔️ | ✔️ | +
+ +**3. Advanced eviction** + +Allows to define `time to live` parameter per map entry. Doesn't use an entry eviction task, entries are cleaned on Redis or Valkey side. + +Each object implements [RMapCacheV2](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMapCacheV2.html), [Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMapCacheV2Async.html), [Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMapCacheV2Reactive.html) and [RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMapCacheV2Rx.html) interfaces. + +Available implementations: + +|RedissonClient
method name | Local
cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :-----------:| :---------:| +|getMapCacheV2()
available only in [Redisson PRO](https://redisson.pro) | ❌ | ✔️ | ✔️ | +|getLocalCachedMapCacheV2()
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ✔️ | ✔️ | +
+ +**4. Native eviction** + +Allows to define `time to live` parameter per map entry. Doesn't use an entry eviction task, entries are cleaned on Redis side. Requires **Redis 7.4+**. + +Each object implements [RMapCacheNative](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMapCacheNative.html), [Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMapCacheNativeAsync.html), [Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMapCacheNativeReactive.html) and [RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMapCacheNativeRx.html) interfaces. + +Available implementations: + +|RedissonClient
method name | Local
cache | Data
partitioning | Ultra-fast
read/write | +| ------------- | :-----------: | :-----------:| :---------:| +|getMapCacheNative()
open-source version | ❌ | ❌ | ❌ | +|getMapCacheNative()
[Redisson PRO](https://redisson.pro) version | ❌ | ❌ | ✔️ | +|getLocalCachedMapCacheNative()
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ❌ | ✔️ | +|getClusteredMapCacheNative()
available only in [Redisson PRO](https://redisson.pro) | ❌ | ✔️ | ✔️ | +
+ +Redisson also provides various [Cache API](../cache-API-implementations.md) implementations. + +It's recommended to use single instance of Map instance with the same name for each Redisson client instance. + +Code example: +```java +RMapCache map = redisson.getMapCache("anyMap"); +// or +RMapCache map = redisson.getMapCache("anyMap", MapCacheOptions.defaults()); +// or +RMapCacheV2 map = redisson.getMapCacheV2("anyMap"); +// or +RMapCacheV2 map = redisson.getMapCacheV2("anyMap", MapOptions.defaults()); +// or +RMapCache map = redisson.getLocalCachedMapCache("anyMap", LocalCachedMapOptions.defaults()); +// or +RMapCache map = redisson.getClusteredLocalCachedMapCache("anyMap", LocalCachedMapOptions.defaults()); +// or +RMapCache map = redisson.getClusteredMapCache("anyMap"); +// or +RMapCache map = redisson.getClusteredMapCache("anyMap", MapCacheOptions.defaults()); + + +// ttl = 10 minutes, +map.put("key1", new SomeObject(), 10, TimeUnit.MINUTES); +// ttl = 10 minutes, maxIdleTime = 10 seconds +map.put("key1", new SomeObject(), 10, TimeUnit.MINUTES, 10, TimeUnit.SECONDS); + +// ttl = 3 seconds +map.putIfAbsent("key2", new SomeObject(), 3, TimeUnit.SECONDS); +// ttl = 40 seconds, maxIdleTime = 10 seconds +map.putIfAbsent("key2", new SomeObject(), 40, TimeUnit.SECONDS, 10, TimeUnit.SECONDS); + +// if object is not used anymore +map.destroy(); +``` + +**Local cache** + +Map object with local cache support implements [RLocalCachedMap](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLocalCachedMap.html) which extends [ConcurrentMap](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentMap.html) interface. This object is thread-safe. + +It's recommended to use single instance of `LocalCachedMap` instance per name for each Redisson client instance. Same `LocalCachedMapOptions` object should be used across all instances with the same name. + +Follow options can be supplied during object creation: +```java + LocalCachedMapOptions options = LocalCachedMapOptions.defaults() + + // Defines whether to store a cache miss into the local cache. + // Default value is false. + .storeCacheMiss(false); + + // Defines store mode of cache data. + // Follow options are available: + // LOCALCACHE - store data in local cache only and use Redis or Valkey only for data update/invalidation. + // LOCALCACHE_REDIS - store data in both Redis or Valkey and local cache. + .storeMode(StoreMode.LOCALCACHE_REDIS) + + // Defines Cache provider used as local cache store. + // Follow options are available: + // REDISSON - uses Redisson own implementation + // CAFFEINE - uses Caffeine implementation + .cacheProvider(CacheProvider.REDISSON) + + // Defines local cache eviction policy. + // Follow options are available: + // LFU - Counts how often an item was requested. Those that are used least often are discarded first. + // LRU - Discards the least recently used items first + // SOFT - Uses soft references, entries are removed by GC + // WEAK - Uses weak references, entries are removed by GC + // NONE - No eviction + .evictionPolicy(EvictionPolicy.NONE) + + // If cache size is 0 then local cache is unbounded. + .cacheSize(1000) + + // Defines strategy for load missed local cache updates after connection failure. + // + // Follow reconnection strategies are available: + // CLEAR - Clear local cache if map instance has been disconnected for a while. + // LOAD - Store invalidated entry hash in invalidation log for 10 minutes + // Cache keys for stored invalidated entry hashes will be removed + // if LocalCachedMap instance has been disconnected less than 10 minutes + // or whole cache will be cleaned otherwise. + // NONE - Default. No reconnection handling + .reconnectionStrategy(ReconnectionStrategy.NONE) + + // Defines local cache synchronization strategy. + // + // Follow sync strategies are available: + // INVALIDATE - Default. Invalidate cache entry across all LocalCachedMap instances on map entry change + // UPDATE - Insert/update cache entry across all LocalCachedMap instances on map entry change + // NONE - No synchronizations on map changes + .syncStrategy(SyncStrategy.INVALIDATE) + + // time to live for each map entry in local cache + .timeToLive(10000) + // or + .timeToLive(10, TimeUnit.SECONDS) + + // max idle time for each map entry in local cache + .maxIdle(10000) + // or + .maxIdle(10, TimeUnit.SECONDS); +``` + +Code example: + +```java +RLocalCachedMap map = redisson.getLocalCachedMap("test", LocalCachedMapOptions.defaults()); +// or +RLocalCachedMap map = redisson.getLocalCachedMapCache("anyMap", LocalCachedMapCacheOptions.defaults()); +// or +RLocalCachedMap map = redisson.getClusteredLocalCachedMapCache("anyMap", LocalCachedMapCacheOptions.defaults()); +// or +RLocalCachedMap map = redisson.getClusteredLocalCachedMap("anyMap", LocalCachedMapOptions.defaults()); + + +String prevObject = map.put("123", 1); +String currentObject = map.putIfAbsent("323", 2); +String obj = map.remove("123"); + +// use fast* methods when previous value is not required +map.fastPut("a", 1); +map.fastPutIfAbsent("d", 32); +map.fastRemove("b"); + +RFuture putAsyncFuture = map.putAsync("321"); +RFuture fastPutAsyncFuture = map.fastPutAsync("321"); + +map.fastPutAsync("321", new SomeObject()); +map.fastRemoveAsync("321"); +``` + +Object should be destroyed if it not used anymore, but it's not necessary to call destroy method if Redisson goes shutdown. +```java +RLocalCachedMap map = ... +map.destroy(); +``` + + +**How to load data to avoid invalidation messages traffic.** + +Code example: + +```java + public void loadData(String cacheName, Map data) { + RLocalCachedMap clearMap = redisson.getLocalCachedMap(cacheName, + LocalCachedMapOptions.defaults().cacheSize(1).syncStrategy(SyncStrategy.INVALIDATE)); + RLocalCachedMap loadMap = redisson.getLocalCachedMap(cacheName, + LocalCachedMapOptions.defaults().cacheSize(1).syncStrategy(SyncStrategy.NONE)); + + loadMap.putAll(data); + clearMap.clearLocalCache(); + } +``` + +**Data partitioning** + +Map object with data partitioning support implements `org.redisson.api.RClusteredMap` which extends `java.util.concurrent.ConcurrentMap` interface. Read more details about data partitioning [here](data-partitioning.md). + +Code example: + +```java +RClusteredMap map = redisson.getClusteredMap("anyMap"); +// or +RClusteredMap map = redisson.getClusteredLocalCachedMapCache("anyMap", LocalCachedMapCacheOptions.defaults()); +// or +RClusteredMap map = redisson.getClusteredLocalCachedMap("anyMap", LocalCachedMapOptions.defaults()); +// or +RClusteredMap map = redisson.getClusteredMapCache("anyMap"); + +SomeObject prevObject = map.put("123", new SomeObject()); +SomeObject currentObject = map.putIfAbsent("323", new SomeObject()); +SomeObject obj = map.remove("123"); + +map.fastPut("321", new SomeObject()); +map.fastRemove("321"); +``` + +### Persistence + +Redisson allows to store Map data in external storage along with Redis or Valkey store. +Use cases: + +1. Redisson Map object as a cache between an application and external storage. +2. Increase durability of Redisson Map data and life-span of evicted entries. +3. Caching for databases, web services or any other data source. + +**Read-through strategy** + +If requested entry doesn't exist in the Redisson Map object +when it will be loaded using provided [MapLoader](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/map/MapLoader.html) object. Code example: + +```java + MapLoader mapLoader = new MapLoader() { + + @Override + public Iterable loadAllKeys() { + List list = new ArrayList(); + Statement statement = conn.createStatement(); + try { + ResultSet result = statement.executeQuery("SELECT id FROM student"); + while (result.next()) { + list.add(result.getString(1)); + } + } finally { + statement.close(); + } + + return list; + } + + @Override + public String load(String key) { + PreparedStatement preparedStatement = conn.prepareStatement("SELECT name FROM student where id = ?"); + try { + preparedStatement.setString(1, key); + ResultSet result = preparedStatement.executeQuery(); + if (result.next()) { + return result.getString(1); + } + return null; + } finally { + preparedStatement.close(); + } + } + }; +``` +Configuration example: +```java +MapOptions options = MapOptions.defaults() + .loader(mapLoader); + +MapCacheOptions mcoptions = MapCacheOptions.defaults() + .loader(mapLoader); + + +RMap map = redisson.getMap("test", options); +// or +RMapCache map = redisson.getMapCache("test", mcoptions); +// or with performance boost up to 45x times +RLocalCachedMap map = redisson.getLocalCachedMap("test", options); +// or with performance boost up to 45x times +RLocalCachedMapCache map = redisson.getLocalCachedMapCache("test", mcoptions); +``` + +**Write-through (synchronous) strategy** + +When the Map entry is being updated method won't return until +Redisson update it in an external storage using [MapWriter](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/map/MapWriter.html) object. Code example: + +```java + MapWriter mapWriter = new MapWriter() { + + @Override + public void write(Map map) { + PreparedStatement preparedStatement = conn.prepareStatement("INSERT INTO student (id, name) values (?, ?)"); + try { + for (Entry entry : map.entrySet()) { + preparedStatement.setString(1, entry.getKey()); + preparedStatement.setString(2, entry.getValue()); + preparedStatement.addBatch(); + } + preparedStatement.executeBatch(); + } finally { + preparedStatement.close(); + } + } + + @Override + public void delete(Collection keys) { + PreparedStatement preparedStatement = conn.prepareStatement("DELETE FROM student where id = ?"); + try { + for (String key : keys) { + preparedStatement.setString(1, key); + preparedStatement.addBatch(); + } + preparedStatement.executeBatch(); + } finally { + preparedStatement.close(); + } + } + }; +``` +Configuration example: +```java +MapOptions options = MapOptions.defaults() + .writer(mapWriter) + .writeMode(WriteMode.WRITE_THROUGH); + +MapCacheOptions mcoptions = MapCacheOptions.defaults() + .writer(mapWriter) + .writeMode(WriteMode.WRITE_THROUGH); + + +RMap map = redisson.getMap("test", options); +// or +RMapCache map = redisson.getMapCache("test", mcoptions); +// or with performance boost up to 45x times +RLocalCachedMap map = redisson.getLocalCachedMap("test", options); +// or with performance boost up to 45x times +RLocalCachedMapCache map = redisson.getLocalCachedMapCache("test", mcoptions); +``` + +**Write-behind (asynchronous) strategy** + +Updates of Map object are accumulated in batches and asynchronously written with defined delay to external storage through [MapWriter](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/map/MapWriter.html) object. +`writeBehindDelay` - delay of batched write or delete operation. Default value is 1000 milliseconds. +`writeBehindBatchSize` - size of batch. Each batch contains Map Entry write or delete commands. Default value is 50. + +Configuration example: + +```java +MapOptions options = MapOptions.defaults() + .writer(mapWriter) + .writeMode(WriteMode.WRITE_BEHIND) + .writeBehindDelay(5000) + .writeBehindBatchSize(100); + +MapCacheOptions mcoptions = MapCacheOptions.defaults() + .writer(mapWriter) + .writeMode(WriteMode.WRITE_BEHIND) + .writeBehindDelay(5000) + .writeBehindBatchSize(100); + + +RMap map = redisson.getMap("test", options); +// or +RMapCache map = redisson.getMapCache("test", mcoptions); +// or with performance boost up to 45x times +RLocalCachedMap map = redisson.getLocalCachedMap("test", options); +// or with performance boost up to 45x times +RLocalCachedMapCache map = redisson.getLocalCachedMapCache("test", mcoptions); +``` + +This feature available for `RMap`, `RMapCache`, `RLocalCachedMap` and `RLocalCachedMapCache` objects. + +Usage of `RLocalCachedMap` and `RLocalCachedMapCache` objects boost Redis or Valkey read-operations up to **45x times** and give almost instant speed for database, web service or any other data source. + +### Listeners + +Redisson allows to bind listeners per `RMap` object. + +`RMap` object allows to track follow events over the data. + +|Listener class name|Event description | +|:--:|:--:| +|org.redisson.api.listener.TrackingListener|Entry created/removed/updated after read operation| +|org.redisson.api.listener.MapPutListener|Entry created/updated| +|org.redisson.api.listener.MapRemoveListener|Entry removed| +|org.redisson.api.ExpiredObjectListener|`RMap` object expired| +|org.redisson.api.DeletedObjectListener|`RMap` object deleted| + +Usage examples: +```java +RMap map = redisson.getMap("anyMap"); + +int listenerId = map.addListener(new DeletedObjectListener() { + @Override + public void onDeleted(String name) { + // ... + } +}); + +int listenerId = map.addListener(new ExpiredObjectListener() { + @Override + public void onExpired(String name) { + // ... + } +}); + +int listenerId = map.addListener(new MapPutListener() { + @Override + public void onPut(String name) { + // ... + } +}); + +int listenerId = map.addListener(new MapRemoveListener() { + @Override + public void onRemove(String name) { + // ... + } +}); + +map.removeListener(listenerId); +``` + +`RMapCache` object allows to track additional events over the data. + +|Listener class name|Event description | +|:--:|:--:| +|org.redisson.api.map.event.EntryCreatedListener|Entry created| +|org.redisson.api.map.event.EntryExpiredListener|Entry expired| +|org.redisson.api.map.event.EntryRemovedListener|Entry removed| +|org.redisson.api.map.event.EntryUpdatedListener|Entry updated| + +Usage examples: + +```java +RMapCache map = redisson.getMapCache("anyMap"); +// or +RMapCache map = redisson.getLocalCachedMapCache(LocalCachedMapCacheOptions.name("anyMap")); +// or +RMapCache map = redisson.getClusteredLocalCachedMapCache("anyMap", LocalCachedMapOptions.defaults()); +// or +RMapCache map = redisson.getClusteredMapCache("anyMap"); + + +int listenerId = map.addListener(new EntryUpdatedListener() { + @Override + public void onUpdated(EntryEvent event) { + event.getKey(); // key + event.getValue() // new value + event.getOldValue() // old value + // ... + } +}); + +int listenerId = map.addListener(new EntryCreatedListener() { + @Override + public void onCreated(EntryEvent event) { + event.getKey(); // key + event.getValue() // value + // ... + } +}); + +int listenerId = map.addListener(new EntryExpiredListener() { + @Override + public void onExpired(EntryEvent event) { + event.getKey(); // key + event.getValue() // value + // ... + } +}); + +int listenerId = map.addListener(new EntryRemovedListener() { + @Override + public void onRemoved(EntryEvent event) { + event.getKey(); // key + event.getValue() // value + // ... + } +}); + +map.removeListener(listenerId); +``` + +### LRU/LFU bounded Map +Map object which implements `RMapCache` interface could be bounded using [Least Recently Used (LRU)](https://en.wikipedia.org/wiki/Cache_replacement_policies#LRU) or [Least Frequently Used (LFU)](https://en.wikipedia.org/wiki/Least_frequently_used) order. Bounded Map allows to store map entries within defined limit and retire entries in defined order. + +Use cases: limited Redis or Valkey memory. + +```java +RMapCache map = redisson.getMapCache("anyMap"); +// or +RMapCache map = redisson.getMapCache("anyMap", MapCacheOptions.defaults()); +// or +RMapCache map = redisson.getLocalCachedMapCache("anyMap", LocalCachedMapOptions.defaults()); +// or +RMapCache map = redisson.getClusteredLocalCachedMapCache("anyMap", LocalCachedMapOptions.defaults()); +// or +RMapCache map = redisson.getClusteredMapCache("anyMap"); +// or +RMapCache map = redisson.getClusteredMapCache("anyMap", MapCacheOptions.defaults()); + + +// tries to set limit map to 10 entries using LRU eviction algorithm +map.trySetMaxSize(10); +// ... using LFU eviction algorithm +map.trySetMaxSize(10, EvictionMode.LFU); + +// set or change limit map to 10 entries using LRU eviction algorithm +map.setMaxSize(10); +// ... using LFU eviction algorithm +map.setMaxSize(10, EvictionMode.LFU); + +map.put("1", "2"); +map.put("3", "3", 1, TimeUnit.SECONDS); +``` + +## Multimap +Redis or Valkey based [Multimap](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMultimap.html) for Java allows to bind multiple values per key. This object is thread-safe. Keys amount limited to `4 294 967 295` elements. Redis or Valkey uses serialized state to check key uniqueness instead of key's `hashCode()`/`equals()` methods. + +It has [Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMultimapAsync.html), [Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMultimapReactive.html) and [RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMultimapRx.html) interfaces. + +### Set based Multimap +Set based Multimap doesn't allow duplications for values per key. +```java +RSetMultimap map = redisson.getSetMultimap("myMultimap"); +map.put(new SimpleKey("0"), new SimpleValue("1")); +map.put(new SimpleKey("0"), new SimpleValue("2")); +map.put(new SimpleKey("3"), new SimpleValue("4")); + +Set allValues = map.get(new SimpleKey("0")); + +List newValues = Arrays.asList(new SimpleValue("7"), new SimpleValue("6"), new SimpleValue("5")); +Set oldValues = map.replaceValues(new SimpleKey("0"), newValues); + +Set removedValues = map.removeAll(new SimpleKey("0")); +``` +### List based Multimap +List based [Multimap](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RMultimap.html) object for Java stores entries in insertion order and allows duplicates for values mapped to key. +```java +RListMultimap map = redisson.getListMultimap("test1"); +map.put(new SimpleKey("0"), new SimpleValue("1")); +map.put(new SimpleKey("0"), new SimpleValue("2")); +map.put(new SimpleKey("0"), new SimpleValue("1")); +map.put(new SimpleKey("3"), new SimpleValue("4")); + +List allValues = map.get(new SimpleKey("0")); + +Collection newValues = Arrays.asList(new SimpleValue("7"), new SimpleValue("6"), new SimpleValue("5")); +List oldValues = map.replaceValues(new SimpleKey("0"), newValues); + +List removedValues = map.removeAll(new SimpleKey("0")); +``` +### Multimap eviction +Multimap distributed object for Java with eviction support implemented by separated MultimapCache object. There are [RSetMultimapCache](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RSetMultimapCache.html) and [RListMultimapCache](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RListMultimapCache.html) objects for Set and List based Multimaps respectively. + +Eviction task is started once per unique object name at the moment of getting Multimap instance. If instance isn't used and has expired entries it should be get again to start the eviction process. This leads to extra Redis or Valkey calls and eviction task per unique map object name. + +Entries are cleaned time to time by `org.redisson.eviction.EvictionScheduler`. By default, it removes 100 expired entries at a time. This can be changed through [cleanUpKeysAmount](../configuration.md) setting. Task launch time tuned automatically and depends on expired entries amount deleted in previous time and varies between 5 second to 30 minutes by default. This time interval can be changed through [minCleanUpDelay](../configuration.md) and [maxCleanUpDelay](../configuration.md). For example, if clean task deletes 100 entries each time it will be executed every 5 seconds (minimum execution delay). But if current expired entries amount is lower than previous one then execution delay will be increased by 1.5 times and decreased otherwise. + +RSetMultimapCache example: +```java +RSetMultimapCache multimap = redisson.getSetMultimapCache("myMultimap"); +multimap.put("1", "a"); +multimap.put("1", "b"); +multimap.put("1", "c"); + +multimap.put("2", "e"); +multimap.put("2", "f"); + +multimap.expireKey("2", 10, TimeUnit.MINUTES); + +// if object is not used anymore +multimap.destroy(); +``` + +## JSON Store + +_This feature is available only in [Redisson PRO](https://redisson.pro) edition._ + +Distributed Java implementation of Redis or Valkey based Key Value store for JSON objects. This object is thread-safe. Allows to store JSON value mapped by key. Operations can be executed per key or group of keys. Value is stored/retrieved using `JSON.*` commands. Both key and value are POJO objects. + +Allows to define `time to live` parameter per entry. Doesn't use an entry eviction task, entries are cleaned on Redis or Valkey side. + +Implements **Async**, **Reactive** and **RxJava3** interfaces. + +Data write code example: +```java +RJsonStore store = redisson.getJsonStore("test", StringCodec.INSTANCE, new JacksonCodec(MyObject.class)); + +MyObject t1 = new MyObject(); +t1.setName("name1"); +MyObject t2 = new MyObject(); +t2.setName("name2"); + +Map entries = new HashMap<>(); +entries.put("1", t1); +entries.put("2", t2); + +// multiple entries at once +store.set(entries); + +// or set entry per call +store.set("1", t1); +store.set("2", t2); + +// with ttl +store.set("1", t1, Duration.ofSeconds(100)); + +// set if not set previously +store.setIfAbsent("1", t1); + +// set if entry already exists +store.setIfExists("1", t1); +``` + +Data read code example: +```java +RJsonStore store = redisson.getJsonStore("test", StringCodec.INSTANCE, new JacksonCodec(MyObject.class)); + +// multiple entries at once +Map entries = store.get(Set.of("1", "2")); + +// or read entry per call +MyObject value1 = store.get("1"); +MyObject value2 = store.get("2"); +``` + +Data deletion code example: +```java +RJsonStore store = redisson.getJsonStore("test", StringCodec.INSTANCE, new JacksonCodec(MyObject.class)); + +// multiple entries at once +long deleted = store.delete(Set.of("1", "2")); + +// or delete entry per call +boolean status = store.delete("1"); +boolean status = store.delete("2"); +``` + +For data searching index prefix should be defined in `:` format. For example for object name "test" prefix is "test:". + +Data search code example: +```java +RSearch s = redisson.getSearch(); +s.createIndex("idx", IndexOptions.defaults() + .on(IndexType.JSON) + .prefix(Arrays.asList("test:")), + FieldIndex.text("name")); + +RJsonStore store = redisson.getJsonStore("test", StringCodec.INSTANCE, new JacksonCodec(MyObject.class)); + +MyObject t1 = new MyObject(); +t1.setName("name1"); +MyObject t2 = new MyObject(); +t2.setName("name2"); + +Map entries = new HashMap<>(); +entries.put("1", t1); +entries.put("2", t2); +store.set(entries); + +// search +SearchResult r = s.search("idx", "*", QueryOptions.defaults() + .returnAttributes(new ReturnAttribute("name"))); + +// aggregation +AggregationResult ar = s.aggregate("idx", "*", AggregationOptions.defaults() + .withCursor().load("name")); +``` + +Keys access code examples: +```java +RJsonStore store = redisson.getJsonStore("test", StringCodec.INSTANCE, new JacksonCodec(MyObject.class)); + +// iterate keys +Set keys = store.keySet(); + +// read all keys at once +Set keys = store.readAllKeySet(); +``` + +### Local Cache + +Redisson provides [JSON Store](#json-store) implementation with local cache. + +**local cache** - so called near cache used to speed up read operations and avoid network roundtrips. It caches JSON Store entries on Redisson side and executes read operations up to **45x faster** in comparison with regular implementation. Local cached instances with the same name are connected to the same pub/sub channel. This channel is used for exchanging of update/invalidate events between all instances. Local cache store doesn't use `hashCode()`/`equals()` methods of key object, instead it uses hash of serialized state. It's recommended to use each local cached instance as a singleton per unique name since it has own state for local cache. + +It's recommended to use a single instance of `RLocalCachedJsonStore` instance per name for each Redisson client instance. Same `LocalCachedJsonStoreOptions` object should be used across all instances with the same name. + +Follow options can be supplied during object creation: +```java + LocalCachedJsonStoreOptions options = LocalCachedJsonStoreOptions.name("object_name_example") + + // Defines codec used for key + .keyCodec(codec) + + // Defines codec used for JSON value + .valueCodec(codec) + + // Defines whether to store a cache miss into the local cache. + // Default value is false. + .storeCacheMiss(false); + + // Defines store mode of cache data. + // Follow options are available: + // LOCALCACHE - store data in local cache only and use Redis or Valkey only for data update/invalidation. + // LOCALCACHE_REDIS - store data in both Redis or Valkey and local cache. + .storeMode(StoreMode.LOCALCACHE_REDIS) + + // Defines Cache provider used as local cache store. + // Follow options are available: + // REDISSON - uses Redisson own implementation + // CAFFEINE - uses Caffeine implementation + .cacheProvider(CacheProvider.REDISSON) + + // Defines local cache eviction policy. + // Follow options are available: + // LFU - Counts how often an item was requested. Those that are used least often are discarded first. + // LRU - Discards the least recently used items first + // SOFT - Uses soft references, entries are removed by GC + // WEAK - Uses weak references, entries are removed by GC + // NONE - No eviction + .evictionPolicy(EvictionPolicy.NONE) + + // If cache size is 0 then local cache is unbounded. + .cacheSize(1000) + + // Defines strategy for load missed local cache updates after connection failure. + // + // Follow reconnection strategies are available: + // CLEAR - Clear local cache if map instance has been disconnected for a while. + // NONE - Default. No reconnection handling + .reconnectionStrategy(ReconnectionStrategy.NONE) + + // Defines local cache synchronization strategy. + // + // Follow sync strategies are available: + // INVALIDATE - Default. Invalidate cache entry across all RLocalCachedJsonStore instances on map entry change + // UPDATE - Insert/update cache entry across all RLocalCachedJsonStore instances on map entry change + // NONE - No synchronizations on map changes + .syncStrategy(SyncStrategy.INVALIDATE) + + // time to live for each entry in local cache + .timeToLive(Duration.ofSeconds(10)) + + // max idle time for each entry in local cache + .maxIdle(Duration.ofSeconds(10)); + + // Defines how to listen expired event sent by Redis or Valkey upon this instance deletion + // + // Follow expiration policies are available: + // DONT_SUBSCRIBE - Don't subscribe on expire event + // SUBSCRIBE_WITH_KEYEVENT_PATTERN - Subscribe on expire event using __keyevent@*:expired pattern + // SUBSCRIBE_WITH_KEYSPACE_CHANNEL - Subscribe on expire event using __keyspace@N__:name channel + .expirationEventPolicy(ExpirationEventPolicy.SUBSCRIBE_WITH_KEYEVENT_PATTERN) +``` + +Data write code example: +```java +LocalCachedJsonStoreOptions ops = LocalCachedJsonStoreOptions.name("test") + .keyCodec(StringCodec.INSTANCE) + .valueCodec(new JacksonCodec<>(MyObject.class)); +RLocalCachedJsonStore store = redisson.getLocalCachedJsonStore(ops); + +MyObject t1 = new MyObject(); +t1.setName("name1"); +MyObject t2 = new MyObject(); +t2.setName("name2"); + +Map entries = new HashMap<>(); +entries.put("1", t1); +entries.put("2", t2); + +Map entries = new HashMap<>(); +entries.put("1", t1); +entries.put("2", t2); + +// multiple entries at once +store.set(entries); + +// or set entry per call +store.set("1", t1); +store.set("2", t2); + +// with ttl +store.set("1", t1, Duration.ofSeconds(100)); + +// set if not set previously +store.setIfAbsent("1", t1); + +// set if entry already exists +store.setIfExists("1", t1); +``` + +Data read code example: +```java +LocalCachedJsonStoreOptions ops = LocalCachedJsonStoreOptions.name("test") + .keyCodec(StringCodec.INSTANCE) + .valueCodec(new JacksonCodec<>(MyObject.class)); +RLocalCachedJsonStore store = redisson.getLocalCachedJsonStore(ops); + +// multiple entries at once +Map entries = store.get(Set.of("1", "2")); + +// or read entry per call +MyObject value1 = store.get("1"); +MyObject value2 = store.get("2"); +``` + +Data deletion code example: +```java +LocalCachedJsonStoreOptions ops = LocalCachedJsonStoreOptions.name("test") + .keyCodec(StringCodec.INSTANCE) + .valueCodec(new JacksonCodec<>(MyObject.class)); +RLocalCachedJsonStore store = redisson.getLocalCachedJsonStore(ops); + +// multiple entries at once +long deleted = store.delete(Set.of("1", "2")); + +// or delete entry per call +boolean status = store.delete("1"); +boolean status = store.delete("2"); +``` + +For data searching index prefix should be defined in `:` format. For example for object name "test" prefix is "test:". + +Data search code example: +```java +RSearch s = redisson.getSearch(); +s.createIndex("idx", IndexOptions.defaults() + .on(IndexType.JSON) + .prefix(Arrays.asList("test:")), + FieldIndex.text("name")); + +LocalCachedJsonStoreOptions ops = LocalCachedJsonStoreOptions.name("test") + .keyCodec(StringCodec.INSTANCE) + .valueCodec(new JacksonCodec<>(MyObject.class)); +RLocalCachedJsonStore store = redisson.getLocalCachedJsonStore(ops); + +MyObject t1 = new MyObject(); +t1.setName("name1"); +MyObject t2 = new MyObject(); +t2.setName("name2"); + +Map entries = new HashMap<>(); +entries.put("1", t1); +entries.put("2", t2); +store.set(entries); + +// search +SearchResult r = s.search("idx", "*", QueryOptions.defaults() + .returnAttributes(new ReturnAttribute("name"))); + +// aggregation +AggregationResult ar = s.aggregate("idx", "*", AggregationOptions.defaults() + .withCursor().load("name")); +``` + +Keys access code examples: +```java +LocalCachedJsonStoreOptions ops = LocalCachedJsonStoreOptions.name("test") + .keyCodec(StringCodec.INSTANCE) + .valueCodec(new JacksonCodec<>(MyObject.class)); +RLocalCachedJsonStore store = redisson.getLocalCachedJsonStore(ops); + +// iterate keys +Set keys = store.keySet(); + +// read all keys at once +Set keys = store.readAllKeySet(); +``` + +## Set +Redis or Valkey based [Set](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RSet.html) object for Java implements [Set](https://docs.oracle.com/javase/8/docs/api/java/util/Set.html) interface. This object is thread-safe. Keeps elements uniqueness via element state comparison. Set size limited to `4 294 967 295` elements. Redis or Valkey uses serialized state to check value uniqueness instead of value's `hashCode()`/`equals()` methods. + +It has [Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RSetAsync.html), [Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RSetReactive.html) and [RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RSetRx.html) interfaces. + +```java +RSet set = redisson.getSet("anySet"); +set.add(new SomeObject()); +set.remove(new SomeObject()); +``` +RSet object allows to bind a [Lock](locks-and-synchronizers.md/#lock)/[ReadWriteLock](locks-and-synchronizers.md/#readwritelock)/[Semaphore](locks-and-synchronizers.md/#semaphore)/[CountDownLatch](locks-and-synchronizers.md/#countdownlatch) object per value: +``` +RSet set = redisson.getSet("anySet"); +MyObject value = new MyObject(); +RLock lock = map.getLock(value); +lock.lock(); +try { + // process value ... +} finally { + lock.unlock(); +} +``` + +### Eviction and data partitioning + +Redisson provides various Set structure implementations with a few important features: + +**data partitioning** - although any Set object is cluster compatible its content isn't scaled/partitioned across multiple master nodes in cluster. Data partitioning allows to scale available memory, read/write operations and entry eviction process for individual Set instance in cluster. + +**entry eviction** - allows to define `time to live` parameter per SetCache entry. Redis or Valkey set structure doesn't support eviction thus it's done on Redisson side through a custom scheduled task which removes expired entries using Lua script. Eviction task is started once per unique object name at the moment of getting SetCache instance. If instance isn't used and has expired entries it should be get again to start the eviction process. This leads to extra Redis or Valkey calls and eviction task per unique SetCache object name. + +Entries are cleaned time to time by `org.redisson.eviction.EvictionScheduler`. By default, it removes 100 expired entries at a time. This can be changed through [cleanUpKeysAmount](../configuration.md) setting. Task launch time tuned automatically and depends on expired entries amount deleted in previous time and varies between 5 second to 30 minutes by default. This time interval can be changed through [minCleanUpDelay](../configuration.md) and [maxCleanUpDelay](../configuration.md). For example, if clean task deletes 100 entries each time it will be executed every 5 seconds (minimum execution delay). But if current expired entries amount is lower than previous one then execution delay will be increased by 1.5 times and decreased otherwise. + +**advanced entry eviction** - improved version of the **entry eviction** process. Doesn't use an entry eviction task. + +**Eviction** + +Set object with eviction support implements [RSetCache](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RSetCache.html), [Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RSetCacheAsync.html), [Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RSetCacheReactive.html) and [RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RSetCacheRx.html) interfaces. + +Code example: +```java +RSetCache set = redisson.getSetCache("mySet"); +// or +RMapCache set = redisson.getClusteredSetCache("mySet"); + +// ttl = 10 minutes, +set.add(new SomeObject(), 10, TimeUnit.MINUTES); + +// if object is not used anymore +map.destroy(); +``` + +**Data partitioning** +Map object with data partitioning support implements `org.redisson.api.RClusteredSet`. Read more details about data partitioning [here](data-partitioning.md). + +Code example: + +```java +RClusteredSet set = redisson.getClusteredSet("mySet"); +// or +RClusteredSet set = redisson.getClusteredSetCache("mySet"); + +// ttl = 10 minutes, +map.add(new SomeObject(), 10, TimeUnit.MINUTES); +``` + +Below is the list of all available Set implementations: + +|RedissonClient
method name | Data
partitioning | Entry
eviction | Advanced
entry eviction | Ultra-fast
read/write | +| ------------- | :----------:| :----------:| :----------:| :----------:| +|getSet()
open-source version | ❌ | ❌ | ❌ | ❌ | +|getSetCache()
open-source version | ❌ | ✔️ | ❌ | ❌ | +|getSet()
[Redisson PRO](https://redisson.pro) version | ❌ | ❌ | ❌ | ✔️ | +|getSetCache()
[Redisson PRO](https://redisson.pro) version | ❌ | ✔️ | ❌ | ✔️ | +|getSetCacheV2()
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ❌ | ✔️ | ✔️ | +|getClusteredSet()
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ❌ | ❌ | ✔️ | +|getClusteredSetCache()
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ✔️ | ❌ | ✔️ | + +### Listeners + +Redisson allows to bind listeners per `RSet` object. + +|Listener class name|Event description | +|:--:|:--:| +|org.redisson.api.listener.TrackingListener|Element added/removed/updated after read operation| +|org.redisson.api.ExpiredObjectListener|`RSet` object expired| +|org.redisson.api.DeletedObjectListener|`RSet` object deleted| +|org.redisson.api.listener.SetAddListener|Element added| +|org.redisson.api.listener.SetRemoveListener|Element removed| +|org.redisson.api.listener.SetRemoveRandomListener|Element randomly removed| + +Usage example: + +```java +RSet set = redisson.getSet("anySet"); + +int listenerId = set.addListener(new DeletedObjectListener() { + @Override + public void onDeleted(String name) { + // ... + } +}); + +// ... + +set.removeListener(listenerId); +``` + +## SortedSet +Redis or Valkey based distributed [SortedSet](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RSortedSet.html) for Java implements [SortedSet](https://docs.oracle.com/javase/8/docs/api/java/util/SortedSet.html) interface. This object is thread-safe. It uses comparator to sort elements and keep uniqueness. For String data type it's recommended to use [LexSortedSet](#lexsortedset) object due to performance gain. +```java +RSortedSet set = redisson.getSortedSet("anySet"); +set.trySetComparator(new MyComparator()); // set object comparator +set.add(3); +set.add(1); +set.add(2); + +set.removeAsync(0); +set.addAsync(5); +``` +## ScoredSortedSet +Redis or Valkey based distributed [ScoredSortedSet](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RScoredSortedSet.html) object. Sorts elements by score defined during element insertion. Keeps elements uniqueness via element state comparison. + +It has [Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RScoredSortedSetAsync.html), [Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RScoredSortedSetReactive.html) and [RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RScoredSortedSetRx.html) interfaces. Set size is limited to `4 294 967 295` elements. +```java +RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + +set.add(0.13, new SomeObject(a, b)); +set.addAsync(0.251, new SomeObject(c, d)); +set.add(0.302, new SomeObject(g, d)); + +set.pollFirst(); +set.pollLast(); + +int index = set.rank(new SomeObject(g, d)); // get element index +Double score = set.getScore(new SomeObject(g, d)); // get element score +``` + +### Data partitioning + +Although 'RScoredSortedSet' object is cluster compatible its content isn't scaled across multiple master nodes. `RScoredSortedSet` data partitioning available only in cluster mode and implemented by separate `RClusteredScoredSortedSet` object. Size is limited by whole Cluster memory. More about partitioning [here](data-partitioning.md). + +Below is the list of all available `RScoredSortedSet` implementations: + +|RedissonClient
method name | Data partitioning
support | Ultra-fast read/write | +| ------------- | :----------:| :----------:| +|getScoredSortedSet()
open-source version | ❌ | ❌ | +|getScoredSortedSet()
[Redisson PRO](https://redisson.pro) version | ❌ | ✔️ | +|getClusteredScoredSortedSet()
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ✔️ | + +Code example: +```java +RClusteredScoredSortedSet set = redisson.getClusteredScoredSortedSet("simpleBitset"); +set.add(1.1, "v1"); +set.add(1.2, "v2"); +set.add(1.3, "v3"); + +ScoredEntry s = set.firstEntry(); +ScoredEntry e = set.pollFirstEntry(); +``` + +### Listeners + +Redisson allows to bind listeners per `RScoredSortedSet` object. + +|Listener class name|Event description | +|:--:|:--:| +|org.redisson.api.listener.TrackingListener|Element created/removed/updated after read operation| +|org.redisson.api.listener.ScoredSortedSetAddListener|Element created/updated| +|org.redisson.api.listener.ScoredSortedSetRemoveListener|Element removed| +|org.redisson.api.ExpiredObjectListener|`RScoredSortedSet` object expired| +|org.redisson.api.DeletedObjectListener|`RScoredSortedSet` object deleted| + +Usage example: + +```java +RScoredSortedSet set = redisson.getScoredSortedSet("anySet"); + +int listenerId = set.addListener(new DeletedObjectListener() { + @Override + public void onDeleted(String name) { + // ... + } +}); + +// ... + +set.removeListener(listenerId); +``` + +## LexSortedSet +Redis or Valkey based distributed [Set](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLexSortedSet.html) object for Java allows String objects only and implements `java.util.Set` interface. It keeps elements in lexicographical order and maintain elements uniqueness via element state comparison. + +It has [Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLexSortedSetAsync.html), [Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLexSortedSetReactive.html) and [RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLexSortedSetRx.html) interfaces. +```java +RLexSortedSet set = redisson.getLexSortedSet("simple"); +set.add("d"); +set.addAsync("e"); +set.add("f"); + +set.rangeTail("d", false); +set.countHead("e"); +set.range("d", true, "z", false); +``` + +### Listeners + +Redisson allows to bind listeners per `RLexSortedSet` object. + +|Listener class name|Event description | +|:--:|:--:| +|org.redisson.api.listener.TrackingListener|Element created/removed/updated after read operation| +|org.redisson.api.listener.ScoredSortedSetAddListener|Element created/updated| +|org.redisson.api.listener.ScoredSortedSetRemoveListener|Element removed| +|org.redisson.api.ExpiredObjectListener|`RScoredSortedSet` object expired| +|org.redisson.api.DeletedObjectListener|`RScoredSortedSet` object deleted| + +Usage example: + +```java +RLexSortedSet set = redisson.getLexSortedSet("anySet"); + +int listenerId = set.addListener(new DeletedObjectListener() { + @Override + public void onDeleted(String name) { + // ... + } +}); + +// ... + +set.removeListener(listenerId); +``` + +## List +Redis or Valkey based distributed [List](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RList.html) object for Java implements `java.util.List` interface. It keeps elements in insertion order. + +It has [Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RListAsync.html), [Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RListReactive.html) and [RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RListRx.html) interfaces. List size is limited to `4 294 967 295` elements. +```java +RList list = redisson.getList("anyList"); +list.add(new SomeObject()); +list.get(0); +list.remove(new SomeObject()); +``` + +### Listeners + +Redisson allows to bind listeners per `RList` object. + +|Listener class name|Event description | +|:--:|:--:| +|org.redisson.api.listener.TrackingListener|Element created/removed/updated after read operation| +|org.redisson.api.listener.ListAddListener|Element created| +|org.redisson.api.listener.ListInsertListener|Element inserted| +|org.redisson.api.listener.ListSetListener|Element set/updated| +|org.redisson.api.listener.ListRemoveListener|Element removed| +|org.redisson.api.listener.ListTrimListener|List trimmed| +|org.redisson.api.ExpiredObjectListener|`RList` object expired| +|org.redisson.api.DeletedObjectListener|`RList` object deleted| + +Usage example: + +```java +RList list = redisson.getList("anyList"); + +int listenerId = list.addListener(new DeletedObjectListener() { + @Override + public void onDeleted(String name) { + // ... + } +}); + +// ... + +list.removeListener(listenerId); +``` + + +## Queue +Redis or Valkey based distributed unbounded [Queue](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RQueue.html) object for Java implements [java.util.Queue](https://docs.oracle.com/javase/8/docs/api/java/util/Queue.html) interface. This object is thread-safe. + +It has [Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RQueueAsync.html), [Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RQueueReactive.html) and [RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RQueueRx.html) interfaces. + +```java +RQueue queue = redisson.getQueue("anyQueue"); +queue.add(new SomeObject()); +SomeObject obj = queue.peek(); +SomeObject someObj = queue.poll(); +``` + +### Listeners + +Redisson allows to bind listeners per `RQueue` object. + +|Listener class name|Event description | +|:--:|:--:| +|org.redisson.api.listener.TrackingListener|Element created/removed/updated after read operation| +|org.redisson.api.listener.ListAddListener|Element created| +|org.redisson.api.listener.ListRemoveListener|Element removed| +|org.redisson.api.ExpiredObjectListener|`RQueue` object expired| +|org.redisson.api.DeletedObjectListener|`RQueue` object deleted| + +Usage example: + +```java +RQueue queue = redisson.getQueue("anyList"); + +int listenerId = queue.addListener(new DeletedObjectListener() { + @Override + public void onDeleted(String name) { + // ... + } +}); + +// ... + +queue.removeListener(listenerId); +``` + +## Deque +Redis or Valkey based distributed unbounded [Deque](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RDeque.html) object for Java implements `java.util.Deque` interface. This object is thread-safe. + +It has [Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RDequeAsync.html), [Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RDequeReactive.html) and [RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RDequeRx.html) interfaces. +```java +RDeque queue = redisson.getDeque("anyDeque"); +queue.addFirst(new SomeObject()); +queue.addLast(new SomeObject()); +SomeObject obj = queue.removeFirst(); +SomeObject someObj = queue.removeLast(); +``` + +### Listeners + +Redisson allows to bind listeners per `RDeque` object. + +|Listener class name|Event description | +|:--:|:--:| +|org.redisson.api.listener.TrackingListener|Element created/removed/updated after read operation| +|org.redisson.api.listener.ListAddListener|Element created| +|org.redisson.api.listener.ListRemoveListener|Element removed| +|org.redisson.api.ExpiredObjectListener|`RDeque` object expired| +|org.redisson.api.DeletedObjectListener|`RDeque` object deleted| + +Usage example: + +```java +RDeque deque = redisson.getDeque("anyList"); + +int listenerId = deque.addListener(new DeletedObjectListener() { + @Override + public void onDeleted(String name) { + // ... + } +}); + +// ... + +deque.removeListener(listenerId); +``` + +## Blocking Queue +Redis or Valkey based distributed unbounded [BlockingQueue](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBlockingQueue.html) object for Java implements `java.util.concurrent.BlockingQueue` interface. This object is thread-safe. + +It has [Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBlockingQueueAsync.html), [Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBlockingQueueReactive.html) and [RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBlockingQueueRx.html) interfaces. + +```java +RBlockingQueue queue = redisson.getBlockingQueue("anyQueue"); + +queue.offer(new SomeObject()); + +SomeObject obj = queue.peek(); +SomeObject obj = queue.poll(); +SomeObject obj = queue.poll(10, TimeUnit.MINUTES); +``` +`poll`, `pollFromAny`, `pollLastAndOfferFirstTo` and `take` methods are resubscribed automatically during re-connection to server or failover. + +## Bounded Blocking Queue +Redis or Valkey based distributed [BoundedBlockingQueue](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBoundedBlockingQueue.html) for Java implements `java.util.concurrent.BlockingQueue` interface. BoundedBlockingQueue size limited to `4 294 967 295` elements. This object is thread-safe. + +Queue capacity should be defined once by `trySetCapacity()` method before the usage: +```java +RBoundedBlockingQueue queue = redisson.getBoundedBlockingQueue("anyQueue"); +// returns `true` if capacity set successfully and `false` if it already set. +queue.trySetCapacity(2); + +queue.offer(new SomeObject(1)); +queue.offer(new SomeObject(2)); +// will be blocked until free space available in queue +queue.put(new SomeObject()); + +SomeObject obj = queue.peek(); +SomeObject someObj = queue.poll(); +SomeObject ob = queue.poll(10, TimeUnit.MINUTES); +``` + +`poll`, `pollFromAny`, `pollLastAndOfferFirstTo` and `take` methods will be resubscribed automatically during reconnection to server or failover. + +## Blocking Deque +Java implementation of Redis or Valkey based [BlockingDeque](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBlockingDeque.html) implements `java.util.concurrent.BlockingDeque` interface. This object is thread-safe. + +It has [Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBlockingDequeAsync.html), [Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBlockingDequeReactive.html) and [RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBlockingDequeRx.html) interfaces. + +```java +RBlockingDeque deque = redisson.getBlockingDeque("anyDeque"); +deque.putFirst(1); +deque.putLast(2); +Integer firstValue = queue.takeFirst(); +Integer lastValue = queue.takeLast(); +Integer firstValue = queue.pollFirst(10, TimeUnit.MINUTES); +Integer lastValue = queue.pollLast(3, TimeUnit.MINUTES); +``` +`poll`, `pollFromAny`, `pollLastAndOfferFirstTo` and `take` methods are resubscribed automatically during re-connection to server or failover. + +## Delayed Queue +Redis or Valkey based [DelayedQueue](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RDelayedQueue.html) object for Java allows to transfer each element to destination queue with specified delay. Destination queue could be any queue implemented `RQueue` interface. This object is thread-safe. + +Could be useful for exponential backoff strategy used for message delivery to consumer. If application is restarted, an instance of delayed queue should created in order for the pending items to be added to the destination queue. + +```java +RBlockingQueue distinationQueue = ... +RDelayedQueue delayedQueue = getDelayedQueue(distinationQueue); +// move object to distinationQueue in 10 seconds +delayedQueue.offer("msg1", 10, TimeUnit.SECONDS); +// move object to distinationQueue in 1 minutes +delayedQueue.offer("msg2", 1, TimeUnit.MINUTES); + + +// msg1 will appear in 10 seconds +distinationQueue.poll(15, TimeUnit.SECONDS); + +// msg2 will appear in 2 seconds +distinationQueue.poll(2, TimeUnit.SECONDS); + +``` + +Object should be destroyed if it not used anymore, but it's not necessary to call destroy method if Redisson goes shutdown. +```java +RDelayedQueue delayedQueue = ... +delayedQueue.destroy(); +``` + +## Priority Queue +Java implementation of Redis or Valkey based [PriorityQueue](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RPriorityQueue.html) implements [java.util.Queue](https://docs.oracle.com/javase/8/docs/api/java/util/Queue.html) interface. Elements are ordered according to natural order of [Comparable](https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html) interface or defined [Comparator](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html). This object is thread-safe. + +Use `trySetComparator()` method to define own [Comparator](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html). + +Code example: +```java +public class Entry implements Comparable, Serializable { + + private String key; + private Integer value; + + public Entry(String key, Integer value) { + this.key = key; + this.value = value; + } + + @Override + public int compareTo(Entry o) { + return key.compareTo(o.key); + } + +} + +RPriorityQueue queue = redisson.getPriorityQueue("anyQueue"); +queue.add(new Entry("b", 1)); +queue.add(new Entry("c", 1)); +queue.add(new Entry("a", 1)); + +// Entry [a:1] +Entry e = queue.poll(); +// Entry [b:1] +Entry e = queue.poll(); +// Entry [c:1] +Entry e = queue.poll(); +``` + +## Priority Deque +Java implementation of Redis or Valkey based [PriorityDeque](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RPriorityDeque.html) implements [java.util.Deque](https://docs.oracle.com/javase/8/docs/api/java/util/Deque.html) interface. Elements are ordered according to natural order of [java.lang.Comparable](https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html) interface or defined [java.util.Comparator](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html). This object is thread-safe. + +Use `trySetComparator()` method to define own [Comparator](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html). + +Code example: +```java +public class Entry implements Comparable, Serializable { + + private String key; + private Integer value; + + public Entry(String key, Integer value) { + this.key = key; + this.value = value; + } + + @Override + public int compareTo(Entry o) { + return key.compareTo(o.key); + } + +} + +RPriorityDeque queue = redisson.getPriorityDeque("anyQueue"); +queue.add(new Entry("b", 1)); +queue.add(new Entry("c", 1)); +queue.add(new Entry("a", 1)); + +// Entry [a:1] +Entry e = queue.pollFirst(); +// Entry [c:1] +Entry e = queue.pollLast(); +``` + +## Priority Blocking Queue +Java implementation of Redis or Valkey based [PriorityBlockingQueue](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RPriorityBlockingQueue.html) similar to JDK [java.util.concurrent.PriorityBlockingQueue](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/PriorityBlockingQueue.html) object. Elements are ordered according to natural order of [java.lang.Comparable](https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html) interface or defined [java.util.Comparator](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html). This object is thread-safe. + +Use `trySetComparator()` method to define own [java.util.Comparator](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html). + +`poll`, `pollLastAndOfferFirstTo` and `take` methods are resubscribed automatically during re-connection to a server or failover. + +Code example: +```java +public class Entry implements Comparable, Serializable { + + private String key; + private Integer value; + + public Entry(String key, Integer value) { + this.key = key; + this.value = value; + } + + @Override + public int compareTo(Entry o) { + return key.compareTo(o.key); + } + +} + +RPriorityBlockingQueue queue = redisson.getPriorityBlockingQueue("anyQueue"); +queue.add(new Entry("b", 1)); +queue.add(new Entry("c", 1)); +queue.add(new Entry("a", 1)); + +// Entry [a:1] +Entry e = queue.take(); +``` + +## Priority Blocking Deque +Java implementation of Redis or Valkey based [PriorityBlockingDeque](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RPriorityBlockingDeque.html) implements [java.util.concurrent.BlockingDeque](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/BlockingDeque.html) interface. Elements are ordered according to natural order of [java.lang.Comparable](https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html) interface or defined [java.util.Comparator](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html). This object is thread-safe. + +Use `trySetComparator()` method to define own [java.util.Comparator](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html). + +`poll`, `pollLastAndOfferFirstTo`, `take` methods are resubscribed automatically during re-connection to Redis or Valkey server or failover. + +Code example: +```java +public class Entry implements Comparable, Serializable { + + private String key; + private Integer value; + + public Entry(String key, Integer value) { + this.key = key; + this.value = value; + } + + @Override + public int compareTo(Entry o) { + return key.compareTo(o.key); + } + +} + +RPriorityBlockingDeque queue = redisson.getPriorityBlockingDeque("anyQueue"); +queue.add(new Entry("b", 1)); +queue.add(new Entry("c", 1)); +queue.add(new Entry("a", 1)); + +// Entry [a:1] +Entry e = queue.takeFirst(); +// Entry [c:1] +Entry e = queue.takeLast(); +``` + +## Stream +Java implementation of Redis or Valkey based [Stream](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RStream.html) object wraps [Stream](https://redis.io/topics/streams-intro) feature. Basically it allows to create Consumers Group which consume data added by Producers. This object is thread-safe. + +```java +RStream stream = redisson.getStream("test"); + +StreamMessageId sm = stream.add(StreamAddArgs.entry("0", "0")); + +stream.createGroup("testGroup"); + +StreamId id1 = stream.add(StreamAddArgs.entry("1", "1")); +StreamId id2 = stream.add(StreamAddArgs.entry("2", "2")); + +Map> group = stream.readGroup("testGroup", "consumer1", StreamReadGroupArgs.neverDelivered()); + +// return entries in pending state after read group method execution +Map> pendingData = stream.pendingRange("testGroup", "consumer1", StreamMessageId.MIN, StreamMessageId.MAX, 100); + +// transfer ownership of pending messages to a new consumer +List transferedIds = stream.fastClaim("testGroup", "consumer2", 1, TimeUnit.MILLISECONDS, id1, id2); + +// mark pending entries as correctly processed +long amount = stream.ack("testGroup", id1, id2); +``` + +Code example of **[Async interface](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RStreamAsync.html)** usage: + +```java +RStream stream = redisson.getStream("test"); + +RFuture smFuture = stream.addAsync(StreamAddArgs.entry("0", "0")); + +RFuture groupFuture = stream.createGroupAsync("testGroup"); + +RFuture id1Future = stream.addAsync(StreamAddArgs.entry("1", "1")); +RFuture id2Future = stream.addAsync(StreamAddArgs.entry("2", "2")); + +RFuture>> groupResultFuture = stream.readGroupAsync("testGroup", "consumer1", StreamReadGroupArgs.neverDelivered()); + +// return entries in pending state after read group method execution +RFuture>> pendingDataFuture = stream.pendingRangeAsync("testGroup", "consumer1", StreamMessageId.MIN, StreamMessageId.MAX, 100); + +// transfer ownership of pending messages to a new consumer +RFuture> transferedIdsFuture = stream.fastClaim("testGroup", "consumer2", 1, TimeUnit.MILLISECONDS, id1, id2); + +// mark pending entries as correctly processed +RFuture amountFuture = stream.ackAsync("testGroup", id1, id2); + +amountFuture.whenComplete((res, exception) -> { + // ... +}); +``` + +Code example of **[Reactive interface](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RStreamReactive.html)** usage: + +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RStreamReactive stream = redisson.getStream("test"); + +Mono smMono = stream.add(StreamAddArgs.entry("0", "0")); + +Mono groupMono = stream.createGroup("testGroup"); + +Mono id1Mono = stream.add(StreamAddArgs.entry("1", "1")); +Mono id2Mono = stream.add(StreamAddArgs.entry("2", "2")); + +Mono>> groupMono = stream.readGroup("testGroup", "consumer1", StreamReadGroupArgs.neverDelivered()); + +// return entries in pending state after read group method execution +Mono>> pendingDataMono = stream.pendingRange("testGroup", "consumer1", StreamMessageId.MIN, StreamMessageId.MAX, 100); + +// transfer ownership of pending messages to a new consumer +Mono> transferedIdsMono = stream.fastClaim("testGroup", "consumer2", 1, TimeUnit.MILLISECONDS, id1, id2); + +// mark pending entries as correctly processed +Mono amountMono = stream.ack("testGroup", id1, id2); + +amountMono.doOnNext(res -> { + // ... +}).subscribe(); +``` + +Code example of **[RxJava3 interface](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RStreamRx.html)** usage: + +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RStreamRx stream = redisson.getStream("test"); + +Single smRx = stream.add(StreamAddArgs.entry("0", "0")); + +Completable groupRx = stream.createGroup("testGroup"); + +Single id1Rx = stream.add(StreamAddArgs.entry("1", "1")); +Single id2Rx = stream.add(StreamAddArgs.entry("2", "2")); + +Single>> groupRx = stream.readGroup("testGroup", "consumer1", StreamReadGroupArgs.neverDelivered()); + +// return entries in pending state after read group method execution +Single>> pendingDataRx = stream.pendingRange("testGroup", "consumer1", StreamMessageId.MIN, StreamMessageId.MAX, 100); + +// transfer ownership of pending messages to a new consumer +Single> transferedIdsRx = stream.fastClaim("testGroup", "consumer2", 1, TimeUnit.MILLISECONDS, id1, id2); + +// mark pending entries as correctly processed +Single amountRx = stream.ack("testGroup", id1, id2); + +amountRx.doOnSuccess(res -> { + // ... +}).subscribe(); +``` + +### Listeners + +Redisson allows to bind listeners per `RStream` object. + +|Listener class name|Event description | +|:--:|:--:| +|org.redisson.api.listener.TrackingListener|Element added/removed/updated after read operation| +|org.redisson.api.ExpiredObjectListener|`RStream` object expired| +|org.redisson.api.DeletedObjectListener|`RStream` object deleted| +|org.redisson.api.listener.StreamAddListener|Element added| +|org.redisson.api.listener.StreamRemoveListener|Element removed| +|org.redisson.api.listener.StreamCreateGroupListener|Group created| +|org.redisson.api.listener.StreamRemoveGroupListener|Group removed| +|org.redisson.api.listener.StreamCreateConsumerListener|Consumer created| +|org.redisson.api.listener.StreamRemoveConsumerListener|Consumer removed| +|org.redisson.api.listener.StreamTrimListener|Stream trimmed| + +Usage example: + +```java +RStream stream = redisson.getStream("anySet"); + +int listenerId = stream.addListener(new DeletedObjectListener() { + @Override + public void onDeleted(String name) { + // ... + } +}); + +int listenerId = stream.addListener(new StreamAddListener() { + @Override + public void onAdd(String name) { + // ... + } +}); + + +// ... + +stream.removeListener(listenerId); +``` + +## Ring Buffer + +Java implementation of Redis or Valkey based [RingBuffer](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RRingBuffer.html) implements [java.util.Queue](https://docs.oracle.com/javase/8/docs/api/java/util/Queue.html) interface. This structure evicts elements from the head if queue capacity became full. This object is thread-safe. + +Should be initialized with capacity size by `trySetCapacity()` method before usage. + +Code example: + +```java +RRingBuffer buffer = redisson.getRingBuffer("test"); + +// buffer capacity is 4 elements +buffer.trySetCapacity(4); + +buffer.add(1); +buffer.add(2); +buffer.add(3); +buffer.add(4); + +// buffer state is 1, 2, 3, 4 + +buffer.add(5); +buffer.add(6); + +// buffer state is 3, 4, 5, 6 +``` + +Code example of **[Async interface](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RRingBufferAsync.html)** usage: + +```java +RRingBuffer buffer = redisson.getRingBuffer("test"); + +// buffer capacity is 4 elements +RFuture capacityFuture = buffer.trySetCapacityAsync(4); + +RFuture addFuture = buffer.addAsync(1); +RFuture addFuture = buffer.addAsync(2); +RFuture addFuture = buffer.addAsync(3); +RFuture addFuture = buffer.addAsync(4); + +// buffer state is 1, 2, 3, 4 + +RFuture addFuture = buffer.addAsync(5); +RFuture addFuture = buffer.addAsync(6); + +// buffer state is 3, 4, 5, 6 + +addFuture.whenComplete((res, exception) -> { + // ... +}); +``` + +Code example of **[Reactive interface](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RRingBufferReactive.html)** usage: + +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RRingBufferReactive buffer = redisson.getRingBuffer("test"); + +// buffer capacity is 4 elements +Mono capacityMono = buffer.trySetCapacity(4); + +Mono addMono = buffer.add(1); +Mono addMono = buffer.add(2); +Mono addMono = buffer.add(3); +Mono addMono = buffer.add(4); + +// buffer state is 1, 2, 3, 4 + +Mono addMono = buffer.add(5); +Mono addMono = buffer.add(6); + +// buffer state is 3, 4, 5, 6 + +addMono.doOnNext(res -> { + // ... +}).subscribe(); +``` + +Code example of **[RxJava3 interface](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RRingBufferRx.html)** usage: + +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RRingBufferRx buffer = redisson.getRingBuffer("test"); + +// buffer capacity is 4 elements +Single capacityRx = buffer.trySetCapacity(4); + +Single addRx = buffer.add(1); +Single addRx = buffer.add(2); +Single addRx = buffer.add(3); +Single addRx = buffer.add(4); + +// buffer state is 1, 2, 3, 4 + +Single addRx = buffer.add(5); +Single addRx = buffer.add(6); + +// buffer state is 3, 4, 5, 6 + +addRx.doOnSuccess(res -> { + // ... +}).subscribe(); +``` + +### Listeners + +Redisson allows to bind listeners per `RRingBuffer` object. + +|Listener class name|Event description | +|:--:|:--:| +|org.redisson.api.listener.TrackingListener|Element created/removed/updated after read operation| +|org.redisson.api.listener.ListAddListener|Element created| +|org.redisson.api.listener.ListRemoveListener|Element removed| +|org.redisson.api.ExpiredObjectListener|`RRingBuffer` object expired| +|org.redisson.api.DeletedObjectListener|`RRingBuffer` object deleted| + +Usage example: + +```java +RRingBuffer queue = redisson.getRingBuffer("anyList"); + +int listenerId = queue.addListener(new DeletedObjectListener() { + @Override + public void onDeleted(String name) { + // ... + } +}); + +// ... + +queue.removeListener(listenerId); +``` + + +## Transfer Queue + +Java implementation of Redis or Valkey based [TransferQueue](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RTransferQueue.html) implements [java.util.concurrent.TransferQueue](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/TransferQueue.html) interface. Provides set of `transfer` methods which return only when value was successfully hand off to consumer. This object is thread-safe. + +`poll` and `take` methods are resubscribed automatically during re-connection to a server or failover. + +Code example: +```java +RTransferQueue queue = redisson.getTransferQueue("myCountDownLatch"); + +queue.transfer("data"); +// or try transfer immediately +queue.tryTransfer("data"); +// or try transfer up to 10 seconds +queue.tryTransfer("data", 10, TimeUnit.SECONDS); + +// in other thread or JVM + +queue.take(); +// or +queue.poll(); +``` + +Code example of **[Async interface](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RTransferQueueAsync.html)** usage: +```java +RTransferQueue queue = redisson.getTransferQueue("myCountDownLatch"); + +RFuture future = queue.transferAsync("data"); +// or try transfer immediately +RFuture future = queue.tryTransferAsync("data"); +// or try transfer up to 10 seconds +RFuture future = queue.tryTransferAsync("data", 10, TimeUnit.SECONDS); + +// in other thread or JVM + +RFuture future = queue.takeAsync(); +// or +RFuture future = queue.pollAsync(); + +future.whenComplete((res, exception) -> { + // ... +}); +``` + +Code example of **[Reactive interface](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RTransferQueueReactive.html)** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RTransferQueueReactive queue = redisson.getTransferQueue("myCountDownLatch"); + +Mono mono = queue.transfer("data"); +// or try transfer immediately +Mono mono = queue.tryTransfer("data"); +// or try transfer up to 10 seconds +Mono mono = queue.tryTransfer("data", 10, TimeUnit.SECONDS); + +// in other thread or JVM + +Mono mono = queue.take(); +// or +Mono mono = queue.poll(); + +mono.doOnNext(res -> { + // ... +}).subscribe(); +``` + +Code example of **[RxJava3 interface](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RTransferQueueRx.html)** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RTransferQueueRx queue = redisson.getTransferQueue("myCountDownLatch"); + +Completable res = queue.transfer("data"); +// or try transfer immediately +Single resRx = queue.tryTransfer("data"); +// or try transfer up to 10 seconds +Single resRx = queue.tryTransfer("data", 10, TimeUnit.SECONDS); + +// in other thread or JVM + +Single resRx = queue.take(); +// or +Maybe resRx = queue.poll(); + +resRx.doOnSuccess(res -> { + // ... +}).subscribe(); +``` + +## Time Series +Java implementation of Redis or Valkey based [TimeSeries](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RTimeSeries.html) object allows to store value by timestamp and define TTL(time-to-live) per entry. Values are ordered by timestamp. This object is thread-safe. + +Code example: +```java +RTimeSeries ts = redisson.getTimeSeries("myTimeSeries"); + +ts.add(201908110501, "10%"); +ts.add(201908110502, "30%"); +ts.add(201908110504, "10%"); +ts.add(201908110508, "75%"); + +// entry time-to-live is 10 hours +ts.add(201908110510, "85%", 10, TimeUnit.HOURS); +ts.add(201908110510, "95%", 10, TimeUnit.HOURS); + +String value = ts.get(201908110508); +ts.remove(201908110508); + +Collection values = ts.pollFirst(2); +Collection range = ts.range(201908110501, 201908110508); +``` + +Code example of **[Async interface](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RTimeSeriesAsync.html)** usage: +```java +RTimeSeries ts = redisson.getTimeSeries("myTimeSeries"); + +RFuture future = ts.addAsync(201908110501, "10%"); +RFuture future = ts.addAsync(201908110502, "30%"); +RFuture future = ts.addAsync(201908110504, "10%"); +RFuture future = ts.addAsync(201908110508, "75%"); + +// entry time-to-live is 10 hours +RFuture future = ts.addAsync(201908110510, "85%", 10, TimeUnit.HOURS); +RFuture future = ts.addAsync(201908110510, "95%", 10, TimeUnit.HOURS); + +RFuture future = ts.getAsync(201908110508); +RFuture future = ts.removeAsync(201908110508); + +RFuture> future = t.pollFirstAsync(2); +RFuture> future = t.rangeAsync(201908110501, 201908110508); + +future.whenComplete((res, exception) -> { + // ... +}); +``` + +Code example of **[Reactive interface](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RTimeSeriesReactive.html)** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RTimeSeriesReactive ts = redisson.getTimeSeries("myTimeSeries"); + +Mono mono = ts.add(201908110501, "10%"); +Mono mono = ts.add(201908110502, "30%"); +Mono mono = ts.add(201908110504, "10%"); +Mono mono = ts.add(201908110508, "75%"); + +// entry time-to-live is 10 hours +Mono mono = ts.add(201908110510, "85%", 10, TimeUnit.HOURS); +Mono mono = ts.add(201908110510, "95%", 10, TimeUnit.HOURS); + +Mono mono = ts.get(201908110508); +Mono mono = ts.remove(201908110508); + +Mono> mono = ts.pollFirst(2); +Mono> mono = ts.range(201908110501, 201908110508); + +mono.doOnNext(res -> { + // ... +}).subscribe(); +``` + +Code example of **[RxJava3 interface](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RTimeSeriesRx.html)** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RTimeSeriesRx ts = redisson.getTimeSeries("myTimeSeries"); + +Completable rx = ts.add(201908110501, "10%"); +Completable rx = ts.add(201908110502, "30%"); +Completable rx = ts.add(201908110504, "10%"); +Completable rx = ts.add(201908110508, "75%"); + +// entry time-to-live is 10 hours +Completable rx = ts.add(201908110510, "85%", 10, TimeUnit.HOURS); +Completable rx = ts.add(201908110510, "95%", 10, TimeUnit.HOURS); + +Maybe rx = ts.get(201908110508); +Single rx = ts.remove(201908110508); + +Single> rx = ts.pollFirst(2); +Single> rx = ts.range(201908110501, 201908110508); + +rx.doOnSuccess(res -> { + // ... +}).subscribe(); +``` \ No newline at end of file diff --git a/docs/data-and-services/common-methods.md b/docs/data-and-services/common-methods.md new file mode 100644 index 000000000..35b3f1061 --- /dev/null +++ b/docs/data-and-services/common-methods.md @@ -0,0 +1,140 @@ +**1. Object name** + +Name of Redisson object stored as a key in Redis or Valkey. + +Example: +```java +RMap map = redisson.getMap("mymap"); + +map.getName(); // = mymap +``` + +**2. Common methods** + +Each Redisson object implements [RObject](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RObject.html) and [RExpirable](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RExpirable.html) interfaces. + +Below are the most commonly used methods. + +```java +RObject object = ... + +// Copy methods + +object.copy("myNewCopy"); + +object.copyAndReplace("myNewCopy"); + +// Delete methods + +object.delete(); + +object.unlink(); // works faster because it's executed on the database side in a different thread + +// Rename methods + +object.rename("myNewName"); + +object.renamenx("myNewName"); // rename only if the new key doesn't exist + +// Dump and restore methods + +byte[] state = object.dump(); +object.restore(state); +``` + +**3. Listeners per Redisson object instance** + +Listeners can be attached per Redisson object instance. Base listeners are `ExpiredObjectListener` and `DeletedObjectListener`. Redisson objects may support specific listeners. Like [RScoredSortedSet](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RScoredSortedSet.html#addListener(org.redisson.api.ObjectListener)), [RStream](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RStream.html#addListener(org.redisson.api.ObjectListener)) and others. + +```java +RObject object = ... + +// listening to expired events +object.addListener((ExpiredObjectListener) name -> { + + //... + +}); + +// listening to delete events +object.addListener((DeletedObjectListener) name -> { + + //... + +}); +``` + +**4. Operations over all Redisson object instances** + +Operations over all objects are exposed by [RKeys](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RKeys.html) interface. + +Usage example: +```java +RKeys keys = redisson.getKeys(); + +// Keys iteration + +Iterable allKeys = keys.getKeys(); + +Iterable foundedKeys = keys.getKeys(KeysScanOptions.defaults().pattern("key*")); + +String randomKey = keys.randomKey(); + +long keysAmount = keys.count(); + +long keysAmount = keys.countExists("obj1", "obj2", "obj3"); // amount of existing keys + +// Delete methods + +long delKeys = keys.delete("obj1", "obj2", "obj3"); + +long delKeys = keys.deleteByPattern("test?"); + +long delKeys = keys.unlink("obj1", "obj2", "obj3"); // works faster because it's executed on the database side in a different thread + +long delKeys = keys.unlinkByPattern("test?"); // works faster because it's executed on the database side in a different thread + +keys.flushall(); // Delete all keys of all existing databases + +keys.flushallParallel(); // Delete all keys of all existing databases in background without blocking server + +keys.flushdb(); // Delete all keys of currently selected database +``` + +**5. Global listeners** + +Global listeners are attached to all Redisson object instances. + +Available listeners: + +- [TrackingListener](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/listener/TrackingListener.html), +- [SetObjectListener](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/listener/SetObjectListener.html), +- [NewObjectListener](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/listener/NewObjectListener.html), +- [ExpiredObjectListener](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/ExpiredObjectListener.html), +- [DeletedObjectListener](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/DeletedObjectListener.html), +- [FlushListener](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/listener/FlushListener.html) + +Usage example: + +```java +RKeys keys = redisson.getKeys(); +int id = keys.addListener((NewObjectListener) name -> { + + //... + +}); + +int id = keys.addListener((DeletedObjectListener) name -> { + + //... + +}); + +int id = keys.addListener((FlushListener) address -> { + + //... + +}); + +keys.removeListener(id); +``` \ No newline at end of file diff --git a/docs/data-and-services/counters.md b/docs/data-and-services/counters.md new file mode 100644 index 000000000..ca4773c8d --- /dev/null +++ b/docs/data-and-services/counters.md @@ -0,0 +1,167 @@ +## Id generator +Redis or Valkey based Java Id generator [RIdGenerator](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RIdGenerator.html) generates unique numbers but not monotonically increased. At first request, batch of id numbers is allocated and cached on Java side till it's exhausted. This approach allows to generate ids faster than [RAtomicLong](#atomiclong). + +Default allocation size is 5000. +Default start value is 0. + +Code example: +```java +RIdGenerator generator = redisson.getIdGenerator("generator"); + +// Initialize with start value = 12 and allocation size = 20000 +generator.tryInit(12, 20000); + +long id = generator.nextId(); +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RIdGeneratorAsync.html) interface** usage: +```java +RIdGenerator generator = redisson.getIdGenerator("generator"); + +// Initialize with start value = 12 and allocation size = 20000 +RFuture initFuture = generator.tryInitAsync(12, 20000); + +RFuture idFuture = generator.nextIdAsync(); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RIdGeneratorReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RIdGenerator generator = redisson.getIdGenerator("generator"); + +// Initialize with start value = 12 and allocation size = 20000 +Mono initMono = generator.tryInit(12, 20000); + +Mono idMono = generator.nextId(); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RIdGeneratorRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RIdGenerator generator = redisson.getIdGenerator("generator"); + +// Initialize with start value = 12 and allocation size = 20000 +Single initRx = generator.tryInit(12, 20000); + +Single idRx = generator.nextId(); +``` + +## AtomicLong +Java implementation of Redis or Valkey based [AtomicLong](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RAtomicLong.html) object provides API similar to [java.util.concurrent.atomic.AtomicLong](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicLong.html) object. + +Code example: +```java +RAtomicLong atomicLong = redisson.getAtomicLong("myAtomicLong"); +atomicLong.set(3); +atomicLong.incrementAndGet(); +atomicLong.get(); +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RAtomicLongAsync.html) interface** usage: +```java +RAtomicLongAsync atomicLong = redisson.getAtomicLong("myAtomicLong"); + +RFuture setFuture = atomicLong.setAsync(3); +RFuture igFuture = atomicLong.incrementAndGetAsync(); +RFuture getFuture = atomicLong.getAsync(); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RAtomicLongReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RAtomicLongReactive atomicLong = redisson.getAtomicLong("myAtomicLong"); + +Mono setMono = atomicLong.set(3); +Mono igMono = atomicLong.incrementAndGet(); +RFuture getMono = atomicLong.getAsync(); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RAtomicLongRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RAtomicLongRx atomicLong = redisson.getAtomicLong("myAtomicLong"); + +Completable setMono = atomicLong.set(3); +Single igMono = atomicLong.incrementAndGet(); +Single getMono = atomicLong.getAsync(); +``` + +## AtomicDouble +Java implementation of Redis or Valkey based [AtomicDouble](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RAtomicDouble.html) object. + +Code example: +```java +RAtomicDouble atomicDouble = redisson.getAtomicDouble("myAtomicDouble"); +atomicDouble.set(2.81); +atomicDouble.addAndGet(4.11); +atomicDouble.get(); +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RAtomicDoubleAsync.html) interface** usage: +```java +RAtomicDoubleAsync atomicDouble = redisson.getAtomicDouble("myAtomicDouble"); + +RFuture setFuture = atomicDouble.setAsync(2.81); +RFuture agFuture = atomicDouble.addAndGetAsync(4.11); +RFuture getFuture = atomicDouble.getAsync(); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RAtomicDoubleReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RAtomicDoubleReactive atomicDouble = redisson.getAtomicDouble("myAtomicDouble"); + +Mono setMono = atomicDouble.set(2.81); +Mono agMono = atomicDouble.addAndGet(4.11); +Mono getMono = atomicDouble.get(); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RAtomicDoubleRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RAtomicDoubleRx atomicDouble = redisson.getAtomicDouble("myAtomicDouble"); + +Completable setMono = atomicDouble.set(2.81); +Single igMono = atomicDouble.addAndGet(4.11); +Single getMono = atomicDouble.get(); +``` + +## LongAdder +Java implementation of Redis or Valkey based [LongAdder](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLongAdder.html) object provides API similar to [java.util.concurrent.atomic.LongAdder](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/LongAdder.html) object. + +It maintains internal LongAdder object on client side and provides superior performance for both increment and decrement operations. Up to __12000x__ faster than similar `AtomicLong` object. Suitable for distributed metric objects. + +Code example: +```java +RLongAdder atomicLong = redisson.getLongAdder("myLongAdder"); +atomicLong.add(12); +atomicLong.increment(); +atomicLong.decrement(); +atomicLong.sum(); +``` + +Object should be destroyed if it's not used anymore, but it's not necessary to call destroy method if Redisson goes shutdown. +```java +RLongAdder atomicLong = ... +atomicLong.destroy(); +``` + +## DoubleAdder +Java implementation of Redis or Valkey based [DoubleAdder](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RDoubleAdder.html) object provides API similar to [java.util.concurrent.atomic.DoubleAdder](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/DoubleAdder.html) object. + +It maintains internal DoubleAdder object on client side and provides superior performance for both increment and decrement operations. Up to __12000x__ faster than similar `AtomicDouble` object. Suitable for distributed metric objects. + +Code example: +```java +RLongDouble atomicDouble = redisson.getLongDouble("myLongDouble"); +atomicDouble.add(12); +atomicDouble.increment(); +atomicDouble.decrement(); +atomicDouble.sum(); +``` + +Object should be destroyed if it's not used anymore, but it's not necessary to call destroy method if Redisson goes shutdown. +```java +RLongDouble atomicDouble = ... +atomicDouble.destroy(); +``` \ No newline at end of file diff --git a/docs/data-and-services/data-partitioning.md b/docs/data-and-services/data-partitioning.md new file mode 100644 index 000000000..1e232acaf --- /dev/null +++ b/docs/data-and-services/data-partitioning.md @@ -0,0 +1,22 @@ +All Redisson Java objects are Redis or Valkey cluster compatible, but their state isn't scaled/partitioned to multiple master nodes in cluster. [Redisson PRO](https://redisson.pro/) offers data partitioning for some of them. This feature offers several advantages: + +1. State of single Redisson object evenly distributed across master nodes instead of single master node. This allows to avoid Redis or Valkey OutOfMemory problem. +2. Scaling read/write operations to all master nodes. + +Redisson splits data to **231 partitions by default**. Minimal number of partition is **3**. Partitions are uniformly distributed across all cluster nodes. This means that each node contains nearly equal amount of partitions. For default partitions amount (231) and 4 master nodes in cluster, each node contains nearly 57 data partitions. 46 data partitions per node for 5 master nodes cluster and so on. This feature achieved thanks to special slot distribution algorithm used in Redisson. + +Data partitioning supported for [Set](collections.md/#set), [Map](collections.md/#map), [BitSet](objects.md/#bitset), [Bloom filter](objects.md/#bloom-filter), [Spring Cache](../integration-with-spring.md/#spring-cache), [Hibernate Cache](../cache-api-implementations.md/#hibernate-cache), [JCache](../cache-api-implementations.md/#jcache-api-jsr-107), [Quarkus Cache](../cache-api-implementations.md/#quarkus-cache) and [Micronaut Cache](../cache-api-implementations.md/#micronaut-cache) structures. + +**Joined Redis deployments** + +Multiple Redis deployments could be joined and used as a single partitioned (sharded) Redis deployment. + +```java +RedissonClient redisson1 = ...; +RedissonClient redisson2 = ...; +RedissonClient redisson3 = ...; + +RedissonClient redisson = ShardedRedisson.create(redisson1, redisson2, redisson3); +``` + +_This feature available only in [Redisson PRO](https://redisson.pro) edition._ \ No newline at end of file diff --git a/docs/data-and-services/data-serialization.md b/docs/data-and-services/data-serialization.md new file mode 100644 index 000000000..87fbb3f54 --- /dev/null +++ b/docs/data-and-services/data-serialization.md @@ -0,0 +1,23 @@ +Data serialization is extensively used by Redisson to marshall and unmarshall bytes received or sent over network link with Redis or Valkey server. Many popular codecs are available for usage: + +Codec class name| Description +--- | --- +`org.redisson.codec.Kryo5Codec`| [Kryo 5](https://github.com/EsotericSoftware/kryo) binary codec
(**Android** compatible) __Default codec__ +`org.redisson.codec.KryoCodec`| [Kryo 4](https://github.com/EsotericSoftware/kryo) binary codec +`org.redisson.codec.JsonJacksonCodec`| [Jackson JSON](https://github.com/FasterXML/jackson) codec.
Stores type information in `@class` field
(**Android** compatible) +`org.redisson.codec.TypedJsonJacksonCodec`| Jackson JSON codec which doesn't store type id (`@class` field) +`org.redisson.codec.AvroJacksonCodec`| [Avro](http://avro.apache.org/) binary json codec +`org.redisson.codec.ProtobufCodec`| [Protobuf](https://github.com/protocolbuffers/protobuf) codec +`org.redisson.codec.FuryCodec`| [Apache Fury](https://github.com/apache/fury) codec +`org.redisson.codec.SmileJacksonCodec`| [Smile](http://wiki.fasterxml.com/SmileFormatSpec) binary json codec +`org.redisson.codec.CborJacksonCodec`| [CBOR](http://cbor.io/) binary json codec +`org.redisson.codec.MsgPackJacksonCodec`| [MsgPack](http://msgpack.org/) binary json codec +`org.redisson.codec.IonJacksonCodec`| [Amazon Ion](https://amzn.github.io/ion-docs/) codec +`org.redisson.codec.SerializationCodec`| JDK Serialization binary codec
(**Android** compatible) +`org.redisson.codec.LZ4Codec`| [LZ4](https://github.com/jpountz/lz4-java) compression codec.
Uses `Kryo5Codec` for serialization by default +`org.redisson.codec.LZ4CodecV2`| [LZ4 Apache Commons](https://github.com/apache/commons-compress) compression codec.
Uses `Kryo5Codec` for serialization by default +`org.redisson.codec.SnappyCodecV2` | Snappy compression codec based on [snappy-java](https://github.com/xerial/snappy-java) project.
Uses `Kryo5Codec` for serialization by default +`org.redisson.client.codec.StringCodec`| String codec +`org.redisson.client.codec.LongCodec`| Long codec +`org.redisson.client.codec.ByteArrayCodec` | Byte array codec +`org.redisson.codec.CompositeCodec` | Allows to mix different codecs as one \ No newline at end of file diff --git a/docs/data-and-services/locks-and-synchronizers.md b/docs/data-and-services/locks-and-synchronizers.md new file mode 100644 index 000000000..2d28cc735 --- /dev/null +++ b/docs/data-and-services/locks-and-synchronizers.md @@ -0,0 +1,946 @@ +## Lock +Redis or Valkey based distributed reentrant [Lock](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLock.html) object for Java and implements [Lock](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Lock.html) interface. Uses pub/sub channel to notify other threads across all Redisson instances waiting to acquire a lock. + +If Redisson instance which acquired lock crashes then such lock could hang forever in acquired state. To avoid this Redisson maintains lock watchdog, it prolongs lock expiration while lock holder Redisson instance is alive. By default lock watchdog timeout is 30 seconds and can be changed through [Config.lockWatchdogTimeout](../configuration.md) setting. + +`leaseTime` parameter during lock acquisition can be defined. After specified time interval locked lock will be released automatically. + +`RLock` object behaves according to the Java Lock specification. It means only lock owner thread can unlock it otherwise `IllegalMonitorStateException` would be thrown. Otherwise consider to use [RSemaphore](#semaphore) object. + +Code example: +```java +RLock lock = redisson.getLock("myLock"); + +// traditional lock method +lock.lock(); + +// or acquire lock and automatically unlock it after 10 seconds +lock.lock(10, TimeUnit.SECONDS); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS); +if (res) { + try { + ... + } finally { + lock.unlock(); + } +} +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockAsync.html) interface** usage: +```java +RLock lock = redisson.getLock("myLock"); + +long threadId = Thread.currentThread().getId(); +RFuture lockFuture = lock.lockAsync(threadId); + +// or acquire lock and automatically unlock it after 10 seconds +RFuture lockFuture = lock.lockAsync(10, TimeUnit.SECONDS, threadId); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +RFuture lockFuture = lock.tryLockAsync(100, 10, TimeUnit.SECONDS, threadId); + +lockFuture.whenComplete((res, exception) -> { + + // ... + + lock.unlockAsync(threadId); +}); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RLockReactive lock = redisson.getLock("myLock"); + +long threadId = Thread.currentThread().getId(); +Mono lockMono = lock.lock(threadId); + +// or acquire lock and automatically unlock it after 10 seconds +Mono lockMono = lock.lock(10, TimeUnit.SECONDS, threadId); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +Mono lockMono = lock.tryLock(100, 10, TimeUnit.SECONDS, threadId); + +lockMono.doOnSuccess(res -> { + // ... +}) +.doFinally(r -> lock.unlock(threadId).subscribe()) +.subscribe(); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RLockRx lock = redisson.getLock("myLock"); + +long threadId = Thread.currentThread().getId(); +Completable lockRes = lock.lock(threadId); + +// or acquire lock and automatically unlock it after 10 seconds +Completable lockRes = lock.lock(10, TimeUnit.SECONDS, threadId); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +Single lockRes = lock.tryLock(100, 10, TimeUnit.SECONDS, threadId); + +lockRes.doOnSuccess(res -> { + // ... +}) +.doFinally(() -> lock.unlock(threadId).subscribe()) +.subscribe(); +``` + + +## Fair Lock +Redis or Valkey based distributed reentrant fair [Lock](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLock.html) object for Java implements [Lock](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Lock.html) interface. + +Fair lock guarantees that threads will acquire it in is same order they requested it. All waiting threads are queued and if some thread has died then Redisson waits its return for 5 seconds. For example, if 5 threads are died for some reason then delay will be 25 seconds. + +If Redisson instance which acquired lock crashes then such lock could hang forever in acquired state. To avoid this Redisson maintains lock watchdog, it prolongs lock expiration while lock holder Redisson instance is alive. By default lock watchdog timeout is 30 seconds and can be changed through [Config.lockWatchdogTimeout](../configuration.md) setting. + +`leaseTime` parameter during lock acquisition can be defined. After specified time interval locked lock will be released automatically. + +`RLock` object behaves according to the Java Lock specification. It means only lock owner thread can unlock it otherwise `IllegalMonitorStateException` would be thrown. Otherwise consider to use [RSemaphore](#semaphore) object. + +Code example: +```java +RLock lock = redisson.getFairLock("myLock"); + +// traditional lock method +lock.lock(); + +// or acquire lock and automatically unlock it after 10 seconds +lock.lock(10, TimeUnit.SECONDS); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS); +if (res) { + try { + ... + } finally { + lock.unlock(); + } +} +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockAsync.html) interface** usage: +```java +RLock lock = redisson.getFairLock("myLock"); + +RFuture lockFuture = lock.lockAsync(); + +// or acquire lock and automatically unlock it after 10 seconds +RFuture lockFuture = lock.lockAsync(10, TimeUnit.SECONDS); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +RFuture lockFuture = lock.tryLockAsync(100, 10, TimeUnit.SECONDS); + +lockFuture.whenComplete((res, exception) -> { + // ... + lock.unlockAsync(); +}); + +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RLockReactive lock = redisson.getFairLock("myLock"); + +long threadId = Thread.currentThread().getId(); +Mono lockMono = lock.lock(threadId); + +// or acquire lock and automatically unlock it after 10 seconds +Mono lockMono = lock.lock(10, TimeUnit.SECONDS, threadId); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +Mono lockMono = lock.tryLock(100, 10, TimeUnit.SECONDS, threadId); + +lockMono.doOnSuccess(res -> { + // ... +}) +.doFinally(r -> lock.unlock(threadId).subscribe()) +.subscribe(); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RLockRx lock = redisson.getFairLock("myLock"); + +long threadId = Thread.currentThread().getId(); +Completable lockRes = lock.lock(threadId); + +// or acquire lock and automatically unlock it after 10 seconds +Completable lockRes = lock.lock(10, TimeUnit.SECONDS, threadId); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +Single lockRes = lock.tryLock(100, 10, TimeUnit.SECONDS, threadId); + +lockRes.doOnSuccess(res -> { + // ... +}) +.doFinally(() -> lock.unlock(threadId).subscribe()) +.subscribe(); +``` + +## MultiLock +Redis or Valkey based distributed `MultiLock` object allows to group [Lock](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLock.html) objects and handle them as a single lock. Each `RLock` object may belong to different Redisson instances. + +If Redisson instance which acquired `MultiLock` crashes then such `MultiLock` could hang forever in acquired state. To avoid this Redisson maintains lock watchdog, it prolongs lock expiration while lock holder Redisson instance is alive. By default lock watchdog timeout is 30 seconds and can be changed through [Config.lockWatchdogTimeout](../configuration.md) setting. + +`leaseTime` parameter during lock acquisition can be defined. After specified time interval locked lock will be released automatically. + +`MultiLock` object behaves according to the Java Lock specification. It means only lock owner thread can unlock it otherwise `IllegalMonitorStateException` would be thrown. Otherwise consider to use [RSemaphore](#semaphore) object. + +Code example: +```java +RLock lock1 = redisson1.getLock("lock1"); +RLock lock2 = redisson2.getLock("lock2"); +RLock lock3 = redisson3.getLock("lock3"); + +RLock multiLock = anyRedisson.getMultiLock(lock1, lock2, lock3); + +// traditional lock method +multiLock.lock(); + +// or acquire lock and automatically unlock it after 10 seconds +multiLock.lock(10, TimeUnit.SECONDS); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +boolean res = multiLock.tryLock(100, 10, TimeUnit.SECONDS); +if (res) { + try { + ... + } finally { + multiLock.unlock(); + } +} +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockAsync.html) interface** usage: +```java +RLock lock1 = redisson1.getLock("lock1"); +RLock lock2 = redisson2.getLock("lock2"); +RLock lock3 = redisson3.getLock("lock3"); + +RLock multiLock = anyRedisson.getMultiLock(lock1, lock2, lock3); + +long threadId = Thread.currentThread().getId(); +RFuture lockFuture = multiLock.lockAsync(threadId); + +// or acquire lock and automatically unlock it after 10 seconds +RFuture lockFuture = multiLock.lockAsync(10, TimeUnit.SECONDS, threadId); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +RFuture lockFuture = multiLock.tryLockAsync(100, 10, TimeUnit.SECONDS, threadId); + +lockFuture.whenComplete((res, exception) -> { + // ... + multiLock.unlockAsync(threadId); +}); + +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockReactive.html) interface** usage: +```java +RedissonReactiveClient anyRedisson = redissonClient.reactive(); + +RLockReactive lock1 = redisson1.getLock("lock1"); +RLockReactive lock2 = redisson2.getLock("lock2"); +RLockReactive lock3 = redisson3.getLock("lock3"); + +RLockReactive multiLock = anyRedisson.getMultiLock(lock1, lock2, lock3); + +long threadId = Thread.currentThread().getId(); +Mono lockMono = multiLock.lock(threadId); + +// or acquire lock and automatically unlock it after 10 seconds +Mono lockMono = multiLock.lock(10, TimeUnit.SECONDS, threadId); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +Mono lockMono = multiLock.tryLock(100, 10, TimeUnit.SECONDS, threadId); + +lockMono.doOnSuccess(res -> { + // ... +}) +.doFinally(r -> multiLock.unlock(threadId).subscribe()) +.subscribe(); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockRx.html) interface** usage: +```java +RedissonRxClient anyRedisson = redissonClient.rxJava(); + +RLockRx lock1 = redisson1.getLock("lock1"); +RLockRx lock2 = redisson2.getLock("lock2"); +RLockRx lock3 = redisson3.getLock("lock3"); + +RLockRx multiLock = anyRedisson.getMultiLock(lock1, lock2, lock3); + +long threadId = Thread.currentThread().getId(); +Completable lockRes = multiLock.lock(threadId); + +// or acquire lock and automatically unlock it after 10 seconds +Completable lockRes = multiLock.lock(10, TimeUnit.SECONDS, threadId); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +Single lockRes = multiLock.tryLock(100, 10, TimeUnit.SECONDS, threadId); + +lockRes.doOnSuccess(res -> { + // ... +}) +.doFinally(() -> multiLock.unlock(threadId).subscribe()) +.subscribe(); +``` + +## RedLock +_This object is deprecated. Use [RLock](#81-lock) or [RFencedLock](#810-fenced-lock) instead._ + +## ReadWriteLock +Redis or Valkey based distributed reentrant [ReadWriteLock](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RReadWriteLock.html) object for Java implements [ReadWriteLock](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReadWriteLock.html) interface. Both Read and Write locks implement [RLock](#lock) interface. + +Multiple ReadLock owners and only one WriteLock owner are allowed. + +If Redisson instance which acquired lock crashes then such lock could hang forever in acquired state. To avoid this Redisson maintains lock watchdog, it prolongs lock expiration while lock holder Redisson instance is alive. By default lock watchdog timeout is 30 seconds and can be changed through [Config.lockWatchdogTimeout](../configuration.md) setting. + +Also Redisson allow to specify `leaseTime` parameter during lock acquisition. After specified time interval locked lock will be released automatically. + +`RLock` object behaves according to the Java Lock specification. It means only lock owner thread can unlock it otherwise `IllegalMonitorStateException` would be thrown. Otherwise consider to use [RSemaphore](#semaphore) object. + +Code example: +```java +RReadWriteLock rwlock = redisson.getReadWriteLock("myLock"); + +RLock lock = rwlock.readLock(); +// or +RLock lock = rwlock.writeLock(); + +// traditional lock method +lock.lock(); + +// or acquire lock and automatically unlock it after 10 seconds +lock.lock(10, TimeUnit.SECONDS); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS); +if (res) { + try { + ... + } finally { + lock.unlock(); + } +} +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockAsync.html) interface** usage: +```java +RReadWriteLock rwlock = redisson.getReadWriteLock("myLock"); + +long threadId = Thread.currentThread().getId(); +RLock lock = rwlock.readLock(); +// or +RLock lock = rwlock.writeLock(); + +RFuture lockFuture = lock.lockAsync(threadId); + +// or acquire lock and automatically unlock it after 10 seconds +RFuture lockFuture = lock.lockAsync(10, TimeUnit.SECONDS, threadId); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +RFuture lockFuture = lock.tryLockAsync(100, 10, TimeUnit.SECONDS, threadId); + +lockFuture.whenComplete((res, exception) -> { + + // ... + + lock.unlockAsync(threadId); +}); + +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); + +RReadWriteLockReactive rwlock = redisson.getReadWriteLock("myLock"); + +RLockReactive lock = rwlock.readLock(); +// or +RLockReactive lock = rwlock.writeLock(); + +long threadId = Thread.currentThread().getId(); +Mono lockMono = lock.lock(threadId); + +// or acquire lock and automatically unlock it after 10 seconds +Mono lockMono = lock.lock(10, TimeUnit.SECONDS, threadId); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +Mono lockMono = lock.tryLock(100, 10, TimeUnit.SECONDS, threadId); + +lockMono.doOnSuccess(res -> { + // ... +}) +.doFinally(r -> lock.unlock(threadId).subscribe()) +.subscribe(); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); + +RReadWriteLockRx rwlock = redisson.getReadWriteLock("myLock"); + +RLockRx lock = rwlock.readLock(); +// or +RLockRx lock = rwlock.writeLock(); + +long threadId = Thread.currentThread().getId(); +Completable lockRes = lock.lock(threadId); + +// or acquire lock and automatically unlock it after 10 seconds +Completable lockRes = lock.lock(10, TimeUnit.SECONDS, threadId); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +Single lockRes = lock.tryLock(100, 10, TimeUnit.SECONDS, threadId); + +lockRes.doOnSuccess(res -> { + // ... +}) +.doFinally(() -> lock.unlock(threadId).subscribe()) +.subscribe(); +``` + +## Semaphore +Redis or Valkey based distributed [Semaphore](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RSemaphore.html) object for Java similar to [Semaphore](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Semaphore.html) object. + +Could be initialized before usage, but it's not requirement, with available permits amount through `trySetPermits(permits)` method. + +Code example: +```java +RSemaphore semaphore = redisson.getSemaphore("mySemaphore"); + +// acquire single permit +semaphore.acquire(); + +// or acquire 10 permits +semaphore.acquire(10); + +// or try to acquire permit +boolean res = semaphore.tryAcquire(); + +// or try to acquire permit or wait up to 15 seconds +boolean res = semaphore.tryAcquire(15, TimeUnit.SECONDS); + +// or try to acquire 10 permit +boolean res = semaphore.tryAcquire(10); + +// or try to acquire 10 permits or wait up to 15 seconds +boolean res = semaphore.tryAcquire(10, 15, TimeUnit.SECONDS); +if (res) { + try { + ... + } finally { + semaphore.release(); + } +} +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RSemaphoreAsync.html) interface** usage: +```java +RSemaphore semaphore = redisson.getSemaphore("mySemaphore"); + +// acquire single permit +RFuture acquireFuture = semaphore.acquireAsync(); + +// or acquire 10 permits +RFuture acquireFuture = semaphore.acquireAsync(10); + +// or try to acquire permit +RFuture acquireFuture = semaphore.tryAcquireAsync(); + +// or try to acquire permit or wait up to 15 seconds +RFuture acquireFuture = semaphore.tryAcquireAsync(15, TimeUnit.SECONDS); + +// or try to acquire 10 permit +RFuture acquireFuture = semaphore.tryAcquireAsync(10); + +// or try to acquire 10 permits or wait up to 15 seconds +RFuture acquireFuture = semaphore.tryAcquireAsync(10, 15, TimeUnit.SECONDS); + +acquireFuture.whenComplete((res, exception) -> { + // ... + semaphore.releaseAsync(); +}); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RSemaphoreReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); + +RSemaphoreReactive semaphore = redisson.getSemaphore("mySemaphore"); + +// acquire single permit +Mono acquireMono = semaphore.acquire(); + +// or acquire 10 permits +Mono acquireMono = semaphore.acquire(10); + +// or try to acquire permit +Mono acquireMono = semaphore.tryAcquire(); + +// or try to acquire permit or wait up to 15 seconds +Mono acquireMono = semaphore.tryAcquire(15, TimeUnit.SECONDS); + +// or try to acquire 10 permit +Mono acquireMono = semaphore.tryAcquire(10); + +// or try to acquire 10 permits or wait up to 15 seconds +Mono acquireMono = semaphore.tryAcquire(10, 15, TimeUnit.SECONDS); + +acquireMono.doOnSuccess(res -> { + // ... +}) +.doFinally(r -> semaphore.release().subscribe()) +.subscribe(); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RSemaphoreRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); + +RSemaphoreRx semaphore = redisson.getSemaphore("mySemaphore"); + +// acquire single permit +Completable acquireRx = semaphore.acquire(); + +// or acquire 10 permits +Completable acquireRx = semaphore.acquire(10); + +// or try to acquire permit +Single acquireRx = semaphore.tryAcquire(); + +// or try to acquire permit or wait up to 15 seconds +Single acquireRx = semaphore.tryAcquire(15, TimeUnit.SECONDS); + +// or try to acquire 10 permit +Single acquireRx = semaphore.tryAcquire(10); + +// or try to acquire 10 permits or wait up to 15 seconds +Single acquireRx = semaphore.tryAcquire(10, 15, TimeUnit.SECONDS); + +acquireRx.doOnSuccess(res -> { + // ... +}) +.doFinally(() -> semaphore.release().subscribe()) +.subscribe(); +``` + +## PermitExpirableSemaphore +Redis or Valkey based distributed [Semaphore](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RPermitExpirableSemaphore.html) object for Java with lease time parameter support for each acquired permit. Each permit identified by own id and could be released only using its id. + +Should be initialized before usage with available permits amount through `trySetPermits(permits)` method. Allows to increase/decrease number of available permits through `addPermits(permits)` method. + +Code example: +```java +RPermitExpirableSemaphore semaphore = redisson.getPermitExpirableSemaphore("mySemaphore"); + +semaphore.trySetPermits(23); + +// acquire permit +String id = semaphore.acquire(); + +// or acquire permit with lease time in 10 seconds +String id = semaphore.acquire(10, TimeUnit.SECONDS); + +// or try to acquire permit +String id = semaphore.tryAcquire(); + +// or try to acquire permit or wait up to 15 seconds +String id = semaphore.tryAcquire(15, TimeUnit.SECONDS); + +// or try to acquire permit with least time 15 seconds or wait up to 10 seconds +String id = semaphore.tryAcquire(10, 15, TimeUnit.SECONDS); +if (id != null) { + try { + ... + } finally { + semaphore.release(id); + } +} +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RPermitExpirableSemaphoreAsync.html) interface** usage: +```java +RPermitExpirableSemaphore semaphore = redisson.getPermitExpirableSemaphore("mySemaphore"); + +RFuture setFuture = semaphore.trySetPermitsAsync(23); + +// acquire permit +RFuture acquireFuture = semaphore.acquireAsync(); + +// or acquire permit with lease time in 10 seconds +RFuture acquireFuture = semaphore.acquireAsync(10, TimeUnit.SECONDS); + +// or try to acquire permit +RFuture acquireFuture = semaphore.tryAcquireAsync(); + +// or try to acquire permit or wait up to 15 seconds +RFuture acquireFuture = semaphore.tryAcquireAsync(15, TimeUnit.SECONDS); + +// or try to acquire permit with least time 15 seconds or wait up to 10 seconds +RFuture acquireFuture = semaphore.tryAcquireAsync(10, 15, TimeUnit.SECONDS); +acquireFuture.whenComplete((id, exception) -> { + // ... + semaphore.releaseAsync(id); +}); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RPermitExpirableSemaphoreReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); + +RPermitExpirableSemaphoreReactive semaphore = redisson.getPermitExpirableSemaphore("mySemaphore"); + +Mono setMono = semaphore.trySetPermits(23); + +// acquire permit +Mono acquireMono = semaphore.acquire(); + +// or acquire permit with lease time in 10 seconds +Mono acquireMono = semaphore.acquire(10, TimeUnit.SECONDS); + +// or try to acquire permit +Mono acquireMono = semaphore.tryAcquire(); + +// or try to acquire permit or wait up to 15 seconds +Mono acquireMono = semaphore.tryAcquire(15, TimeUnit.SECONDS); + +// or try to acquire permit with least time 15 seconds or wait up to 10 seconds +Mono acquireMono = semaphore.tryAcquire(10, 15, TimeUnit.SECONDS); + +acquireMono.flatMap(id -> { + // ... + return semaphore.release(id); +}).subscribe(); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RPermitExpirableSemaphoreRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); + +RPermitExpirableSemaphoreRx semaphore = redisson.getPermitExpirableSemaphore("mySemaphore"); + +Single setRx = semaphore.trySetPermits(23); + +// acquire permit +Single acquireRx = semaphore.acquire(); + +// or acquire permit with lease time in 10 seconds +Single acquireRx = semaphore.acquire(10, TimeUnit.SECONDS); + +// or try to acquire permit +Maybe acquireRx = semaphore.tryAcquire(); + +// or try to acquire permit or wait up to 15 seconds +Maybe acquireRx = semaphore.tryAcquire(15, TimeUnit.SECONDS); + +// or try to acquire permit with least time 15 seconds or wait up to 10 seconds +Maybe acquireRx = semaphore.tryAcquire(10, 15, TimeUnit.SECONDS); + +acquireRx.flatMap(id -> { + // ... + return semaphore.release(id); +}).subscribe(); +``` + +## CountDownLatch +Redis or Valkey based distributed [CountDownLatch](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RCountDownLatch.html) object for Java has structure similar to [CountDownLatch](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CountDownLatch.html) object. + +Should be initialized with count by `trySetCount(count)` method before usage. + +Code example: +```java +RCountDownLatch latch = redisson.getCountDownLatch("myCountDownLatch"); + +latch.trySetCount(1); +// await for count down +latch.await(); + +// in other thread or JVM +RCountDownLatch latch = redisson.getCountDownLatch("myCountDownLatch"); +latch.countDown(); +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RCountDownLatchAsync.html) interface** usage: +```java +RCountDownLatch latch = redisson.getCountDownLatch("myCountDownLatch"); + +RFuture setFuture = lock.trySetCountAsync(1); +// await for count down +RFuture awaitFuture = latch.awaitAsync(); + +// in other thread or JVM +RCountDownLatch latch = redisson.getCountDownLatch("myCountDownLatch"); +RFuture countFuture = latch.countDownAsync(); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RCountDownLatchReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RCountDownLatchReactive latch = redisson.getCountDownLatch("myCountDownLatch"); + +Mono setMono = latch.trySetCount(1); +// await for count down +Mono awaitMono = latch.await(); + +// in other thread or JVM +RCountDownLatchReactive latch = redisson.getCountDownLatch("myCountDownLatch"); +Mono countMono = latch.countDown(); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RCountDownLatchRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RCountDownLatchRx latch = redisson.getCountDownLatch("myCountDownLatch"); + +Single setRx = latch.trySetCount(1); +// await for count down +Completable awaitRx = latch.await(); + +// in other thread or JVM +RCountDownLatchRx latch = redisson.getCountDownLatch("myCountDownLatch"); +Completable countRx = latch.countDown(); +``` + +## Spin Lock +Redis or Valkey based distributed reentrant [SpinLock](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLock.html) object for Java and implements [Lock](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Lock.html) interface. + +Thousands or more locks acquired/released per short time interval may cause reaching of network throughput limit and Redis or Valkey CPU overload because of pubsub usage in [Lock](#lock) object. This occurs due to nature of pubsub - messages are distributed to all nodes in cluster. Spin Lock uses Exponential Backoff strategy by default for lock acquisition instead of pubsub channel. + +If Redisson instance which acquired lock crashes then such lock could hang forever in acquired state. To avoid this Redisson maintains lock watchdog, it prolongs lock expiration while lock holder Redisson instance is alive. By default lock watchdog timeout is 30 seconds and can be changed through [Config.lockWatchdogTimeout](../configuration.md) setting. + +`leaseTime` parameter during lock acquisition can be defined. After specified time interval locked lock will be released automatically. + +`RLock` object behaves according to the Java Lock specification. It means only lock owner thread can unlock it otherwise `IllegalMonitorStateException` would be thrown. Otherwise consider to use [RSemaphore](#semaphore) object. + +Code example: +```java +RLock lock = redisson.getSpinLock("myLock"); + +// traditional lock method +lock.lock(); + +// or acquire lock and automatically unlock it after 10 seconds +lock.lock(10, TimeUnit.SECONDS); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS); +if (res) { + try { + ... + } finally { + lock.unlock(); + } +} +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockAsync.html) interface** usage: +```java +RLock lock = redisson.getSpinLock("myLock"); + +long threadId = Thread.currentThread().getId(); +RFuture lockFuture = lock.lockAsync(threadId); + +// or acquire lock and automatically unlock it after 10 seconds +RFuture lockFuture = lock.lockAsync(10, TimeUnit.SECONDS, threadId); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +RFuture lockFuture = lock.tryLockAsync(100, 10, TimeUnit.SECONDS, threadId); + +lockFuture.whenComplete((res, exception) -> { + + // ... + + lock.unlockAsync(threadId); +}); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RLockReactive lock = redisson.getSpinLock("myLock"); + +long threadId = Thread.currentThread().getId(); +Mono lockMono = lock.lock(threadId); + +// or acquire lock and automatically unlock it after 10 seconds +Mono lockMono = lock.lock(10, TimeUnit.SECONDS, threadId); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +Mono lockMono = lock.tryLock(100, 10, TimeUnit.SECONDS, threadId); + +lockMono.doOnSuccess(res -> { + // ... +}) +.doFinally(r -> lock.unlock(threadId).subscribe()) +.subscribe(); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RLockRx lock = redisson.getSpinLock("myLock"); + +long threadId = Thread.currentThread().getId(); +Completable lockRes = lock.lock(threadId); + +// or acquire lock and automatically unlock it after 10 seconds +Completable lockRes = lock.lock(10, TimeUnit.SECONDS, threadId); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +Single lockRes = lock.tryLock(100, 10, TimeUnit.SECONDS, threadId); + +lockRes.doOnSuccess(res -> { + // ... +}) +.doFinally(() -> lock.unlock(threadId).subscribe()) +.subscribe(); +``` + +## Fenced Lock +Redis or Valkey based distributed reentrant [FencedLock](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RFencedLock.html) object for Java and implements [Lock](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Lock.html) interface. + +This type of lock maintains the fencing token to avoid cases when Client acquired the lock was delayed due to long GC pause or other reason and can't detect that it doesn't own the lock anymore. To resolve this issue token is returned by locking methods or `getToken()` method. Token should be checked if it's greater or equal with the previous one by the service guarded by this lock and reject operation if condition is false. + +If Redisson instance which acquired lock crashes then such lock could hang forever in acquired state. To avoid this Redisson maintains lock watchdog, it prolongs lock expiration while lock holder Redisson instance is alive. By default lock watchdog timeout is 30 seconds and can be changed through [Config.lockWatchdogTimeout](../configuration.md) setting. + +`leaseTime` parameter during lock acquisition can be defined. After specified time interval locked lock will be released automatically. + +`RLock` object behaves according to the Java Lock specification. It means only lock owner thread can unlock it otherwise `IllegalMonitorStateException` would be thrown. Otherwise consider to use [RSemaphore](#semaphore) object. + +Code example: +```java +RFencedLock lock = redisson.getFencedLock("myLock"); + +// traditional lock method +Long token = lock.lockAndGetToken(); + +// or acquire lock and automatically unlock it after 10 seconds +token = lock.lockAndGetToken(10, TimeUnit.SECONDS); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +Long token = lock.tryLockAndGetToken(100, 10, TimeUnit.SECONDS); +if (token != null) { + try { + // check if token >= old token + ... + } finally { + lock.unlock(); + } +} +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockAsync.html) interface** usage: +```java +RFencedLock lock = redisson.getFencedLock("myLock"); + +RFuture lockFuture = lock.lockAndGetTokenAsync(); + +// or acquire lock and automatically unlock it after 10 seconds +RFuture lockFuture = lock.lockAndGetTokenAsync(10, TimeUnit.SECONDS); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +RFuture lockFuture = lock.tryLockAndGetTokenAsync(100, 10, TimeUnit.SECONDS); + +long threadId = Thread.currentThread().getId(); +lockFuture.whenComplete((token, exception) -> { + if (token != null) { + try { + // check if token >= old token + ... + } finally { + lock.unlockAsync(threadId); + } + } +}); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RFencedLockReactive lock = redisson.getFencedLock("myLock"); + +long threadId = Thread.currentThread().getId(); +Mono lockMono = lock.lockAndGetToken(); + +// or acquire lock and automatically unlock it after 10 seconds +Mono lockMono = lock.lockAndGetToken(10, TimeUnit.SECONDS); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +Mono lockMono = lock.tryLockAndGetToken(100, 10, TimeUnit.SECONDS); + +lockMono.doOnSuccess(token -> { + if (token != null) { + try { + // check if token >= old token + ... + } finally { + lock.unlock(threadId).subscribe(); + } + } +}) +.subscribe(); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RLockRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RFencedLockRx lock = redisson.getFencedLock("myLock"); + +Single lockRes = lock.lockAndGetToken(); + +// or acquire lock and automatically unlock it after 10 seconds +Single lockRes = lock.lockAndGetToken(10, TimeUnit.SECONDS); + +// or wait for lock aquisition up to 100 seconds +// and automatically unlock it after 10 seconds +Single lockRes = lock.tryLock(100, 10, TimeUnit.SECONDS); + +long threadId = Thread.currentThread().getId(); +lockRes.doOnSuccess(token -> { + if (token != null) { + try { + // check if token >= old token + ... + } finally { + lock.unlock(threadId).subscribe(); + } + } +}) +.subscribe(); +``` diff --git a/docs/data-and-services/object-references.md b/docs/data-and-services/object-references.md new file mode 100644 index 000000000..1097ebdc2 --- /dev/null +++ b/docs/data-and-services/object-references.md @@ -0,0 +1,16 @@ +It's possible to use a Redisson object inside another Redisson object in any combination. In this case a special reference object will be used and handled by Redisson. +Usage example: +```java +RMap, RList> map = redisson.getMap("myMap"); +RSet set = redisson.getSet("mySet"); +RList list = redisson.getList("myList"); + +map.put(set, list); +// With the help of the special reference object, we can even create a circular +// reference which is impossible to achieve if we were to serialize its content +set.add(list); +list.add(map); +``` +As you may have noticed there is no need to re "save/persist" the map object after its elements have changed. Because it does not contain any value but merely a reference, this makes Redisson objects behaves much more like standard Java objects. In effect, making Redis or Valkey becomes part of JVM's memory rather than just a simple repository. + +One Redis HASH, one Redis SET and one Redis LIST will be created in the example above. \ No newline at end of file diff --git a/docs/data-and-services/objects.md b/docs/data-and-services/objects.md new file mode 100644 index 000000000..6e4b89a9d --- /dev/null +++ b/docs/data-and-services/objects.md @@ -0,0 +1,626 @@ +## Object holder +Java implementation of Redis or Valkey based [RBucket](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBucket.html) object is a holder for any type of object. Size is limited to 512Mb. + +Code example: +```java +RBucket bucket = redisson.getBucket("anyObject"); + +bucket.set(new AnyObject(1)); +AnyObject obj = bucket.get(); + +bucket.trySet(new AnyObject(3)); +bucket.compareAndSet(new AnyObject(4), new AnyObject(5)); +bucket.getAndSet(new AnyObject(6)); +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBucketAsync.html) interface** usage: +```java +RBucket bucket = redisson.getBucket("anyObject"); + +RFuture future = bucket.setAsync(new AnyObject(1)); +RFuture objfuture = bucket.getAsync(); + +RFuture tsFuture = bucket.trySetAsync(new AnyObject(3)); +RFuture csFuture = bucket.compareAndSetAsync(new AnyObject(4), new AnyObject(5)); +RFuture gsFuture = bucket.getAndSetAsync(new AnyObject(6)); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBucketReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RBucketReactive bucket = redisson.getBucket("anyObject"); + +Mono mono = bucket.set(new AnyObject(1)); +Mono objMono = bucket.get(); + +Mono tsMono = bucket.trySet(new AnyObject(3)); +Mono csMono = bucket.compareAndSet(new AnyObject(4), new AnyObject(5)); +Mono gsMono = bucket.getAndSet(new AnyObject(6)); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBucketRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RBucketRx bucket = redisson.getBucket("anyObject"); + +Completable rx = bucket.set(new AnyObject(1)); +Maybe objRx = bucket.get(); + +Single tsRx = bucket.trySet(new AnyObject(3)); +Single csRx = bucket.compareAndSet(new AnyObject(4), new AnyObject(5)); +Maybe gsRx = bucket.getAndSet(new AnyObject(6)); +``` +
+
+ +Use [RBuckets](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBuckets.html) interface to execute operations over multiple [RBucket](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBucket.html) objects: + +Code example: +```java +RBuckets buckets = redisson.getBuckets(); + +// get all bucket values +Map loadedBuckets = buckets.get("myBucket1", "myBucket2", "myBucket3"); + +Map map = new HashMap<>(); +map.put("myBucket1", new MyObject()); +map.put("myBucket2", new MyObject()); + +// sets all or nothing if some bucket is already exists +buckets.trySet(map); +// store all at once +buckets.set(map); +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBucketsAsync.html) interface** usage: +```java +RBuckets buckets = redisson.getBuckets(); + +// get all bucket values +RFuture> bucketsFuture = buckets.getAsync("myBucket1", "myBucket2", "myBucket3"); + +Map map = new HashMap<>(); +map.put("myBucket1", new MyObject()); +map.put("myBucket2", new MyObject()); + +// sets all or nothing if some bucket is already exists +RFuture tsFuture = buckets.trySetAsync(map); +// store all at once +RFuture sFuture = buckets.setAsync(map); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBucketsReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RBucketsReactive buckets = redisson.getBuckets(); + +// get all bucket values +Mono> bucketsMono = buckets.getAsync("myBucket1", "myBucket2", "myBucket3"); + +Map map = new HashMap<>(); +map.put("myBucket1", new MyObject()); +map.put("myBucket2", new MyObject()); + +// sets all or nothing if some bucket is already exists +Mono tsMono = buckets.trySet(map); +// store all at once +Mono sMono = buckets.set(map); +``` + +Code example of **[RxJava](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBucketsRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RBucketsRx buckets = redisson.getBuckets(); + +// get all bucket values +Single> bucketsRx = buckets.get("myBucket1", "myBucket2", "myBucket3"); + +Map map = new HashMap<>(); +map.put("myBucket1", new MyObject()); +map.put("myBucket2", new MyObject()); + +// sets all or nothing if some bucket is already exists +Single tsRx = buckets.trySet(map); +// store all at once +Completable sRx = buckets.set(map); +``` + +### Listeners + +Redisson allows to bind listeners per `RBucket` object. + +|Listener class name|Event description | +|:--:|:--:| +|org.redisson.api.listener.TrackingListener|Data created/updated after read operation| +|org.redisson.api.listener.SetObjectListener|Data created/updated| +|org.redisson.api.ExpiredObjectListener|`RBucket` object expired| +|org.redisson.api.DeletedObjectListener|`RBucket` object deleted| + +Usage example: + +```java +RBucket set = redisson.getBucket("anyObject"); + +int listenerId = set.addListener(new SetObjectListener() { + @Override + public void onSet(String name) { + // ... + } +}); + + +int listenerId = set.addListener(new DeletedObjectListener() { + @Override + public void onDeleted(String name) { + // ... + } +}); + +// ... + +set.removeListener(listenerId); +``` + +## Binary stream holder +Java implementation of Redis or Valkey based [RBinaryStream](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RBinaryStream.html) object holds sequence of bytes. It extends [RBucket](#object-holder) interface and size is limited to 512Mb. + +Code example: +```java +RBinaryStream stream = redisson.getBinaryStream("anyStream"); + +byte[] content = ... +stream.set(content); +stream.getAndSet(content); +stream.trySet(content); +stream.compareAndSet(oldContent, content); +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBucketAsync.html) interface** usage: +```java +RBinaryStream stream = redisson.getBinaryStream("anyStream"); + +byte[] content = ... +RFuture future = stream.set(content); +RFuture future = stream.getAndSet(content); +RFuture future = stream.trySet(content); +RFuture future = stream.compareAndSet(oldContent, content); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBinaryStreamReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RBinaryStreamReactive stream = redisson.getBinaryStream("anyStream"); + +ByteBuffer content = ... +Mono mono = stream.set(content); +Mono mono = stream.getAndSet(content); +Mono mono = stream.trySet(content); +Mono mono = stream.compareAndSet(oldContent, content); + +Mono mono = stream.write(content); +stream.position(0); +Mono mono = stream.read(b); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBinaryStreamRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RBinaryStreamRx stream = redisson.getBinaryStream("anyStream"); + +ByteBuffer content = ... +Completable rx = stream.set(content); +Maybe rx = stream.getAndSet(content); +Single rx = stream.trySet(content); +Single rx = stream.compareAndSet(oldContent, content); + +Single rx = stream.write(content); +stream.position(0); +Single rx = stream.read(b); +``` + +Code example of [java.io.InputStream](https://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html) and [java.io.OutputStream](https://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html) interfaces usage: +```java +RBinaryStream stream = redisson.getBinaryStream("anyStream"); + +InputStream is = stream.getInputStream(); +byte[] readBuffer = ... +is.read(readBuffer); + +OutputStream os = stream.getOuputStream(); +byte[] contentToWrite = ... +os.write(contentToWrite); +``` + +Code example of [java.nio.channels.SeekableByteChannel](https://docs.oracle.com/javase/8/docs/api/java/nio/channels/SeekableByteChannel.html) interface usage: +```java +RBinaryStream stream = redisson.getBinaryStream("anyStream"); + +SeekableByteChannel sbc = stream.getChannel(); +ByteBuffer readBuffer = ... +sbc.read(readBuffer); + +sbc.position(0); + +ByteBuffer contentToWrite = ... +sbc.write(contentToWrite); + +sbc.truncate(234); +``` + +Code example of [java.nio.channels.AsynchronousByteChannel](https://docs.oracle.com/javase/8/docs/api/java/nio/channels/AsynchronousByteChannel.html) interface usage: +```java +RBinaryStream stream = redisson.getBinaryStream("anyStream"); + +AsynchronousByteChannel sbc = stream.getAsynchronousChannel(); +ByteBuffer readBuffer = ... +sbc.read(readBuffer); + +ByteBuffer contentToWrite = ... +sbc.write(contentToWrite); +``` + +### Listeners + +Redisson allows to bind listeners per `RBinaryStream` object. + +|Listener class name|Event description | +|:--:|:--:| +|org.redisson.api.listener.TrackingListener|Data created/updated after read operation| +|org.redisson.api.listener.SetObjectListener|Data created/updated| +|org.redisson.api.ExpiredObjectListener|`RBinaryStream` object expired| +|org.redisson.api.DeletedObjectListener|`RBinaryStream` object deleted| + +Usage example: + +```java +RBinaryStream stream = redisson.getBinaryStream("anyObject"); + +int listenerId = set.addListener(new DeletedObjectListener() { + @Override + public void onDeleted(String name) { + // ... + } +}); + +// ... + +set.removeListener(listenerId); +``` + +## JSON object holder +Java implementation of [RJsonBucket](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RJsonBucket.html) object stores data in JSON format using `JSON.*` commands. JSON data encoding/decoding handled by `JsonCodec` which is a required parameter. Available implementation is `org.redisson.codec.JacksonCodec` which is thread-safe. + +Use [JSON Store](collections.md/#json-store) for key-value implementation and local cache. + +### Local cache + +Redisson provides JSON object holder implementation with local cache. + +**local cache** - so called near cache used to speed up read operations and avoid network roundtrips. It caches whole JSON object on Redisson side and executes read operations up to **45x faster** in comparison with common implementation. Local cache instances with the same name connected to the same pub/sub channel. This channel is used to exchange invalidate events between instances. + +|RedissonClient
method name | Local cache | Ultra-fast
read/write | +| ------------- | :-----------: | :---------:| +|getJsonBucket()
open-source version | ❌ | ❌ | +|getJsonBucket()
[Redisson PRO](https://redisson.pro) version | ❌ | ✔️ | +|getLocalCachedJsonBucket()
[Redisson PRO](https://redisson.pro) version | ✔️ | ✔️ | + +Code example: +```java +RJsonBucket bucket = redisson.getJsonBucket("anyObject", new JacksonCodec<>(AnyObject.class)); +// or local cached instance +RLocalCachedJsonBucket bucket = redisson.getLocalCachedJsonBucket("anyObject", new JacksonCodec<>(AnyObject.class)); + + +bucket.set(new AnyObject(1)); +AnyObject obj = bucket.get(); + +bucket.trySet(new AnyObject(3)); +bucket.compareAndSet(new AnyObject(4), new AnyObject(5)); +bucket.getAndSet(new AnyObject(6)); + +List values = bucket.get(new JacksonCodec<>(new TypeReference>() {}), "values"); +long aa = bucket.arrayAppend("$.obj.values", "t3", "t4"); +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RJsonBucketAsync.html) interface** usage: +```java +RJsonBucket bucket = redisson.getJsonBucket("anyObject", new JacksonCodec<>(AnyObject.class)); +// or local cached instace +RLocalCachedJsonBucket bucket = redisson.getLocalCachedJsonBucket("anyObject", new JacksonCodec<>(AnyObject.class)); + +RFuture future = bucket.setAsync(new AnyObject(1)); +RFuture objfuture = bucket.getAsync(); + +RFuture tsFuture = bucket.trySetAsync(new AnyObject(3)); +RFuture csFuture = bucket.compareAndSetAsync(new AnyObject(4), new AnyObject(5)); +RFuture gsFuture = bucket.getAndSetAsync(new AnyObject(6)); + +RFutue> gFuture = bucket.getAsync(new JacksonCodec<>(new TypeReference>() {}), "obj.values"); +RFutue aaFuture = bucket.arrayAppendAsync("$.obj.values", "t3", "t4"); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RJsonBucketReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RJsonBucketReactive bucket = redisson.getJsonBucket("anyObject", new JacksonCodec<>(AnyObject.class)); +// or local cached instance +RLocalCachedJsonBucketReactive bucket = redisson.getLocalCachedJsonBucket("anyObject", new JacksonCodec<>(AnyObject.class)); + + +Mono mono = bucket.set(new AnyObject(1)); +Mono objMono = bucket.get(); + +Mono tsMono = bucket.trySet(new AnyObject(3)); +Mono csMono = bucket.compareAndSet(new AnyObject(4), new AnyObject(5)); +Mono gsMono = bucket.getAndSet(new AnyObject(6)); + +Mono> vsMono = bucket.get(new JacksonCodec<>(new TypeReference>() {}), "values"); +Mono aaMono = bucket.arrayAppend("$.obj.values", "t3", "t4"); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RJsonBucketRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RJsonBucketRx bucket = redisson.getJsonBucket("anyObject", new JacksonCodec<>(AnyObject.class)); +// or local cached instance +RLocalCachedJsonBucketRx bucket = redisson.getLocalCachedJsonBucket("anyObject", new JacksonCodec<>(AnyObject.class)); + +Completable rx = bucket.set(new AnyObject(1)); +Maybe objRx = bucket.get(); + +Single tsRx = bucket.trySet(new AnyObject(3)); +Single csRx = bucket.compareAndSet(new AnyObject(4), new AnyObject(5)); +Maybe gsRx = bucket.getAndSet(new AnyObject(6)); + +Single> valuesRx = bucket.get(new JacksonCodec<>(new TypeReference>() {}), "values"); +Single aaRx = bucket.arrayAppend("$.obj.values", "t3", "t4"); +``` + +## Geospatial holder +Java implementation of Redis or Valkey based [RGeo](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RGeo.html) object is a holder for geospatial items. + +Code example: +```java +RGeo geo = redisson.getGeo("test"); + +geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), + new GeoEntry(15.087269, 37.502669, "Catania")); + +Double distance = geo.dist("Palermo", "Catania", GeoUnit.METERS); +Map positions = geo.pos("test2", "Palermo", "test3", "Catania", "test1"); + +List cities = geo.search(GeoSearchArgs.from(15, 37).radius(200, GeoUnit.KILOMETERS)); +Map citiesWithPositions = geo.searchWithPosition(GeoSearchArgs.from(15, 37).radius(200, GeoUnit.KILOMETERS)); +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RGeoAsync.html) interface** usage: +```java +RGeo geo = redisson.getGeo("test"); + +RFuture addFuture = geo.addAsync(new GeoEntry(13.361389, 38.115556, "Palermo"), + new GeoEntry(15.087269, 37.502669, "Catania")); + +RFuture distanceFuture = geo.distAsync("Palermo", "Catania", GeoUnit.METERS); +RFuture> positionsFuture = geo.posAsync("test2", "Palermo", "test3", "Catania", "test1"); + +RFuture> citiesFuture = geo.searchAsync(GeoSearchArgs.from(15, 37).radius(200, GeoUnit.KILOMETERS)); +RFuture> citiesWithPositions = geo.searchWithPositionAsync(GeoSearchArgs.from(15, 37).radius(200, GeoUnit.KILOMETERS)); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RGeoReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RGeoReactive bucket = redisson.getGeo("test"); + +Mono addFuture = geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), + new GeoEntry(15.087269, 37.502669, "Catania")); + +Mono distanceFuture = geo.dist("Palermo", "Catania", GeoUnit.METERS); +Mono> positionsFuture = geo.pos("test2", "Palermo", "test3", "Catania", "test1"); + +Mono> citiesFuture = geo.search(GeoSearchArgs.from(15, 37).radius(200, GeoUnit.KILOMETERS)); +Mono> citiesWithPositions = geo.searchWithPosition(GeoSearchArgs.from(15, 37).radius(200, GeoUnit.KILOMETERS)); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RGeoRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RGeoRx bucket = redisson.getGeo("test"); + +Single addFuture = geo.add(new GeoEntry(13.361389, 38.115556, "Palermo"), + new GeoEntry(15.087269, 37.502669, "Catania")); + +Single distanceFuture = geo.dist("Palermo", "Catania", GeoUnit.METERS); +Single> positionsFuture = geo.pos("test2", "Palermo", "test3", "Catania", "test1"); + +Single> citiesFuture = geo.search(GeoSearchArgs.from(15, 37).radius(200, GeoUnit.KILOMETERS)); +Single> citiesWithPositions = geo.searchWithPosition(GeoSearchArgs.from(15, 37).radius(200, GeoUnit.KILOMETERS)); +``` + +## BitSet +Java implementation of Redis or Valkey based [RBitSet](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBitSet.html) object provides API similar to [java.util.BitSet](https://docs.oracle.com/javase/8/docs/api/java/util/BitSet.html). It represents vector of bits that grows as needed. Size limited to `4 294 967 295` bits. + +Code example: +```java +RBitSet set = redisson.getBitSet("simpleBitset"); + +set.set(0, true); +set.set(1812, false); + +set.clear(0); + +set.and("anotherBitset"); +set.xor("anotherBitset"); +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBitSetAsync.html) interface** usage: +```java +RBitSetAsync set = redisson.getBitSet("simpleBitset"); + +RFuture setFuture = set.setAsync(0, true); +RFuture setFuture = set.setAsync(1812, false); + +RFuture clearFuture = set.clearAsync(0); + +RFuture andFuture = set.andAsync("anotherBitset); +RFuture xorFuture = set.xorAsync("anotherBitset"); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBitSetReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RBitSetReactive stream = redisson.getBitSet("simpleBitset"); + +Mono setMono = set.set(0, true); +Mono setMono = set.set(1812, false); + +Mono clearMono = set.clear(0); + +Mono andMono = set.and("anotherBitset); +Mono xorMono = set.xor("anotherBitset"); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBitSetRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RBitSetRx stream = redisson.getBitSet("simpleBitset"); + +Single setRx = set.set(0, true); +Single setRx = set.set(1812, false); + +Completable clearRx = set.clear(0); + +Completable andRx = set.and("anotherBitset); +Completable xorRx = set.xor("anotherBitset"); +``` + +### Data partitioning + +Although 'RBitSet' object is cluster compatible its content isn't scaled across multiple master nodes. BitSet data partitioning available only in cluster mode and implemented by separate `RClusteredBitSet` object. It uses distributed implementation of roaring bitmap structure. Size is limited by whole Cluster memory. More details about partitioning [here](data-partitioning.md). + +Below is the list of all available BitSet implementations: + +|RedissonClient
method name | Data partitioning
support | Ultra-fast read/write | +| ------------- | :----------:| :----------:| +|getBitSet()
open-source version | ❌ | ❌ | +|getBitSet()
[Redisson PRO](https://redisson.pro) version | ❌ | ✔️ | +|getClusteredBitSet()
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ✔️ | + +Code example: +```java +RClusteredBitSet set = redisson.getClusteredBitSet("simpleBitset"); +set.set(0, true); +set.set(1812, false); +set.clear(0); +set.addAsync("e"); +set.xor("anotherBitset"); +``` + +## Bloom filter +Redis or Valkey based distributed [RBloomFilter](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RBloomFilter.html) bloom filter for Java. Number of contained bits is limited to `2^32` with [data partitioning](data-partitioning.md) to `2^63` + +Must be initialized with capacity size by `tryInit(expectedInsertions, falseProbability)` method before usage. + +```java +RBloomFilter bloomFilter = redisson.getBloomFilter("sample"); +// initialize bloom filter with +// expectedInsertions = 55000000 +// falseProbability = 0.03 +bloomFilter.tryInit(55000000L, 0.03); + +bloomFilter.add(new SomeObject("field1Value", "field2Value")); +bloomFilter.add(new SomeObject("field5Value", "field8Value")); + +bloomFilter.contains(new SomeObject("field1Value", "field8Value")); +bloomFilter.count(); +``` + +### Data partitioning + +_This feature available only in [Redisson PRO](https://redisson.pro) edition._ + +Although 'RBloomFilter' object is cluster compatible its content isn't scaled across multiple master nodes. Bloom Filter data partitioning support available only in cluster mode and implemented by separate `RClusteredBloomFilter` object. This implementation uses more efficient distributed memory allocation algorithm. It allows to "shrink" memory space consumed by unused bits across all Redis or Valkey nodes. State of each instance is partitioned across all nodes in Redis or Valkey cluster. Number of contained bits is limited to `2^63`. More details about partitioning [here](data-partitioning.md). + +Below is the list of all available BloomFilter implementations: + +|RedissonClient
method name | Data partitioning
support | Ultra-fast read/write | Bits amount limit | +| ------------- | :----------:| :----------:| :----------:| +|getBloomFilter()
open-source version | ❌ | ❌ | 2^32 | +|getBloomFilter()
[Redisson PRO](https://redisson.pro) version | ❌ | ✔️ | 2^32 | +|getClusteredBloomFilter()
available only in [Redisson PRO](https://redisson.pro) | ✔️ | ✔️ | **2^63** | + +```java +RClusteredBloomFilter bloomFilter = redisson.getClusteredBloomFilter("sample"); +// initialize bloom filter with +// expectedInsertions = 255000000 +// falseProbability = 0.03 +bloomFilter.tryInit(255000000L, 0.03); +bloomFilter.add(new SomeObject("field1Value", "field2Value")); +bloomFilter.add(new SomeObject("field5Value", "field8Value")); +bloomFilter.contains(new SomeObject("field1Value", "field8Value")); +``` + + +## HyperLogLog +Redis or Valkey based distributed [RHyperLogLog](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RHyperLogLog.html) object for Java. Probabilistic data structure that lets you maintain counts of millions of items with extreme space efficiency. + +It has [Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RHyperLogLogAsync.html), [Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RHyperLogLogReactive.html) and [RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RHyperLogLogRx.html) interfaces. + +```java +RHyperLogLog log = redisson.getHyperLogLog("log"); +log.add(1); +log.add(2); +log.add(3); + +log.count(); +``` + +## RateLimiter +Redis or Valkey based distributed [RateLimiter](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RRateLimiter.html) object for Java restricts the total rate of calls either from all threads regardless of Redisson instance or from all threads working with the same Redisson instance. Doesn't guarantee fairness. + +Code example: +```java +RRateLimiter limiter = redisson.getRateLimiter("myLimiter"); +// Initialization required only once. +// 5 permits per 2 seconds +limiter.trySetRate(RateType.OVERALL, 5, 2, RateIntervalUnit.SECONDS); + +// acquire 3 permits or block until they became available +limiter.acquire(3); +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RRateLimiterAsync.html) interface** usage: +```java +RRateLimiter limiter = redisson.getRateLimiter("myLimiter"); +// Initialization required only once. +// 5 permits per 2 seconds +RFuture setRateFuture = limiter.trySetRate(RateType.OVERALL, 5, 2, RateIntervalUnit.SECONDS); + +// acquire 3 permits or block until they became available +RFuture aquireFuture = limiter.acquire(3); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RRateLimiterReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RRateLimiterReactive limiter = redisson.getRateLimiter("myLimiter"); +// Initialization required only once. +// 5 permits per 2 seconds +Mono setRateMono = limiter.trySetRate(RateType.OVERALL, 5, 2, RateIntervalUnit.SECONDS); + +// acquire 3 permits or block until they became available +Mono aquireMono = limiter.acquire(3); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RRateLimiterRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RRateLimiterRx limiter = redisson.getRateLimiter("anyObject"); + +// Initialization required only once. +// 5 permits per 2 seconds +Single setRateRx = limiter.trySetRate(RateType.OVERALL, 5, 2, RateIntervalUnit.SECONDS); + +// acquire 3 permits or block until they became available +Completable aquireRx = limiter.acquire(3); +``` diff --git a/docs/data-and-services/publish-subscribe.md b/docs/data-and-services/publish-subscribe.md new file mode 100644 index 000000000..1579cf7f0 --- /dev/null +++ b/docs/data-and-services/publish-subscribe.md @@ -0,0 +1,262 @@ +## Topic +Java implementation of Redis or Valkey based [RTopic](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RTopic.html) object implements Publish / Subscribe mechanism. It allows to subscribe on events published with multiple instances of `RTopic` object with the same name. + +Listeners are re-subscribed automatically after reconnection or failover. All messages sent during absence of connection are lost. Use [Reliable Topic](#reliable-topic) for reliable delivery. + +Code example: +```java +RTopic topic = redisson.getTopic("myTopic"); +int listenerId = topic.addListener(SomeObject.class, new MessageListener() { + @Override + public void onMessage(String channel, SomeObject message) { + //... + } +}); + +// in other thread or JVM +RTopic topic = redisson.getTopic("myTopic"); +long clientsReceivedMessage = topic.publish(new SomeObject()); +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RTopicAsync.html) interface** usage: +```java +RTopicAsync topic = redisson.getTopic("myTopic"); +RFuture listenerFuture = topic.addListenerAsync(SomeObject.class, new MessageListener() { + @Override + public void onMessage(String channel, SomeObject message) { + //... + } +}); + +// in other thread or JVM +RTopicAsync topic = redisson.getTopic("myTopic"); +RFuture publishFuture = topic.publishAsync(new SomeObject()); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RTopicReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RTopicReactive topic = redisson.getTopic("myTopic"); +Mono listenerMono = topic.addListener(SomeObject.class, new MessageListener() { + @Override + public void onMessage(String channel, SomeObject message) { + //... + } +}); + +// in other thread or JVM +RTopicReactive topic = redisson.getTopic("myTopic"); +Mono publishMono = topic.publish(new SomeObject()); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RTopicRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RTopicRx topic = redisson.getTopic("myTopic"); +Single listenerMono = topic.addListener(SomeObject.class, new MessageListener() { + @Override + public void onMessage(String channel, SomeObject message) { + //... + } +}); + +// in other thread or JVM +RTopicRx topic = redisson.getTopic("myTopic"); +Single publishMono = topic.publish(new SomeObject()); +``` + +## Topic pattern +Java implementation of Redis or Valkey based [RPatternTopic](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RPatternTopic.html) object. It allows to subscribe to multiple topics by specified glob-style pattern. + +Listeners are re-subscribed automatically after reconnection to a server or failover. + +Pattern examples: + +* `topic?` subscribes to `topic1`, `topicA` ... +* `topic?_my` subscribes to `topic_my`, `topic123_my`, `topicTEST_my` ... +* `topic[ae]` subscribes to `topica` and `topice` only + +Code example: +```java +// subscribe to all topics by `topic*` pattern +RPatternTopic patternTopic = redisson.getPatternTopic("topic*"); +int listenerId = patternTopic.addListener(Message.class, new PatternMessageListener() { + @Override + public void onMessage(String pattern, String channel, Message msg) { + //... + } +}); +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RPatternTopicAsync.html) interface** usage: +```java +RPatternTopicAsync patternTopic = redisson.getPatternTopic("topic*"); +RFuture listenerFuture = patternTopic.addListenerAsync(Message.class, new PatternMessageListener() { + @Override + public void onMessage(String pattern, String channel, Message msg) { + //... + } +}); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RPatternTopicReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RTopicReactive patternTopic = redisson.getPatternTopic("topic*"); +Mono listenerMono = patternTopic.addListener(Message.class, new PatternMessageListener() { + @Override + public void onMessage(String pattern, String channel, Message msg) { + //... + } +}); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RPatternTopicRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RTopicRx patternTopic = redisson.getPatternTopic("topic*"); +Single listenerSingle = patternTopic.addListener(Message.class, new PatternMessageListener() { + @Override + public void onMessage(String pattern, String channel, Message msg) { + //... + } +}); +``` + +## Sharded topic +Java implementation of Redis or Valkey based [RShardedTopic](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RShardedTopic.html) object implements Sharded Publish / Subscribe mechanism. It allows to subscribe on events published with multiple instances of `RShardedTopic` object with the same name. Subscribe/publish operations are executed only on Redis or Valkey node in Cluster which is bounded to specific topic name. Published messages via `RShardedTopic` aren't broadcasted across all nodes as for `RTopic` object. Which reduces network bandwidth and Redis or Valkey load. + +Listeners are re-subscribed automatically after reconnection to a server or failover. All messages sent during absence of connection are lost. Use [Reliable Topic](#reliable-topic) for reliable delivery. + +Code example: +```java +RShardedTopic topic = redisson.getShardedTopic("myTopic"); +int listenerId = topic.addListener(SomeObject.class, new MessageListener() { + @Override + public void onMessage(String channel, SomeObject message) { + //... + } +}); + +// in other thread or JVM +RShardedTopic topic = redisson.getShardedTopic("myTopic"); +long clientsReceivedMessage = topic.publish(new SomeObject()); +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RShardedTopicAsync.html) interface** usage: +```java +RShardedTopicAsync topic = redisson.getShardedTopic("myTopic"); +RFuture listenerFuture = topic.addListenerAsync(SomeObject.class, new MessageListener() { + @Override + public void onMessage(String channel, SomeObject message) { + //... + } +}); + +// in other thread or JVM +RShardedTopicAsync topic = redisson.getShardedTopic("myTopic"); +RFuture publishFuture = topic.publishAsync(new SomeObject()); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RShardedTopicReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RShardedTopicReactive topic = redisson.getShardedTopic("myTopic"); +Mono listenerMono = topic.addListener(SomeObject.class, new MessageListener() { + @Override + public void onMessage(String channel, SomeObject message) { + //... + } +}); + +// in other thread or JVM +RShardedTopicReactive topic = redisson.getShardedTopic("myTopic"); +Mono publishMono = topic.publish(new SomeObject()); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RShardedTopicRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RShardedTopicRx topic = redisson.getShardedTopic("myTopic"); +Single listenerMono = topic.addListener(SomeObject.class, new MessageListener() { + @Override + public void onMessage(String channel, SomeObject message) { + //... + } +}); + +// in other thread or JVM +RShardedTopicRx topic = redisson.getShardedTopic("myTopic"); +Single publishMono = topic.publish(new SomeObject()); +``` + +## Reliable Topic +Java implementation of Redis or Valkey based [RReliableTopic](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RReliableTopic.html) object implements Publish / Subscribe mechanism with reliable delivery of messages. In case of Redis or Valkey connection interruption all missed messages are delivered after reconnection to Redis. Message considered as delivered when it was received by Redisson and submited for processing by topic listeners. + +Each `RReliableTopic` object instance (subscriber) has own watchdog which is started when the first listener was registered. Subscriber expires after `org.redisson.config.Config#reliableTopicWatchdogTimeout` timeout if watchdog didn't extend it to the next timeout time interval. This prevents against infinity grow of stored messages in topic due to Redisson client crash or any other reason when subscriber unable to consume messages. + +Topic listeners are resubscribed automatically after reconnection to a server or failover. + +Code example: +```java +RReliableTopic topic = redisson.getReliableTopic("anyTopic"); +topic.addListener(SomeObject.class, new MessageListener() { + @Override + public void onMessage(CharSequence channel, SomeObject message) { + //... + } +}); + +// in other thread or JVM +RReliableTopic topic = redisson.getReliableTopic("anyTopic"); +long subscribersReceivedMessage = topic.publish(new SomeObject()); +``` + +Code example of **[Async](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RReliableTopicAsync.html) interface** usage: +```java +RReliableTopicAsync topic = redisson.getReliableTopic("anyTopic"); +RFuture listenerFuture = topic.addListenerAsync(SomeObject.class, new MessageListener() { + @Override + public void onMessage(CharSequence channel, SomeObject message) { + //... + } +}); + +// in other thread or JVM +RReliableTopicAsync topic = redisson.getReliableTopic("anyTopic"); +RFuture future = topic.publishAsync(new SomeObject()); +``` + +Code example of **[Reactive](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RReliableTopicReactive.html) interface** usage: +```java +RedissonReactiveClient redisson = redissonClient.reactive(); + +RReliableTopicReactive topic = redisson.getReliableTopic("anyTopic"); +Mono listenerMono = topic.addListener(SomeObject.class, new MessageListener() { + @Override + public void onMessage(CharSequence channel, SomeObject message) { + //... + } +}); + +// in other thread or JVM +RReliableTopicReactive topic = redisson.getReliableTopic("anyTopic"); +Mono publishMono = topic.publish(new SomeObject()); +``` + +Code example of **[RxJava3](https://static.javadoc.io/org.redisson/redisson/latest/org/redisson/api/RReliableTopicRx.html) interface** usage: +```java +RedissonRxClient redisson = redissonClient.rxJava(); + +RReliableTopicRx topic = redisson.getReliableTopic("anyTopic"); +Single listenerRx = topic.addListener(SomeObject.class, new MessageListener() { + @Override + public void onMessage(CharSequence channel, SomeObject message) { + //... + } +}); + +// in other thread or JVM +RReliableTopicRx topic = redisson.getReliableTopic("anyTopic"); +Single publisRx = topic.publish(new SomeObject()); +``` \ No newline at end of file diff --git a/docs/data-and-services/services.md b/docs/data-and-services/services.md new file mode 100644 index 000000000..1cd628421 --- /dev/null +++ b/docs/data-and-services/services.md @@ -0,0 +1,1337 @@ +## Remote service +Redisson provides Java Remote Services to execute remote procedure call using Redis or Valkey. Remote interface could have any type of method parameters and result object. Redis or Valkey is used to store method request and corresponding execution result. + +The RemoteService provides two types of [RRemoteService](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RRemoteService.html) instances: + +* __Server side instance__ - executes remote method (worker instance). +Example: +```java +RRemoteService remoteService = redisson.getRemoteService(); +SomeServiceImpl someServiceImpl = new SomeServiceImpl(); + +// register remote service before any remote invocation +// can handle only 1 invocation concurrently +remoteService.register(SomeServiceInterface.class, someServiceImpl); + +// register remote service able to handle up to 12 invocations concurrently +remoteService.register(SomeServiceInterface.class, someServiceImpl, 12); +``` +* __Client side instance__ - invokes remote method. +Example: +```java +RRemoteService remoteService = redisson.getRemoteService(); +SomeServiceInterface service = remoteService.get(SomeServiceInterface.class); + +String result = service.doSomeStuff(1L, "secondParam", new AnyParam()); +``` + +Client and server side instances shall be using the same remote interface and backed by redisson instances created using the same server connection configuration. Client and server side instances could be run in same JVM. There are no limits to the amount of client and/or server instances. +(Note: While redisson does not enforce any limits, [limitations from Redis or Valkey still apply](https://redis.io/topics/faq#what-is-the-maximum-number-of-keys-a-single-redis-instance-can-hold-and-what-the-max-number-of-elements-in-a-hash-list-set-sorted-set).) + +Remote invocations executes in __parallel mode__ if __1+__ workers are available. + + + +The total number of parallel executors is calculated as such: +`T` = `R` * `N` + +`T` - total available parallel executors +`R` - Redisson server side instance amount +`N` - executors amount defined during service registration + +Commands exceeding this number will be queued for the next available executor. + +Remote invocations executes in __sequential mode__ if only __1__ workers are available. Only one command can be handled concurrently in this case and the rest of commands will be queued. + + + +### Message flow +RemoteService creates two queues per invocation. One queue for request (being listened by server side instance) and another one is for ack-response and result-response (being listened by client side instance). Ack-response used to determine if method executor has got a request. If it doesn't during ack timeout then `RemoteServiceAckTimeoutException` will be thrown. + + Below is depicted a message flow for each remote invocation. + + + +### Fire-and-forget and ack-response modes +RemoteService options for each remote invocation could be defined via [RemoteInvocationOptions](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RemoteInvocationOptions.html) object. Such options allow to change timeouts and skip ack-response and/or result-response. Examples: + +```java +// 1 second ack timeout and 30 seconds execution timeout +RemoteInvocationOptions options = RemoteInvocationOptions.defaults(); + +// no ack but 30 seconds execution timeout +RemoteInvocationOptions options = RemoteInvocationOptions.defaults().noAck(); + +// 1 second ack timeout then forget the result +RemoteInvocationOptions options = RemoteInvocationOptions.defaults().noResult(); + +// 1 minute ack timeout then forget about the result +RemoteInvocationOptions options = RemoteInvocationOptions.defaults().expectAckWithin(1, TimeUnit.MINUTES).noResult(); + +// no ack and forget about the result (fire and forget) +RemoteInvocationOptions options = RemoteInvocationOptions.defaults().noAck().noResult(); + +RRemoteService remoteService = redisson.getRemoteService(); +YourService service = remoteService.get(YourService.class, options); +``` +### Asynchronous, Reactive and RxJava3 calls +Remote method could be executed using Async, Reactive and RxJava3 Api. + +**Asynchronous Remote interface**. Interface should be annotated with [@RRemoteAsync](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/annotation/RRemoteAsync.html). Method signatures match with the methods in remote interface and return [RFuture](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RFuture.html) object. + +**Reactive Remote interface**. Interface should be annotated with [@RRemoteReactive](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/annotation/RRemoteReactive.html). Method signatures match with the methods in remote interface and return `reactor.core.publisher.Mono` object. + +**RxJava3 Remote interface**. Interface should be annotated with [@RRemoteRx](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/annotation/RRemoteRx.html). Method signatures match with the methods in remote interface and return one of the following object: `io.reactivex.Completable`, `io.reactivex.Single`, `io.reactivex.Maybe`. + +It's not necessary to list all methods, only those which are needed. Below is an example of Remote Service interface: + +```java +public interface RemoteInterface { + + Long someMethod1(Long param1, String param2); + + void someMethod2(MyObject param); + + MyObject someMethod3(); + +} +``` + +**Asynchronous Remote interface** and method call example: +```java +@RRemoteAsync(RemoteInterface.class) +public interface RemoteInterfaceAsync { + + RFuture someMethod1(Long param1, String param2); + + RFuture someMethod2(MyObject param); + +} + +RedissonClient redisson = Redisson.create(config); + +RRemoteService remoteService = redisson.getRemoteService(); +RemoteInterfaceAsync asyncService = remoteService.get(RemoteInterfaceAsync.class); + +asyncService.someMethod1(1L, "myparam"); +``` + +**Reactive Remote interface** and method call example: +```java +@RRemoteReactive(RemoteInterface.class) +public interface RemoteInterfaceReactive { + + Mono someMethod1(Long param1, String param2); + + Mono someMethod2(MyObject param); + +} + +RedissonReactiveClient redisson = Redisson.createReactive(config); + +RRemoteService remoteService = redisson.getRemoteService(); +RemoteInterfaceReactive reactiveService = remoteService.get(RemoteInterfaceReactive.class); + +reactiveService.someMethod1(1L, "myparam"); +``` + +**RxJava3 Remote interface** and method call example: +```java +@RRemoteRx(RemoteInterface.class) +public interface RemoteInterfaceRx { + + Single someMethod1(Long param1, String param2); + + Completable someMethod2(MyObject param); + +} + +RedissonRxClient redisson = Redisson.createRx(config); + +RRemoteService remoteService = redisson.getRemoteService(); +RemoteInterfaceReactive rxService = remoteService.get(RemoteInterfaceRx.class); + +rxService.someMethod1(1L, "myparam"); +``` + +### Asynchronous, Reactive and RxJava3 call cancellation +Remote service provides ability to cancel invocation in any stages of its execution. There are three stages: + +1. Remote invocation request in queue +2. Remote invocation request received by remote service but not lunched and Ack-response hasn't send yet +3. Remote invocation execution in progress + +To handle third stage you need to check for `Thread.currentThread().isInterrupted()` status in your Remote service code. Here is an example: + +```java +public interface MyRemoteInterface { + + Long myBusyMethod(Long param1, String param2); + +} + +@RRemoteAsync(MyRemoteInterface.class) +public interface MyRemoteInterfaceAsync { + + RFuture myBusyMethod(Long param1, String param2); + +} + +@RRemoteReactive(MyRemoteInterface.class) +public interface MyRemoteInterfaceReactive { + + Mono myBusyMethod(Long param1, String param2); + +} + +@RRemoteRx(MyRemoteInterface.class) +public interface MyRemoteInterfaceRx { + + Single myBusyMethod(Long param1, String param2); + +} + + +// remote service implementation +public class MyRemoteServiceImpl implements MyRemoteInterface { + + public Long myBusyMethod(Long param1, String param2) { + for (long i = 0; i < Long.MAX_VALUE; i++) { + iterations.incrementAndGet(); + if (Thread.currentThread().isInterrupted()) { + System.out.println("interrupted! " + i); + return; + } + } + } + +} + +RRemoteService remoteService = redisson.getRemoteService(); +ExecutorService executor = Executors.newFixedThreadPool(5); +// register remote service using separate +// ExecutorService used to execute remote invocation +MyRemoteInterface serviceImpl = new MyRemoteServiceImpl(); +remoteService.register(MyRemoteInterface.class, serviceImpl, 5, executor); + +// call Asynchronous method +MyRemoteInterfaceAsync asyncService = remoteService.get(MyRemoteInterfaceAsync.class); +RFuture future = asyncService.myBusyMethod(1L, "someparam"); +// cancel invocation +future.cancel(true); + +// call Reactive method +MyRemoteInterfaceReactive reactiveService = remoteService.get(MyRemoteInterfaceReactive.class); +Mono mono = reactiveService.myBusyMethod(1L, "someparam"); +Disposable disp = mono.doOnSubscribe(s -> s.request(1)).subscribe(); +// cancel invocation +disp.dispose(); + +// call RxJava3 method +MyRemoteInterfaceRx asyncService = remoteService.get(MyRemoteInterfaceRx.class); +Single single = asyncService.myBusyMethod(1L, "someparam"); +Disposable disp = single.subscribe(); +// cancel invocation +disp.dispose(); +``` +## Live Object service + +### Introduction +A **Live Object** can be understood as an enhanced version of standard Java object, of which an instance reference can be shared not only between threads in a single JVM, but can also be shared between different JVMs across different machines.Wikipedia discribes it as: +> Live distributed object (also abbreviated as live object) refers to a running instance of a distributed multi-party (or peer-to-peer) protocol, viewed from the object-oriented perspective, as an entity that has a distinct identity, may encapsulate internal state and threads of execution, and that exhibits a well-defined externally visible behavior. + +Redisson Live Object (RLO) realised this idea by mapping all the fields inside a Java class to a HASH through a runtime-constructed proxy class. All the getters/setters methods of each field are translated to hget/hset commands operated on the HASH, making it accessable to/from any clients connected to the same Redis or Valkey server. Getters/setters/constructors can't be generated by byte-code tools like Lombok. Additional methods should use getters and not fields. As we all know, the field values of an object represent its state; having them stored in a remote repository, redis, makes it a distributed object. This object is a Redisson Live Object. + +By using RLO, sharing an object between applications and/or servers is the same as sharing one in a standalone application. This removes the need for serialization and deserialization, and at the same time reduces the complexity of the programming model: Changes made to one field is (almost^) immediately accessable to other processes, applications and servers. + +Since the Redis or Valkey server is a single-threaded application, all field access to the live object is automatically executed in atomic fashion: a value will not be changed when you are reading it. + +With RLO, you can treat the Redis or Valkey server as a shared Heap space for all connected JVMs. + +### Usage +Redisson provides different [annotations](#annotations) for Live Object. `@RId` and `@REntity` annotation are required to create and use Live Object. + +```java +@REntity +public class MyObject { + + @RId + private String id; + @RIndex + private String value; + private transient SimpleObject simpleObject; + private MyObject parent; + + public MyObject(String id) { + this.id = id; + } + + public MyObject() { + } + + // getters and setters + +} +``` + +Please note: fields marked with `transient` keyword aren't stored in Redis. + +To start use it you should use one of methods below: + +`RLiveObjectService.attach()` - attaches object to Redis. Discard all the field values already in the detached instance +`RLiveObjectService.merge()` - overrides current object state in Redis or Valkey with the given object state +`RLiveObjectService.persist()` - stores only new object + +Code example: + +```java +RLiveObjectService service = redisson.getLiveObjectService(); +MyLiveObject myObject = new MyLiveObject(); +myObject1.setId("1"); +// current state of myObject is now persisted and attached to Redis +myObject = service.persist(myObject); + +MyLiveObject myObject = new MyLiveObject("1"); +// current state of myObject is now cleared and attached to Redis +myObject = service.attach(myObject); + +MyLiveObject myObject = new MyLiveObject(); +myObject.setId("1"); +// current state of myObject is now merged to already existed object and attached to Redis +myObject = service.merge(myObject); +myObject.setValue("somevalue"); + +// get Live Object by Id +MyLiveObject myObject = service.get(MyLiveObject.class, "1"); + +// find Live Objects by value field +Collection myObjects = service.find(MyLiveObject.class, Conditions.in("value", "somevalue", "somevalue2")); + +Collection myObjects = service.find(MyLiveObject.class, Conditions.and(Conditions.in("value", "somevalue", "somevalue2"), Conditions.eq("secondfield", "test"))); +``` +"parent" field has a link to another instances of Live Object of the same type, but the type could be different. Redisson stores this link into Redis or Valkey as a reference object and not the whole object state, so you continue to work with reference object as Live Object. +```java +//Redisson Live Object behavior: +MyObject myObject = service.get(MyObject.class, "1"); +MyObject myParentObject = service.get(MyObject.class, "2"); +myObject.setValue(myParentObject); +``` + +Field types in the RLO can be almost anything, from Java util classes to collection/map types and of course your own custom objects, as long as it can be encoded and decoded by a supplied codec. More details about the codec can be found in the [Advanced Usage](#advanced-usage) section. + +In order to keep RLOs behaving as closely to standard Java objects as possible, Redisson automatically converts the following standard Java field types to its counter types supported by Redisson `RObject`. + +Standard Java Class | Converted Redisson Class +------------ | ------------ +SortedSet.class | RedissonSortedSet.class +Set.class | RedissonSet.class +ConcurrentMap.class | RedissonMap.class +Map.class | RedissonMap.class +BlockingDeque.class | RedissonBlockingDeque.class +Deque.class | RedissonDeque.class +BlockingQueue.class | RedissonBlockingQueue.class +Queue.class | RedissonQueue.class +List.class | RedissonList.class + +The conversion prefers the one nearer to the top of the table if a field type matches more than one entries. i.e. `LinkedList` implements `Deque`, `List`, `Queue`, it will be converted to a `RedissonDeque` because of this. + +Instances of these Redisson classes retains their states/values/entries in Redis or Valkey too, changes to them are directly reflected into Redis or Valkey without keeping values in local VM. + +### Search by Object properties + +Redisson provides comprehensive search engine for Live Object. To make a property participate in search it should be annotated with `@RIndex` annotation. + +!!! note "Open-source version of search engine is slow" + Use [Redisson PRO](https://redisson.pro) for **ultra-fast search engine**, **low JVM memory consumption during search process** and **search index partitiong in cluster**. + +Usage example: + +```java +@REntity +public class MyObject { + + @RId + private String id; + + @RIndex + private String field1; + + @RIndex + private Integer field2; + + @RIndex + private Long field3; +} +``` + +Different type of search conditions are available: + +`Conditions.and` - **AND** condition for collection of nested conditions +`Conditions.eq` - **EQUALS** condition which restricts property to defined value +`Conditions.or` - **OR** condition for collection of nested conditions +`Conditions.in` - **IN** condition which restricts property to set of defined values +`Conditions.gt` - **GREATER THAN** condition which restricts property to defined value +`Conditions.ge` - **GREATER THAN ON EQUAL** condition which restricts property to defined value +`Conditions.lt` - **LESS THAN** condition which restricts property to defined value +`Conditions.le` - **LESS THAN ON EQUAL** condition which restricts property to defined value + +Once object stored in Redis or Valkey we can run search: + +```java +RLiveObjectService liveObjectService = redisson.getLiveObjectService(); + +liveObjectService.persist(new MyObject()); + +// find all objects where field1 = value and field2 < 12 or field1 = value2 and field2 > 23 or field3 in [1, 2] + +Collection objects = liveObjectService.find(MyObject.class, + Conditions.or(Conditions.and(Conditions.eq("field1", "value"), Conditions.lt("field2", 12)), + Conditions.and(Conditions.eq("field1", "value2"), Conditions.gt("field2", 23)), + Conditions.in("field3", 1L, 2L)); +``` + +Search index expires after `expireXXX()` method call only if the Redis or Valkey `notify-keyspace-events` setting contains the letters `Kx`. + + +### Advanced Usage +As described before, RLO classes are proxy classes which can be fabricated when needed and then get cached in a `RedissonClient` instance against its original class. This process can be a bit slow and it is recommended to pre-register all the Redisson Live Object classes via `RedissonLiveObjectService` for any kind of delay-sensitive applications. The service can also be used to unregister a class if it is no longer needed. And of course it can be used to check if the class has already been registered. + +```java +RLiveObjectService service = redisson.getLiveObjectService(); +service.registerClass(MyClass.class); +service.unregisterClass(MyClass.class); +Boolean registered = service.isClassRegistered(MyClass.class); +``` + +### Annotations + +**@REntity** +Applied to a class. The behaviour of each type of RLO can be customised through properties of the `@REntity` annotation. You can specify each of those properties to gain fine control over its behaviour: + +* `namingScheme` - You can specify a naming scheme which tells Redisson how to assign key names for each instance of this class. It is used to create a reference to an existing Redisson Live Object and materialising a new one in redis. It defaults to use Redisson provided `DefaultNamingScheme`. +* `codec` - You can tell Redisson which `Codec` class you want to use for the RLO. Redisson will use an instance pool to locate the instance based on the class type. It defaults to `JsonJacksonCodec` provided by Redisson. +* `fieldTransformation` - You can also specify a field transformation mode for the RLO. As mentioned before, in order to keep everything as close to standard Java as possible, Redisson will automatically transform fields with commonly-used Java util classes to Redisson compatible classes. This uses `ANNOTATION_BASED` as the default value. You can set it to `IMPLEMENTATION_BASED` which will skip the transformation. + +**@RId** +Applied to a field. Defines `primary key` field of this class. The value of this field is used to create a reference to existing RLO. The field with this annotation is the only field that has its value also kept in the local VM. You can only have one `RId` annotation per class. + +You can supply a `generator` strategy to the `@RId` annotation if you want the value of this field to be programatically generated. The default generator is `null`. + +**@RIndex** +Applied to a field. Specifies that the field is used in search index. Allows to execute search query based on that field through `RLiveObjectService.find` method. + +**@RObjectField** +Applied to a field. Allows to specify `namingScheme` and/or `codec` different from what is specified in `@REntity`. + +**@RCascade** +Applied to a field. Specifies that the defined cascade types are applied to the object/objects contained in Live Object field. +Different cascade types are available: + +* `RCascadeType.ALL` - Includes all cascade types +* `RCascadeType.PERSIST` - Cascade persist operation during `RLiveObjectService.persist()` method invocation +* `RCascadeType.DETACH` - Cascade detach operation during `RLiveObjectService.detach()` method invocation +* `RCascadeType.MERGE` - Cascade merge operation during `RLiveObjectService.merge()` method invocation +* `RCascadeType.DELETE` - Cascade delete operation during `RLiveObjectService.delete()` method invocation. + +## Executor service + +### Overview +Redis or Valkey based [RExecutorService](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RExecutorService.html) object implements of [ExecutorService](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) interface to run [Callable](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Callable.html), [Runnable](https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) and Lambda tasks. Task and result objects are serialized using defined codec and stored in two request/response Redis or Valkey queues. Redisson instance and task id can be injected in task object. Task id has size 128-bits and globally unique. This allows to process Redis or Valkey data and execute distributed computations in fast and efficient way. + +### Workers + +Worker runs task submitted through [RExecutorService](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RExecutorService.html) interface. Worker should be registered with `WorkerOptions` settings object. Each worker polls a task from a head of Redis or Valkey queue in order it requested it. Polled tasks are executed with ExecutorService. Global default ExecutorService from Redisson configuration is used if ExecutorService in `WorkerOptions` isn't defined. + +Consider to use [Redisson node](./12.-Standalone-node) if you need standalone jar which register and executes workers. + +WorkerOptions exposes follow settings: +```java + WorkerOptions options = WorkerOptions.defaults() + + // Defines workers amount used to execute tasks. + // Default is 1. + .workers(2) + + // Defines Spring BeanFactory instance to execute tasks + // with Spring's '@Autowired', '@Value' or JSR-330's '@Inject' annotation. + .beanFactory(beanFactory) + + // Defines custom ExecutorService to execute tasks + // Config.executor is used by default. + .executorService() + + // Defines task timeout since task execution start moment + .taskTimeout(60, TimeUnit.SECONDS) + + // add listener which is invoked on task successed event + .addListener(new TaskSuccessListener() { + public void onSucceeded(String taskId, T result) { + // ... + } + }) + + // add listener which is invoked on task failed event + .addListener(new TaskFailureListener() { + public void onFailed(String taskId, Throwable exception) { + // ... + } + }) + + // add listener which is invoked on task started event + .addListener(new TaskStartedListener() { + public void onStarted(String taskId) { + // ... + } + }) + + // add listener which is invoked on task finished event + .addListener(new TaskFinishedListener() { + public void onFinished(String taskId) { + // ... + } + }); +``` + +Code example of worker registration with options defined above: +```java +RExecutorService executor = redisson.getExecutorService("myExecutor"); +executor.registerWorkers(options); +``` + +### Tasks +Redisson node doesn't require presence of task classes in classpath. Task classes are loaded automatically by Redisson node ClassLoader. Thus each new task class doesn't require restart of Redisson node. + +Example with `Callable` task: +```java +public class CallableTask implements Callable { + + @RInject + private RedissonClient redissonClient; + + @RInject + private String taskId; + + @Override + public Long call() throws Exception { + RMap map = redissonClient.getMap("myMap"); + Long result = 0; + for (Integer value : map.values()) { + result += value; + } + return result; + } + +} +``` + +Example with `Runnable` task: +```java +public class RunnableTask implements Runnable { + + @RInject + private RedissonClient redissonClient; + + @RInject + private String taskId; + + private long param; + + public RunnableTask() { + } + + public RunnableTask(long param) { + this.param = param; + } + + @Override + public void run() { + RAtomicLong atomic = redissonClient.getAtomicLong("myAtomic"); + atomic.addAndGet(param); + } + +} +``` + +Follow options could be supplied during ExecutorService aquisition: + +```java +ExecutorOptions options = ExecutorOptions.defaults() + +// Defines task retry interval at the end of which task is executed again. +// ExecutorService worker re-schedule task execution retry every 5 seconds. +// +// Set 0 to disable. +// +// Default is 5 minutes +options.taskRetryInterval(10, TimeUnit.MINUTES); +``` + +```java +RExecutorService executorService = redisson.getExecutorService("myExecutor", options); +executorService.submit(new RunnableTask(123)); + +RExecutorService executorService = redisson.getExecutorService("myExecutor", options); +Future future = executorService.submit(new CallableTask()); +Long result = future.get(); +``` + +Example with Lambda task: +```java +RExecutorService executorService = redisson.getExecutorService("myExecutor", options); +Future future = executorService.submit((Callable & Serializable)() -> { + System.out.println("task has been executed!"); +}); +Long result = future.get(); +``` + +Each Redisson node has ready to use RedissonClient which could be injected using `@RInject` annotation. + +### Tasks with Spring beans +Redisson allows to inject not only RedissonClient and task id using `@RInject` annotation but also supports Spring's `@Autowire`, `@Value` and JSR-330's `@Inject` annotations. Injected Spring Bean service interface should implement Serializable. + +Redisson uses Spring's BeanFactory object for injection. It should be defined in Redisson Node [configuration](../standalone-node.md#settings). + +Example with `Callable` task: +```java +public class CallableTask implements Callable { + + @Autowired + private MySpringService service; + + @Value("myProperty") + private String prop; + + @RInject + private RedissonClient redissonClient; + + @Override + public Integer call() throws Exception { + RMap map = redissonClient.getMap(prop); + + Integer result = service.someMethod(); + map.put("test", result); + return result; + } + +} +``` + +```java +RExecutorService executorService = redisson.getExecutorService("myExecutor", options); +Future future = executorService.submit(new CallableTask()); +Integer result = future.get(); +``` + +### Task execution cancellation +It's easy to cancel any submitted task via `RFuture.cancel()` or `RExecutorService.cancelTask()` method. To handle case then task execution is already in progress you need to check Thread status for interruption with `Thread.currentThread().isInterrupted()` invocation: +```java +public class CallableTask implements Callable { + + @RInject + private RedissonClient redissonClient; + + @Override + public Long call() throws Exception { + RMap map = redissonClient.getMap("myMap"); + Long result = 0; + // map contains many entries + for (Integer value : map.values()) { + if (Thread.currentThread().isInterrupted()) { + // task has been canceled + return null; + } + result += value; + } + return result; + } + +} + +RExecutorService executorService = redisson.getExecutorService("myExecutor"); + +// submit tasks synchronously for asynchronous execution. +RExecutorFuture future = executorService.submit(new CallableTask()); + +// submit tasks asynchronously for asynchronous execution. +RExecutorFuture future = executorService.submitAsync(new CallableTask()); + +// cancel future +future.cancel(true); + +// cancel by taskId +executorService.cancelTask(future.getTaskId()); +``` +## Scheduled executor service + +### Overview +Redis or Valkey based [RScheduledExecutorService](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RScheduledExecutorService.html) implements [ScheduledExecutorService](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html) interface to schedule [Callable](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Callable.html), [Runnable](https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) and Lambda tasks. Scheduled task is a job which needs to be execute in the future at a particular time one or more times. Task and result objects are serialized and stored in request/response Redis or Valkey queues. Redisson instance and task id can be injected into task object. Task id has size 128-bits and globally unique. This allows to process Redis or Valkey data and execute distributed computations in fast and efficient way. + +### Workers + +Worker runs task submitted through [RScheduledExecutorService](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RScheduledExecutorService.html) interface. Worker should be registered with `WorkerOptions` settings object. Each worker polls a task from head of Redis or Valkey queue in order it requested it. Polled tasks are executed with ExecutorService. Global default ExecutorService from Redisson configuration is used if ExecutorService in `WorkerOptions` isn't defined. + +Consider to use [Redisson node](./12.-Standalone-node) if you need standalone jar which register and executes workers. + +WorkerOptions exposes follow settings: +```java + WorkerOptions options = WorkerOptions.defaults() + + // Defines workers amount used to execute tasks. + // Default is 1. + .workers(2) + + // Defines Spring BeanFactory instance to execute tasks + // with Spring's '@Autowired', '@Value' or JSR-330's '@Inject' annotation. + .beanFactory(beanFactory) + + // Defines custom ExecutorService to execute tasks + // Config.executor is used by default. + .executorService() + + // Defines task timeout since task execution start moment + .taskTimeout(60, TimeUnit.SECONDS) + + // add listener which is invoked on task successed event + .addListener(new TaskSuccessListener() { + public void onSucceeded(String taskId, T result) { + // ... + } + }) + + // add listener which is invoked on task failed event + .addListener(new TaskFailureListener() { + public void onFailed(String taskId, Throwable exception) { + // ... + } + }) + + // add listener which is invoked on task started event + .addListener(new TaskStartedListener() { + public void onStarted(String taskId) { + // ... + } + }) + + // add listener which is invoked on task finished event + .addListener(new TaskFinishedListener() { + public void onFinished(String taskId) { + // ... + } + }); +``` + +Code example of worker registration with options defined above: +```java +RScheduledExecutorService executor = redisson.getExecutorService("myExecutor"); +executor.registerWorkers(options); +``` + + +### Scheduling a task +Redisson node doesn't require presence of task classes in classpath. Task classes are loaded automatically by Redisson node ClassLoader. Thus each new task class doesn't require restart of Redisson node. + +Example with `Callable` task: +```java +public class CallableTask implements Callable { + + @RInject + private RedissonClient redissonClient; + + @Override + public Long call() throws Exception { + RMap map = redissonClient.getMap("myMap"); + Long result = 0; + for (Integer value : map.values()) { + result += value; + } + return result; + } + +} +``` +Follow options could be supplied during ExecutorService aquisition: + +```java +ExecutorOptions options = ExecutorOptions.defaults() + +// Defines task retry interval at the end of which task is executed again. +// ExecutorService worker re-schedule task execution retry every 5 seconds. +// +// Set 0 to disable. +// +// Default is 5 minutes +options.taskRetryInterval(10, TimeUnit.MINUTES); +``` + +```java +RScheduledExecutorService executorService = redisson.getExecutorService("myExecutor"); +ScheduledFuture future = executorService.schedule(new CallableTask(), 10, TimeUnit.MINUTES); +Long result = future.get(); +``` + +Example with Lambda task: +```java +RScheduledExecutorService executorService = redisson.getExecutorService("myExecutor", options); +ScheduledFuture future = executorService.schedule((Callable & Serializable)() -> { + System.out.println("task has been executed!"); +}, 10, TimeUnit.MINUTES); +Long result = future.get(); +``` + + +Example with `Runnable` task: +```java +public class RunnableTask implements Runnable { + + @RInject + private RedissonClient redissonClient; + + private long param; + + public RunnableTask() { + } + + public RunnableTask(long param) { + this.param= param; + } + + @Override + public void run() { + RAtomicLong atomic = redissonClient.getAtomicLong("myAtomic"); + atomic.addAndGet(param); + } + +} + +RScheduledExecutorService executorService = redisson.getExecutorService("myExecutor"); +ScheduledFuture future1 = executorService.schedule(new RunnableTask(123), 10, TimeUnit.HOURS); +// ... +ScheduledFuture future2 = executorService.scheduleAtFixedRate(new RunnableTask(123), 10, 25, TimeUnit.HOURS); +// ... +ScheduledFuture future3 = executorService.scheduleWithFixedDelay(new RunnableTask(123), 5, 10, TimeUnit.HOURS); +``` + +### Scheduling a task with Spring beans +Redisson allows to inject not only RedissonClient using `@RInject` annotation but also supports Spring's `@Autowire`, `@Value` and JSR-330's `@Inject` annotations. Injected Spring Bean service interface should implement Serializable. + +Redisson uses Spring's BeanFactory object for injection. It should be defined in Redisson Node [configuration](https://github.com/redisson/redisson/wiki/12.-Standalone-node#beanfactory). + +Example with `Callable` task: +```java +public class RunnableTask implements Runnable { + + @Autowired + private MySpringService service; + + @Value("myProperty") + private String prop; + + @RInject + private RedissonClient redissonClient; + + private long param; + + public RunnableTask() { + } + + public RunnableTask(long param) { + this.param = param; + } + + @Override + public void run() { + RMap map = redissonClient.getMap(prop); + + Integer result = service.someMethod(); + map.put("test", result); + return result; + } + +} +``` + +```java +RScheduledExecutorService executorService = redisson.getExecutorService("myExecutor"); +ScheduledFuture future1 = executorService.schedule(new RunnableTask(123), 10, TimeUnit.HOURS); +// ... +ScheduledFuture future2 = executorService.scheduleAtFixedRate(new RunnableTask(123), 10, 25, TimeUnit.HOURS); +// ... +ScheduledFuture future3 = executorService.scheduleWithFixedDelay(new RunnableTask(123), 5, 10, TimeUnit.HOURS); +``` + +### Scheduling a task with cron expression +Tasks scheduler service allows to define more complex schedule with cron expressions. Which fully compatible with [Quartz cron format](https://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html). + +Example: +```java +RScheduledExecutorService executorService = redisson.getExecutorService("myExecutor"); +executorService.schedule(new RunnableTask(), CronSchedule.of("10 0/5 * * * ?")); +// ... +executorService.schedule(new RunnableTask(), CronSchedule.dailyAtHourAndMinute(10, 5)); +// ... +executorService.schedule(new RunnableTask(), CronSchedule.weeklyOnDayAndHourAndMinute(12, 4, Calendar.MONDAY, Calendar.FRIDAY)); +``` +### Task scheduling cancellation +Scheduled executor service provides two ways to cancel scheduled task via `RScheduledFuture.cancel()` or `RScheduledExecutorService.cancelTask()` method. To handle case then task execution is already in progress you need to check Thread status for interruption with `Thread.currentThread().isInterrupted()` invocation: +```java +public class RunnableTask implements Callable { + + @RInject + private RedissonClient redissonClient; + + @Override + public Long call() throws Exception { + RMap map = redissonClient.getMap("myMap"); + Long result = 0; + // map contains many entries + for (Integer value : map.values()) { + if (Thread.currentThread().isInterrupted()) { + // task has been canceled + return null; + } + result += value; + } + return result; + } + +} + +RScheduledExecutorService executorService = redisson.getExecutorService("myExecutor"); + +// submit tasks synchronously for asynchronous execution. +RScheduledFuture future = executorService.schedule(new RunnableTask(), CronSchedule.dailyAtHourAndMinute(10, 5)); + +// submit tasks asynchronously for asynchronous execution. +RScheduledFuture future = executorService.scheduleAsync(new RunnableTask(), CronSchedule.dailyAtHourAndMinute(10, 5)); + +// cancel future +future.cancel(true); + +// cancel by taskId +executorService.cancelTask(future.getTaskId()); +``` + +## MapReduce service + +### Overview + +Redisson provides MapReduce programming model to process large amount of data stored in Redis. Based on ideas from different implementations and [Google's MapReduce publication](https://research.google.com/archive/mapreduce.html). Supported by different objects: `RMap`, `RMapCache`, `RLocalCachedMap`, `RClusteredMap`, `RClusteredMapCache`, `RClusteredLocalCachedMap`, `RClusteredSet`, `RClusteredSetCache`, `RSet`, `RSetCache`, `RList`, `RSortedSet`, `RScoredSortedSet`, `RQueue`, `RBlockingQueue`, `RDeque`, `RBlockingDeque`, `RPriorityQueue` and `RPriorityDeque`. + +MapReduce model based on few objects: `RMapper`/`RCollectionMapper`, `RReducer` and `RCollator`. + +All tasks for _map_ and _reduce_ phases are executed across [Redisson Nodes](https://github.com/redisson/redisson/wiki/12.-Standalone-node). + +For `RLocalCachedMap`, `RClusteredMap`, `RClusteredMapCache`, `RClusteredLocalCachedMap`, `RClusteredSet`, `RClusteredSetCache` objects _Map phase_ is splitted to multiple sub-tasks and executes in parallel. For other objects _Map phase_ is executed in single sub-task. _Reduce phase_ is always splitted to multiple sub-tasks and executes in parallel. Sub-tasks amount equals to total amount of registered MapReduce workers. + +**1. `RMapper` applied only for Map objects and transforms each Map's entry to intermediate key/value pair.** +```java +public interface RMapper extends Serializable { + + void map(KIn key, VIn value, RCollector collector); + +} +``` + +**2. `RCollectionMapper ` applied only for Collection objects and transforms each Collection's entry to intermediate key/value pair.** +```java +public interface RCollectionMapper extends Serializable { + + void map(VIn value, RCollector collector); + +} +``` + +**3. `RReducer` reduces a list of intermediate key/value pairs.** +```java +public interface RReducer extends Serializable { + + V reduce(K reducedKey, Iterator values); + +} +``` +**4. `RCollator` converts reduced result to a single object.** + +```java +public interface RCollator extends Serializable { + + R collate(Map resultMap); + +} +``` + +Each type of task has ability to inject RedissonClient using `@RInject` annotation like this: +```java + public class WordMapper implements RMapper { + + @RInject + private RedissonClient redissonClient; + + @Override + public void map(String key, String value, RCollector collector) { + + // ... + + redissonClient.getAtomicLong("mapInvocations").incrementAndGet(); + } + + } + +``` + +By default MapReduce time execution is not limited, but timeout could be defined if required: +```java + = list.mapReduce() + .mapper(new WordMapper()) + .reducer(new WordReducer()) + .timeout(60, TimeUnit.MINUTES); +``` + +### Map example +MapReduce is available for map type objects: `RMap`, `RMapCache` and `RLocalCachedMap`. + +Below is word count example using MapReduce: + +1. Mapper object applied to each Map entry and split value by space to separate words. +```java + public class WordMapper implements RMapper { + + @Override + public void map(String key, String value, RCollector collector) { + String[] words = value.split("[^a-zA-Z]"); + for (String word : words) { + collector.emit(word, 1); + } + } + + } +``` + +2. Reducer object calculates the total sum for each word. +```java + public class WordReducer implements RReducer { + + @Override + public Integer reduce(String reducedKey, Iterator iter) { + int sum = 0; + while (iter.hasNext()) { + Integer i = (Integer) iter.next(); + sum += i; + } + return sum; + } + + } +``` + +3. Collator object counts total amount of words. +```java + public class WordCollator implements RCollator { + + @Override + public Integer collate(Map resultMap) { + int result = 0; + for (Integer count : resultMap.values()) { + result += count; + } + return result; + } + + } +``` + +4. Here is how to run all together: +```java + RMap map = redisson.getMap("wordsMap"); + map.put("line1", "Alice was beginning to get very tired"); + map.put("line2", "of sitting by her sister on the bank and"); + map.put("line3", "of having nothing to do once or twice she"); + map.put("line4", "had peeped into the book her sister was reading"); + map.put("line5", "but it had no pictures or conversations in it"); + map.put("line6", "and what is the use of a book"); + map.put("line7", "thought Alice without pictures or conversation"); + + RMapReduce mapReduce + = map.mapReduce() + .mapper(new WordMapper()) + .reducer(new WordReducer()); + + // count occurrences of words + Map mapToNumber = mapReduce.execute(); + + // count total words amount + Integer totalWordsAmount = mapReduce.execute(new WordCollator()); +``` + + +### Collection example +MapReduce is available for collection type objects: `RSet`, `RSetCache`, `RList`, `RSortedSet`, `RScoredSortedSet`, `RQueue`, `RBlockingQueue`, `RDeque`, `RBlockingDeque`, `RPriorityQueue` and `RPriorityDeque`. + +Below is word count example using MapReduce: + +```java + public class WordMapper implements RCollectionMapper { + + @Override + public void map(String value, RCollector collector) { + String[] words = value.split("[^a-zA-Z]"); + for (String word : words) { + collector.emit(word, 1); + } + } + + } +``` +```java + public class WordReducer implements RReducer { + + @Override + public Integer reduce(String reducedKey, Iterator iter) { + int sum = 0; + while (iter.hasNext()) { + Integer i = (Integer) iter.next(); + sum += i; + } + return sum; + } + + } +``` +```java + public class WordCollator implements RCollator { + + @Override + public Integer collate(Map resultMap) { + int result = 0; + for (Integer count : resultMap.values()) { + result += count; + } + return result; + } + + } +``` +```java + RList list = redisson.getList("myList"); + list.add("Alice was beginning to get very tired"); + list.add("of sitting by her sister on the bank and"); + list.add("of having nothing to do once or twice she"); + list.add("had peeped into the book her sister was reading"); + list.add("but it had no pictures or conversations in it"); + list.add("and what is the use of a book"); + list.add("thought Alice without pictures or conversation"); + + RCollectionMapReduce mapReduce + = list.mapReduce() + .mapper(new WordMapper()) + .reducer(new WordReducer()); + + // count occurrences of words + Map mapToNumber = mapReduce.execute(); + + // count total words amount + Integer totalWordsAmount = mapReduce.execute(new WordCollator()); +``` + +## RediSearch service + +Redisson provides RediSearch integration. Supports field indexing of [RMap](Distributed-collections.md/#map), [RJSONStore](Distributed-collections.md/#json-store) and [RJSONBucket](Distributed-objects.md/#json-object-holder) objects, query execution and aggregation. + +It has [Async](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RSearchAsync.html), [Reactive](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RSearchReactive.html) and [RxJava3](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RSearchRx.html) interfaces. + +### Query execution + +Code example for RMap object: +```java +RMap m = redisson.getMap("doc:1", new CompositeCodec(StringCodec.INSTANCE, redisson.getConfig().getCodec())); +m.put("v1", new SimpleObject("name1")); +m.put("v2", new SimpleObject("name2")); +RMap m2 = redisson.getMap("doc:2", new CompositeCodec(StringCodec.INSTANCE, redisson.getConfig().getCodec())); +m2.put("v1", new SimpleObject("name3")); +m2.put("v2", new SimpleObject("name4")); + +RSearch s = redisson.getSearch(); + +// creates an index for documents with prefix "doc" +s.createIndex("idx", IndexOptions.defaults() + .on(IndexType.HASH) + .prefix(Arrays.asList("doc:")), + FieldIndex.text("v1"), + FieldIndex.text("v2")); + +SearchResult r = s.search("idx", "*", QueryOptions.defaults() + .returnAttributes(new ReturnAttribute("v1"), new ReturnAttribute("v2"))); + +SearchResult r = s.search("idx", "*", QueryOptions.defaults() + .filters(QueryFilter.geo("field") + .from(1, 1) + .radius(10, GeoUnit.FEET))); + +``` + +Code example for JSON object: +```java + public class TestClass { + + private List arr; + private String value; + + public TestClass() { + } + + public TestClass(List arr, String value) { + this.arr = arr; + this.value = value; + } + + public List getArr() { + return arr; + } + + public TestClass setArr(List arr) { + this.arr = arr; + return this; + } + + public String getValue() { + return value; + } + + public TestClass setValue(String value) { + this.value = value; + return this; + } + } + + +RJsonBucket b = redisson.getJsonBucket("doc:1", new JacksonCodec<>(TestClass.class)); +b.set(new TestClass(Arrays.asList(1, 2, 3), "hello")); + +RSearch s = redisson.getSearch(StringCodec.INSTANCE); +// creates an index for documents with prefix "doc" +s.createIndex("idx", IndexOptions.defaults() + .on(IndexType.JSON) + .prefix(Arrays.asList("doc:")), + FieldIndex.numeric("$..arr").as("arr"), + FieldIndex.text("$..value").as("val")); + +SearchResult r = s.search("idx", "*", QueryOptions.defaults() + .returnAttributes(new ReturnAttribute("arr"), new ReturnAttribute("val"))); +// total amount of found documents +long total = r.getTotal(); +// found documents +List docs = r.getDocuments(); + +for (Document doc: docs) { + String id = doc.getId(); + Map attrs = doc.getAttributes(); +} +``` + +### Aggregation + +Code example for Map object: + +```java +RMap m = redisson.getMap("doc:1", new CompositeCodec(StringCodec.INSTANCE, redisson.getConfig().getCodec())); +m.put("v1", new SimpleObject("name1")); +m.put("v2", new SimpleObject("name2")); +RMap m2 = redisson.getMap("doc:2", new CompositeCodec(StringCodec.INSTANCE, redisson.getConfig().getCodec())); +m2.put("v1", new SimpleObject("name3")); +m2.put("v2", new SimpleObject("name4")); + +RSearch s = redisson.getSearch(); +// creates an index for documents with prefix "doc" +s.createIndex("idx", IndexOptions.defaults() + .on(IndexType.HASH) + .prefix(Arrays.asList("doc:")), + FieldIndex.text("v1"), + FieldIndex.text("v2")); + +AggregationResult r = s.aggregate("idx", "*", AggregationOptions.defaults() + .load("v1", "v2")); +// total amount of attributes +long total = r.getTotal(); +// list of attributes mapped by attribute name +List> attrs = r.getAttributes(); +``` + +Code example for JSON object: + +```java + public class TestClass { + + private List arr; + private String value; + + public TestClass() { + } + + public TestClass(List arr, String value) { + this.arr = arr; + this.value = value; + } + + public List getArr() { + return arr; + } + + public TestClass setArr(List arr) { + this.arr = arr; + return this; + } + + public String getValue() { + return value; + } + + public TestClass setValue(String value) { + this.value = value; + return this; + } + } + +RJsonBucket b = redisson.getJsonBucket("doc:1", new JacksonCodec<>(TestClass.class)); +// stores object in JSON format +b.set(new TestClass(Arrays.asList(1, 2, 3), "hello")); + +RSearch s = redisson.getSearch(StringCodec.INSTANCE); +// creates an index for documents with prefix "doc" +s.createIndex("idx", IndexOptions.defaults() + .on(IndexType.JSON) + .prefix(Arrays.asList("doc:")), + FieldIndex.numeric("$..arr").as("arr"), + FieldIndex.text("$..value").as("val")); + +AggregationResult r = s.aggregate("idx", "*", AggregationOptions.defaults() + .load("arr", "val")); +// total amount of attributes +long total = r.getTotal(); +// list of attributes mapped by attribute name +List> attrs = r.getAttributes(); + +``` + +### Spellcheck + +Code example. + +```java +RSearch s = redisson.getSearch(); + +s.createIndex("idx", IndexOptions.defaults() + .on(IndexType.HASH) + .prefix(Arrays.asList("doc:")), + FieldIndex.text("t1"), + FieldIndex.text("t2")); + +s.addDict("name", "hockey", "stik"); + +Map> res = s.spellcheck("idx", "Hocke sti", SpellcheckOptions.defaults() + .includedTerms("name")); + +// returns misspelled terms and their score - "hockey", 0 +Map m = res.get("hocke"); + +// returns misspelled terms and their score - "stik", 0 +Map m = res.get("sti"); +``` \ No newline at end of file diff --git a/docs/dependency-list.md b/docs/dependency-list.md new file mode 100644 index 000000000..9982e162a --- /dev/null +++ b/docs/dependency-list.md @@ -0,0 +1,26 @@ +Below is the libraries used by Redisson: + +| Group id | Artifact Id | Version | Dependency | +| ------------- | ------------- | ------------| ------------| +| com.esotericsoftware | kryo | 5.4+| **required** (if Kryo is used as codec)| +| com.esotericsoftware | reflectasm | 1.11+ | **required** (if Kryo is used as codec)| +| com.esotericsoftware | minlog | 1.3+ | **required** (if Kryo is used as codec)| +| org.objenesis | objenesis| 3.3+ | **required** (if Kryo is used as codec)| +| io.netty | netty-common | 4.1+ | **required**| +| io.netty | netty-codec | 4.1+ | **required** | +| io.netty | netty-buffer | 4.1+ | **required** | +| io.netty | netty-transport | 4.1+ | **required** | +| io.netty | netty-handler | 4.1+ | **required** | +| io.netty | netty-resolver | 4.1+ | **required** | +| io.netty | netty-resolver-dns | 4.1+ | **required** | +| com.fasterxml.jackson.dataformat | jackson-core | 2.7+ | **required** | +| com.fasterxml.jackson.dataformat | jackson-databind | 2.7+ | **required** | +| com.fasterxml.jackson.dataformat | jackson-annotations | 2.7+ | **required** | +| com.fasterxml.jackson.dataformat | jackson-dataformat-yaml | 2.7+ | **required** | +| org.yaml | snakeyaml | 1.0+ | **required** | +| net.bytebuddy | byte-buddy | 1.6+ | _optional (used by LiveObject service)_ | +| org.jodd | jodd-bean | 3.7+ | _optional (used by LiveObject service)_ | +| javax.cache | cache-api | 1.1.1 | _optional (used by JCache implementation)_ | +| io.projectreactor | reactor-core | 3.1+ | _optional (used by RedissonReactiveClient)_ | +| io.reactivex.rxjava3 | rxjava | 3.0+ | _optional (used by RedissonRxClient)_ | + diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 000000000..5e1c6926a --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,47 @@ +**Q: What is the cause of RedisTimeoutException?** + +**A** : There are multiple reasons: + +1. All netty threads are busy, leading to delays in both Redis response decoding and sending commands to Redis. +2. All connections are busy. +3. Redis server is busy and takes too long to respond the request. +4. Java application is busy. +5. Blocking invocation in async/reactive/rx listeners or subscribeOnElements methods. +6. Server CPU throttling. For GCP hosting try to add `--no-cpu-throttling` to CloudRun container which connects via Redisson to the Redis instance. +7. Unstable network with TCP packet drops. +8. Redis vendor limits concurrent connections amount. + +First try to set follow values for `nettyThreads` setting: 32, 64, 128, 256 this allows Redisson to get a free netty thread to decode a response or send a command. Next, try to increase `retryInterval` and/or `timeout` to a reasonable value so that a command can still gracefully fail without having the end user wait forever. At the last step, try to increase `connection pool` setting so that Redisson can stand a better chance in getting a free connection. + +Complex commands such as `keys`, `hmget` and big loops in Lua scripts are more likely to see it than other commands. It is important to understand an operation can still timeout despite the absence of it from the Redis slowlog. Slowlogs only record the time a command is been processed by the Redis event loop and not anything before or after. Network issue may also cause this exception when a response is still in-flight. + +**Q: When do I need to shut down a Redisson instance, at the end of each request or the end of the life of a thread?** + +**A** : Redisson instance requires manual shutdown only if you want to stop using all of its features. It is a common pattern that Redisson starts and stops along with the application. Since it is completely thread safe, you may treat a Redisson instance as a singleton. The shutdown sequence will disconnect all the active connections held in each connection pool, and it will clean up certain types of Redisson objects require a manual destroy action upon disposal, it will then stop the event loops. Please be advised, the entire shutdown process is not instant. + +**Q: In MapCache/SetCache/SpringCache/JCache, I have set an expiry time to an entry, why is it still in Redis when it should be disappeared?** + +**A** : The first and foremost thing you need to understand is entry expiry feature is not supported by Redis. This is one of Redisson’s own creation. Which means it requires Redisson to work. You can’t expect a correct behaviour using other clients such as redis-cli or even jedis. + +Redisson employs both active approach and passive approach, just like Redis server, to ensure an element is evicted when its time is due. There is are scheduled eviction tasks that runs periodically to remove the expired elements from the collection, Redisson also checks the expiry information when an element is accessed: if it has expired, it will be removed and a null value will be returned. + +So if you saw the stale value in redis-cli, please do not panic, it will be removed when the scheduled task catches up or when you next request it through Redisson. + +**Q: How can I perform Pipelining/Transaction through Redisson?** + +**A** : The fact is pipelining and transaction are dealt with virtually in the same way by the `RBatch` object in Redisson. This is the design decision we took based on the analysis of the characteristics of both techniques. From the client side point of view, through both techniques, you are expected to issue a series of commands consecutively, and more importantly you will only expect to see the results after the last command is executed. + +There are only subtle differences between the two techniques lies within the `RBatch` API. There is an `atomic()` method you should use before issuing batch commands if you want to execute a series of commands in a single transaction. There is no other usage difference. + +And yes, if this answers your other question, transactional commands are always dispatched in a single pipeline. + +**Q: Is Redisson thread safe? Can I share an instance of it between different threads?** + +**A** : The Redisson instance itself and all the objects it provides are thread safe. APIs exposed by those objects are just operation handles. None of the these objects keep any states that would break the thread safety local to the instance. Most of those objects that do not “contain” any data local to the JVM with exception to LocalCached instances for obvious reasons. And here is a piece of pro advice: You can even share a RObject to multiple machines by publish it over a RTopic. + +**Q: Can I use different encoder/decoders for different tasks?** + +**A** : A different codec to the default one can be supplied when creating a `RObject` instance: +```java +RMap map = redisson.getMap("myMap", new MyCodec()); +``` diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 000000000..0a9e29cc0 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,81 @@ +**1. Add dependency** + +``` python +import tensorflow as tf +``` + +Maven + +```xml + + org.redisson + redisson + xVERSIONx + +``` + +Gradle + +```java +compile 'org.redisson:redisson:xVERSIONx' +``` + +SBT + +```java +libraryDependencies += "org.redisson" % "redisson" % "xVERSIONx" +``` + +**2. Start development** + +```java +// 1. Create config object +Config config = new Config(); +config.useClusterServers() + // use "rediss://" for SSL connection + .addNodeAddress("redis://127.0.0.1:7181"); + +// or read config from file +config = Config.fromYAML(new File("config-file.yaml")); +``` + +```java +// 2. Create Redisson instance + +// Sync and Async API +RedissonClient redisson = Redisson.create(config); + +// Reactive API +RedissonReactiveClient redissonReactive = redisson.reactive(); + +// RxJava3 API +RedissonRxClient redissonRx = redisson.rxJava(); +``` + +```java +// 3. Get Redis or Valkey based implementation of java.util.concurrent.ConcurrentMap +RMap map = redisson.getMap("myMap"); + +RMapReactive mapReactive = redissonReactive.getMap("myMap"); + +RMapRx mapRx = redissonRx.getMap("myMap"); +``` + +```java +// 4. Get Redis or Valkey based implementation of java.util.concurrent.locks.Lock +RLock lock = redisson.getLock("myLock"); + +RLockReactive lockReactive = redissonReactive.getLock("myLock"); + +RLockRx lockRx = redissonRx.getLock("myLock"); +``` + +```java +// 4. Get Redis or Valkey based implementation of java.util.concurrent.ExecutorService +RExecutorService executor = redisson.getExecutorService("myExecutorService"); + +// over 50 Redis or Valkey based Java objects and services ... + +``` + +Upgrade to __[Redisson PRO](https://redisson.pro)__ with **advanced features**. \ No newline at end of file diff --git a/docs/integration-with-spring.md b/docs/integration-with-spring.md new file mode 100644 index 000000000..18d7a7180 --- /dev/null +++ b/docs/integration-with-spring.md @@ -0,0 +1,584 @@ +## Spring Boot Starter + +Integrates Redisson with Spring Boot library. Depends on [Spring Data Redis](#spring-data-redis) module. + +Supports Spring Boot 1.3.x - 3.3.x + +### Usage + +**1. Add `redisson-spring-boot-starter` dependency into your project:** + +Maven + +```xml + + org.redisson + redisson-spring-boot-starter + xVERSIONx + +``` + +Gradle + +```groovy +compile 'org.redisson:redisson-spring-boot-starter:xVERSIONx' +``` + +`redisson-spring-boot-starter` depends on `redisson-spring-data` module compatible with the latest version of Spring Boot. Downgrade `redisson-spring-data` module if necessary to support previous Spring Boot versions: + +|redisson-spring-data
module name|Spring Boot
version| +|----------------------------|-------------------| +|redisson-spring-data-16 |1.3.y | +|redisson-spring-data-17 |1.4.y | +|redisson-spring-data-18 |1.5.y | +|redisson-spring-data-2x |2.x.y | +|redisson-spring-data-3x |3.x.y | + +For Gradle, you can downgrade to `redisson-spring-data-27` this way: + +```groovy +implementation ("org.redisson:redisson-spring-boot-starter:xVERSIONx") { + exclude group: 'org.redisson', module: 'redisson-spring-data-33' +} +implementation "org.redisson:redisson-spring-data-27:xVERSIONx" +``` + +**2. Add settings into `application.settings` file** + +Using common Spring Boot 3.x+ settings: + +```yaml +spring: + data: + redis: + database: + host: + port: + password: + ssl: + timeout: + connectTimeout: + clientName: + cluster: + nodes: + sentinel: + master: + nodes: +``` + +Using common Spring Boot up to 2.7.x settings: + +```yaml +spring: + redis: + database: + host: + port: + password: + ssl: + timeout: + connectTimeout: + clientName: + cluster: + nodes: + sentinel: + master: + nodes: +``` + + +Using Redisson config file: +([single mode](configuration.md/#single-yaml-config-format), +[replicated mode](configuration.md/#replicated-yaml-config-format), +[cluster mode](configuration.md/#cluster-yaml-config-format), +[sentinel mode](configuration.md/#sentinel-yaml-config-format), +[proxy mode](configuration.md/#proxy-mode-yaml-config-format), +[multi cluster mode](configuration.md/#multi-cluster-yaml-config-format), +[multi sentinel mode](configuration.md/#multi-sentinel-yaml-config-format)) + + +```yaml +spring: + redis: + redisson: + file: classpath:redisson.yaml +``` + +Using Redisson settings: +([single mode](configuration.md/#single-settings), +[replicated mode](configuration.md/#replicated-settings), +[cluster mode](configuration.md/#cluster-settings), +[sentinel mode](configuration.md/#sentinel-settings), +[proxy mode](configuration.md/#proxy-mode-settings), +[multi cluster mode](configuration.md/#multi-cluster-settings), +[multi sentinel mode](configuration.md/#multi-sentinel-settings)) + +```yaml +spring: + redis: + redisson: + config: | + clusterServersConfig: + idleConnectionTimeout: 10000 + connectTimeout: 10000 + timeout: 3000 + retryAttempts: 3 + retryInterval: 1500 + failedSlaveReconnectionInterval: 3000 + failedSlaveCheckInterval: 60000 + password: null + subscriptionsPerConnection: 5 + clientName: null + loadBalancer: ! {} + subscriptionConnectionMinimumIdleSize: 1 + subscriptionConnectionPoolSize: 50 + slaveConnectionMinimumIdleSize: 24 + slaveConnectionPoolSize: 64 + masterConnectionMinimumIdleSize: 24 + masterConnectionPoolSize: 64 + readMode: "SLAVE" + subscriptionMode: "SLAVE" + nodeAddresses: + - "redis://127.0.0.1:7004" + - "redis://127.0.0.1:7001" + - "redis://127.0.0.1:7000" + scanInterval: 1000 + pingConnectionInterval: 0 + keepAlive: false + tcpNoDelay: false + threads: 16 + nettyThreads: 32 + codec: ! {} + transportMode: "NIO" + +``` + +**3. Available Spring Beans:** + +- `RedissonClient` +- `RedissonRxClient` +- `RedissonReactiveClient` +- `RedisTemplate` +- `ReactiveRedisTemplate` + +Upgrade to __[Redisson PRO](https://redisson.pro)__ with **advanced features**. + +### FAQ + +**Q: How to replace Netty version brought by Spring Boot?** + +You need to define netty version in properties section of your Maven project. + +```xml + + 4.1.107.Final + +``` + +**Q: How to disable Redisson?** + +You may not have Redis or Valkey in some environments. In this case Redisson can be disabled: + +- Using Annotations +Spring Boot 2.7+ +```java +@SpringBootApplication +@EnableAutoConfiguration(exclude = { + RedissonAutoConfigurationV2.class}) +public class Application { + + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } +} +``` +Spring Boot up to 2.6 +```java +@SpringBootApplication +@EnableAutoConfiguration(exclude = { + RedissonAutoConfiguration.class}) +public class Application { + + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } +} +``` +- Using application.yml file +Spring Boot 2.7+ +```yaml +spring: + autoconfigure: + exclude: + - org.redisson.spring.starter.RedissonAutoConfigurationV2 +``` +Spring Boot up to 2.6 +```yaml +spring: + autoconfigure: + exclude: + - org.redisson.spring.starter.RedissonAutoConfiguration +``` + +{% include 'cache/Spring-cache.md' %} + +## Spring Session +Please note that Redis or Valkey `notify-keyspace-events` setting should contain `Exg` letters to make Spring Session integration work. + +Ensure you have Spring Session library in your classpath, add it if necessary: + +**Maven** + +```xml + + org.springframework.session + spring-session-core + 3.2.1 + + + + org.redisson + redisson-spring-data-33 + xVERSIONx + +``` + +**Gradle** + +```gradle +compile 'org.springframework.session:spring-session-core:3.2.1' + +compile 'org.redisson:redisson-spring-data-33:xVERSIONx' +``` + +### Spring Http Session configuration + +Add configuration class which extends `AbstractHttpSessionApplicationInitializer` class: + ```java + @Configuration + @EnableRedisHttpSession + public class SessionConfig extends AbstractHttpSessionApplicationInitializer { + + @Bean + public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) { + return new RedissonConnectionFactory(redisson); + } + + @Bean(destroyMethod = "shutdown") + public RedissonClient redisson(@Value("classpath:/redisson.yaml") Resource configFile) throws IOException { + Config config = Config.fromYAML(configFile.getInputStream()); + return Redisson.create(config); + } + + } + ``` + +### Spring WebFlux’s Session configuration + +Add configuration class which extends `AbstractReactiveWebInitializer` class: + ```java + @Configuration + @EnableRedisWebSession + public class SessionConfig extends AbstractReactiveWebInitializer { + + @Bean + public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) { + return new RedissonConnectionFactory(redisson); + } + + @Bean(destroyMethod = "shutdown") + public RedissonClient redisson(@Value("classpath:/redisson.yaml") Resource configFile) throws IOException { + Config config = Config.fromYAML(configFile.getInputStream()); + return Redisson.create(config); + } + } + ``` + +### Spring Boot configuration + +1. Add Spring Session Data Redis library in classpath: +Maven: +```xml + + org.springframework.session + spring-session-data-redis + 3.2.1 + +``` +Gradle: +```gradle +compile 'org.springframework.session:spring-session-data-redis:3.2.1' +``` +2. Add Redisson Spring Data Redis library in classpath: +Maven: +```xml + + org.redisson + redisson-spring-data-33 + xVERSIONx + +``` +Gradle: +```gradle +compile 'org.redisson:redisson-spring-data-33:xVERSIONx' +``` +3. Define follow properties in spring-boot settings +``` +spring.session.store-type=redis +spring.redis.redisson.file=classpath:redisson.yaml +spring.session.timeout.seconds=900 +``` + +Upgrade to __[Redisson PRO](https://redisson.pro)__ with **advanced features**. + +## Spring Transaction Manager + +Redisson provides implementation of both `org.springframework.transaction.PlatformTransactionManager` and `org.springframework.transaction.ReactiveTransactionManager` interfaces to participant in Spring transactions. See also [Transactions](Transactions.md) section. + +### Spring Transaction Management + +```java +@Configuration +@EnableTransactionManagement +public class RedissonTransactionContextConfig { + + @Bean + public TransactionalBean transactionBean() { + return new TransactionalBean(); + } + + @Bean + public RedissonTransactionManager transactionManager(RedissonClient redisson) { + return new RedissonTransactionManager(redisson); + } + + @Bean(destroyMethod="shutdown") + public RedissonClient redisson(@Value("classpath:/redisson.yaml") Resource configFile) throws IOException { + Config config = Config.fromYAML(configFile.getInputStream()); + return Redisson.create(config); + } + +} + + +public class TransactionalBean { + + @Autowired + private RedissonTransactionManager transactionManager; + + @Transactional + public void commitData() { + RTransaction transaction = transactionManager.getCurrentTransaction(); + RMap map = transaction.getMap("test1"); + map.put("1", "2"); + } + +} +``` + +### Reactive Spring Transaction Management + +```java +@Configuration +@EnableTransactionManagement +public class RedissonReactiveTransactionContextConfig { + + @Bean + public TransactionalBean transactionBean() { + return new TransactionalBean(); + } + + @Bean + public ReactiveRedissonTransactionManager transactionManager(RedissonReactiveClient redisson) { + return new ReactiveRedissonTransactionManager(redisson); + } + + @Bean(destroyMethod="shutdown") + public RedissonReactiveClient redisson(@Value("classpath:/redisson.yaml") Resource configFile) throws IOException { + Config config = Config.fromYAML(configFile.getInputStream()); + return Redisson.createReactive(config); + } + +} + +public class TransactionalBean { + + @Autowired + private ReactiveRedissonTransactionManager transactionManager; + + @Transactional + public Mono commitData() { + Mono transaction = transactionManager.getCurrentTransaction(); + return transaction.flatMap(t -> { + RMapReactive map = t.getMap("test1"); + return map.put("1", "2"); + }).then(); + } + +} +``` + +## Spring Cloud Stream + +_This feature is available only in [Redisson PRO](https://redisson.pro) edition._ + +Redisson implements Spring Cloud Stream integration based on the reliable Redis Stream structure for message delivery. To use Redis binder with Redisson you need to add [Spring Cloud Stream](https://spring.io/projects/spring-cloud-stream) Binder library in classpath: + +Maven: +```xml + + pro.redisson + spring-cloud-stream-binder-redisson + xVERSIONx + +``` +Gradle: +```gradle +compile 'pro.redisson:spring-cloud-stream-binder-redisson:xVERSIONx' +``` + +Compatible with Spring versions below. + +Spring Cloud Stream | Spring Cloud | Spring Boot +-- | -- | -- +4.1.x | 2023.0.x | 3.0.x, 3.1.x, 3.2.x +4.0.x | 2022.0.x | 3.0.x, 3.1.x, 3.2.x +3.2.x | 2021.0.x | 2.6.x, 2.7.x (Starting with 2021.0.3 of Spring Cloud) +3.1.x | 2020.0.x | 2.4.x, 2.5.x (Starting with 2020.0.3 of Spring Cloud) + +### Receiving messages + +Register the input binder (an event sink) for receiving messages as follows: + +```java +@Bean +public Consumer receiveMessage() { + return obj -> { + // consume received object ... + }; +} +``` + +Define channel id in the configuration file `application.properties`. Example for `receiveMessage` bean defined above connected to `my-channel` channel: + +``` +spring.cloud.stream.bindings.receiveMessage-in-0.destination=my-channel +``` + +### Publishing messages + +Register the output binder (an event source) for publishing messages as follows: + +```java +@Bean +public Supplier feedSupplier() { + return () -> { + // ... + return new MyObject(); + }; +} +``` + +Define channel id in the configuration file `application.properties`. Example for `feedSupplier` bean defined above connected to `my-channel` channel: + +``` +spring.cloud.stream.bindings.feedSupplier-out-0.destination=my-channel +spring.cloud.stream.bindings.feedSupplier-out-0.producer.useNativeEncoding=true +``` + +## Spring Data Redis + +Integrates Redisson with Spring Data Redis library. Implements Spring Data's `RedisConnectionFactory` and `ReactiveRedisConnectionFactory` interfaces and allows to interact with Redis through `RedisTemplate` or `ReactiveRedisTemplate` object. + +### Usage +1. Add `redisson-spring-data` dependency into your project: +Maven: +```xml + + org.redisson + + redisson-spring-data-16 + + redisson-spring-data-17 + + redisson-spring-data-18 + + redisson-spring-data-20 + + redisson-spring-data-21 + + redisson-spring-data-22 + + redisson-spring-data-23 + + redisson-spring-data-24 + + redisson-spring-data-25 + + redisson-spring-data-26 + + redisson-spring-data-27 + + redisson-spring-data-30 + + redisson-spring-data-31 + + redisson-spring-data-32 + + redisson-spring-data-33 + xVERSIONx + +``` +Gradle: +```groovy +// for Spring Data Redis v.1.6.x +compile 'org.redisson:redisson-spring-data-16:xVERSIONx' +// for Spring Data Redis v.1.7.x +compile 'org.redisson:redisson-spring-data-17:xVERSIONx' +// for Spring Data Redis v.1.8.x +compile 'org.redisson:redisson-spring-data-18:xVERSIONx' +// for Spring Data Redis v.2.0.x +compile 'org.redisson:redisson-spring-data-20:xVERSIONx' +// for Spring Data Redis v.2.1.x +compile 'org.redisson:redisson-spring-data-21:xVERSIONx' +// for Spring Data Redis v.2.2.x +compile 'org.redisson:redisson-spring-data-22:xVERSIONx' +// for Spring Data Redis v.2.3.x +compile 'org.redisson:redisson-spring-data-23:xVERSIONx' +// for Spring Data Redis v.2.4.x +compile 'org.redisson:redisson-spring-data-24:xVERSIONx' +// for Spring Data Redis v.2.5.x +compile 'org.redisson:redisson-spring-data-25:xVERSIONx' +// for Spring Data Redis v.2.6.x +compile 'org.redisson:redisson-spring-data-26:xVERSIONx' +// for Spring Data Redis v.2.7.x +compile 'org.redisson:redisson-spring-data-27:xVERSIONx' +// for Spring Data Redis v.3.0.x +compile 'org.redisson:redisson-spring-data-30:xVERSIONx' +// for Spring Data Redis v.3.1.x +compile 'org.redisson:redisson-spring-data-31:xVERSIONx' +// for Spring Data Redis v.3.2.x +compile 'org.redisson:redisson-spring-data-32:xVERSIONx' +// for Spring Data Redis v.3.3.x +compile 'org.redisson:redisson-spring-data-33:xVERSIONx' +``` +2. Register `RedissonConnectionFactory` in Spring context: +```java +@Configuration +public class RedissonSpringDataConfig { + + @Bean + public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) { + return new RedissonConnectionFactory(redisson); + } + + @Bean(destroyMethod = "shutdown") + public RedissonClient redisson(@Value("classpath:/redisson.yaml") Resource configFile) throws IOException { + Config config = Config.fromYAML(configFile.getInputStream()); + return Redisson.create(config); + } + +} +``` + +Upgrade to __[Redisson PRO](https://redisson.pro)__ with **advanced features**. \ No newline at end of file diff --git a/docs/logo.png b/docs/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2a799d693345df6bcfa2f0975828eaabfa66e200 GIT binary patch literal 14476 zcmW+-bzD>L7e-V{K|ltAA|WHj00jx@n!rF}Y%p3Tq0${Aqyz>?NK5xdj&1}Q9inun zNC}8^{r3I+aqs=y`^SCX_n!Bh^PJ~AH(Xm&g_??miiCuOT1{0MM!Y8ecTkWK&(b%X zmWh{J)(Wo_NJ#L}cP`9t6YnV?0O6a zaEoB7zg<-HA&TZlC+SgHaZ?$8nXS_$xSo*~GBHmQoAY5bCWD=w){ufL<)CFZ9o>=g zMFhxkn`?dPcwlf=%;j3{s&H0Vja>bO7~h6?%OjeJcbe9V7NNOlq3D%=vV45{her~j zNBi}Ec6OXvCV#G^xnX8ru$8@M_Cnb5*PR`eA;*1{2q0nrB-yM1`=_Q5#&^IRFI;(#j} zz4Fdp|LRn5La|45g|zNG{Ep0!jgV_YWBOF~x1}T=;AQloj#2-W=>xFE9e=jMgFs!G zj8OJL{T7)FR9H3OC&HK3lU-FhW%CI`0Gv)9>?B$Lb%)C2w|gl2_+yPEvqg8t^c8nM z()L}+-ZjaGe+!p#uR#~ohW%GC8gqch$Nsh7UzVkBBc`im?I&=$CS^M@h1`j%amv9z z!nyGMdCvA26?vQ?dt&b?ZE1P8$yoo&EasY`U+bto2+q zi=teLV`6>4;^h3FtbHAmSxrr&ZGm$S*!gtv|aboZ(A7$G8BxYTrY%x zX#ztecTJzCzyrcG*-=TVJU_z4>IYu1!$6wq#=orNGjAhmL(unl1Cg0W&P_bWZcRT+ zcjpaRnksf&S!o>oLsx3=%$1RbRX-#R;T;KKWKt09Wn&QhXu(u_5LQLo`Lp5?WQGa5 zObO8>NmIceLGv+$&8Gt?C&uRcYy4Fup~2q;OQgqkOatb(UKQr$%~Ut6y&r# z8=AW6aT(eP*w$H)>J*bj$NrE$aM1j@J7ny!s(nTyWlX}^!) ziXbPcWRj1HCFZm?yn#*vfn|MMAt-m@_Ud>wp0PFLFXv`cbEkaLg|@phNmDzvdb#p_ zfLGl2DZc4Ld&#^lYH|DtFQV2oyN}+e12SHcL;krnfzBG!)j6hCU8bwwS+h2d=kSF_ zszD|Hxks-{gdM&Jo>t1vgU8Xuyq9(x_t}__*KVWijK^LZL#g#YEnaDym74sIyOM6*kR9XY4w& zj!5Q23)d=ze&cDJn~3sgNL&FJfEwWzkA6iflJJf_(8APHYYRf}>vBVQ`E;A_x>1up zW_(|+!JS4nVSXmrY&!hzPV2B+xpGDME4B8kZ#VPM`EiO^7~k$2nJIMsYsr+JjbxsD zPV2?pgP0aUC&^!CzZ{L-72a=}k8{ZW?&I)im^0z7H~#ruVu31KaX*jK);g-o`Hdqx z-x%_#Y<_xujR~~g#NvtJLy+M|VTfYZ;Px#wE;>a6ZC>Xfg8Wm8$gkE-O}^-T?NInL z(#%7aLS5r!3{daHA#*6fD_O}KUq=QS z4h_c|DX-?Aq8@=Wx~z;)dXo~hasGVH0(gcyinBBIds)kau?N~ znNgWj)8|s!i@$wae*T3MKZNH!2WQ@Gjl#`-+gm+^;E*ZM5QTUIdm-N#m!tJL4eHVA z@&kQEMUEKr>>RjPvR*zcge7c|n(Y2ejYx5Gi(-Db%0L^39!_8SMg#W}dj4)UFOykH zOPRVoe_6omBt=duK?Fz_9C*!T9=mM-g%UJ?A(d(&`Vrc4g)SnVu@k`t*(Yo9`adgu z%JV(Ex7-{GWgJd~39e>5`;TZY(hH37GYEG)w*t3bfUuDcPKtfZWYOLtN{2#hO(*1i z5&Q^Da376AdOTn_g`A98x#F(op1b?+c9%EyeYNny~t{i|FjipN2qkXgG!W&y*#-6wC26 ztb_*^Wj2m`2&_g5IGfV~s$JBM^xz^z3mxrC1arp1LaG4SaAm>nT~6W*Nv$dZXd|3s zZ+Py)D7wUw86pifG$2JHH_)F9v791^Qg~cM`cPkl$xz+opHyddvun{7n~8&#;2hM!aUOTi{>dWm0;#9$`i zig;`p$Nc`TFhiY|S4M?JEDD3i1rJEZSwDXL0s~5Q(k_2p@U%S2d2K;kz&8m% z9&xIdxo9--j?K@TAwenmycVw5Gd|;O=MeS@RC5Me{*%Wu-IhOt+t;`{-FBN+RqR!e zKBA=V(qK8W$N25{t$a=l+~akF6h|T-fI>fjRRjA+p|+7Nz&Gm+?uD-ij=rX^_;WTG zo~?nUy(N4r!180xBL-k9999x~V?8A4Rk_K>-Dnk~I9Liw0VF)S{E|YH0!96D#^&km zo3_7?yRBb;lza$Fzt>?`Gzr5R5FKD_hj5}r%ARC%+dHa|15Q3PMo$X6{4MJ3zu9km zC7DP1R@VS_1A+5k2c*2~CA@!DOLVb69nSF>1bcqtL;7A@2)0LwVlVy^E8+FOG7YTl zZ=7^T#(XISi4>WwQt!4BHV?`05}slesILE?b{ zJ+Ci0PR3$Z-m5~&+N&wYpG=_6Bpqc6%D)|zeUqFBQb>Mm8~DziZFcpk6uqc;om~@2 znyWlKqF?)R%{!`C?>uVE<+AitI^sl>ZBKMNN81eip|-uv&tR1?~`V_46I9Kyr}u&#Fee1MP~WEI#n z#Q0vGd>JoH4gcpOvIda_WGrqcr_+WWQh0)?Q*O3s@##O$w*RPo|2sGO0ZcZ5g%MLU z1?|g$LU=iz5ju)}8w~eJ+M-Q}A6g@toi_SsyfPHR%>LoQhGWX^7g&m_PM$o-JYZY9 zXo=QUxl%Whi$?z~(r7((DOBq0Pn~lqjq3~V(gh?G>wI$x<@A--!6q*bwUOt|)=X}C zNY*LS^9W6t=1*X-5ymM|^7DI%hPRSIb zgQrMmP!BY|M-b3(=cz6}R1%oW&fB$l?&}HUFg9!e)%MkGB*iVhK!AJ z;~NV)0sAZnCK`V{Fkt;F2Q5WL@+!c>*V|L zD+iu^i9pV^I2aa{LbNJiMlV{P7D})^k5l$~Z{X9|Hg!K41i28OyiMkA?M6sGC zMLrZ_jC^Qa`5hHYK<%{-Gky5%R z>!~H|oYxBu@c?ip64qhs!_~b z^HW~#%Jqoa)$}4+2M?v^6!Hp_BonHA-9D0c{f{RBTix zC!uAho8r0&hL*SL!5xqetOethLiq(asBK^W{fI&>rk|mA>5eTdk`XJOJp^2YaEd`6 zHyn-yXCFb=B4aki;O%d|miB&nUtJ^HI=|FYp*a69|GDU4#n_D%2U@J56(~V8#^y4f z&#KQsAWF9bv8IRjIkMgNjDB2TeE<6&MF+vO{At6^I80|Ov?F74vDU7*TyDFf@Jl|_ z_=Ee{w7jFXT~nVR07?D#(-xn*-=SrF>6jr4KDe_WQ2N={pC*m=KMLiasnO8SMZ_%i z7N(X=N&eXHWH@NLR;EpjTx+VV@7^t~fy0OV>^gQ$P@eJk7Nv0tK@h~da5s5F^*4#Q zxh6tmzItcqeV#Dus7L#+n?9kyx59Z4%^Hxd)C!#2Gn)`?;81|^i$t<0GfXqjo>Qb-(NL4TuC*Jip{&xKpGgK;1z0V6uX?iqD8Cas(SykF`r@fCJzD$ z?x=m(iBta77<%k$2zqXBu++a1V7kbi4wuBM{=xHL?9BY(6P_=ocyHv2>S&v<+?%rmK5f_A9c#b%<_2c*t_}V(4pMVY`3vaVx{$=nX{1F_Hhe;@Ph0$pqoINvT6D!(Ta-exT zGyaATznkB)CBv|TjBev)dVO2ln7rjHczgxx9z_}Hv{Kzd1YVCuXP|Cnd|J^o-mU40}%jPI0;(*miviL z&i5qX#mR(WYK456$rthP=W@w5y+yqu#o*^o(_j#WRIZ~Ik1koarlkVX#jgU-f-@_EyUA=vE1;4T|}j% z{#sV2=1tS#p^>_7;@?1@XD(k!_Dkem_YrVTP^gq{qOx#8JSOsHjRAkdV4BV6&q0Zt zheDiu8~QfQT|ayDrs>>s?yFe>kYsq-3c>KxqOI7RlQDh6;!Z2xZ0)GY3R|q_&g?;_ z2yMg|r_jA+OS$H4DIKgthgR3JjED`eu5>dq&Br>(Va_c5@xQVoa{V3k+8kuF;Fi*w zWO4bhw9z&gT;1wt^X^CB_1A{oVftZ@KPU`v>CmdVKp+DS1ABF!Ia*8Dn554howxo` z+PeO3@~N_%`j*2m^#jq7&L81wC#Z1O?kR%`sxFTQN$Jk9~b=6-8==PxGfjgQ0?63)^$4FFFYOO75w&QnXd z%#c%?Z47sO=37E}(Oa+RaJb6S?JOKK4#FEy z%~TgJ`08uOR5o$yo&2KfKy!GBOOhjY6xFa?&A0>)Dw7T8?Tt0}Pu11oW`Bi+No`2m zh1g?{l&dtkq}=hXK}Z-8zwEs$wB28^%Z0q!YUs4$lk%VEoo{vj;5j{Dh|b5g1l;=B zESO1+Mu7SXFpx>5N555R4qrL>PV&SSP6n6X6&*5L6Cc;1ZGuzx>IEy&;NY3s;Jp{C zpj62kM41(c;IbaZg#K6bB*9AvxJb<%U8_~|cr+&o6Fg%`GcvrxD}hfh<{1t5&F3Rm zKDgP{5>o^P1a)w60(2&x*fw92@Wuzjd|%?f))i(5)(V|wA1b?vFzAIZ$Q+lPypf#N z$7sVqUNaAiyU3^a^=ev%f5tq`-FGYc$#*6d$FF%P5o#z02DGe_lMea#A2pMP#W;kw zFOd@YCA4IA7>Hr%uLWP02HjL&C#{Q!i}KWJZ;lmu>+3t0_|lNu%Y>Cr(V#KYj=BoG zc{vo~>A=P2?9A#NX5DBa#0Dsr1pg&FXE>?i=Sz%_NWs7a^uxFfp5qg5V3e^GRRA>R zfSoa^f6F<3@~F#|!w?<*$~3t~Hsv{`LBBqKye4Q~OKsAonH=#%i;`$FB@<1j+J5|% zXI)cqzV;mF*}qd!i!&w*DlCewww#wD|7?V8;WOz*9;fji^{`G2NGs`r63AZvNIF7O zMrqryy65byG@nm>D(5!p{qdV+C~)ajee+aewCE(io|K4<$+CNh=D+$sp^9hEN`DM! z__xZ{_X#F&-P3*m?YIjMjeO+txGKd2+;?091c(5%4pg(>Z^S==mTyLboWA@&5+uw? zbcvABJVyua6Y*-S%J&NKDyBS6pVrOSbw+8c&R0E|Pio;9u#Io~9gfi>B#IChvBR6N z#*8l3z=(GD!HpAVuum9!?zjWKT>p!t*qO3p4IKZ$0GtiGj_@qdeOd-r#z2sw^W6{M zaR9XL?CX$N*PG0Ia*;$Efp!E+$VMLe+#((8jDvE{nBcV4+2i_PudSXn!@z<{e7R~s zzD;yuk#T)RP10m%{=cTv<&ekSW4gGOJrKf5s896kO4*C~l~j@hP9f6SW zVh4^8(s*8y8I{WRGOa38wjE0?6GPaXk=PTP>TTa7DRFz+ zax>>}Ptq}S>FjOTnCxEg6n}ougQU0)v(aSJLdjJ`4lQm{kYPj(B4Kh%4Rb=p8%0?d zV5~1_6y_E8Hc*Xuf@MzGeD|mF(!0Wl3yD>OlH0{!p%tXy-8*}YERK(ROV{qkqPt#j z9+$WfKzlclcNt$gemW`JWnPv7w^8_%S4fZhozp?zqg4paNXmi%0?haFwQdNIE_BG0TplthnM8J5Od$ z(z>``OnW?RGW&r$RyFu3QgiMjLj<#60TRj0GRs)U0sN^3?CwV%>VR6gK>&0agZiK$ z45Sxvh`ri&yd0LVhmNWPy+S>N_q~_~ja$7}+kgLm&7;|e+F1CbI3is{YLAjU;>D7ZG;k`0+Pp4 z6zf3qtoZw~KdYgb)F5_MzJW)KnFQQ@sO3yNii&#t67>8QJja)gFKLDI|@Qp|w zfDR!pAPGiCMOfRUfIO9>TXhe=bfLF;)<Di|CraMa_9#|dN&EL zr*C6E@+H=My4ws&jIVl@hv8;C{im1gU0)TQJ*JN9)`QPsPRCK;YZ^gZ(TH75k$kRL3^7Qq=pJ4)K!S8CQ=XW~(P zwGTr<$qg*Jpk99t{SuJVPz=X^mUN79*US9bsUGtr79_;Nu%$`typ;C#dZc(jb5$gw z$dkwo^o$PkzZ;s7@s%MxD0%sFh(E!zU&ZFE8FP81|#jQ;eZ%mJA1bg5%VACS* zw*@Coo4$6zDn*Ib8_%fGI7j7m5T6bNqTCmL4(`_``KDL@;OdB&ClPu_!|_JO%NO?J zntSPWYyZt@Z7jt^LVykdwYVz0Pg;^gW@JA>Q&$cp;Gz($#T^vcsO_Jv_ZsX4I}K^@ zez$BroFdhE7)C_Lfg_z~&y_y)O#o~N2g3#&4jGDg+281oe2k|_FH@BlX*)0ZSUY1Z z)~oSqYN0%9=aC`joPcod#Kt+~4LzM80i6)e#x6+$$_=SPq}`P2*cxc$niwJ~Si80% z?W5acv{sg#cHu!O2TdqH zw#Q)=7x(;@*mvd!2vE)u=*?{d)V*pG-VXN|?>|~c7Zlf4H(O<4PG8)a4?chrxr}9g zC6B#%j&)U}Lx4Im*3q>fC%xf}UkUdH4QkidBaSsCDATE~q+ccL`=l(hi8;{+Fp<)z zx%+t8gwpJIrJsu-;y%g;JYYwFu1W=S?^LjgE(-q!;q&4~F(L0*k9H0a2^j6?=}3C2 zt}fr|XT_exz$*b7USJd^P+5dSbh*Do``Hdvx*fiMb`X}!*|{uKE1CygeM-;{U1`eR znRe3v{d*{iBcWE{VZ2!|G;!O2Xr3G@h{do_s&?=j_%`$Ak;3-OL~S=%Z`$I=zKnL2 zeh!Qd1CdogN3^Fox!%M3{W0dB9n=>SFy)%>8~8*Uu}7}4^`%C_;+gOi9u+*Z13^6C zQ&ajLPLRhP>?r8z+!PEFT&mrUa%FYi`%Hb}dHyt?R5DxRY4nanx(4V%dX{I#%oaVh z_Jb0V2VZr0Dm7^UZeF3$sj2ePQmkTVlT&XF2Tci%8}($B=OiK78a!*hsTT5ITP5{p z=22A&O%h-a|KK}e#Bs_#LwWAUDyr4nR{QqsI2-cE-#Tb;NY|j}YYfgkxpU*c#iUqp z9?U(}-j+8i1eWz09(As`1;;g0UhTZ{p;$Hu6P?}wzq~2?JqOkMp|}Y4?EL9lB0Mz# z-M^JSgWr8(jt#3ZE^$vZ0aM<7bM&K^7;yLOc3V;Xbq50vAEma)uL_-+VTswLeO4JA zE=~P>dJXgrj(}Y^CrR#vp+~*cOUMY$H1JP>hpgNJ}9-+A64R& ziueFZ2K`J*Nhi$SM~5e@@Pyg@D$lPOxKA^6GkvTj^hJRc)23aBKD$0Af>TR5+O07n zRT)f{pa5S7awf(@cTRy|0O?T1bl-jN@WA#uOeMz?0$Ir6_}0q^>bjThhMF)**F4@@ zoxtP!`4lDXlKlX^$kta3vd3w>W)<@A-C6UHdQ0e3Hyg$yQt(;6cenTM0%c_1!K0O1 zbvzzn{cCT^pXNcoYoD~4U?4i9UL}kxY6LgSVIlME4+Ag@f)h`~-{blFV@7?DSEsGI zbEi}o$8^gpzkR{IR4BNQ6~~rq=>C(IUJU7Hdw!tba&+yfH1#kj|lvz=#__z z2csS@R8^=02|4ge7Bx612SX^SIh(zsjM&-*b_pbW5eqLbYkk?AKo`lMu<8a6m(1p3 zuz^CGyq?U|?Z&{D?<5w{KWD%|uwm1!oWI=m<#_;>kz)rxdo4OP@0El8HsS)!l^}p! zpLYT}{RJU+I>}|wDii>9+|%}C9-J5FS%5pMT&sYn=xw`_Mf(4ymBLF|r4{b`c)scm zu(<6lf^CHA_BOHO8EZNBiT;b3_q(1pt75m|lUTV5FWlhw@nMrDF+-h>J-{VQJ6lJ1gjyRt6V4alW3!oJLn z*C@j9(6dFevA28KwF+Asf#(EiEI(zV9F`@y9X)a7gw4mL^@q^pXS)8Qw2 z46&5)xwahqy7AnB{a@Yl>nFpvg4)zUhqu|PBED<$GTyZDH%kD;X0R0wZU?zdT`fyC zBqt_4-dY91aMPfMnGw`^+tt*WymqK1S3qJ;~)G+M!B{QpTZs=fyclW3k9qQ5ao=!Wp_H*UC6+e zb$lGAqX&|eJghE)ZE(SssO#xX+wy7BG}VVEPdyx7e|MF2BSS1v(-u2@d_=2yj=ozD z0#pHtpI!~RCzi$A&#RnskKt>r^#(mAgy}sq=D;U%g?eL{uoi=n&v4DBL@o z)~3I@T9YZ+V}pS_@_>Du5uLReF#x9T_>%;kkB?y2qj-1FGYN16e(;xWnv?JY1&4hl^YysC-c^Vt(x0j7;93Ga9KQOxw1g&NM1A(@ zq9on4)1J&qyuK#Ga2m(FT!Bid_I@5znb*`%z-uh2x9dC-i=E;tKyFD|)T-RPy04lm zXC1aU+Q()7m4OJIMSHBq-&&l!81+@n)27#JcTSHR5JYW>XtBB8ENl`lVLBkErWX<6 zBZz@?D@mCzA8AdeIXlkA=Qcb&D>Y3?VA^803Fl|-onnz>*rX4 zn&9+)L=-T!-_lt&EGzRC*SX0$1NqZ(j1dYn8MN7!yWRXKRWAOJ?u6EdWS*QM_{XRU zZwQ9@s^rTntc5}si~hDaXtg~_cwbZze2Xs?-YBmwVP=muTk>-||4vCT<;f1KnW0z> zqn@}+JC+X|&%Rz`ys|Z$>KBS|`1IR!Jx7VM{NBpiNbs`SDevRm=_}g30B#UY`)`Xu zKnb--OZum(c&a3~4TD0))5?j1uLu5h417A+ZRS3j^$9X}w#D`Dc5;dE;Qpc+zTT^r zNwLEaGj}06X0HRL%!t#%G#mg^#KhL6gz5fb$7f>K739gLE$Bb~LtI2;#t0gs&c|Y0 zM3eM4t5_Hk2BBqdf*-NZn8%#r^i-$=G6}7AC?-r1jIPys;kr>|h^~q0AnB=J0D?1b z8t)u}oZW@(TNbAICPG7H`m?=#y{w4J3SwE-VRi{Rw2}#398(Ts2vHUR#J0~f;dxz3 z?9_?sg$$ZyemRd$cO76^+bWe#Lfbf|OQ>)g#7EyxM$Kq;yXkqjhFc!h14NHS-JUh2 zKz>Jv1H*Y`S^qIJnv<)N6vY#(0W{ZUcC|_i`^rsmTsUK&15+EFHy2=jfjT$|=Y&r+ zYmM0SRTk$VFB~oX_Yvc!Sl75wrgZH%t(#L{2Eu-r%Yg6z^m})S{TV9y$ z7QfnV=x=ntEX{{m7_i>GD(kUnj(7~;DffRzfc@GKP7++a3!bPU7iBb_Z?!u=8uOhb zHko|1j98!8*gbM=DFVH32tD0(Y4^vl61x^-`eyvU48}4wW6rYrESNCKiUBWf}(> z$FH@XD;K4(h0Z<5x6%)ply>=ubQ-#idL|6m?fdoF(Y3PkNrpyn|NbY3KS6bMgVYaf zem#6ykRQ`(yLYa8=kjH?Nd~cRVDT&N#>8z=9egq(RT0#&^ZQ5*8okW{YFl~|Op|7c z8XQVT&3)FwSav@WV$416?lz7K`Osb$aX-RSkxr1vY>8gJVn07EcX6IJq}ovTW8Z6| z-4dcDTmUKz9T%Tmjk8-;kn55e&v@3hiy@TqIgfm2mW1)%gwLuFv=D{{hsw&dh`n!+>#W4)vX)VW_xjlnS*|u7SA55d4+d%v4 z#-@wne&fBlK~qpgKGz$+&?79v^d?Gkg7D|q?emD+a)tKS+|22Og4o>SEt|TN2unGc ziwfk_u?C*2xa!mxosRsg;M91O2!cINp;*_^6jh(4L)T^dkfpJh;y8Jt4KF+Cwt`T$l9c-7C#zpf@z*tx^rpplZy0Z z0zfN10ZPig*0j4#P zdBza^d$jybkAWxsHO)dFV32-2WlRl8#c=@@Kpiynrlxz{ub2FMQ@(bZx}mJ1TCF7B zE6A71cS;VsJgN_->L3aS!+pW(JPDd$1Q#G(BK-CBBRBbk`qMv6bfbwa-wNMfdg}=L z1S6+TRSzod!x@ORR4fcnr^U-uT& ze#n-Q@ggDb2FQUORBMu)&c4yxtcfJE|hhuEF%EFehD=f zygz8W0_r_-(lEy7r~wI_0~!nR2VI#TCKn z1-qX;JNF*sODHq^1lfq~4o`PzoytL=L|OJMiC~pYq=}15?>Uy@I&rvof)cVVtTs@K zU_YPRy-3#;fUxL6b#?FY|9$8`c>E9nyOj>_6pUpp`xe>#Q*^*E*gy%>WtplDnxE*o z+)&2d^bVIxcWEs$tm>n3>5s<=e1R-QiKoCeSawJ9IANfxNuErP_JzR$V@Yh7Cg|@g z_NGTW#l_!i-+R`^-6pRSA~oxf*Z$xeOC(;!n{^E!8BgH1e^UOSg<~n|(p9DUxt^%l z{`OCx*uhnF%nDncPnbm{bZ5N95XiGfRlrsfW!*F+Yp6G-Jb)23%VqIj!^3@l4c>1X zoldc8ymXTgm~eoL{C3B?a;iy~0iYRbYtu+24N>D&{Da`HmE6#+1}A~zcX7BjsBr+(x(QLP#7-ZM>I7kBUcjM?3?h|>n~ zrE7A9+xio}F;Mq!2^)g36zdzeFjVO9x+u|1t!dP4n8Wqv9sI@QeH0Nt14DjRxIzA)ckGO4uHSgSk|;Q5GmC**u#7(o9Q*iKq#TV zOetJH543Spe3tB-I{>80W zaa11i(Kd`f(N11bO&zYwYH^-{};eky5Ad?%sw+O3~D0pq8~&LjCK2ll^LuSJ=cwv}!mU zyNZ^?^K#}#3MT5*K8+GTy#UKg*ht zGp`-Rd?o=J%yrflUQOnRw~WK#VR7edwEdCxZkes_-~$QX_e@X-kIr-GclC?YUq+C@v&@pGbsAXXrkAT3F0ChYu%f?3 zurUBX7+{XTS>Rayky;NQcQ+{HV5x*Cz(-_ki{F$Vlpt>aM}lG4&oz6dzTbEBJB9-I zlt?FuG-88~l|J6GkAV(n-%kGfSWMyFz zyo%o~v|}+8%d=+5Z(Zz$$mUKNSQdV#m_cNnM}Y&Yn- z!%WP40$ytM&;9G`{*)BS{Km8iVTPwDR-zH`VWN;7&w=49pcf5qD)_xIy8u49BMTZ< z5;Cz^jeJxeLTg)?u9dBP`X@)5lJAt`R6-7@+%oef-ev=aKWe^HBj;ZTg-C$2F*On- z*o*ei#2WQImZJi}c$;*hPy;)R43>W>4ftNdso=X7hwAJQj0--fH~VA8^NHqAMEt@0 zzq9X3oYlE&exOe`3%{72B_Z2wR-7K8!U3^((yl+2ek%u=c=*?`NW3{Ol^ns^-q9WDxxA~g1TbjM?QA`fL) zw$v}$JrXUg&PpBsm{}_Pz_;-okcB3mL}pf#&j(Hg(|7iv9@R&3Cz)_}95>jd%e!x9 z7UG`{KhC0>_Wb!O$9sj0e?UMNS7*!U#bieBMvWRe4&ENo;J$v2fJvzMg zvx+QBp-sv7SoUTy>F!0cd0K^;I`EFl`-yxHNwyUJCF|}l26rddavaaAwBso7@=ghmD7;tr6%TJ{c57F z*cbgOibzz!6}HM6U%~+ZLv#C-N+~(5RTEdWlh7ag4HT7tJSxAYA76ZDZvYd(J_E;E zrnz$75yhj~Vam@Qo=eaUpoG=;FE16i0SI2VFyQcSZZRSz^BS{`09XvzW%_XH9ZEa{ zCoW7@%_dz=_5Is^a!(a&VIJfcROgaOU%qdybJO2EvOTUy!B%XKIY;OGbnksF(~WZb z1cTb&MJvjuqw5etQf36EJT)o(HTmRGuS(9IRfhV&eDjZ_%K;V#M*q0guL)4bi{TT* zJN71*aJds;picRKCE48@?VCC5a2Y#I>r5?&1s~~+JmYyT8Ir|LW77_;Xf`^= z0Rk4QA|%OX49rh084Bj2S_dBY81J&X*y#u5icSO+C}R)rD+An>-|n>x*~kLafrgZn9i!qFGRFA| zoj6A6#!;p;e#xtiV4 z?9n7Q2fX;neVTt6W{nmXevsB}sKqugW)q{>wlJ2Oom#Z__3znP-17tR5^KeZK+fuK zt^qF`pE(qsw~i)VdRP_x{nh&J-)q4VDonG<9ATsVzRs&pf``m(xwu*5-{QE#_UU2D zIu7;ki9XB=8`E*sLsMIlkGv#WH``x)ZD5O68pveT9MoJ2^3<`Ljb8L2t7ZSPGl2lg znu!X-6k>(801S-YQsi9WlC-N%d5p0e^>+_{oLcO;3({h8Qt{kH<-ic8oiH=?35 zuDT=wdf84%v6+dvXrI9Nv`5E|`DM9MoVk-N2tt&yRmjM>g)G_$0zB-6CLJS&|Bj;nXLC) connFuture = client.connectAsync(); + +// execute SET command in sync way +conn.sync(StringCodec.INSTANCE, RedisCommands.SET, "test", 0); +// execute GET command in async way +conn.async(StringCodec.INSTANCE, RedisCommands.GET, "test"); + +conn.close() +// or +conn.closeAsync() + +client.shutdown(); +// or +client.shutdownAsync(); +``` diff --git a/docs/microservices-integration.md b/docs/microservices-integration.md new file mode 100644 index 000000000..47fdf9545 --- /dev/null +++ b/docs/microservices-integration.md @@ -0,0 +1,257 @@ +## Spring Boot + +For Spring Boot usage please refer to [Spring Boot](integration-with-spring.md/#spring-boot-starter) article. + +## Micronaut + +Redisson integrates with [Micronaut](https://micronaut.io/) framework. + +Supports Micronaut 2.0.x - 4.x.x + +### Usage + +**1. Add `redisson-micronaut` dependency into your project:** + +Maven + +```xml + + org.redisson + + redisson-micronaut-20 + + redisson-micronaut-30 + + redisson-micronaut-40 + xVERSIONx + +``` + +Gradle + +```groovy +// for Micronaut v2.0.x - v2.5.x +compile 'org.redisson:redisson-micronaut-20:xVERSIONx' +// for Micronaut v3.x.x +compile 'org.redisson:redisson-micronaut-30:xVERSIONx' +// for Micronaut v4.x.x +compile 'org.redisson:redisson-micronaut-40:xVERSIONx' +``` + +**2. Add settings into `application.yml` file** + +Config structure is a Redisson YAML configuration - +[single mode](configuration.md/#single-yaml-config-format), +[replicated mode](configuration.md/#replicated-yaml-config-format), +[cluster mode](configuration.md/#cluster-yaml-config-format), +[sentinel mode](configuration.md/#sentinel-yaml-config-format), +[proxy mode](configuration.md/#proxy-mode-yaml-config-format), +[multi cluster mode](configuration.md/#multi-cluster-yaml-config-format), +[multi sentinel mode](configuration.md/#multi-sentinel-yaml-config-format) + +NOTE: Setting names in camel case should be joined with hyphens (-). + +Config example: +```yaml +redisson: + single-server-config: + address: "redis://127.0.0.1:6379" + threads: 16 + netty-threads: 32 +``` + +Upgrade to __[Redisson PRO](https://redisson.pro)__ with **advanced features**. + +### Cache + +For Micronaut Cache usage please refer to [Micronaut Cache](cache-api-implementations.md/#micronaut-cache) article. + +### Session + +Redisson provides Micronanut [Session](https://docs.micronaut.io/latest/api/io/micronaut/session/Session.html) store implementation. +Extra settings to [HttpSessionConfiguration](https://docs.micronaut.io/2.5.4/api/io/micronaut/session/http/HttpSessionConfiguration.html) object: + +|Setting name|Type|Description| +|------------|----|-----------| +|micronaut.session.http.redisson.enabled|java.lang.Boolean|Enables Session store| +|micronaut.session.http.redisson.key-prefix|java.lang.Integer|Defines string prefix applied to all objects stored in Redis.| +|micronaut.session.http.redisson.codec|java.lang.Class|Data codec applied to cache entries. Default is Kryo5Codec codec.| +|micronaut.session.http.redisson.update-mode|java.lang.String|Defines session attributes update mode.
`WRITE_BEHIND` - session changes stored asynchronously.
`AFTER_REQUEST` - session changes stored only on `SessionStore#save(Session)` method invocation. Default value.| +|micronaut.session.http.redisson.broadcastSessionUpdates|java.lang.Boolean|Defines broadcasting of session updates across all micronaut services.| + + +Config example: + +```yaml +micronaut: + session: + http: + redisson: + enabled: true + update-mode: "WRITE_BEHIND" + broadcast-session-updates: false +``` + +## Quarkus + +Redisson integrates with [Quarkus](https://quarkus.io/) framework and implements [Quarkus Cache](https://quarkus.io/guides/cache). + +Supports Quarkus 1.6.x - 3.x.x + +??? note "Native image with RemoteService. Click to expand" + To use RemoteService in native image add **dynamic-proxy.json** and **reflection-config.json** files in `quarkus.native.additional-build-args` setting. + + ``` + -H:DynamicProxyConfigurationResources=dynamic-proxy.json,-H:ReflectionConfigurationFiles=reflection-config.json + ``` + + dynamic-proxy.json: + ``` + [ + [""] + ] + ``` + + reflection-config.json: + ``` + [ + { + "name":"", + "allDeclaredMethods":true + } + ] + ``` + + +### Usage + +**1. Add `redisson-quarkus` dependency into your project:** + +Maven + +```xml + + org.redisson + + redisson-quarkus-16 + + redisson-quarkus-20 + + redisson-quarkus-30 + xVERSIONx + +``` + +Gradle + +```groovy +// for Quarkus v1.6.x - v1.13.x +compile 'org.redisson:redisson-quarkus-16:xVERSIONx' +// for Quarkus v2.x.x +compile 'org.redisson:redisson-quarkus-20:xVERSIONx' +// for Quarkus v3.x.x +compile 'org.redisson:redisson-quarkus-30:xVERSIONx' +``` + +**2. Add settings into `application.properties` file** + +Config structure is a flat Redisson YAML configuration - +[single mode](configuration.md/#single-yaml-config-format), +[replicated mode](configuration.md/#replicated-yaml-config-format), +[cluster mode](configuration.md/#cluster-yaml-config-format), +[sentinel mode](configuration.md/#sentinel-yaml-config-format), +[proxy mode](configuration.md/#proxy-mode-yaml-config-format), +[multi cluster mode](configuration.md/#multi-cluster-yaml-config-format), +[multi sentinel mode](configuration.md/#multi-sentinel-yaml-config-format) + +NOTE: Setting names in camel case should be joined with hyphens (-). + +Below is the configuration example for a single Redis or Valkey node setup. +``` +quarkus.redisson.single-server-config.address=redis://localhost:6379 +quarkus.redisson.single-server-config.password=null +quarkus.redisson.threads=16 +quarkus.redisson.netty-threads=32 +``` + +Use `quarkus.redisson.file` setting to specify path to a config file. + +**3. Use Redisson** + +```java +@Inject +RedissonClient redisson; +``` + +Upgrade to __[Redisson PRO](https://redisson.pro)__ with **advanced features**. + +### Cache + +For Quarkus Cache usage please refer to [Quarkus Cache](cache-api-implementations.md/#quarkus-cache) article. + +## Helidon + +Redisson implements [Helidon](https://helidon.io/) CDI extension for Redis. + +Supports Helidon 1.4.x - 4.x.x + +### Usage + +**1. Add `redisson-helidon` dependency into your project:** + +Maven + +```xml + + org.redisson + + redisson-helidon-20 + + redisson-helidon-30 + + redisson-helidon-40 + xVERSIONx + +``` + +Gradle + +```groovy +// for Helidon v1.4.x - v2.5.x +compile 'org.redisson:redisson-helidon-20:xVERSIONx' +// for Helidon v3.x.x +compile 'org.redisson:redisson-helidon-30:xVERSIONx' +// for Helidon v4.x.x +compile 'org.redisson:redisson-helidon-40:xVERSIONx' +``` + +**2. Add settings into `META-INF/microprofile-config.properties` file** + +Config structure is a flat Redisson YAML configuration - +[single mode](configuration.md/#single-yaml-config-format), +[replicated mode](configuration.md/#replicated-yaml-config-format), +[cluster mode](configuration.md/#cluster-yaml-config-format), +[sentinel mode](configuration.md/#sentinel-yaml-config-format), +[proxy mode](configuration.md/#proxy-mode-yaml-config-format), +[multi cluster mode](configuration.md/#multi-cluster-yaml-config-format), +[multi sentinel mode](configuration.md/#multi-sentinel-yaml-config-format) + +Below is the configuration example for Redisson instance named `simple`. +``` +org.redisson.Redisson.simple.singleServerConfig.address=redis://127.0.0.1:6379 +org.redisson.Redisson.simple.singleServerConfig.connectionPoolSize=64 +org.redisson.Redisson.simple.threads=16 +org.redisson.Redisson.simple.nettyThreads=32 +``` + +**3. Use Redisson** + +```java +@Inject +@Named("simple") +private RedissonClient redisson; +``` + +For injection without @Named annotation use instance name - `default`. + +Upgrade to __[Redisson PRO](https://redisson.pro)__ with **advanced features**. diff --git a/docs/nodes-operations.md b/docs/nodes-operations.md new file mode 100644 index 000000000..854a9214b --- /dev/null +++ b/docs/nodes-operations.md @@ -0,0 +1,33 @@ +Redisson provides API to manage Redis or Valkey nodes. + +Code example of operations with **Cluster** setup: +```java +RedisCluster cluster = redisson.getRedisNodes(RedisNodes.CLUSTER); +cluster.pingAll(); +Collection masters = cluster.getMasters(); +Collection slaves = cluster.getSlaves(); +``` + +Code example of operations with **Master Slave** setup: +```java +RedisMasterSlave masterSlave = redisson.getRedisNodes(RedisNodes.MASTER_SLAVE); +masterSlave.pingAll(); +RedisMaster master = masterSlave.getMaster(); +Collection slaves = masterSlave.getSlaves(); +``` + +Code example of operations with **Sentinel** setup: +```java +RedisSentinelMasterSlave sentinelMasterSlave = redisson.getRedisNodes(RedisNodes.SENTINEL_MASTER_SLAVE); +sentinelMasterSlave.pingAll(); +RedisMaster master = sentinelMasterSlave.getMaster(); +Collection slaves = sentinelMasterSlave.getSlaves(); +Collection sentinels = sentinelMasterSlave.getSentinels(); +``` + +Code example of operations with **Single** setup: +```java +RedisSingle single = redisson.getRedisNodes(RedisNodes.SINGLE); +single.pingAll(); +RedisMaster instance = single.getInstance(); +``` diff --git a/docs/observability.md b/docs/observability.md new file mode 100644 index 000000000..7a546797f --- /dev/null +++ b/docs/observability.md @@ -0,0 +1,711 @@ +## Metrics + +_This feature is available only in [Redisson PRO](https://redisson.pro)_ + +### Monitoring systems integration + +Redisson provides integration the most popular monitoring systems through [Micrometer](https://micrometer.io/) framework. + +**1. AppOptics** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-appoptics` + +Class: `org.redisson.config.metrics.AppOpticsMeterRegistryProvider` + +Parameters: + +* `uri` - AppOptics host uri +* `hostTag` - tag mapped to host +* `apiToken` - AppOptics api token +* `numThreads` - number of threads used by scheduler (default is 2) +* `step` - update interval in ISO-8601 format (default is 1 min) +* `batchSize` - number of measurements sent per request (default is 500) + +**2. Atlas** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-atlas` + +Class: `org.redisson.config.metrics.AtlasMeterRegistryProvider` + +Parameters: + +* `uri` - Atlas host uri +* `configUri` - Atlas LWC endpoint uri to retrieve current subscriptions +* `evalUri` - Atlas LWC endpoint uri to evaluate the data for a subscription +* `numThreads` - number of threads used by scheduler (default is 4) +* `step` - update interval in ISO-8601 format (default is 1 min) +* `batchSize` - number of measurements sent per request (default is 10000) + +**3. Azure** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-azure-monitor` + +Class: `org.redisson.config.metrics.AzureMonitorMeterRegistryProvider` + +Parameters: + +* `instrumentationKey` - instrumentation key +* `numThreads` - number of threads used by scheduler (default is 2) +* `step` - update interval in ISO-8601 format (default is 1 min) +* `batchSize` - number of measurements sent per request (default is 500) + +**4. Amazon CloudWatch** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-cloudwatch` + +Class: `org.redisson.config.metrics.CloudWatchMeterRegistryProvider` + +Parameters: + +* `accessKey` - AWS access key +* `secretKey` - AWS secret access key +* `namespace` - namespace value +* `numThreads` - number of threads used by scheduler (default is 2) +* `step` - update interval in ISO-8601 format (default is 1 min) +* `batchSize` - number of measurements sent per request (default is 500) + +**5. Datadog** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-datadog` + +Class: `org.redisson.config.metrics.DatadogMeterRegistryProvider` + +Parameters: + +* `uri` - Datadog host uri +* `hostTag` - tag mapped to host +* `apiKey` - api key +* `numThreads` - number of threads used by scheduler (default is 2) +* `step` - update interval in ISO-8601 format (default is 1 min) +* `batchSize` - number of measurements sent per request (default is 500) + +**6. Dropwizard** + +Class: `org.redisson.config.metrics.DropwizardMeterRegistryProvider` + +Parameters: + +* `sharedRegistryName` - name used to store instance in `SharedMetricRegistries` +* `nameMapper` - custom implementation of `io.micrometer.core.instrument.util.HierarchicalNameMapper` + +**7. Dynatrace** + +Class: `org.redisson.config.metrics.DynatraceMeterRegistryProvider` + +Parameters: + +* `uri` - Dynatrace host uri +* `apiToken` - api token +* `deviceId` - device id +* `numThreads` - number of threads used by scheduler (default is 2) +* `step` - update interval in ISO-8601 format (default is 1 min) +* `batchSize` - number of measurements sent per request (default is 500) + +**8. Elastic** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-elastic` + +Class: `org.redisson.config.metrics.ElasticMeterRegistryProvider` + +Parameters: + +* `host` - Elasticsearch host uri +* `userName` - user name +* `password` - password +* `numThreads` - number of threads used by scheduler (default is 2) +* `step` - update interval in ISO-8601 format (default is 1 min) +* `batchSize` - number of measurements sent per request (default is 500) + +**9. Ganglia** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-ganglia` + +Class: `org.redisson.config.metrics.GangliaMeterRegistryProvider` + +Parameters: + +* `host` - Ganglia host address +* `port` - Ganglia port +* `numThreads` - number of threads used by scheduler (default is 2) +* `step` - update interval in ISO-8601 format (default is 1 min) +* `batchSize` - number of measurements sent per request (default is 500) + +**10. Graphite** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-graphite` + +Class: `org.redisson.config.metrics.GraphiteMeterRegistryProvider` + +Parameters: + +* `host` - Graphite host address +* `port` - Graphite port + +**11. Humio** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-humio` + +Class: `org.redisson.config.metrics.HumioMeterRegistryProvider` + +Parameters: + +* `uri` - Humio host uri +* `repository` - repository name +* `apiToken` - api token +* `numThreads` - number of threads used by scheduler (default is 2) +* `step` - update interval in ISO-8601 format (default is 1 min) +* `batchSize` - number of measurements sent per request (default is 500) + +**12. Influx** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-influx` + +Class: `org.redisson.config.metrics.InfluxMeterRegistryProvider` + +Parameters: + +* `uri` - Influx host uri +* `db` - db name +* `userName` - user name +* `password` - password +* `numThreads` - number of threads used by scheduler (default is 2) +* `step` - update interval in ISO-8601 format (default is 1 min) +* `batchSize` - number of measurements sent per request (default is 500) + +**13. JMX** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-jmx` + +Class: `org.redisson.config.metrics.JmxMeterRegistryProvider` + +Parameters: + +* `domain` - domain name +* `sharedRegistryName` - name used to store instance in `SharedMetricRegistries` + +**14. Kairos** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-kairos` + +Class: `org.redisson.config.metrics.KairosMeterRegistryProvider` + +Parameters: + +* `uri` - Kairos host uri +* `userName` - user name +* `password` - password +* `numThreads` - number of threads used by scheduler (default is 2) +* `step` - update interval in ISO-8601 format (default is 1 min) +* `batchSize` - number of measurements sent per request (default is 500) + +**15. NewRelic** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-new-relic` + +Class: `org.redisson.config.metrics.NewRelicMeterRegistryProvider` + +Parameters: + +* `uri` - NewRelic host uri +* `apiKey` - api key +* `accountId` - account id +* `numThreads` - number of threads used by scheduler (default is 2) +* `step` - update interval in ISO-8601 format (default is 1 min) +* `batchSize` - number of measurements sent per request (default is 500) + +**16. Prometheus** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-prometheus` + +Class: `org.redisson.config.metrics.MeterRegistryWrapper` + +Parameters: + +* `registry` - instance of `PrometheusMeterRegistry` object + +**17. SingnalFx** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-signalfx` + +Class: `org.redisson.config.metrics.SingnalFxMeterRegistryProvider` + +Parameters: + +* `apiHost` - SingnalFx host uri +* `accessToken` - access token +* `source` - application instance id +* `numThreads` - number of threads used by scheduler (default is 2) +* `step` - update interval in ISO-8601 format (default is 10 secs) +* `batchSize` - number of measurements sent per request (default is 500) + +**18. Stackdriver** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-stackdriver` + +Class: `org.redisson.config.metrics.StackdriverMeterRegistryProvider` + +Parameters: + +* `projectId` - project id +* `numThreads` - number of threads used by scheduler (default is 2) +* `step` - update interval in ISO-8601 format (default is 1 min) +* `batchSize` - number of measurements sent per request (default is 500) + +**19. Statsd** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-statsd` + +Class: `org.redisson.config.metrics.StatsdMeterRegistryProvider` + +Parameters: + +* `host` - Statsd host address +* `port` - Statsd port +* `flavor` - metrics format ETSY/DATADOG/TELEGRAF/SYSDIG + +**20. Wavefront** + +Required dependency: + +* groupId: `io.micrometer` +* artifactId: `micrometer-registry-wavefront` + +Class: `org.redisson.config.metrics.WavefrontMeterRegistryProvider` + +Parameters: + +* `uri` - Wavefront host uri +* `source` - application instance id +* `apiToken` - api token +* `numThreads` - number of threads used by scheduler (default is 2) +* `step` - update interval in ISO-8601 format (default is 1 min) +* `batchSize` - number of measurements sent per request (default is 500) + +### Config examples + +**JMX config** + +```java +Config config = ... // Redisson PRO config object + +JmxMeterRegistryProvider provider = new JmxMeterRegistryProvider(); +provider.setDomain("appStats"); +config.setMeterRegistryProvider(provider); +``` + +**Prometheus config** + +```java +Config config = ... // Redisson PRO config object + +PrometheusMeterRegistry registry = ... +config.setMeterRegistryProvider(new MeterRegistryWrapper(registry)); +``` + +**Dynatrace config** + +```java +Config config = ... // Redisson PRO config object + +DynatraceMeterRegistryProvider p = new DynatraceMeterRegistryProvider(); +p.setApiToken("Hg3M0iadsQC2Pcjk6QIW0g"); +p.setUri("https://qtd9012301.live.dynatrace.com/"); +p.setDeviceId("myHost"); +config.setMeterRegistryProvider(p); +``` + +**Influx config** + +```java +Config config = ... // Redisson PRO config object + +InfluxMeterRegistryProvider provider = new InfluxMeterRegistryProvider(); +provider.setUri("http://localhost:8086/"); +provider.setDb("myinfluxdb"); +provider.setUserName("admin"); +provider.setPassword("admin"); +config.setMeterRegistryProvider(provider); +``` + +### YAML config examples + +YAML config is appended to Redisson config. + +**JMX config** +```yaml +meterRegistryProvider: ! + domain: "appStats" +``` + +**Dynatrace config** +```yaml +meterRegistryProvider: ! + apiToken: "Hg3M0iadsQC2Pcjk6QIW0g" + uri: "https://qtd9012301.live.dynatrace.com" + deviceId: "myHost" +``` + +**Influx config** +```yaml +meterRegistryProvider: ! + uri: "http://localhost:8086/" + db: "myinfluxdb" + userName: "admin" + password: "admin" +``` + +### Metrics list + +The following metrics are available: + +**Configuration metrics** + +* `redisson.license.expiration-year` - A Gauge of the number of the license expiration year +* `redisson.license.expiration-month` - A Gauge of the number of the license expiration month +* `redisson.license.expiration-day` - A Gauge of the number of the license expiration day +* `redisson.license.active-instances` - A Gauge of the number of active Redisson PRO clients +* `redisson.executor-pool-size` - A Gauge of the number of executor threads pool size +* `redisson.netty-pool-size` - A Gauge of the number of netty threads pool size +* `netty.eventexecutor.tasks.pending` - Number of pending tasks in Netty event executor + +**Metrics per Redis or Valkey node** + +Base name: `redisson.redis.:` + +* `status` - A Gauge of the number value of Redis or Valkey node status [1 = connected, -1 = disconnected] +* `type` - A Gauge of the number value of Redis or Valkey node type [1 = MASTER, 2 = SLAVE, 3 = SENTINEL] +
+ +* `total-response-bytes` - A Meter of the total amount of bytes received from Redis or Valkey +* `response-bytes` - A Histogram of the number of bytes received from Redis or Valkey +* `total-request-bytes` - A Meter of the total amount of bytes sent to Redis or Valkey +* `request-bytes` - A Histogram of the number of bytes sent to Redis or Valkey +
+ +* `connections.active` - A Counter of the number of busy connections +* `connections.free` - A Counter of the number of free connections +* `connections.max-pool-size` - A Counter of the number of maximum connection pool size +* `connections.reconnected` - A Counter of the number of reconnected connections +* `connections.total` - A Counter of the number of total connections in pool +
+ +* `operations.total` - A Meter of the number of total executed operations +* `operations.total-failed` - A Meter of the number of total failed operations +* `operations.total-successful` - A Meter of the number of total successful operations +* `operations.latency` - A Histogram of the number of operations latency in milliseconds +* `operations.retry-attempt` - A Histogram of the number of operations retry attempts +
+ +* `publish-subscribe-connections.active` - A Counter of the number of active publish subscribe connections +* `publish-subscribe-connections.free` - A Counter of the number of free publish subscribe connections +* `publish-subscribe-connections.max-pool-size` - A Counter of the number of maximum publish subscribe connection pool size +* `publish-subscribe-connections.total` - A Counter of the number of total publish subscribe connections in pool + +**Metrics per RRemoteService object** + +Base name: `redisson.remote-service.` + +* `invocations.total` - A Meter of the number of total executed invocations +* `invocations.total-failed` - A Meter of the number of total failed to execute invocations +* `invocations.total-successful` - A Meter of the number of total successful to execute invocations + +**Metrics per RExecutorService object** + +Base name: `redisson.executor-service.` + +* `tasks.submitted` - A Meter of the number of submitted tasks +* `tasks.executed` - A Meter of the number of executed tasks +
+ +* `workers.active` - A Gauge of the number of busy task workers +* `workers.free` - A Gauge of the number of free task workers +* `workers.total` - A Gauge of the number of total task workers +* `workers.tasks-executed.total` - A Meter of the number of total executed tasks by workers +* `workers.tasks-executed.total-failed` - A Meter of the number of total failed to execute tasks by workers +* `workers.tasks-executed.total-successful` - A Meter of the number of total successful to execute tasks by workers + +**Metrics per RMap object** + +Base name: `redisson.map.` + +* `hits` - A Meter of the number of get requests for data contained in cache +* `misses` - A Meter of the number of get requests for data not contained in cache +* `puts` - A Meter of the number of puts to the cache +* `removals` - A Meter of the number of removals from the cache + +**Metrics per RMapCache object** + +Base name: `redisson.map-cache.` + +* `hits` - A Meter of the number of get requests for data contained in cache +* `misses` - A Meter of the number of get requests for data not contained in cache +* `puts` - A Meter of the number of puts to the cache +* `removals` - A Meter of the number of removals from the cache + +**Metrics per RMapCacheV2 object** + +Base name: `redisson.map-cache-v2.` + +* `hits` - A Meter of the number of get requests for data contained in cache +* `misses` - A Meter of the number of get requests for data not contained in cache +* `puts` - A Meter of the number of puts to the cache +* `removals` - A Meter of the number of removals from the cache + +**Metrics per RMapCacheNative object** + +Base name: `redisson.map-cache-native.` + +* `hits` - A Meter of the number of get requests for data contained in cache +* `misses` - A Meter of the number of get requests for data not contained in cache +* `puts` - A Meter of the number of puts to the cache +* `removals` - A Meter of the number of removals from the cache + +**Metrics per RClusteredMapCache object** + +Base name: `redisson.clustered-map-cache.` + +* `hits` - A Meter of the number of get requests for data contained in cache +* `misses` - A Meter of the number of get requests for data not contained in cache +* `puts` - A Meter of the number of puts to the cache +* `removals` - A Meter of the number of removals from the cache + +**Metrics per RClusteredMapCacheNative object** + +Base name: `redisson.clustered-map-cache-native.` + +* `hits` - A Meter of the number of get requests for data contained in cache +* `misses` - A Meter of the number of get requests for data not contained in cache +* `puts` - A Meter of the number of puts to the cache +* `removals` - A Meter of the number of removals from the cache + + +**Metrics per RLocalCachedMap object** + +Base name: `redisson.local-cached-map.` + +* `hits` - A Meter of the number of get requests for data contained in cache +* `misses` - A Meter of the number of get requests for data not contained in cache +* `puts` - A Meter of the number of puts to the cache +* `removals` - A Meter of the number of removals from the cache +
+ +* `local-cache.hits` - A Meter of the number of get requests for data contained in local cache +* `local-cache.misses` - A Meter of the number of get requests for data contained in local cache +* `local-cache.evictions` - A Meter of the number of evictions for data contained in local cache +* `local-cache.size` - A Gauge of the number of local cache size + +**Metrics per RClusteredLocalCachedMap object** + +Base name: `redisson.clustered-local-cached-map.` + +* `hits` - A Meter of the number of get requests for data contained in cache +* `misses` - A Meter of the number of get requests for data not contained in cache +* `puts` - A Meter of the number of puts to the cache +* `removals` - A Meter of the number of removals from the cache +
+ +* `local-cache.hits` - A Meter of the number of get requests for data contained in local cache +* `local-cache.misses` - A Meter of the number of get requests for data contained in local cache +* `local-cache.evictions` - A Meter of the number of evictions for data contained in local cache +* `local-cache.size` - A Gauge of the number of local cache size + +**Metrics per RLocalCachedMapCache object** + +Base name: `redisson.local-cached-map-cache.` + +* `hits` - A Meter of the number of get requests for data contained in cache +* `misses` - A Meter of the number of get requests for data not contained in cache +* `puts` - A Meter of the number of puts to the cache +* `removals` - A Meter of the number of removals from the cache +
+ +* `local-cache.hits` - A Meter of the number of get requests for data contained in local cache +* `local-cache.misses` - A Meter of the number of get requests for data contained in local cache +* `local-cache.evictions` - A Meter of the number of evictions for data contained in local cache +* `local-cache.size` - A Gauge of the number of local cache size + +**Metrics per RLocalCachedMapCacheV2 object** + +Base name: `redisson.local-cached-map-cache-v2.` + +* `hits` - A Meter of the number of get requests for data contained in cache +* `misses` - A Meter of the number of get requests for data not contained in cache +* `puts` - A Meter of the number of puts to the cache +* `removals` - A Meter of the number of removals from the cache +
+ +* `local-cache.hits` - A Meter of the number of get requests for data contained in local cache +* `local-cache.misses` - A Meter of the number of get requests for data contained in local cache +* `local-cache.evictions` - A Meter of the number of evictions for data contained in local cache +* `local-cache.size` - A Gauge of the number of local cache size + +**Metrics per RLocalCachedMapCacheNative object** + +Base name: `redisson.local-cached-map-cache-native.` + +* `hits` - A Meter of the number of get requests for data contained in cache +* `misses` - A Meter of the number of get requests for data not contained in cache +* `puts` - A Meter of the number of puts to the cache +* `removals` - A Meter of the number of removals from the cache +
+ +* `local-cache.hits` - A Meter of the number of get requests for data contained in local cache +* `local-cache.misses` - A Meter of the number of get requests for data contained in local cache +* `local-cache.evictions` - A Meter of the number of evictions for data contained in local cache +* `local-cache.size` - A Gauge of the number of local cache size + +**Metrics per RClusteredLocalCachedMapCache object** + +Base name: `redisson.clustered-local-cached-map-cache.` + +* `hits` - A Meter of the number of get requests for data contained in cache +* `misses` - A Meter of the number of get requests for data not contained in cache +* `puts` - A Meter of the number of puts to the cache +* `removals` - A Meter of the number of removals from the cache +
+ +* `local-cache.hits` - A Meter of the number of get requests for data contained in local cache +* `local-cache.misses` - A Meter of the number of get requests for data contained in local cache +* `local-cache.evictions` - A Meter of the number of evictions for data contained in local cache +* `local-cache.size` - A Gauge of the number of local cache size + +**Metrics per RTopic object** + +Base name: `redisson.topic.` + +* `messages-sent` - A Meter of the number of messages sent for topic +* `messages-received` - A Meter of the number of messages received for topic + +**Metrics per RBucket object** + +Base name: `redisson.bucket.` + +* `gets` - A Meter of the number of get operations executed for bucket object +* `sets` - A Meter of the number of set operations executed for bucket object + +**Metrics per JCache object** + +Base name: `redisson.JCache.` + +* `cache.evictions` - A Meter of the number of evictions for data contained in JCache +* `cache.puts` - A Meter of the number of puts to the JCache +* `hit.cache.gets` - A Meter of the number of get requests for data contained in JCache +* `miss.cache.gets` - A Meter of the number of get requests for data contained in JCache + +## Tracing + +_This feature is available only in [Redisson PRO](https://redisson.pro)_ + +Redisson provides integration with the most popular tracer libraries through [Micrometer Observation API](https://docs.micrometer.io/micrometer/reference/observation.html) and [Micrometer Tracing](https://docs.micrometer.io/tracing/reference/) framework. This feature allows to gather extra statistics about invoked Redisson methods: name, arguments, invocation path, duration and frequency. + +Configuration metrics per Tracing event. + +* `invocation` - Redisson method with arguments. Arguments are included only if `includeArgs` set `true` +* `object_name` - Redisson object name +* `address` - Redis or Valkey server address +* `username` - username used to connect to Redis or Valkey server + +### OpenZipkin Brave + +Configuration example: + +```java +OkHttpSender sender = OkHttpSender.create("http://localhost:9411/api/v2/spans"); +AsyncZipkinSpanHandler zipkinSpanHandler = AsyncZipkinSpanHandler.create(sender); +StrictCurrentTraceContext braveCurrentTraceContext = StrictCurrentTraceContext.create(); + +Tracing tracing = Tracing.newBuilder() + .localServiceName("myservice") + .currentTraceContext(braveCurrentTraceContext) + .sampler(Sampler.ALWAYS_SAMPLE) + .addSpanHandler(zipkinSpanHandler) + .build(); + +config.setTracingProvider(new BraveTracingProvider(tracing)); +``` + +Required dependencies: + +1. groupId: `io.zipkin.reporter2`, artifactId: `zipkin-sender-okhttp3` +2. groupId: `io.zipkin.reporter2`, artifactId: `zipkin-reporter-brave` + +### OpenTelemetry + +Configuration example: + +```java +SpanExporter spanExporter = new ZipkinSpanExporterBuilder() + .setSender(URLConnectionSender.create("http://localhost:9411/api/v2/spans")) + .build(); + +SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() + .addSpanProcessor(BatchSpanProcessor.builder(spanExporter).build()) + .build(); + +OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() + .setPropagators(ContextPropagators.create(B3Propagator.injectingSingleHeader())) + .setTracerProvider(sdkTracerProvider) + .build(); + +io.opentelemetry.api.trace.Tracer otelTracer = openTelemetrySdk.getTracerProvider().get("io.micrometer.micrometer-tracing"); + +config.setTracingProvider(new OtelTracingProvider(otelTracer, openTelemetrySdk.getPropagators())); +``` + +Required dependencies: + +1. groupId: `io.opentelemetry`, artifactId: `opentelemetry-exporter-zipkin` +2. groupId: `io.zipkin.reporter2`, artifactId: `zipkin-sender-urlconnection` + + diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 000000000..0f86f7586 --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,18 @@ +Redisson is the Java Client and Real-Time Data Platform for [Redis](https://redis.io) or [Valkey](https://valkey.io). Providing the most convenient and easiest way to work with Redis or Valkey. Redisson objects provide an abstraction layer between Redis or Valkey and your Java code, which allowing maintain focus on data modeling and application logic. + +Redisson is compatible with the following Redis and Valkey cloud providers: + +* [AWS ElastiCache](https://docs.aws.amazon.com/AmazonElastiCache/latest/UserGuide/Replication.html) +* [AWS ElastiCache Cluster](https://docs.aws.amazon.com/AmazonElastiCache/latest/UserGuide/Clusters.html) +* [Azure Redis Cache](https://azure.microsoft.com/en-us/services/cache/) +* [Google Cloud Memorystore for Redis](https://cloud.google.com/memorystore/docs/redis/) +* [HUAWEI Distributed Cache Service for Redis](https://www.huaweicloud.com/en-us/product/dcs.html) +* [Aiven Redis hosting](https://aiven.io/redis) +* [RLEC Multiple Active Proxy](https://docs.redislabs.com/latest/rs/administering/designing-production/networking/multiple-active-proxy/) +* and many others. + +Use the [Redis or Valkey commands mapping](commands-mapping.md) table to find the Redisson method for a particular Redis or Valkey command. + +Redisson is Based on the [Netty](http://netty.io/) framework and is [Redis](http://redis.io) 3.0+ and JDK 1.8+ compatible. + +Try __[Redisson PRO](https://redisson.pro)__ with **advanced features** and **support by SLA**. \ No newline at end of file diff --git a/docs/pipelining.md b/docs/pipelining.md new file mode 100644 index 000000000..fbe5b0d25 --- /dev/null +++ b/docs/pipelining.md @@ -0,0 +1,115 @@ +Multiple commands can be sent in a batch using `RBatch` object in a single network call. Command batches allows to reduce the overall execution time of a group of commands. In Redis or Valkey this approach called [Pipelining](https://redis.io/topics/pipelining). +Follow options could be supplied during object creation: +```java +BatchOptions options = BatchOptions.defaults() +// Sets execution mode +// +// ExecutionMode.REDIS_READ_ATOMIC - Store batched invocations in Redis and execute them atomically as a single command +// +// ExecutionMode.REDIS_WRITE_ATOMIC - Store batched invocations in Redis and execute them atomically as a single command +// +// ExecutionMode.IN_MEMORY - Store batched invocations in memory on Redisson side and execute them on Redis. Default mode +// +// ExecutionMode.IN_MEMORY_ATOMIC - Store batched invocations on Redisson side and executes them atomically on Redis or Valkey as a single command +.executionMode(ExecutionMode.IN_MEMORY) + +// Inform Redis or Valkey not to send reply back to client. This allows to save network traffic for commands with batch with big +.skipResult() + +// Synchronize write operations execution across defined amount of Redis or Valkey slave nodes +// +// sync with 2 slaves with 1 second for timeout +.syncSlaves(2, 1, TimeUnit.SECONDS) + +// Response timeout +.responseTimeout(2, TimeUnit.SECONDS) + +// Retry interval for each attempt to send Redis or Valkey commands batch +.retryInterval(2, TimeUnit.SECONDS); + +// Attempts amount to re-send Redis or Valkey commands batch if it wasn't sent due to network delays or other issues +.retryAttempts(4); +``` + +Result Batch object contains follow data: +```java +// list of result objects per command in batch +List responses = res.getResponses(); +// amount of successfully synchronized slaves during batch execution +int slaves = res.getSyncedSlaves(); +``` +Code example for **Sync / Async** mode: +```java +RBatch batch = redisson.createBatch(BatchOptions.defaults()); +batch.getMap("test1").fastPutAsync("1", "2"); +batch.getMap("test2").fastPutAsync("2", "3"); +batch.getMap("test3").putAsync("2", "5"); +RFuture future = batch.getAtomicLong("counter").incrementAndGetAsync(); +batch.getAtomicLong("counter").incrementAndGetAsync(); + +// result could be acquired through RFuture object returned by batched method +// or +// through result list by corresponding index +future.whenComplete((res, exception) -> { + // ... +}); + +BatchResult res = batch.execute(); +// or +Future resFuture = batch.executeAsync(); + +List list = res.getResponses(); +Long result = list.get(4); +``` + +Code example of **[Reactive](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RTransactionReactive.html) interface** usage: +```java +RBatchReactive batch = redisson.createBatch(BatchOptions.defaults()); +batch.getMap("test1").fastPut("1", "2"); +batch.getMap("test2").fastPut("2", "3"); +batch.getMap("test3").put("2", "5"); +Mono commandMono = batch.getAtomicLongAsync("counter").incrementAndGet(); +batch.getAtomicLongAsync("counter").incrementAndGet(); + +// result could be acquired through Reactive object returned by batched method +// or +// through result list by corresponding index +commandMono.doOnNext(res -> { + // ... +}).subscribe(); + +Mono resMono = batch.execute(); +resMono.doOnNext(res -> { + List list = res.getResponses(); + Long result = list.get(4); + + // ... +}).subscribe(); +``` + +Code example of **[RxJava3](https://www.javadoc.io/doc/org.redisson/redisson/latest/org/redisson/api/RTransactionRx.html) interface** usage: +```java +RBatchRx batch = redisson.createBatch(BatchOptions.defaults()); +batch.getMap("test1").fastPut("1", "2"); +batch.getMap("test2").fastPut("2", "3"); +batch.getMap("test3").put("2", "5"); +Single commandSingle = batch.getAtomicLongAsync("counter").incrementAndGet(); +batch.getAtomicLongAsync("counter").incrementAndGet(); + +// result could be acquired through RxJava3 object returned by batched method +// or +// through result list by corresponding index +commandSingle.doOnSuccess(res -> { + // ... +}).subscribe(); + +Mono resSingle = batch.execute(); +resSingle.doOnSuccess(res -> { + List list = res.getResponses(); + Long result = list.get(4); + + // ... +}).subscribe(); +``` + +In cluster environment batch executed in map\reduce way. It aggregates commands for each node and sends them simultaneously, then result got from each node added to common result list. diff --git a/docs/server-side-scripting.md b/docs/server-side-scripting.md new file mode 100644 index 000000000..e08d1b7c4 --- /dev/null +++ b/docs/server-side-scripting.md @@ -0,0 +1,107 @@ +## LUA Scripting +Redisson provides `RScript` object to execute Lua script. It has atomicity property and used to process data on Redis or Valkey side. Script could be executed in two modes: + +* `Mode.READ_ONLY` scripts are executed as read operation +* `Mode.READ_WRITE` scripts are executed as write operation + +One of the follow types returned as a script result object: + +* `ReturnType.BOOLEAN` - Boolean type. +* `ReturnType.INTEGER` - Integer type. +* `ReturnType.MULTI` - List type of user defined type. +* `ReturnType.STATUS` - Lua String type. +* `ReturnType.VALUE` - User defined type. +* `ReturnType.MAPVALUE` - Map value type. +* `ReturnType.MAPVALUELIST` - List of Map value type. + +Code example: + +```java +RBucket bucket = redisson.getBucket("foo"); +bucket.set("bar"); + +RScript script = redisson.getScript(StringCodec.INSTANCE); + +String r = script.eval(Mode.READ_ONLY, + "return redis.call('get', 'foo')", + RScript.ReturnType.VALUE); + +// execute the same script stored in Redis or Valkey lua script cache + +// load lua script into Redis or Valkey cache to all master instances +String res = script.scriptLoad("return redis.call('get', 'foo')"); +// res == 282297a0228f48cd3fc6a55de6316f31422f5d17 + +// call lua script by sha digest +Future r1 = redisson.getScript().evalShaAsync(Mode.READ_ONLY, + "282297a0228f48cd3fc6a55de6316f31422f5d17", + RScript.ReturnType.VALUE, Collections.emptyList()); +``` + +## Functions +Redisson provides `RFunction` object to execute [Functions](https://redis.io/topics/functions-intro). It has atomicity property and used to process data on Redis or Valkey side. Function can be executed in two modes: + +* `Mode.READ` executes function as read operation +* `Mode.WRITE` executes function as write operation + +One of the follow types returned as a script result object: + +* `ReturnType.BOOLEAN` - Boolean type +* `ReturnType.LONG` - Long type +* `ReturnType.LIST` - List type of user defined type. +* `ReturnType.STRING` - plain String type +* `ReturnType.VALUE` - user defined type +* `ReturnType.MAPVALUE` - Map Value type. Codec.getMapValueDecoder() and Codec.getMapValueEncoder() methods are used for data deserialization or serialization +* `ReturnType.MAPVALUELIST` - List type, which consists of objects of Map Value type. Codec.getMapValueDecoder() and Codec.getMapValueEncoder() methods are used for data deserialization or serialization + +Code example: + +```java +RFunction f = redisson.getFunction(); + +// load function +f.load("lib", "redis.register_function('myfun', function(keys, args) return args[1] end)"); + +// execute function +String value = f.call(RFunction.Mode.READ, "myfun", RFunction.ReturnType.STRING, Collections.emptyList(), "test"); +``` + +Code example of Async interface usage: + +```java +RFunction f = redisson.getFunction(); + +// load function +RFuture loadFuture = f.loadAsync("lib", "redis.register_function('myfun', function(keys, args) return args[1] end)"); + +// execute function +RFuture valueFuture = f.callAsync(RFunction.Mode.READ, "myfun", RFunction.ReturnType.STRING, Collections.emptyList(), "test"); +``` + +Code example of Reactive interface usage: + +```java +RedissonReactiveClient redisson = redissonClient.reactive(); +RFunctionReactive f = redisson.getFunction(); + +// load function +Mono loadMono = f.load("lib", "redis.register_function('myfun', function(keys, args) return args[1] end)"); + +// execute function +Mono valueMono = f.callAsync(RFunction.Mode.READ, "myfun", RFunction.ReturnType.STRING, Collections.emptyList(), "test"); +``` + +Code example of RxJava3 interface usage: + +```java +RedissonRxClient redisson = redissonClient.rxJava(); +RFunctionRx f = redisson.getFunction(); + +// load function +Completable loadMono = f.load("lib", "redis.register_function('myfun', function(keys, args) return args[1] end)"); + +// execute function +Maybe valueMono = f.callAsync(RFunction.Mode.READ, "myfun", RFunction.ReturnType.STRING, Collections.emptyList(), "test"); +``` + + diff --git a/docs/standalone-node.md b/docs/standalone-node.md new file mode 100644 index 000000000..e19f9d15f --- /dev/null +++ b/docs/standalone-node.md @@ -0,0 +1,125 @@ +## Overview +Redisson offers ability to run as standalone node and participate in distributed computing. Such Nodes are used to run [MapReduce](data-and-services/services.md#mapreduce-service), [ExecutorService](data-and-services/services.md#executor-service), [ScheduledExecutorService](data-and-services/services.md#scheduled-executor-service) tasks or [RemoteService](data-and-services/services.md#remote-service) services. All tasks are kept in Redis until their execution moment. + +Packaged as a single jar and could be downloaded [here](https://repo1.maven.org/maven2/org/redisson/redisson-all/). + +![](https://redisson.org/architecture.png) + +## Configuration +### Settings +Redisson node uses same [configuration](configuration.md) as Redisson framework with additional settings. Threads amount available for ExecutorService set through `threads` setting. + +**mapReduceWorkers** + +Default value: `0` + +MapReduce workers amount. +`0 = current_processors_amount` + +**executorServiceWorkers** + +Default value: `null` + +Map with key as service name and value as workers amount. + +**redissonNodeInitializer** + +Default value: `null` + +Listener runs during Redisson node startup. + +**beanFactory** + +Default value: `null` + +Defines Spring Bean Factory instance to execute tasks with Spring's '@Autowired', '@Value' or JSR-330's '@Inject' annotation. + +### YAML config format + +Below is the configuration example for cluster mode with appended Redisson node settings in YAML format. +```yaml +--- +clusterServersConfig: + nodeAddresses: + - "//127.0.0.1:7004" + - "//127.0.0.1:7001" + - "//127.0.0.1:7000" + scanInterval: 1000 +threads: 0 + +executorServiceWorkers: + myService1: 123 + myService2: 421 +redissonNodeInitializer: ! {} +``` + +## Initialization listener +Redisson node allows to execute initialization logic during startup via `RedissonNodeInitializer` listener. It allows, for example, register your remote service implementations which should be available in Redisson node's classpath, or execute other useful logic. For example, notify all subscribers about new Redisson node is available. +```java +public class MyRedissonNodeInitializer implements RedissonNodeInitializer { + + @Override + public void onStartup(RedissonNode redissonNode) { + RMap map = redissonNode.getRedisson().getMap("myMap"); + // ... + // or + redisson.getRemoteService("myRemoteService").register(MyRemoteService.class, new MyRemoteServiceImpl(...)); + // or + reidsson.getTopic("myNotificationTopic").publish("New node has joined. id:" + redissonNode.getId() + " remote-server:" + redissonNode.getRemoteAddress()); + } + +} +``` + +## How to run +### As Embedded node +Redisson node can be embedded into your application: +```java +// Redisson config +Config config = ... +// Redisson Node config +RedissonNodeConfig nodeConfig = new RedissonNodeConfig(config); +Map workers = new HashMap(); +workers.put("test", 1); +nodeConfig.setExecutorServiceWorkers(workers); + +// create Redisson node +RedissonNode node = RedissonNode.create(nodeConfig); +// or create Redisson node with existing Redisson instance +RedissonNode node = RedissonNode.create(nodeConfig, redisson); + +node.start(); + +//... + +node.shutdown(); +``` + +### From command-line + +1. Download Redisson node jar [here](https://repo1.maven.org/maven2/org/redisson/redisson-all/) +2. Create yaml configuration file +3. Run node using follow command line: +`java -jar redisson-all.jar config.yaml` + +don't forget to add `-Xmx` and `-Xms` params + +### Using Docker + +**with Redis or Valkey instance** + +1. Run Redis + `docker run -d --name redis-node redis` +2. Redisson Node + `docker run -d --network container:redis-node -e JAVA_OPTS="" -v :/opt/redisson-node/redisson.conf redisson/redisson-node` + + `` - path to YAML configuration of Redisson Node + `` - JVM params + +**without Redis or Valkey instance** + +1. Redisson Node + `docker run -d -e JAVA_OPTS="" -v :/opt/redisson-node/redisson.conf redisson/redisson-node` + + `` - path to YAML configuration of Redisson Node + `` - JVM params diff --git a/docs/transactions.md b/docs/transactions.md new file mode 100644 index 000000000..0eeaed7c1 --- /dev/null +++ b/docs/transactions.md @@ -0,0 +1,140 @@ +## Transactions management +`RMap`, `RMapCache`, `RLocalCachedMap`, `RSet`, `RSetCache` and `RBucket` Redisson objects may participant in Transaction with ACID properties. It uses locks for write operations and maintains data modification operations list until `commit()` method is executed. Acquired locks are released after `rollback()` method execution. Implementation throws `org.redisson.transaction.TransactionException` in case of any error during commit/rollback execution. + +Transaction isolation level is: `READ_COMMITTED`. + +Follow Transaction options are available: +```java +TransactionOptions options = TransactionOptions.defaults() +// Synchronization data timeout between Redis or Valkey master participating in transaction and its slaves. +// Default is 5000 milliseconds. +.syncSlavesTimeout(5, TimeUnit.SECONDS) + +// Response timeout +// Default is 3000 milliseconds. +.responseTimeout(3, TimeUnit.SECONDS) + +// Defines time interval for each attempt to send transaction if it hasn't been sent already. +// Default is 1500 milliseconds. +.retryInterval(2, TimeUnit.SECONDS) + +// Defines attempts amount to send transaction if it hasn't been sent already. +// Default is 3 attempts. +.retryAttempts(3) + +// If transaction hasn't committed within timeout it will rollback automatically. +// Default is 5000 milliseconds. +.timeout(5, TimeUnit.SECONDS); +``` + +For execution in Redis or Valkey cluster use {} brackets for collocation data on the same slot otherwise CROSSSLOT error is thrown by Redis. + +Code example for Redis or Valkey cluster: + +```java +RMap map = transaction.getMap("myMap{user:1}"); +map.put("1", "2"); +String value = map.get("3"); +RSet set = transaction.getSet("mySet{user:1}") +set.add(value); +``` + +Code example of **Sync / Async** exection: +```java +RedissonClient redisson = Redisson.create(config); +RTransaction transaction = redisson.createTransaction(TransactionOptions.defaults()); + +RMap map = transaction.getMap("myMap"); +map.put("1", "2"); +String value = map.get("3"); +RSet set = transaction.getSet("mySet") +set.add(value); + +try { + transaction.commit(); +} catch(TransactionException e) { + transaction.rollback(); +} + +// or + +RFuture future = transaction.commitAsync(); +future.exceptionally(exception -> { + transaction.rollbackAsync(); +}); +``` + +Code example of **Reactive** exection: +```java +RedissonReactiveClient redisson = Redisson.create(config).reactive(); +RTransactionReactive transaction = redisson.createTransaction(TransactionOptions.defaults()); + +RMapReactive map = transaction.getMap("myMap"); +map.put("1", "2"); +Mono mapGet = map.get("3"); +RSetReactive set = transaction.getSet("mySet") +set.add(value); + +Mono mono = transaction.commit(); +mono.onErrorResume(exception -> { + return transaction.rollback(); +}); +``` + +Code example of **RxJava3** exection: +```java +RedissonRxClient redisson = Redisson.create(config).rxJava(); +RTransactionRx transaction = redisson.createTransaction(TransactionOptions.defaults()); + +RMapRx map = transaction.getMap("myMap"); +map.put("1", "2"); +Maybe mapGet = map.get("3"); +RSetRx set = transaction.getSet("mySet") +set.add(value); + +Completable res = transaction.commit(); +res.onErrorResumeNext(exception -> { + return transaction.rollback(); +}); +``` + +## XA Transactions + +_This feature is available only in [Redisson PRO](https://redisson.pro) edition._ + +Redisson provides [XAResource](https://docs.oracle.com/javaee/7/api/javax/transaction/xa/XAResource.html) implementation to participate in JTA transactions. `RMap`, `RMapCache`, `RLocalCachedMap`, `RSet`, `RSetCache` and `RBucket` Redisson objects may participant in Transaction. + +Transaction isolation level is: `READ_COMMITTED`. + +For execution in Redis or Valkey cluster use {} brackets for collocation data on the same slot otherwise CROSSSLOT error is thrown by Redis. + +Code example for Redis or Valkey cluster: + +```java +RMap map = transaction.getMap("myMap{user:1}"); +map.put("1", "2"); +String value = map.get("3"); +RSet set = transaction.getSet("mySet{user:1}") +set.add(value); +``` + +Code example: +```java +// transaction obtained from JTA compatible transaction manager +Transaction globalTransaction = transactionManager.getTransaction(); + +RXAResource xaResource = redisson.getXAResource(); +globalTransaction.enlistResource(xaResource); + +RTransaction transaction = xaResource.getTransaction(); +RBucket bucket = transaction.getBucket("myBucket"); +bucket.set("simple"); +RMap map = transaction.getMap("myMap"); +map.put("myKey", "myValue"); + +transactionManager.commit(); +``` + +## Spring Transaction Manager + +For Spring Transaction Manager usage please refer to [Spring Transaction Manager](integration-with-spring.md/#spring-transaction-manager) article. diff --git a/docs/web-session-management.md b/docs/web-session-management.md new file mode 100644 index 000000000..5c9a15f43 --- /dev/null +++ b/docs/web-session-management.md @@ -0,0 +1,89 @@ +## Tomcat Session + +Redisson implements Redis or Valkey based Tomcat Session Manager. It stores session of [Apache Tomcat](http://tomcat.apache.org) in Redis or Valkey and allows to distribute requests across a cluster of Tomcat servers. Implements non-sticky session management backed by Redis. + +Supports Apache Tomcat 7.x, 8.x, 9.x, 10.x + +Usage: + +**1. Add session manager** + +Add `RedissonSessionManager` in global context - `tomcat/conf/context.xml` or per application context - `tomcat/conf/server.xml` + +```xml + +``` + +`keyPrefix` - string prefix applied to all keys. Allows to connect different Tomcat environments to the same Redis or Valkey instance. + +`readMode` - read Session attributes mode. Two modes are available: + +* `MEMORY` - stores attributes into local Tomcat Session and Redis. Further Session updates propagated to local Tomcat Session using Redis-based events. +* `REDIS` - stores attributes into Redis or Valkey only. Default mode. + +`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`. + +`broadcastSessionUpdates` - if `true` and `readMode=MEMORY` then session updates are broadcasted across all Tomcat instances. Default is `true`. + +`updateMode` - Session attributes update mode. Two modes are available: + + * `DEFAULT` - session attributes are stored into Redis or Valkey only through the `Session.setAttribute` method. Default mode. + * `AFTER_REQUEST` + * In `readMode=REDIS` all changes of session attributes made through the `Session.setAttribute` method are accumulated in memory and stored into Redis or Valkey only after the end of the request. + * In `readMode=MEMORY` all session attributes are always stored into Redis or Valkey after the end of the request regardless of the `Session.setAttribute` method invocation. It is useful in case when some objects stored in session change their own state without `Session.setAttribute` method execution. Updated attributes are removed from all other Session instances if `broadcastSessionUpdates=true` and reloaded from Redis or Valkey when these attributes are requested. + +`configPath` - path to Redisson YAML config. See [configuration](configuration.md) page for more details. In case session manager is configured programatically, a config object can be passed using the `setConfig()` method + +Shared Redisson instance + +Amount of Redisson instances created by Tomcat for multiple contexts could be reduced through JNDI registry: + +1. Add shared redisson instance produced by `JndiRedissonFactory` into `tomcat/conf/server.xml` in `GlobalNamingResources` tag area: + +```xml + + + +``` + +2. Add `JndiRedissonSessionManager` with resource link to redisson instance into `tomcat/conf/context.xml` + +```xml + + + +``` + +**2. Copy two jars into `TOMCAT_BASE/lib` directory:** + + +[redisson-all-3.36.0.jar](https://repo1.maven.org/maven2/org/redisson/redisson-all/3.36.0/redisson-all-3.36.0.jar) + +Tomcat 7.x - [redisson-tomcat-7-3.36.0.jar](https://repo1.maven.org/maven2/org/redisson/redisson-tomcat-7/3.36.0/redisson-tomcat-7-3.36.0.jar) + +Tomcat 8.x - [redisson-tomcat-8-3.36.0.jar](https://repo1.maven.org/maven2/org/redisson/redisson-tomcat-8/3.36.0/redisson-tomcat-8-3.36.0.jar) + +Tomcat 9.x - [redisson-tomcat-9-3.36.0.jar](https://repo1.maven.org/maven2/org/redisson/redisson-tomcat-9/3.36.0/redisson-tomcat-9-3.36.0.jar) + +Tomcat 10.x - [redisson-tomcat-10-3.36.0.jar](https://repo1.maven.org/maven2/org/redisson/redisson-tomcat-10/3.36.0/redisson-tomcat-10-3.36.0.jar) + +Upgrade to __[Redisson PRO](https://redisson.pro)__ with **advanced features**. + +## Spring Session + +For Spring Session usage please refer to [Spring Session](integration-with-spring.md/#spring-session) article. + +## Micronaut Session + +For Micronaut Session usage please refer to [Micronaut Session](microservices-integration.md/#session) article. \ No newline at end of file