Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.redisson.RedissonMap Maven / Gradle / Ivy
Go to download
Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC
/**
* 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 io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;
import org.redisson.api.*;
import org.redisson.api.MapOptions.WriteMode;
import org.redisson.api.listener.MapPutListener;
import org.redisson.api.listener.MapRemoveListener;
import org.redisson.api.listener.TrackingListener;
import org.redisson.api.map.RetryableMapWriterAsync;
import org.redisson.api.mapreduce.RMapReduce;
import org.redisson.client.RedisClient;
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.client.protocol.convertor.NumberConvertor;
import org.redisson.client.protocol.decoder.*;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.connection.decoder.MapGetAllDecoder;
import org.redisson.iterator.RedissonMapIterator;
import org.redisson.iterator.RedissonMapKeyIterator;
import org.redisson.mapreduce.RedissonMapReduce;
import org.redisson.misc.CompletableFutureWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* Distributed and concurrent implementation of {@link java.util.concurrent.ConcurrentMap}
* and {@link java.util.Map}
*
* @author Nikita Koksharov
*
* @param key
* @param value
*/
public class RedissonMap extends RedissonExpirable implements RMap {
private final Logger log = LoggerFactory.getLogger(getClass());
final RedissonClient redisson;
final MapOptions options;
final WriteBehindService writeBehindService;
final MapWriteBehindTask writeBehindTask;
public RedissonMap(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson, MapOptions options, WriteBehindService writeBehindService) {
super(commandExecutor, name);
this.redisson = redisson;
this.options = options;
if (options != null
&& options.getWriteMode() == WriteMode.WRITE_BEHIND
&& (options.getWriter() != null || options.getWriterAsync() != null)) {
this.writeBehindService = writeBehindService;
writeBehindTask = writeBehindService.start(getRawName(), options);
} else {
this.writeBehindService = null;
writeBehindTask = null;
}
if (options != null
&& options.getWriterRetryAttempts()>1
&& options.getWriterAsync() != null){
((RetryableMapWriterAsync) options.getWriterAsync()).setServiceManager(commandExecutor.getServiceManager());
}
}
public RedissonMap(Codec codec, CommandAsyncExecutor commandExecutor, String name) {
super(codec, commandExecutor, name);
this.name = name;
this.redisson = null;
this.options = null;
this.writeBehindService = null;
writeBehindTask = null;
}
public RedissonMap(Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson, MapOptions options, WriteBehindService writeBehindService) {
super(codec, commandExecutor, name);
this.redisson = redisson;
this.options = options;
if (options != null
&& options.getWriteMode() == WriteMode.WRITE_BEHIND
&& (options.getWriter() != null || options.getWriterAsync() != null)) {
this.writeBehindService = writeBehindService;
writeBehindTask = writeBehindService.start(getRawName(), options);
} else {
this.writeBehindService = null;
writeBehindTask = null;
}
if (options != null
&& options.getWriterRetryAttempts()>1
&& options.getWriterAsync() != null){
((RetryableMapWriterAsync) options.getWriterAsync()).setServiceManager(commandExecutor.getServiceManager());
}
}
@Override
public RMapReduce mapReduce() {
return new RedissonMapReduce<>(this, redisson, commandExecutor);
}
@Override
public RPermitExpirableSemaphore getPermitExpirableSemaphore(K key) {
String lockName = getLockByMapKey(key, "permitexpirablesemaphore");
return new RedissonPermitExpirableSemaphore(commandExecutor, lockName);
}
@Override
public RSemaphore getSemaphore(K key) {
String lockName = getLockByMapKey(key, "semaphore");
return new RedissonSemaphore(commandExecutor, lockName);
}
@Override
public RCountDownLatch getCountDownLatch(K key) {
String lockName = getLockByMapKey(key, "countdownlatch");
return new RedissonCountDownLatch(commandExecutor, lockName);
}
@Override
public RLock getFairLock(K key) {
String lockName = getLockByMapKey(key, "fairlock");
return new RedissonFairLock(commandExecutor, lockName);
}
@Override
public RLock getLock(K key) {
String lockName = getLockByMapKey(key, "lock");
return new RedissonLock(commandExecutor, lockName);
}
@Override
public RReadWriteLock getReadWriteLock(K key) {
String lockName = getLockByMapKey(key, "rw_lock");
return new RedissonReadWriteLock(commandExecutor, lockName);
}
@Override
public int size() {
return get(sizeAsync());
}
@Override
public V merge(K key, V value, BiFunction super V, ? super V, ? extends V> remappingFunction) {
checkNotBatch();
checkKey(key);
checkValue(value);
Objects.requireNonNull(remappingFunction);
RLock lock = getLock(key);
lock.lock();
try {
V oldValue = get(key);
V newValue = value;
if (oldValue != null) {
newValue = remappingFunction.apply(oldValue, value);
}
if (newValue == null) {
fastRemove(key);
} else {
fastPut(key, newValue);
}
return newValue;
} finally {
lock.unlock();
}
}
@Override
public RFuture mergeAsync(K key, V value, BiFunction super V, ? super V, ? extends V> remappingFunction) {
checkNotBatch();
checkKey(key);
checkValue(value);
Objects.requireNonNull(remappingFunction);
RLock lock = getLock(key);
long threadId = Thread.currentThread().getId();
CompletionStage f = lock.lockAsync(threadId).thenCompose(r -> {
RFuture oldValueFuture = getAsync(key, threadId);
return oldValueFuture.thenCompose(oldValue -> {
CompletableFuture newValuePromise = CompletableFuture.completedFuture(value);
if (oldValue != null) {
newValuePromise = CompletableFuture.supplyAsync(() -> remappingFunction.apply(oldValue, value),
getServiceManager().getExecutor());
}
return newValuePromise
.thenCompose(newValue -> {
RFuture> future;
if (newValue != null) {
future = fastPutAsync(key, newValue);
} else {
future = fastRemoveAsync(key);
}
return future.thenApply(res -> newValue);
});
}).whenComplete((c, e) -> {
lock.unlockAsync(threadId);
});
});
return new CompletableFutureWrapper<>(f);
}
@Override
public RFuture computeAsync(K key, BiFunction super K, ? super V, ? extends V> remappingFunction) {
checkNotBatch();
checkKey(key);
Objects.requireNonNull(remappingFunction);
RLock lock = getLock(key);
long threadId = Thread.currentThread().getId();
CompletionStage f = (CompletionStage) lock.lockAsync(threadId)
.thenCompose(r -> {
RFuture oldValueFuture = getAsync(key, threadId);
return oldValueFuture.thenCompose(oldValue -> {
return CompletableFuture.supplyAsync(() -> remappingFunction.apply(key, oldValue), getServiceManager().getExecutor())
.thenCompose(newValue -> {
if (newValue == null) {
if (oldValue != null) {
return fastRemoveAsync(key)
.thenApply(rr -> newValue);
}
return CompletableFuture.completedFuture(newValue);
}
return fastPutAsync(key, newValue)
.thenApply(rr -> newValue);
});
}).whenComplete((c, e) -> {
lock.unlockAsync(threadId);
});
});
return new CompletableFutureWrapper<>(f);
}
@Override
public V compute(K key, BiFunction super K, ? super V, ? extends V> remappingFunction) {
checkNotBatch();
checkKey(key);
Objects.requireNonNull(remappingFunction);
RLock lock = getLock(key);
lock.lock();
try {
V oldValue = get(key);
V newValue = remappingFunction.apply(key, oldValue);
if (newValue == null) {
if (oldValue != null) {
fastRemove(key);
}
} else {
fastPut(key, newValue);
}
return newValue;
} finally {
lock.unlock();
}
}
@Override
public RFuture computeIfAbsentAsync(K key, Function super K, ? extends V> mappingFunction) {
checkNotBatch();
checkKey(key);
Objects.requireNonNull(mappingFunction);
RLock lock = getLock(key);
long threadId = Thread.currentThread().getId();
CompletionStage f = lock.lockAsync(threadId)
.thenCompose(r -> {
RFuture oldValueFuture = getAsync(key, threadId);
return oldValueFuture.thenCompose(oldValue -> {
if (oldValue != null) {
return CompletableFuture.completedFuture(oldValue);
}
return CompletableFuture.supplyAsync(() -> mappingFunction.apply(key), getServiceManager().getExecutor())
.thenCompose(newValue -> {
if (newValue != null) {
return fastPutAsync(key, newValue).thenApply(rr -> newValue);
}
return CompletableFuture.completedFuture(null);
});
}).whenComplete((c, e) -> {
lock.unlockAsync(threadId);
});
});
return new CompletableFutureWrapper<>(f);
}
@Override
public V computeIfAbsent(K key, Function super K, ? extends V> mappingFunction) {
checkNotBatch();
checkKey(key);
Objects.requireNonNull(mappingFunction);
V value = get(key);
if (value != null) {
return value;
}
RLock lock = getLock(key);
lock.lock();
try {
value = get(key);
if (value == null) {
V newValue = mappingFunction.apply(key);
if (newValue != null) {
fastPut(key, newValue);
return newValue;
}
return null;
}
return value;
} finally {
lock.unlock();
}
}
@Override
public RFuture computeIfPresentAsync(K key, BiFunction super K, ? super V, ? extends V> remappingFunction) {
checkNotBatch();
checkKey(key);
Objects.requireNonNull(remappingFunction);
RLock lock = getLock(key);
long threadId = Thread.currentThread().getId();
CompletionStage f = (CompletionStage) lock.lockAsync(threadId)
.thenCompose(r -> {
RFuture oldValueFuture = getAsync(key, threadId);
return oldValueFuture.thenCompose(oldValue -> {
if (oldValue == null) {
return CompletableFuture.completedFuture(null);
}
return CompletableFuture.supplyAsync(() -> remappingFunction.apply(key, oldValue), getServiceManager().getExecutor())
.thenCompose(newValue -> {
if (newValue != null) {
return fastPutAsync(key, newValue).thenApply(rr -> newValue);
}
return fastRemoveAsync(key).thenApply(rr -> null);
});
}).whenComplete((c, e) -> {
lock.unlockAsync(threadId);
});
});
return new CompletableFutureWrapper<>(f);
}
@Override
public V computeIfPresent(K key, BiFunction super K, ? super V, ? extends V> remappingFunction) {
checkNotBatch();
checkKey(key);
Objects.requireNonNull(remappingFunction);
RLock lock = getLock(key);
lock.lock();
try {
V oldValue = get(key);
if (oldValue == null) {
return null;
}
V newValue = remappingFunction.apply(key, oldValue);
if (newValue != null) {
fastPut(key, newValue);
return newValue;
}
fastRemove(key);
return null;
} finally {
lock.unlock();
}
}
@Override
public RFuture sizeAsync() {
return commandExecutor.readAsync(getRawName(), codec, RedisCommands.HLEN, getRawName());
}
@Override
public int valueSize(K key) {
return get(valueSizeAsync(key));
}
@Override
public RFuture valueSizeAsync(K key) {
checkKey(key);
String name = getRawName(key);
return commandExecutor.readAsync(name, codec, RedisCommands.HSTRLEN, name, encodeMapKey(key));
}
protected void checkKey(Object key) {
if (key == null) {
throw new NullPointerException("map key can't be null");
}
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public boolean containsKey(Object key) {
return get(containsKeyAsync(key));
}
@Override
public RFuture containsKeyAsync(Object key) {
checkKey(key);
CompletableFuture promise = new CompletableFuture<>();
return containsKeyAsync(key, promise);
}
protected RFuture containsKeyOperationAsync(String name, Object key) {
return commandExecutor.readAsync(name, codec, RedisCommands.HEXISTS, name, encodeMapKey(key));
}
protected RFuture containsKeyAsync(Object key, CompletableFuture promise) {
String name = getRawName(key);
RFuture future = containsKeyOperationAsync(name, key);
if (hasNoLoader()) {
return future;
}
CompletionStage result = future.thenCompose(res -> {
if (!res) {
CompletableFuture f = loadValue((K) key, false);
commandExecutor.transfer(f, promise);
return promise.thenApply(r -> r != null);
}
promise.complete(null);
return CompletableFuture.completedFuture(res);
});
return new CompletableFutureWrapper<>(result);
}
@Override
public boolean containsValue(Object value) {
return get(containsValueAsync(value));
}
@Override
public RFuture containsValueAsync(Object value) {
checkValue(value);
return commandExecutor.evalReadAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN,
"local s = redis.call('hvals', KEYS[1]);" +
"for i = 1, #s, 1 do "
+ "if ARGV[1] == s[i] then "
+ "return 1 "
+ "end "
+ "end;" +
"return 0",
Collections.singletonList(getRawName()), encodeMapValue(value));
}
@Override
public Map getAll(Set keys) {
if (keys.getClass().getPackage().getName().startsWith("org.redisson")) {
keys = new HashSet<>(keys);
}
return get(getAllAsync(keys));
}
@Override
public Set randomKeys(int count) {
return get(randomKeysAsync(count));
}
@Override
public Map randomEntries(int count) {
return get(randomEntriesAsync(count));
}
@Override
public RFuture> randomKeysAsync(int count) {
return commandExecutor.readAsync(getRawName(), codec, RedisCommands.HRANDFIELD_KEYS, getRawName(), count);
}
@Override
public RFuture> randomEntriesAsync(int count) {
return commandExecutor.readAsync(getRawName(), codec, RedisCommands.HRANDFIELD, getRawName(), count, "WITHVALUES");
}
@Override
public RFuture> getAllAsync(Set keys) {
if (keys.isEmpty()) {
return new CompletableFutureWrapper<>(Collections.emptyMap());
}
RFuture> future = getAllOperationAsync(keys);
if (hasNoLoader()) {
return future;
}
CompletionStage> f = future.thenCompose(res -> {
if (!res.keySet().containsAll(keys)) {
Set newKeys = new HashSet(keys);
newKeys.removeAll(res.keySet());
CompletionStage> ff = loadAllMapAsync(newKeys.spliterator(), false, 1);
return ff.thenApply(map -> {
res.putAll(map);
return res;
});
}
return CompletableFuture.completedFuture(res);
});
return new CompletableFutureWrapper<>(f);
}
protected boolean hasNoLoader() {
return options == null || (options.getLoader() == null && options.getLoaderAsync() == null);
}
public RFuture> getAllOperationAsync(Set keys) {
List args = new ArrayList<>(keys.size() + 1);
args.add(getRawName());
encodeMapKeys(args, keys);
RFuture> future = commandExecutor.readAsync(getRawName(), codec, new RedisCommand<>("HMGET",
new MapValueDecoder(new MapGetAllDecoder(new ArrayList<>(keys), 0))),
args.toArray());
return future;
}
@Override
public V get(Object key) {
return get(getAsync((K) key));
}
@Override
public V put(K key, V value) {
return get(putAsync(key, value));
}
@Override
public V remove(Object key) {
return get(removeAsync((K) key));
}
@Override
public final void putAll(Map extends K, ? extends V> map) {
get(putAllAsync(map));
}
@Override
public void putAll(Map extends K, ? extends V> map, int batchSize) {
get(putAllAsync(map, batchSize));
}
@Override
public RFuture putAllAsync(Map extends K, ? extends V> map, int batchSize) {
Map batch = new HashMap();
AtomicInteger counter = new AtomicInteger();
Iterator> iter = ((Map) map).entrySet().iterator();
CompletionStage f = putAllAsync(batch, iter, counter, batchSize);
return new CompletableFutureWrapper<>(f);
}
private CompletionStage putAllAsync(Map batch, Iterator> iter,
AtomicInteger counter, int batchSize) {
batch.clear();
while (iter.hasNext()) {
Entry entry = iter.next();
batch.put(entry.getKey(), entry.getValue());
counter.incrementAndGet();
if (counter.get() % batchSize == 0) {
RFuture future = putAllAsync(batch);
return future.thenCompose(res -> {
return putAllAsync(batch, iter, counter, batchSize);
});
}
}
if (batch.isEmpty()) {
return CompletableFuture.completedFuture(null);
}
return putAllAsync(batch);
}
@Override
public final RFuture putAllAsync(Map extends K, ? extends V> map) {
if (map.isEmpty()) {
return new CompletableFutureWrapper<>((Void) null);
}
RFuture future = putAllOperationAsync(map);
if (hasNoWriter()) {
return future;
}
return mapWriterFuture(future, new MapWriterTask.Add(map));
}
protected final RFuture mapWriterFuture(RFuture future, MapWriterTask task) {
return mapWriterFuture(future, task, r -> true);
}
protected final RFuture mapWriterFuture(RFuture future, MapWriterTask task, Function condition) {
if (options != null && options.getWriteMode() == WriteMode.WRITE_BEHIND) {
CompletionStage f = future.whenComplete((res, e) -> {
if (e == null && condition.apply(res)) {
writeBehindTask.addTask(task);
}
});
return new CompletableFutureWrapper<>(f);
}
CompletionStage f = future.thenCompose(res -> {
if (condition.apply(res)) {
if (options.getWriter() != null) {
return CompletableFuture.supplyAsync(() -> {
if (task instanceof MapWriterTask.Add) {
options.getWriter().write(task.getMap());
} else {
options.getWriter().delete(task.getKeys());
}
return res;
}, getServiceManager().getExecutor());
}
if (task instanceof MapWriterTask.Add) {
return options.getWriterAsync().write(task.getMap())
.thenApply(r -> res);
} else {
return options.getWriterAsync().delete(task.getKeys())
.thenApply(r -> res);
}
}
return CompletableFuture.completedFuture(res);
});
return new CompletableFutureWrapper<>(f);
}
protected RFuture putAllOperationAsync(Map extends K, ? extends V> map) {
List params = new ArrayList<>(map.size()*2 + 1);
params.add(getRawName());
encodeMapKeys(params, map);
RFuture future = commandExecutor.writeAsync(getRawName(), codec, RedisCommands.HMSET, params.toArray());
return future;
}
@Override
public void clear() {
delete();
}
@Override
public Set keySet() {
return keySet(null);
}
@Override
public Set keySet(String pattern) {
return keySet(pattern, 10);
}
@Override
public Set keySet(String pattern, int count) {
return new KeySet(pattern, count);
}
@Override
public Set keySet(int count) {
return keySet(null, count);
}
@Override
public Collection values() {
return values(null);
}
@Override
public Collection values(String keyPattern, int count) {
return new Values(keyPattern, count);
}
@Override
public Collection values(String keyPattern) {
return values(keyPattern, 10);
}
@Override
public Collection values(int count) {
return values(null, count);
}
@Override
public Set> entrySet() {
return entrySet(null);
}
@Override
public Set> entrySet(String keyPattern) {
return entrySet(keyPattern, 10);
}
@Override
public Set> entrySet(String keyPattern, int count) {
return new EntrySet(keyPattern, count);
}
@Override
public Set> entrySet(int count) {
return entrySet(null, count);
}
@Override
public Set readAllKeySet() {
return get(readAllKeySetAsync());
}
@Override
public RFuture> readAllKeySetAsync() {
return commandExecutor.readAsync(getRawName(), codec, RedisCommands.HKEYS, getRawName());
}
@Override
public Collection readAllValues() {
return get(readAllValuesAsync());
}
@Override
public RFuture> readAllValuesAsync() {
return commandExecutor.readAsync(getRawName(), codec, RedisCommands.HVALS, getRawName());
}
@Override
public Set> readAllEntrySet() {
return get(readAllEntrySetAsync());
}
@Override
public RFuture>> readAllEntrySetAsync() {
return commandExecutor.readAsync(getRawName(), codec, RedisCommands.HGETALL_ENTRY, getRawName());
}
@Override
public Map readAllMap() {
return get(readAllMapAsync());
}
@Override
public RFuture> readAllMapAsync() {
return commandExecutor.readAsync(getRawName(), codec, RedisCommands.HGETALL, getRawName());
}
@Override
public V putIfExists(K key, V value) {
return get(putIfExistsAsync(key, value));
}
@Override
public RFuture putIfExistsAsync(K key, V value) {
checkKey(key);
checkValue(value);
RFuture future = putIfExistsOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
MapWriterTask.Add task = new MapWriterTask.Add(key, value);
return mapWriterFuture(future, task, Objects::nonNull);
}
protected RFuture putIfExistsOperationAsync(K key, V value) {
String name = getRawName(key);
return commandExecutor.evalWriteAsync(name, codec, RedisCommands.EVAL_MAP_VALUE,
"local value = redis.call('hget', KEYS[1], ARGV[1]); "
+ "if value ~= false then "
+ "redis.call('hset', KEYS[1], ARGV[1], ARGV[2]); "
+ "return value; "
+ "end; "
+ "return nil; ",
Collections.singletonList(name), encodeMapKey(key), encodeMapValue(value));
}
@Override
public V putIfAbsent(K key, V value) {
return get(putIfAbsentAsync(key, value));
}
@Override
public RFuture putIfAbsentAsync(K key, V value) {
checkKey(key);
checkValue(value);
RFuture future = putIfAbsentOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
MapWriterTask.Add task = new MapWriterTask.Add(key, value);
return mapWriterFuture(future, task, Objects::isNull);
}
protected boolean hasNoWriter() {
return options == null || (options.getWriter() == null && options.getWriterAsync() == null);
}
protected RFuture putIfAbsentOperationAsync(K key, V value) {
String name = getRawName(key);
return commandExecutor.evalWriteNoRetryAsync(name, codec, RedisCommands.EVAL_MAP_VALUE,
"if redis.call('hsetnx', KEYS[1], ARGV[1], ARGV[2]) == 1 then "
+ "return nil "
+ "else "
+ "return redis.call('hget', KEYS[1], ARGV[1]) "
+ "end",
Collections.singletonList(name), encodeMapKey(key), encodeMapValue(value));
}
@Override
public boolean fastPutIfAbsent(K key, V value) {
return get(fastPutIfAbsentAsync(key, value));
}
@Override
public RFuture fastPutIfAbsentAsync(K key, V value) {
checkKey(key);
checkValue(value);
RFuture future = fastPutIfAbsentOperationAsync(key, value);
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) {
String name = getRawName(key);
return commandExecutor.writeAsync(name, codec, RedisCommands.HSETNX, name, encodeMapKey(key), encodeMapValue(value));
}
@Override
public boolean fastPutIfExists(K key, V value) {
return get(fastPutIfExistsAsync(key, value));
}
@Override
public RFuture fastPutIfExistsAsync(K key, V value) {
checkKey(key);
checkValue(value);
RFuture future = fastPutIfExistsOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
MapWriterTask.Add task = new MapWriterTask.Add(key, value);
return mapWriterFuture(future, task, Function.identity());
}
protected RFuture fastPutIfExistsOperationAsync(K key, V value) {
String name = getRawName(key);
return commandExecutor.evalWriteAsync(name, codec, RedisCommands.EVAL_BOOLEAN,
"local value = redis.call('hget', KEYS[1], ARGV[1]); "
+ "if value ~= false then "
+ "redis.call('hset', KEYS[1], ARGV[1], ARGV[2]); "
+ "return 1; "
+ "end; "
+ "return 0; ",
Collections.singletonList(name),
encodeMapKey(key), encodeMapValue(value));
}
@Override
public boolean remove(Object key, Object value) {
return get(removeAsync(key, value));
}
@Override
public RFuture removeAsync(Object key, Object value) {
checkKey(key);
checkValue(value);
RFuture future = removeOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
MapWriterTask.Remove listener = new MapWriterTask.Remove(key);
return mapWriterFuture(future, listener, Function.identity());
}
protected RFuture removeOperationAsync(Object key, Object value) {
String name = getRawName(key);
RFuture future = commandExecutor.evalWriteAsync(name, codec, RedisCommands.EVAL_BOOLEAN,
"if redis.call('hget', KEYS[1], ARGV[1]) == ARGV[2] then "
+ "return redis.call('hdel', KEYS[1], ARGV[1]) "
+ "else "
+ "return 0 "
+ "end",
Collections.singletonList(name), encodeMapKey(key), encodeMapValue(value));
return future;
}
protected void checkValue(Object value) {
if (value == null) {
throw new NullPointerException("map value can't be null");
}
}
protected void encodeMapKeys(Collection params, Map, ?> map) {
try {
for (java.util.Map.Entry, ?> t : map.entrySet()) {
checkKey(t.getKey());
checkValue(t.getValue());
params.add(encodeMapKey(t.getKey()));
params.add(encodeMapValue(t.getValue()));
}
} catch (Exception e) {
params.forEach(v -> {
ReferenceCountUtil.safeRelease(v);
});
throw e;
}
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
return get(replaceAsync(key, oldValue, newValue));
}
@Override
public RFuture replaceAsync(K key, V oldValue, V newValue) {
checkKey(key);
if (oldValue == null) {
throw new NullPointerException("map oldValue can't be null");
}
if (newValue == null) {
throw new NullPointerException("map newValue can't be null");
}
RFuture future = replaceOperationAsync(key, oldValue, newValue);
if (hasNoWriter()) {
return future;
}
MapWriterTask.Add task = new MapWriterTask.Add(key, newValue);
return mapWriterFuture(future, task, Function.identity());
}
protected RFuture replaceOperationAsync(K key, V oldValue, V newValue) {
String name = getRawName(key);
return commandExecutor.evalWriteAsync(name, codec, RedisCommands.EVAL_BOOLEAN,
"if redis.call('hget', KEYS[1], ARGV[1]) == ARGV[2] then "
+ "redis.call('hset', KEYS[1], ARGV[1], ARGV[3]); "
+ "return 1; "
+ "else "
+ "return 0; "
+ "end",
Collections.singletonList(name), encodeMapKey(key), encodeMapValue(oldValue), encodeMapValue(newValue));
}
@Override
public V replace(K key, V value) {
return get(replaceAsync(key, value));
}
@Override
public RFuture replaceAsync(K key, V value) {
checkKey(key);
checkValue(value);
RFuture future = replaceOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
MapWriterTask.Add task = new MapWriterTask.Add(key, value);
return mapWriterFuture(future, task, r -> r != null);
}
protected RFuture replaceOperationAsync(K key, V value) {
String name = getRawName(key);
return commandExecutor.evalWriteAsync(name, codec, RedisCommands.EVAL_MAP_VALUE,
"if redis.call('hexists', KEYS[1], ARGV[1]) == 1 then "
+ "local v = redis.call('hget', KEYS[1], ARGV[1]); "
+ "redis.call('hset', KEYS[1], ARGV[1], ARGV[2]); "
+ "return v; "
+ "else "
+ "return nil; "
+ "end",
Collections.singletonList(name), encodeMapKey(key), encodeMapValue(value));
}
@Override
public boolean fastReplace(K key, V value) {
return get(fastReplaceAsync(key, value));
}
@Override
public RFuture fastReplaceAsync(K key, V value) {
checkKey(key);
checkValue(value);
RFuture future = fastReplaceOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
MapWriterTask.Add task = new MapWriterTask.Add(key, value);
return mapWriterFuture(future, task, Function.identity());
}
protected RFuture fastReplaceOperationAsync(K key, V value) {
String name = getRawName(key);
return commandExecutor.evalWriteAsync(name, codec, RedisCommands.EVAL_BOOLEAN,
"if redis.call('hexists', KEYS[1], ARGV[1]) == 1 then "
+ "redis.call('hset', KEYS[1], ARGV[1], ARGV[2]); "
+ "return 1; "
+ "else "
+ "return 0; "
+ "end",
Collections.singletonList(name), encodeMapKey(key), encodeMapValue(value));
}
public RFuture getOperationAsync(K key) {
String name = getRawName(key);
return commandExecutor.readAsync(name, codec, RedisCommands.HGET, name, encodeMapKey(key));
}
@Override
public final RFuture getAsync(K key) {
long threadId = Thread.currentThread().getId();
return getAsync(key, threadId);
}
protected RFuture getAsync(K key, long threadId) {
checkKey(key);
RFuture future = getOperationAsync(key);
if (hasNoLoader()) {
return future;
}
CompletionStage f = future.thenCompose(res -> {
if (res == null) {
return loadValue(key, false, threadId);
}
return CompletableFuture.completedFuture(res);
});
return new CompletableFutureWrapper<>(f);
}
@Override
public void loadAll(boolean replaceExistingValues, int parallelism) {
get(loadAllAsync(replaceExistingValues, parallelism));
}
@Override
public RFuture loadAllAsync(boolean replaceExistingValues, int parallelism) {
if (hasNoLoader()) {
throw new NullPointerException("MapLoader isn't defined");
}
if (options.getLoaderAsync() != null) {
return loadAllAsync(options.getLoaderAsync().loadAllKeys(), replaceExistingValues, parallelism);
}
return loadAllAsync(() -> options.getLoader().loadAllKeys().spliterator(), replaceExistingValues, parallelism);
}
RFuture loadAllAsync(AsyncIterator iterator, boolean replaceExistingValues, int parallelism) {
CompletionStage> f = loadAllAsync(iterator, new ArrayList<>(), new AtomicInteger(parallelism));
CompletionStage ff = f.thenCompose(elements -> {
List> futures = new ArrayList<>(elements.size());
for (K k : elements) {
CompletableFuture vFuture = loadValue(k, replaceExistingValues);
futures.add(vFuture);
}
CompletableFuture finalFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
if (elements.size() < parallelism) {
return finalFuture;
}
return finalFuture
.thenCompose(v -> loadAllAsync(iterator, replaceExistingValues, parallelism));
});
return new CompletableFutureWrapper<>(ff);
}
CompletionStage> loadAllAsync(AsyncIterator iterator, List elements, AtomicInteger workers) {
return iterator.hasNext()
.thenCompose(v -> {
int s = workers.decrementAndGet();
if (v) {
return iterator.next().thenCompose(k -> {
if (k != null) {
elements.add(k);
}
if (s > 0) {
return loadAllAsync(iterator, elements, workers);
}
return CompletableFuture.completedFuture(elements);
});
}
return CompletableFuture.completedFuture(elements);
});
}
private RFuture loadAllAsync(Supplier> supplier, boolean replaceExistingValues, int parallelism) {
ForkJoinPool customThreadPool = new ForkJoinPool(parallelism);
CompletableFuture result = new CompletableFuture<>();
customThreadPool.submit(() -> {
try {
Stream s = StreamSupport.stream(supplier.get(), true);
List> r = s.filter(k -> k != null)
.map(k -> {
return loadValue(k, replaceExistingValues).thenApply(v -> null);
}).collect(Collectors.toList());
CompletableFuture ff = CompletableFuture.allOf(r.toArray(new CompletableFuture[0]));
ff.thenApply(v -> {
customThreadPool.shutdown();
return result.complete(v);
});
} catch (Exception e) {
result.completeExceptionally(e);
}
});
return new CompletableFutureWrapper<>(result);
}
protected CompletionStage> loadAllMapAsync(Spliterator spliterator, boolean replaceExistingValues, int parallelism) {
ForkJoinPool customThreadPool = new ForkJoinPool(parallelism);
ConcurrentMap map = new ConcurrentHashMap<>();
CompletableFuture> result = new CompletableFuture<>();
customThreadPool.submit(() -> {
try {
Stream s = StreamSupport.stream(spliterator, true);
List> r = s.filter(k -> k != null)
.map(k -> {
return loadValue(k, replaceExistingValues)
.thenAccept(v -> {
if (v != null) {
map.put(k, v);
}
});
}).collect(Collectors.toList());
CompletableFuture ff = CompletableFuture.allOf(r.toArray(new CompletableFuture[0]));
ff.whenComplete((v, e) -> {
customThreadPool.shutdown();
if (e != null) {
result.completeExceptionally(e);
return;
}
result.complete(map);
});
} catch (Exception e) {
result.completeExceptionally(e);
}
});
return result;
}
@Override
public void loadAll(Set extends K> keys, boolean replaceExistingValues, int parallelism) {
get(loadAllAsync(keys, replaceExistingValues, parallelism));
}
@Override
public RFuture loadAllAsync(Set extends K> keys, boolean replaceExistingValues, int parallelism) {
return loadAllAsync(() -> (Spliterator) keys.spliterator(), replaceExistingValues, parallelism);
}
@Override
public RFuture putAsync(K key, V value) {
checkKey(key);
checkValue(value);
RFuture future = putOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
return mapWriterFuture(future, new MapWriterTask.Add(key, value));
}
protected RFuture putOperationAsync(K key, V value) {
String name = getRawName(key);
return commandExecutor.evalWriteAsync(name, codec, RedisCommands.EVAL_MAP_VALUE,
"local v = redis.call('hget', KEYS[1], ARGV[1]); "
+ "redis.call('hset', KEYS[1], ARGV[1], ARGV[2]); "
+ "return v",
Collections.singletonList(name), encodeMapKey(key), encodeMapValue(value));
}
@Override
public RFuture removeAsync(K key) {
checkKey(key);
RFuture future = removeOperationAsync(key);
if (hasNoWriter()) {
return future;
}
return mapWriterFuture(future, new MapWriterTask.Remove(key));
}
protected RFuture removeOperationAsync(K key) {
String name = getRawName(key);
return commandExecutor.evalWriteAsync(name, codec, RedisCommands.EVAL_MAP_VALUE,
"local v = redis.call('hget', KEYS[1], ARGV[1]); "
+ "redis.call('hdel', KEYS[1], ARGV[1]); "
+ "return v",
Collections.singletonList(name), encodeMapKey(key));
}
@Override
public RFuture fastPutAsync(K key, V value) {
checkKey(key);
checkValue(value);
RFuture future = fastPutOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
return mapWriterFuture(future, new MapWriterTask.Add(key, value));
}
protected RFuture fastPutOperationAsync(K key, V value) {
String name = getRawName(key);
return commandExecutor.writeAsync(name, codec, RedisCommands.HSET, name, encodeMapKey(key), encodeMapValue(value));
}
@Override
public boolean fastPut(K key, V value) {
return get(fastPutAsync(key, value));
}
@Override
public RFuture fastRemoveAsync(K... keys) {
if (keys == null) {
throw new NullPointerException();
}
if (keys.length == 0) {
return new CompletableFutureWrapper<>(0L);
}
if (hasNoWriter()) {
return fastRemoveOperationAsync(keys);
}
RFuture> removeFuture = fastRemoveOperationBatchAsync(keys);
CompletionStage f = removeFuture.thenCompose(res -> {
if (res.isEmpty()) {
return CompletableFuture.completedFuture(0L);
}
List deletedKeys = new ArrayList();
for (int i = 0; i < res.size(); i++) {
if (res.get(i) == 1) {
deletedKeys.add(keys[i]);
}
}
if (options.getWriteMode() == WriteMode.WRITE_BEHIND) {
MapWriterTask.Remove task = new MapWriterTask.Remove(deletedKeys);
writeBehindTask.addTask(task);
return CompletableFuture.completedFuture((long) deletedKeys.size());
} else {
if (options.getWriter() != null) {
return CompletableFuture.runAsync(() -> {
options.getWriter().delete(deletedKeys);
}, getServiceManager().getExecutor())
.thenApply(r -> (long) deletedKeys.size());
}
return options.getWriterAsync().delete(deletedKeys)
.thenApply(r -> (long) deletedKeys.size());
}
});
return new CompletableFutureWrapper<>(f);
}
protected RFuture> fastRemoveOperationBatchAsync(K... keys) {
List args = new ArrayList<>(keys.length);
encodeMapKeys(args, Arrays.asList(keys));
RFuture> future = commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_LIST,
"local result = {}; " +
"for i = 1, #ARGV, 1 do "
+ "local val = redis.call('hdel', KEYS[1], ARGV[i]); "
+ "table.insert(result, val); "
+ "end;"
+ "return result;",
Arrays.asList(getRawName()),
args.toArray());
return future;
}
protected RFuture fastRemoveOperationAsync(K... keys) {
List args = new ArrayList<>(keys.length + 1);
args.add(getRawName());
encodeMapKeys(args, Arrays.asList(keys));
return commandExecutor.writeAsync(getRawName(), codec, RedisCommands.HDEL, args.toArray());
}
@Override
public long fastRemove(K... keys) {
return get(fastRemoveAsync(keys));
}
public ScanResult> scanIterator(String name, RedisClient client, String startPos, String pattern, int count) {
RFuture>> f = scanIteratorAsync(name, client, startPos, pattern, count);
return get(f);
}
public ScanResult scanKeyIterator(String name, RedisClient client, String startPos, String pattern, int count) {
RFuture> f = scanKeyIteratorAsync(name, client, startPos, pattern, count);
return get(f);
}
public RFuture> scanKeyIteratorAsync(String name, RedisClient client, String startPos, String pattern, int count) {
List params = new ArrayList<>();
params.add(startPos);
if (pattern != null) {
params.add(pattern);
}
params.add(count);
RedisCommand> evalScan = new RedisCommand>("EVAL",
new ListMultiDecoder2(new ListScanResultReplayDecoder(), new ObjectDecoder<>(codec.getMapKeyDecoder())));
return commandExecutor.evalReadAsync(client, name, codec, evalScan,
"local result = {}; "
+ "local res; "
+ "if (#ARGV == 3) then "
+ " res = redis.call('hscan', KEYS[1], ARGV[1], 'match', ARGV[2], 'count', ARGV[3]); "
+ "else "
+ " res = redis.call('hscan', KEYS[1], ARGV[1], 'count', ARGV[2]); "
+ "end;"
+ "for i, value in ipairs(res[2]) do "
+ "if i % 2 ~= 0 then "
+ "local key = res[2][i]; "
+ "table.insert(result, key); "
+ "end; "
+ "end;"
+ "return {res[1], result};",
Arrays.asList(name),
params.toArray());
}
public RFuture>> scanIteratorAsync(String name, RedisClient client, String startPos, String pattern, int count) {
if (pattern == null) {
RFuture>> f
= commandExecutor.readAsync(client, name, codec, RedisCommands.HSCAN, name, startPos, "COUNT", count);
return f;
}
RFuture>> f
= commandExecutor.readAsync(client, name, codec, RedisCommands.HSCAN, name, startPos, "MATCH", pattern, "COUNT", count);
return f;
}
@Override
public V addAndGet(K key, Number value) {
return get(addAndGetAsync(key, value));
}
@Override
public RFuture addAndGetAsync(K key, Number value) {
checkKey(key);
checkValue(value);
RFuture future = addAndGetOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
return mapWriterFuture(future, new MapWriterTask.Add() {
@Override
public Map getMap() {
return Collections.singletonMap(key, commandExecutor.getNow(future.toCompletableFuture()));
}
});
}
protected RFuture addAndGetOperationAsync(K key, Number value) {
ByteBuf keyState = encodeMapKey(key);
String name = getRawName(key);
RFuture future = commandExecutor.writeAsync(name, StringCodec.INSTANCE,
new RedisCommand<>("HINCRBYFLOAT", new NumberConvertor(value.getClass())),
name, keyState, new BigDecimal(value.toString()).toPlainString());
return future;
}
@Override
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Map))
return false;
Map, ?> m = (Map, ?>) o;
if (m.size() != size())
return false;
try {
Iterator> i = entrySet().iterator();
while (i.hasNext()) {
Entry e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
return true;
}
@Override
public int hashCode() {
int h = 0;
Iterator> i = entrySet().iterator();
while (i.hasNext()) {
h += i.next().hashCode();
}
return h;
}
protected Iterator keyIterator(String pattern, int count) {
return new RedissonMapKeyIterator(RedissonMap.this, pattern, count);
}
final class KeySet extends AbstractSet {
private final String pattern;
private final int count;
KeySet(String pattern, int count) {
this.pattern = pattern;
this.count = count;
}
@Override
public boolean isEmpty() {
return !iterator().hasNext();
}
@Override
public Iterator iterator() {
return keyIterator(pattern, count);
}
@Override
public boolean contains(Object o) {
return RedissonMap.this.containsKey(o);
}
@Override
public boolean remove(Object o) {
return RedissonMap.this.fastRemove((K) o) == 1;
}
@Override
public int size() {
if (pattern != null) {
int size = 0;
for (K val : this) {
size++;
}
return size;
}
return RedissonMap.this.size();
}
@Override
public void clear() {
RedissonMap.this.clear();
}
}
protected Iterator valueIterator(String pattern, int count) {
return new RedissonMapIterator(RedissonMap.this, pattern, count) {
@Override
protected V getValue(java.util.Map.Entry entry) {
return (V) entry.getValue();
}
};
}
final class Values extends AbstractCollection {
private final String keyPattern;
private final int count;
Values(String keyPattern, int count) {
this.keyPattern = keyPattern;
this.count = count;
}
@Override
public boolean isEmpty() {
return !iterator().hasNext();
}
@Override
public Iterator iterator() {
return valueIterator(keyPattern, count);
}
@Override
public boolean contains(Object o) {
return RedissonMap.this.containsValue(o);
}
@Override
public int size() {
if (keyPattern != null) {
int size = 0;
for (V val : this) {
size++;
}
return size;
}
return RedissonMap.this.size();
}
@Override
public void clear() {
RedissonMap.this.clear();
}
}
protected Iterator> entryIterator(String pattern, int count) {
return new RedissonMapIterator<>(RedissonMap.this, pattern, count);
}
protected CompletableFuture loadValue(K key, boolean replaceValue) {
return loadValue(key, replaceValue, Thread.currentThread().getId());
}
protected CompletableFuture loadValue(K key, boolean replaceValue, long threadId) {
RLock lock = getLock(key);
return lock.lockAsync(threadId).thenCompose(res -> {
if (replaceValue) {
return loadValue(key, lock, threadId);
}
return getOperationAsync(key).thenCompose(r -> {
if (r != null) {
return lock.unlockAsync(threadId).thenApply(v -> r);
}
return loadValue(key, lock, threadId);
});
}).whenComplete((r, e) -> {
if (e != null) {
lock.unlockAsync(threadId);
}
}).toCompletableFuture();
}
private CompletableFuture loadValue(K key, RLock lock, long threadId) {
if (options.getLoader() != null) {
return CompletableFuture
.supplyAsync(() -> options.getLoader().load(key), getServiceManager().getExecutor())
.thenCompose(value -> {
if (value != null) {
return putOperationAsync(key, value)
.thenApply(r -> value);
}
return CompletableFuture.completedFuture(null);
})
.whenComplete((r, e) -> {
lock.unlockAsync(threadId);
}).exceptionally(e -> null);
}
CompletionStage valueFuture = options.getLoaderAsync().load(key);
return valueFuture.handle((r, ex) -> {
if (r == null) {
return lock.unlockAsync(threadId);
}
if (ex != null) {
log.error("Unable to load value by key {} for map {}", key, getRawName(), ex);
return lock.unlockAsync(threadId);
}
return valueFuture;
}).thenCompose(f -> f)
.thenCompose(value -> {
if (value != null) {
return (CompletionStage) putOperationAsync(key, (V) value).handle((r, ex) -> {
RFuture f = lock.unlockAsync(threadId);
if (ex != null) {
log.error("Unable to store value by key {} for map {}", key, getRawName(), ex);
return f;
}
return f.thenApply(res -> value);
}).thenCompose(f -> f);
}
return CompletableFuture.completedFuture((V) value);
}).toCompletableFuture();
}
final class EntrySet extends AbstractSet> {
private final String keyPattern;
private final int count;
EntrySet(String keyPattern, int count) {
this.keyPattern = keyPattern;
this.count = count;
}
@Override
public Iterator> iterator() {
return entryIterator(keyPattern, count);
}
@Override
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry, ?> e = (Map.Entry, ?>) o;
Object key = e.getKey();
V value = get(key);
return value != null && value.equals(e);
}
@Override
public boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry, ?> e = (Map.Entry, ?>) o;
Object key = e.getKey();
Object value = e.getValue();
return RedissonMap.this.remove(key, value);
}
return false;
}
@Override
public int size() {
if (keyPattern != null) {
int size = 0;
for (Entry val : this) {
size++;
}
return size;
}
return RedissonMap.this.size();
}
@Override
public void clear() {
RedissonMap.this.clear();
}
}
@Override
public RFuture clearAsync() {
return deleteAsync();
}
@Override
public void destroy() {
if (writeBehindService != null) {
writeBehindService.stop(getRawName());
}
removeListeners();
}
@Override
public int addListener(ObjectListener listener) {
if (listener instanceof MapPutListener) {
return addListener("__keyevent@*:hset", (MapPutListener) listener, MapPutListener::onPut);
}
if (listener instanceof MapRemoveListener) {
return addListener("__keyevent@*:hdel", (MapRemoveListener) listener, MapRemoveListener::onRemove);
}
if (listener instanceof TrackingListener) {
return addTrackingListener((TrackingListener) listener);
}
return super.addListener(listener);
}
@Override
public RFuture addListenerAsync(ObjectListener listener) {
if (listener instanceof MapPutListener) {
return addListenerAsync("__keyevent@*:hset", (MapPutListener) listener, MapPutListener::onPut);
}
if (listener instanceof MapRemoveListener) {
return addListenerAsync("__keyevent@*:hdel", (MapRemoveListener) listener, MapRemoveListener::onRemove);
}
if (listener instanceof TrackingListener) {
return addTrackingListenerAsync((TrackingListener) listener);
}
return super.addListenerAsync(listener);
}
@Override
public void removeListener(int listenerId) {
removeTrackingListener(listenerId);
removeListener(listenerId, "__keyevent@*:hset", "__keyevent@*:hdel");
super.removeListener(listenerId);
}
@Override
public RFuture removeListenerAsync(int listenerId) {
RFuture f1 = removeTrackingListenerAsync(listenerId);
RFuture f2 = removeListenerAsync(listenerId, "__keyevent@*:hset", "__keyevent@*:hdel");
return new CompletableFutureWrapper<>(CompletableFuture.allOf(f1.toCompletableFuture(), f2.toCompletableFuture()));
}
}