com.github.lontime.shaded.org.redisson.RedissonMap Maven / Gradle / Ivy
The newest version!
/**
* Copyright (c) 2013-2021 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 com.github.lontime.shaded.org.redisson;
import io.netty.buffer.ByteBuf;
import com.github.lontime.shaded.org.redisson.api.*;
import com.github.lontime.shaded.org.redisson.api.MapOptions.WriteMode;
import com.github.lontime.shaded.org.redisson.api.mapreduce.RMapReduce;
import com.github.lontime.shaded.org.redisson.client.RedisClient;
import com.github.lontime.shaded.org.redisson.client.codec.Codec;
import com.github.lontime.shaded.org.redisson.client.codec.LongCodec;
import com.github.lontime.shaded.org.redisson.client.codec.StringCodec;
import com.github.lontime.shaded.org.redisson.client.protocol.RedisCommand;
import com.github.lontime.shaded.org.redisson.client.protocol.RedisCommands;
import com.github.lontime.shaded.org.redisson.client.protocol.convertor.NumberConvertor;
import com.github.lontime.shaded.org.redisson.client.protocol.decoder.MapValueDecoder;
import com.github.lontime.shaded.org.redisson.command.CommandAsyncExecutor;
import com.github.lontime.shaded.org.redisson.command.CommandBatchService;
import com.github.lontime.shaded.org.redisson.connection.decoder.MapGetAllDecoder;
import com.github.lontime.shaded.org.redisson.iterator.RedissonMapIterator;
import com.github.lontime.shaded.org.redisson.mapreduce.RedissonMapReduce;
import com.github.lontime.shaded.org.redisson.misc.CompletableFutureWrapper;
import com.github.lontime.shaded.org.redisson.reactive.CommandReactiveBatchService;
import com.github.lontime.shaded.org.redisson.rx.CommandRxBatchService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* 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) {
this.writeBehindService = writeBehindService;
writeBehindTask = writeBehindService.start(name, options);
} else {
this.writeBehindService = null;
writeBehindTask = null;
}
}
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) {
this.writeBehindService = writeBehindService;
writeBehindTask = writeBehindService.start(name, options);
} else {
this.writeBehindService = null;
writeBehindTask = null;
}
}
@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) {
if (commandExecutor instanceof CommandBatchService
|| commandExecutor instanceof CommandReactiveBatchService
|| commandExecutor instanceof CommandRxBatchService) {
throw new IllegalStateException("This method doesn't work in batch mode.");
}
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) {
if (commandExecutor instanceof CommandBatchService
|| commandExecutor instanceof CommandReactiveBatchService
|| commandExecutor instanceof CommandRxBatchService) {
throw new IllegalStateException("This method doesn't work in batch mode.");
}
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);
return oldValueFuture.thenCompose(oldValue -> {
CompletableFuture newValuePromise = new CompletableFuture<>();
if (oldValue != null) {
commandExecutor.getConnectionManager().getExecutor().execute(() -> {
V newValue;
try {
newValue = remappingFunction.apply(oldValue, value);
} catch (Exception exception) {
newValuePromise.completeExceptionally(exception);
return;
}
newValuePromise.complete(newValue);
});
} else {
newValuePromise.complete(value);
}
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) {
if (commandExecutor instanceof CommandBatchService
|| commandExecutor instanceof CommandReactiveBatchService
|| commandExecutor instanceof CommandRxBatchService) {
throw new IllegalStateException("This method doesn't work in batch mode.");
}
checkKey(key);
Objects.requireNonNull(remappingFunction);
RLock lock = getLock(key);
long threadId = Thread.currentThread().getId();
CompletionStage f = lock.lockAsync(threadId)
.thenCompose(r -> {
RFuture oldValueFuture = getAsync(key);
return oldValueFuture.thenCompose(oldValue -> {
CompletableFuture result = new CompletableFuture<>();
commandExecutor.getConnectionManager().getExecutor().execute(() -> {
V newValue;
try {
newValue = remappingFunction.apply(key, oldValue);
} catch (Exception exception) {
result.completeExceptionally(exception);
return;
}
if (newValue == null) {
if (oldValue != null) {
fastRemoveAsync(key).whenComplete((res, exc) -> {
if (exc != null) {
result.completeExceptionally(exc);
return;
}
result.complete(newValue);
});
return;
}
} else {
fastPutAsync(key, newValue).whenComplete((res, exc) -> {
if (exc != null) {
result.completeExceptionally(exc);
return;
}
result.complete(newValue);
});
return;
}
result.complete(newValue);
});
return result;
}).whenComplete((c, e) -> {
lock.unlockAsync(threadId);
});
});
return new CompletableFutureWrapper<>(f);
}
@Override
public V compute(K key, BiFunction super K, ? super V, ? extends V> remappingFunction) {
if (commandExecutor instanceof CommandBatchService
|| commandExecutor instanceof CommandReactiveBatchService
|| commandExecutor instanceof CommandRxBatchService) {
throw new IllegalStateException("This method doesn't work in batch mode.");
}
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) {
if (commandExecutor instanceof CommandBatchService
|| commandExecutor instanceof CommandReactiveBatchService
|| commandExecutor instanceof CommandRxBatchService) {
throw new IllegalStateException("This method doesn't work in batch mode.");
}
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);
return oldValueFuture.thenCompose(oldValue -> {
if (oldValue != null) {
return CompletableFuture.completedFuture(oldValue);
}
CompletableFuture result = new CompletableFuture<>();
commandExecutor.getConnectionManager().getExecutor().execute(() -> {
V newValue;
try {
newValue = mappingFunction.apply(key);
} catch (Exception exception) {
result.completeExceptionally(exception);
return;
}
if (newValue != null) {
fastPutAsync(key, newValue).thenAccept(res -> {
result.complete(newValue);
});
return;
}
result.complete(null);
});
return result;
}).whenComplete((c, e) -> {
lock.unlockAsync(threadId);
});
});
return new CompletableFutureWrapper<>(f);
}
@Override
public V computeIfAbsent(K key, Function super K, ? extends V> mappingFunction) {
if (commandExecutor instanceof CommandBatchService
|| commandExecutor instanceof CommandReactiveBatchService
|| commandExecutor instanceof CommandRxBatchService) {
throw new IllegalStateException("This method doesn't work in batch mode.");
}
checkKey(key);
Objects.requireNonNull(mappingFunction);
RLock lock = getLock(key);
lock.lock();
try {
V 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) {
if (commandExecutor instanceof CommandBatchService
|| commandExecutor instanceof CommandReactiveBatchService
|| commandExecutor instanceof CommandRxBatchService) {
throw new IllegalStateException("This method doesn't work in batch mode.");
}
checkKey(key);
Objects.requireNonNull(remappingFunction);
RLock lock = getLock(key);
long threadId = Thread.currentThread().getId();
CompletionStage f = lock.lockAsync(threadId)
.thenCompose(r -> {
RFuture oldValueFuture = getAsync(key);
return oldValueFuture.thenCompose(oldValue -> {
if (oldValue == null) {
return CompletableFuture.completedFuture(null);
}
CompletableFuture result = new CompletableFuture<>();
commandExecutor.getConnectionManager().getExecutor().execute(() -> {
V newValue;
try {
newValue = remappingFunction.apply(key, oldValue);
} catch (Exception exception) {
result.completeExceptionally(exception);
return;
}
if (newValue != null) {
RFuture fastPutFuture = fastPutAsync(key, newValue);
fastPutFuture.whenComplete((re, ex1) -> {
if (ex1 != null) {
result.completeExceptionally(ex1);
return;
}
result.complete(newValue);
});
} else {
RFuture removeFuture = fastRemoveAsync(key);
removeFuture.whenComplete((re, ex1) -> {
if (ex1 != null) {
result.completeExceptionally(ex1);
return;
}
result.complete(null);
});
}
});
return result;
}).whenComplete((c, e) -> {
lock.unlockAsync(threadId);
});
});
return new CompletableFutureWrapper<>(f);
}
@Override
public V computeIfPresent(K key, BiFunction super K, ? super V, ? extends V> remappingFunction) {
if (commandExecutor instanceof CommandBatchService
|| commandExecutor instanceof CommandReactiveBatchService
|| commandExecutor instanceof CommandRxBatchService) {
throw new IllegalStateException("This method doesn't work in batch mode.");
}
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);
}
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) {
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