10. additional features - ALANSUDA/redisson GitHub Wiki

10.1. Operations with Redis nodes

Redisson provides API to manage Redis nodes.

Code example of operations with Redis Cluster setup:

RedisCluster cluster = redisson.getRedisNodes(RedisNodes.CLUSTER);
cluster.pingAll();
Collection<RedisClusterMaster> masters = cluster.getMasters();
Collection<RedisClusterMaster> slaves = cluster.getSlaves();

Code example of operations with Redis Master Slave setup:

RedisMasterSlave masterSlave = redisson.getRedisNodes(RedisNodes.MASTER_SLAVE);
masterSlave.pingAll();
RedisMaster master = masterSlave.getMaster();
Collection<RedisSlave> slaves = masterSlave.getSlaves();

Code example of operations with Redis Sentinel setup:

RedisSentinelMasterSlave sentinelMasterSlave = redisson.getRedisNodes(RedisNodes.SENTINEL_MASTER_SLAVE);
sentinelMasterSlave.pingAll();
RedisMaster master = sentinelMasterSlave.getMaster();
Collection<RedisSlave> slaves = sentinelMasterSlave.getSlaves();
Collection<RedisSentinel> sentinels = sentinelMasterSlave.getSentinels();

Code example of operations with Redis Single setup:

RedisSingle single = redisson.getRedisNodes(RedisNodes.SINGLE);
single.pingAll();
RedisMaster instance = single.getInstance();

10.2. References to Redisson objects

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:

RMap<RSet<RList>, RList<RMap>> map = redisson.getMap("myMap");
RSet<RList> set = redisson.getSet("mySet");
RList<RMap> 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 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 this example.

10.3. Execution batches of commands

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 this approach called Pipelining. Follow options could be supplied during object creation:

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 as a single command
.executionMode(ExecutionMode.IN_MEMORY)

// Inform Redis 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 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 commands batch
.retryInterval(2, TimeUnit.SECONDS);

// Attempts amount to re-send Redis commands batch if it wasn't sent due to network delays or other issues
.retryAttempts(4);

Result Batch object contains follow data:

// 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:

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<Long> 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<BatchResult> resFuture = batch.executeAsync();

List<?> list = res.getResponses();
Long result = list.get(4);

Code example for Reactive mode:

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<Long> 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<BatchResult> resMono = batch.execute();
resMono.doOnNext(res -> {
   List<?> list = res.getResponses();
   Long result = list.get(4);

   // ...
}).subscribe();

Code example for RxJava2 mode:

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<Long> commandSingle = batch.getAtomicLongAsync("counter").incrementAndGet();
batch.getAtomicLongAsync("counter").incrementAndGet();

// result could be acquired through RxJava2 object returned by batched method
// or 
// through result list by corresponding index
commandSingle.doOnSuccess(res -> {
   // ...
}).subscribe();

Mono<BatchResult> 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.

10.4. Transactions

Redisson objects like RMap, RMapCache, RLocalCachedMap, RSet, RSetCache and RBucket could participant in Transaction with ACID properties. It uses locks for write operations and maintains data modification operations list till the commit() operation. On rollback() operation all aquired locks are released. Implementation throws org.redisson.transaction.TransactionException in case of any error during commit/rollback execution.

Supported Redis modes: SINGLE, MASTER/SLAVE, SENTINEL, ELASTICACHE REPLICATED, AZURE CACHE, RLEC.

Transaction isolation level is: READ_COMMITTED.

Execution modes: Sync, Async, Reactive, RxJava2.

See also Spring Transaction Manager section.

Follow options could be supplied during Transaction creation:

TransactionOptions options = TransactionOptions.defaults()
// Synchronization data timeout between Redis 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 <code>timeout</code> it will rollback automatically.
// Default is 5000 milliseconds.
.timeout(5, TimeUnit.SECONDS);

Code example of Sync / Async exection:

RedissonClient redisson = Redisson.create(config);
RTransaction transaction = redisson.createTransaction(TransactionOptions.defaults());

RMap<String, String> map = transaction.getMap("myMap");
map.put("1", "2");
String value = map.get("3");
RSet<String> set = transaction.getSet("mySet")
set.add(value);

try {
   transaction.commit();
} catch(TransactionException e) {
   transaction.rollback();
}

// or

RFuture<Void> future = transaction.commitAsync();
future.exceptionally(exception -> {
   transaction.rollbackAsync();
});

Code example of Reactive exection:

RedissonReactiveClient redisson = Redisson.createReactive(config);
RTransactionReactive transaction = redisson.createTransaction(TransactionOptions.defaults());

RMapReactive<String, String> map = transaction.getMap("myMap");
map.put("1", "2");
Mono<String> mapGet = map.get("3");
RSetReactive<String> set = transaction.getSet("mySet")
set.add(value);

Mono<Void> mono = transaction.commit();
mono.onErrorResume(exception -> {
   return transaction.rollback();
});

Code example of RxJava2 exection:

RedissonRxClient redisson = Redisson.createRx(config);
RTransactionRx transaction = redisson.createTransaction(TransactionOptions.defaults());

RMapRx<String, String> map = transaction.getMap("myMap");
map.put("1", "2");
Maybe<String> mapGet = map.get("3");
RSetRx<String> set = transaction.getSet("mySet")
set.add(value);

Completable res = transaction.commit();
res.onErrorResumeNext(exception -> {
   return transaction.rollback();
});

10.5. Scripting

Redisson provides RScript object to execute Lua script. It has atomicity property and used to process data on Redis side. Script could be executed in two modes:

  • Mode.READ_ONLY used for scripts with read-only commands
  • Mode.READ_WRITE used for scripts containing at least one write command.

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:

RBucket<String> 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 lua script cache

// load lua script into Redis cache to all redis master instances
String res = script.scriptLoad("return redis.call('get', 'foo')");
// res == 282297a0228f48cd3fc6a55de6316f31422f5d17

// call lua script by sha digest
Future<Object> r1 = redisson.getScript().evalShaAsync(Mode.READ_ONLY,
   "282297a0228f48cd3fc6a55de6316f31422f5d17",
   RScript.ReturnType.VALUE, Collections.emptyList());

10.6. Low level Redis client

Redisson uses high-perfomance async and lock-free Redis client for Java. It supports both async and sync modes. The most popular use case is to execute a command not supported by Redisson yet. Please make sure that required command is not supported already with Redis command mapping list. org.redisson.client.protocol.RedisCommands - contains all available commands. Code example:

// Use shared EventLoopGroup only if multiple clients are used
EventLoopGroup group = new NioEventLoopGroup();

RedisClientConfig config = new RedisClientConfig();
config.setAddress("redis://localhost:6379") // or rediss:// for ssl connection
      .setPassword("myPassword")
      .setDatabase(0)
      .setClientName("myClient")
      .setGroup(group);

RedisClient client = RedisClient.create(config);
RedisConnection conn = client.connect();
//or
RFuture<RedisConnection> 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();
⚠️ **GitHub.com Fallback** ⚠️