
org.redisson.RedissonMapCacheNative Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of redisson Show documentation
Show all versions of redisson Show documentation
Redis Java client with features of In-Memory Data Grid
/**
* Copyright (c) 2013-2024 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson;
import org.redisson.api.*;
import org.redisson.api.listener.MapExpiredListener;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.codec.StringCodec;
import org.redisson.client.protocol.RedisCommand;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.connection.decoder.MapNativeAllDecoder;
import org.redisson.misc.CompletableFutureWrapper;
import java.time.Duration;
import java.util.*;
import java.util.function.Function;
/**
* Map-based cache with ability to set TTL per entry.
* Uses Redis native commands for entry expiration and not a scheduled eviction task.
*
* @author Nikita Koksharov
*
* @param key
* @param value
*/
public class RedissonMapCacheNative extends RedissonMap implements RMapCacheNative {
public RedissonMapCacheNative(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson, MapOptions options, WriteBehindService writeBehindService) {
super(commandExecutor, name, redisson, options, writeBehindService);
}
public RedissonMapCacheNative(Codec codec, CommandAsyncExecutor commandExecutor, String name) {
super(codec, commandExecutor, name);
}
public RedissonMapCacheNative(Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson, MapOptions options, WriteBehindService writeBehindService) {
super(codec, commandExecutor, name, redisson, options, writeBehindService);
}
@Override
public V put(K key, V value, Duration ttl) {
return get(putAsync(key, value, ttl));
}
@Override
public RFuture putAsync(K key, V value, Duration ttl) {
checkKey(key);
checkValue(value);
if (ttl.toMillis() < 0) {
throw new IllegalArgumentException("ttl can't be negative");
}
if (ttl.toMillis() == 0) {
return putAsync(key, value);
}
RFuture future = putOperationAsync(key, value, ttl);
future = new CompletableFutureWrapper<>(future);
if (hasNoWriter()) {
return future;
}
MapWriterTask.Add listener = new MapWriterTask.Add(key, value);
return mapWriterFuture(future, listener);
}
protected RFuture putOperationAsync(K key, V value, Duration ttl) {
String name = getRawName(key);
return commandExecutor.evalWriteAsync(name, codec, RedisCommands.EVAL_OBJECT,
"local currValue = redis.call('hget', KEYS[1], ARGV[2]); "
+ "redis.call('hset', KEYS[1], ARGV[2], ARGV[3]); "
+ "redis.call('hpexpire', KEYS[1], ARGV[1], 'fields', 1, ARGV[2]); "
+ "return currValue; ",
Collections.singletonList(name),
ttl.toMillis(), encodeMapKey(key), encodeMapValue(value));
}
@Override
public boolean fastPut(K key, V value, Duration ttl) {
return get(fastPutAsync(key, value, ttl));
}
@Override
public RFuture fastPutAsync(K key, V value, Duration ttl) {
checkKey(key);
checkValue(value);
if (ttl.toMillis() < 0) {
throw new IllegalArgumentException("ttl can't be negative");
}
if (ttl.toMillis() == 0) {
return fastPutAsync(key, value);
}
RFuture future = fastPutOperationAsync(key, value, ttl);
future = new CompletableFutureWrapper<>(future);
if (hasNoWriter()) {
return future;
}
return mapWriterFuture(future, new MapWriterTask.Add(key, value));
}
protected RFuture fastPutOperationAsync(K key, V value, Duration ttl) {
String name = getRawName(key);
return commandExecutor.evalWriteAsync(name, StringCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"local added = redis.call('hset', KEYS[1], ARGV[2], ARGV[3]); " +
"redis.call('hpexpire', KEYS[1], ARGV[1], 'fields', 1, ARGV[2]); " +
"return added;",
Collections.singletonList(name),
ttl.toMillis(), encodeMapKey(key), encodeMapValue(value));
}
@Override
public V putIfAbsent(K key, V value, Duration ttl) {
return get(putIfAbsentAsync(key, value, ttl));
}
@Override
public RFuture putIfAbsentAsync(K key, V value, Duration ttl) {
checkKey(key);
checkValue(value);
if (ttl.toMillis() < 0) {
throw new IllegalArgumentException("ttl can't be negative");
}
if (ttl.toMillis() == 0) {
return putIfAbsentAsync(key, value);
}
RFuture future = putIfAbsentOperationAsync(key, value, ttl);
future = new CompletableFutureWrapper<>(future);
if (hasNoWriter()) {
return future;
}
MapWriterTask.Add task = new MapWriterTask.Add(key, value);
return mapWriterFuture(future, task, r -> r == null);
}
protected RFuture putIfAbsentOperationAsync(K key, V value, Duration ttl) {
String name = getRawName(key);
if (value == null) {
return commandExecutor.evalWriteAsync(name, codec, RedisCommands.EVAL_MAP_VALUE,
"local currValue = redis.call('hget', KEYS[1], ARGV[1]); " +
"if currValue ~= false then " +
"return currValue;" +
"end;" +
"redis.call('hdel', KEYS[1], ARGV[1]); " +
"return nil; ",
Collections.singletonList(name), encodeMapKey(key));
}
return commandExecutor.evalWriteAsync(name, codec, RedisCommands.EVAL_MAP_VALUE,
"local currValue = redis.call('hget', KEYS[1], ARGV[2]); " +
"if currValue ~= false then " +
"return currValue;" +
"end;" +
"redis.call('hset', KEYS[1], ARGV[2], ARGV[3]); " +
"redis.call('hpexpire', KEYS[1], ARGV[1], 'fields', 1, ARGV[2]); " +
"return nil; ",
Collections.singletonList(name),
ttl.toMillis(), encodeMapKey(key), encodeMapValue(value));
}
@Override
public boolean fastPutIfAbsent(K key, V value, Duration ttl) {
return get(fastPutIfAbsentAsync(key, value, ttl));
}
@Override
public RFuture fastPutIfAbsentAsync(K key, V value, Duration ttl) {
checkKey(key);
checkValue(value);
if (ttl.toMillis() < 0) {
throw new IllegalArgumentException("ttl can't be negative");
}
if (ttl.toMillis() == 0) {
return fastPutIfAbsentAsync(key, value);
}
RFuture future = fastPutIfAbsentOperationAsync(key, value, ttl);
future = new CompletableFutureWrapper<>(future);
if (hasNoWriter()) {
return future;
}
MapWriterTask.Add task = new MapWriterTask.Add(key, value);
return mapWriterFuture(future, task, Function.identity());
}
protected RFuture fastPutIfAbsentOperationAsync(K key, V value, Duration ttl) {
String name = getRawName(key);
if (value == null) {
return commandExecutor.evalWriteAsync(name, StringCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"local currValue = redis.call('hget', KEYS[1], ARGV[1]); " +
"if currValue ~= false then " +
"return 0;" +
"end;" +
"redis.call('hdel', KEYS[1], ARGV[1]); " +
"return 1; ",
Collections.singletonList(name), encodeMapKey(key));
}
return commandExecutor.evalWriteAsync(name, StringCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"local currValue = redis.call('hget', KEYS[1], ARGV[2]); " +
"if currValue ~= false then " +
"return 0;" +
"end;" +
"redis.call('hset', KEYS[1], ARGV[2], ARGV[3]); " +
"redis.call('hpexpire', KEYS[1], ARGV[1], 'fields', 1, ARGV[2]); " +
"return 1; ",
Collections.singletonList(name),
ttl.toMillis(), encodeMapKey(key), encodeMapValue(value));
}
@Override
public long remainTimeToLive(K key) {
return get(remainTimeToLiveAsync(key));
}
@Override
public RFuture remainTimeToLiveAsync(K key) {
checkKey(key);
String name = getRawName(key);
return commandExecutor.readAsync(name, StringCodec.INSTANCE, RedisCommands.HPTTL, name, "FIELDS", 1, encodeMapKey(key));
}
@Override
public Map remainTimeToLive(Set keys) {
return get(remainTimeToLiveAsync(keys));
}
@Override
public RFuture
© 2015 - 2025 Weber Informatics LLC | Privacy Policy